kubemq-js
Version:
kubemq js/ts library for KubeMQ Message Broker
289 lines • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueuesMessagesPulledResponse = exports.QueuesPollRequest = exports.QueueMessageReceived = void 0;
const pb = require("../protos");
const uuid_1 = require("uuid");
class QueueMessageReceived {
constructor(id, channel, metadata, body, fromClientId, tags, timestamp, sequence, receiveCount, isReRouted, reRouteFromQueue, expiredAt, delayedTo, transactionId, isTransactionCompleted, responseHandler, receiverClientId, visibilitySeconds, isAutoAcked) {
this.messageCompleted = false;
this.timerExpired = false;
this.id = id;
this.channel = channel;
this.metadata = metadata;
this.body = body;
this.fromClientId = fromClientId;
this.tags = tags;
this.timestamp = timestamp;
this.sequence = sequence;
this.receiveCount = receiveCount;
this.isReRouted = isReRouted;
this.reRouteFromQueue = reRouteFromQueue;
this.expiredAt = expiredAt;
this.delayedTo = delayedTo;
this.transactionId = transactionId;
this.isTransactionCompleted = isTransactionCompleted;
this.responseHandler = responseHandler;
this.receiverClientId = receiverClientId;
this.visibilitySeconds = visibilitySeconds;
this.isAutoAcked = isAutoAcked;
if (this.visibilitySeconds > 0) {
this.startVisibilityTimer();
}
}
startVisibilityTimer() {
if (this.visibilitySeconds > 0 &&
!this.timerExpired &&
!this.messageCompleted) {
this.visibilityTimer = setTimeout(() => this.onVisibilityExpired(), this.visibilitySeconds * 1000);
}
}
onVisibilityExpired() {
this.timerExpired = true;
this.clearVisibilityTimer();
this.reject().catch((err) => {
console.error('Visibility expired, failed to reject message:', err);
});
}
extendVisibilityTimer(additionalSeconds) {
if (additionalSeconds <= 0)
throw new Error('Additional seconds must be greater than 0');
if (!this.visibilityTimer)
throw new Error('Cannot extend, timer not active');
if (this.timerExpired)
throw new Error('Cannot extend, timer has expired');
if (this.messageCompleted)
throw new Error('Message transaction is already completed');
clearTimeout(this.visibilityTimer);
this.visibilitySeconds += additionalSeconds;
this.startVisibilityTimer();
}
ack() {
if (this.messageCompleted || this.isTransactionCompleted) {
return Promise.reject(new Error('Transaction is already completed'));
}
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
Channel: this.channel,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.AckRange,
RefTransactionId: this.transactionId,
SequenceRange: [this.sequence],
});
return this.writeToStream(request);
}
reject() {
if (this.messageCompleted || this.isTransactionCompleted) {
return Promise.reject(new Error('Transaction is already completed'));
}
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
Channel: this.channel,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.NAckRange,
RefTransactionId: this.transactionId,
SequenceRange: [this.sequence],
});
return this.writeToStream(request);
}
reQueue(newChannel) {
if (!newChannel)
throw new Error('Re-queue channel cannot be empty');
if (this.messageCompleted || this.isTransactionCompleted) {
return Promise.reject(new Error('Transaction is already completed'));
}
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
Channel: this.channel,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.ReQueueRange,
RefTransactionId: this.transactionId,
SequenceRange: [this.sequence],
ReQueueChannel: newChannel,
});
return this.writeToStream(request);
}
async writeToStream(request) {
return new Promise((resolve, reject) => {
const success = this.responseHandler.write(request, (err) => {
if (err) {
reject(err);
}
else {
this.markTransactionCompleted();
resolve();
}
});
if (!success) {
this.responseHandler.once('drain', () => resolve());
}
});
}
markTransactionCompleted() {
this.messageCompleted = true;
this.isTransactionCompleted = true;
this.clearVisibilityTimer();
}
clearVisibilityTimer() {
if (this.visibilityTimer) {
clearTimeout(this.visibilityTimer);
this.visibilityTimer = undefined;
}
}
static decode(message, transactionId, transactionIsCompleted, receiverClientId, responseHandler, visibilitySeconds, isAutoAcked) {
return new QueueMessageReceived(message.MessageID, message.Channel, message.Metadata, typeof message.Body === 'string'
? new TextEncoder().encode(message.Body)
: message.Body, message.ClientID, new Map(Object.entries(message.Tags)), new Date(message.Attributes.Timestamp / 1000000000), message.Attributes.Sequence, message.Attributes.ReceiveCount, message.Attributes.ReRouted, message.Attributes.ReRoutedFromQueue, message.Attributes.ExpirationAt
? new Date(message.Attributes.ExpirationAt / 1000000)
: undefined, message.Attributes.DelayedTo
? new Date(message.Attributes.DelayedTo / 1000000)
: undefined, transactionId, transactionIsCompleted, responseHandler, receiverClientId, visibilitySeconds, isAutoAcked);
}
}
exports.QueueMessageReceived = QueueMessageReceived;
class QueuesPollRequest {
constructor(data) {
var _a, _b, _c, _d;
this.channel = data.channel;
this.pollMaxMessages = (_a = data.pollMaxMessages) !== null && _a !== void 0 ? _a : 1; // Default to 1 if not provided
this.pollWaitTimeoutInSeconds = (_b = data.pollWaitTimeoutInSeconds) !== null && _b !== void 0 ? _b : 60; // Default to 60 seconds if not provided
this.autoAckMessages = (_c = data.autoAckMessages) !== null && _c !== void 0 ? _c : false; // Default to false if not provided
this.visibilitySeconds = (_d = data.visibilitySeconds) !== null && _d !== void 0 ? _d : 0;
this.validate(); // Validate inputs during initialization
}
validate() {
// Channel validation
if (!this.channel || this.channel.trim() === '') {
throw new Error('Queue subscription must have a valid channel.');
}
// pollMaxMessages validation
if (this.pollMaxMessages < 1) {
throw new Error('pollMaxMessages must be greater than 0.');
}
// pollWaitTimeoutInSeconds validation
if (this.pollWaitTimeoutInSeconds < 1) {
throw new Error('pollWaitTimeoutInSeconds must be greater than 0.');
}
// visibilitySeconds validation
if (this.visibilitySeconds < 0) {
throw new Error('Visibility timeout must be a non-negative integer.');
}
// autoAckMessages and visibilitySeconds should not conflict
if (this.autoAckMessages && this.visibilitySeconds > 0) {
throw new Error('autoAckMessages and visibilitySeconds cannot be set together.');
}
}
encode(clientId) {
// Encodes the request into a protobuf message after validation
return new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(), // Generate a random UUID
ClientID: clientId,
Channel: this.channel,
MaxItems: this.pollMaxMessages,
WaitTimeout: this.pollWaitTimeoutInSeconds * 1000, // Convert seconds to milliseconds
AutoAck: this.autoAckMessages,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.Get, // Assuming this is the correct request type
});
}
// Factory method to create an instance from a plain object, ensuring proper validation
static from(data) {
return new QueuesPollRequest(data);
}
}
exports.QueuesPollRequest = QueuesPollRequest;
/**
* queue messages pull/peek response
*/
class QueuesMessagesPulledResponse {
constructor(id, messages = [], messagesReceived = 0, messagesExpired = 0, isPeek = false, isError = false, error = '', visibilitySeconds = 0, isAutoAcked = false) {
this.id = id;
this.messages = messages;
this.messagesReceived = messagesReceived;
this.messagesExpired = messagesExpired;
this.activeOffsets = [];
this.responseHandler = null;
this.receiverClientId = '';
this.transactionId = '';
this.isPeek = isPeek;
this.isError = isError;
this.error = error;
this.visibilitySeconds = visibilitySeconds;
this.isAutoAcked = isAutoAcked;
}
ackAll() {
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.AckAll,
RefTransactionId: this.transactionId,
SequenceRange: this.activeOffsets,
});
return this.writeToStream(request);
}
rejectAll() {
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.NAckAll,
RefTransactionId: this.transactionId,
SequenceRange: this.activeOffsets,
});
return this.writeToStream(request);
}
reQueueAll(newChannel) {
if (!newChannel)
throw new Error('Re-queue channel cannot be empty');
if (this.isAutoAcked) {
return Promise.reject(new Error('Auto-acked message, operations are not allowed'));
}
const request = new pb.kubemq.QueuesDownstreamRequest({
RequestID: (0, uuid_1.v4)(),
ClientID: this.receiverClientId,
RequestTypeData: pb.kubemq.QueuesDownstreamRequestType.ReQueueAll,
RefTransactionId: this.transactionId,
SequenceRange: this.activeOffsets,
ReQueueChannel: newChannel,
});
return this.writeToStream(request);
}
async writeToStream(request) {
return new Promise((resolve, reject) => {
const success = this.responseHandler.write(request, (err) => {
if (err) {
reject(err);
}
else {
this.markTransactionCompleted();
resolve();
}
});
if (!success) {
this.responseHandler.once('drain', () => resolve());
}
});
}
/**
* Loops through the messages and marks each transaction as completed.
*/
markTransactionCompleted() {
this.messages.forEach((message) => {
message.markTransactionCompleted();
});
}
}
exports.QueuesMessagesPulledResponse = QueuesMessagesPulledResponse;
//# sourceMappingURL=queuesTypes.js.map