redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
411 lines • 14 kB
JavaScript
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Message = void 0;
const cron_parser_1 = require("cron-parser");
const types_1 = require("../../../types");
const message_state_1 = require("./message-state");
const message_error_1 = require("./errors/message.error");
const direct_exchange_1 = require("../exchange/direct-exchange");
const topic_exchange_1 = require("../exchange/topic-exchange");
const fan_out_exchange_1 = require("../exchange/fan-out-exchange");
const message_exchange_required_error_1 = require("./errors/message-exchange-required.error");
class Message {
constructor() {
this.ttl = 0;
this.retryThreshold = 3;
this.retryDelay = 60000;
this.consumeTimeout = 0;
this.body = null;
this.priority = null;
this.scheduledCron = null;
this.scheduledDelay = null;
this.scheduledRepeatPeriod = null;
this.scheduledRepeat = 0;
this.messageState = null;
this.exchange = null;
this.createdAt = Date.now();
const { consumeTimeout, retryDelay, ttl, retryThreshold } = Message.defaultConsumeOptions;
this.setConsumeTimeout(consumeTimeout);
this.setRetryDelay(retryDelay);
this.setTTL(ttl);
this.setRetryThreshold(retryThreshold);
}
getMessageState() {
return this.messageState;
}
getRequiredMessageState() {
if (!this.messageState) {
throw new message_error_1.MessageError(`Expected an instance of MessageState. Probably the message has not yet been published`);
}
return this.messageState;
}
setMessageState(m) {
this.messageState = m;
return this;
}
getSetMessageState() {
if (!this.messageState) {
const m = new message_state_1.MessageState();
if (this.scheduledDelay)
m.setNextScheduledDelay(this.scheduledDelay);
this.setMessageState(m);
return m;
}
return this.messageState;
}
getPublishedAt() {
if (this.messageState) {
return this.messageState.getPublishedAt();
}
return null;
}
getScheduledAt() {
if (this.messageState) {
return this.messageState.getScheduledAt();
}
return null;
}
getId() {
if (this.messageState) {
return this.messageState.getId();
}
return null;
}
getRequiredId() {
if (!this.messageState) {
throw new message_error_1.MessageError(`Message has not yet been published`);
}
return this.messageState.getId();
}
getSetExpired() {
return this.getRequiredMessageState().getSetExpired(this.getTTL(), this.getCreatedAt());
}
setExchange(exchange) {
this.exchange = exchange;
return this;
}
setScheduledRepeatPeriod(period) {
const value = Number(period);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer value in milliseconds');
}
this.scheduledRepeatPeriod = value;
return this;
}
setScheduledDelay(delay) {
const value = Number(delay);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer value in milliseconds');
}
this.scheduledDelay = value;
return this;
}
setScheduledCRON(cron) {
(0, cron_parser_1.parseExpression)(cron);
this.scheduledCron = cron;
return this;
}
setScheduledRepeat(repeat) {
const value = Number(repeat);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer value >= 0');
}
this.scheduledRepeat = value;
return this;
}
setTTL(ttl) {
this.ttl = Message.validateTTL(ttl);
return this;
}
setConsumeTimeout(timeout) {
this.consumeTimeout = Message.validateConsumeTimeout(timeout);
return this;
}
setRetryThreshold(threshold) {
this.retryThreshold = Message.validateRetryThreshold(threshold);
return this;
}
setRetryDelay(delay) {
this.retryDelay = Message.validateRetryDelay(delay);
return this;
}
setBody(body) {
this.body = body;
return this;
}
setPriority(priority) {
if (!Object.values(Message.MessagePriority).includes(priority)) {
throw new message_error_1.MessageError('Invalid message priority.');
}
this.priority = priority;
return this;
}
setFanOut(bindingKey) {
this.exchange = new fan_out_exchange_1.FanOutExchange(bindingKey);
return this;
}
setTopic(topicParams) {
this.exchange = new topic_exchange_1.TopicExchange(topicParams);
return this;
}
setQueue(queueParams) {
this.exchange = new direct_exchange_1.DirectExchange(queueParams);
return this;
}
setDestinationQueue(queue) {
const exchange = this.getRequiredExchange();
exchange.setDestinationQueue(queue);
return this;
}
disablePriority() {
this.priority = null;
return this;
}
hasPriority() {
return this.priority !== null;
}
getQueue() {
if (this.exchange instanceof direct_exchange_1.DirectExchange) {
return this.exchange.getBindingParams();
}
return null;
}
getTopic() {
if (this.exchange instanceof topic_exchange_1.TopicExchange) {
return this.exchange.getBindingParams();
}
return null;
}
getFanOutParams() {
if (this.exchange instanceof fan_out_exchange_1.FanOutExchange) {
return this.exchange.getBindingParams();
}
return null;
}
getDestinationQueue() {
const exchange = this.getRequiredExchange();
return exchange.getRequiredDestinationQueue();
}
getPriority() {
return this.priority;
}
getBody() {
return this.body;
}
getTTL() {
return this.ttl;
}
getRetryThreshold() {
return this.retryThreshold;
}
getRetryDelay() {
return this.retryDelay;
}
getConsumeTimeout() {
return this.consumeTimeout;
}
getCreatedAt() {
return this.createdAt;
}
getScheduledRepeat() {
return this.scheduledRepeat;
}
getScheduledRepeatPeriod() {
return this.scheduledRepeatPeriod;
}
getScheduledCRON() {
return this.scheduledCron;
}
getMessageScheduledDelay() {
return this.scheduledDelay;
}
hasNextDelay() {
if (this.messageState) {
return this.messageState.hasDelay();
}
return !!this.getMessageScheduledDelay();
}
getNextScheduledTimestamp() {
if (this.isSchedulable()) {
const messageState = this.getRequiredMessageState();
const delay = messageState.getSetNextDelay();
if (delay) {
return Date.now() + delay;
}
const msgScheduledCron = this.getScheduledCRON();
const cronTimestamp = msgScheduledCron
? (0, cron_parser_1.parseExpression)(msgScheduledCron).next().getTime()
: 0;
const msgScheduledRepeat = this.getScheduledRepeat();
let repeatTimestamp = 0;
if (msgScheduledRepeat) {
const newCount = messageState.getMessageScheduledRepeatCount() + 1;
if (newCount <= msgScheduledRepeat) {
const scheduledRepeatPeriod = this.getScheduledRepeatPeriod();
const now = Date.now();
if (scheduledRepeatPeriod) {
repeatTimestamp = now + scheduledRepeatPeriod;
}
else {
repeatTimestamp = now;
}
}
}
if (repeatTimestamp && cronTimestamp) {
if (repeatTimestamp < cronTimestamp &&
messageState.hasScheduledCronFired()) {
messageState.incrMessageScheduledRepeatCount();
return repeatTimestamp;
}
}
if (cronTimestamp) {
messageState.resetMessageScheduledRepeatCount();
messageState.setMessageScheduledCronFired(true);
return cronTimestamp;
}
if (repeatTimestamp) {
messageState.incrMessageScheduledRepeatCount();
return repeatTimestamp;
}
}
return 0;
}
getExchange() {
return this.exchange;
}
getRequiredExchange() {
if (!this.exchange) {
throw new message_exchange_required_error_1.MessageExchangeRequiredError();
}
return this.exchange;
}
toString() {
return JSON.stringify(this);
}
toJSON() {
return {
createdAt: this.createdAt,
ttl: this.ttl,
retryThreshold: this.retryThreshold,
retryDelay: this.retryDelay,
consumeTimeout: this.consumeTimeout,
body: this.body,
priority: this.priority,
scheduledCron: this.scheduledCron,
scheduledDelay: this.scheduledDelay,
scheduledRepeatPeriod: this.scheduledRepeatPeriod,
scheduledRepeat: this.scheduledRepeat,
messageState: this.messageState ? this.messageState.toJSON() : null,
queue: this.exchange ? this.exchange.getDestinationQueue() : null,
exchange: this.exchange ? this.exchange.toJSON() : null,
};
}
hasRetryThresholdExceeded() {
const messageState = this.getMessageState();
if (!messageState) {
return false;
}
const threshold = this.getRetryThreshold();
return messageState.getAttempts() + 1 >= threshold;
}
isSchedulable() {
return this.hasNextDelay() || this.isPeriodic();
}
isPeriodic() {
return this.getScheduledCRON() !== null || this.getScheduledRepeat() > 0;
}
static validateRetryDelay(delay) {
const value = Number(delay);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer in milliseconds >= 0');
}
return value;
}
static validateTTL(ttl) {
const value = Number(ttl);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer value in milliseconds >= 0');
}
return value;
}
static validateConsumeTimeout(timeout) {
const value = Number(timeout);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Expected a positive integer value in milliseconds >= 0');
}
return value;
}
static validateRetryThreshold(threshold) {
const value = Number(threshold);
if (isNaN(value) || value < 0) {
throw new message_error_1.MessageError('Retry threshold should be a positive integer >= 0');
}
return value;
}
static createFromMessage(message, hardReset = false) {
const _a = typeof message === 'string' ? JSON.parse(message) : message.toJSON(), { exchange, messageState, queue } = _a, params = __rest(_a, ["exchange", "messageState", "queue"]);
const m = new Message();
Object.assign(m, params);
if (messageState) {
const meta = new message_state_1.MessageState();
if (!hardReset)
Object.assign(meta, messageState);
m.setMessageState(meta);
}
if (exchange) {
if (exchange['type'] === types_1.EExchangeType.DIRECT) {
m.setExchange(direct_exchange_1.DirectExchange.fromJSON(exchange));
}
else if (exchange['type'] === types_1.EExchangeType.FANOUT) {
m.setExchange(fan_out_exchange_1.FanOutExchange.fromJSON(exchange));
}
else {
m.setExchange(topic_exchange_1.TopicExchange.fromJSON(exchange));
}
}
return m;
}
static setDefaultConsumeOptions(consumeOptions) {
const { ttl = null, retryThreshold = null, retryDelay = null, consumeTimeout = null, } = consumeOptions;
if (ttl !== null)
Message.defaultConsumeOptions.ttl = Message.validateTTL(ttl);
if (retryDelay !== null)
Message.defaultConsumeOptions.retryDelay =
Message.validateRetryDelay(retryDelay);
if (retryThreshold !== null)
Message.defaultConsumeOptions.retryThreshold =
Message.validateRetryThreshold(retryThreshold);
if (consumeTimeout !== null)
Message.defaultConsumeOptions.consumeTimeout =
Message.validateConsumeTimeout(consumeTimeout);
}
}
exports.Message = Message;
Message.MessagePriority = {
LOWEST: 7,
VERY_LOW: 6,
LOW: 5,
NORMAL: 4,
ABOVE_NORMAL: 3,
HIGH: 2,
VERY_HIGH: 1,
HIGHEST: 0,
};
Message.defaultConsumeOptions = {
ttl: 0,
retryThreshold: 3,
retryDelay: 60000,
consumeTimeout: 0,
};
//# sourceMappingURL=message.js.map