@lykmapipo/postman
Version:
collective notifications for nodejs
1,262 lines (1,145 loc) • 29.2 kB
JavaScript
'use strict';
/**
* @module Message
* @name Message
* @description A discrete unit of communication intended by the source(sender)
* for consumption by some recipient(receiver) or group of recipients(receivers).
*
* A message may be delivered by various means(transports) including email, sms,
* push notification etc.
*
* @see {@link https://en.wikipedia.org/wiki/Message}
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @version 0.1.0
* @since 0.1.0
* @public
*/
/* @todo country data */
/* @todo transport data */
/* @todo message form alert, announcement, reminder etc */
/* @todo support recipient name */
/* dependencies */
const _ = require('lodash');
const { parallel, waterfall } = require('async');
const { hashOf, mergeObjects } = require('@lykmapipo/common');
const { getString, getBoolean } = require('@lykmapipo/env');
const {
model,
Schema,
Mixed,
ObjectId,
} = require('@lykmapipo/mongoose-common');
const actions = require('mongoose-rest-actions');
const exportable = require('@lykmapipo/mongoose-exportable');
const isHtml = require('is-html');
const { plugin: runInBackground, worker } = require('mongoose-kue');
/* transports */
const ECHO_TRANSPORT_NAME = 'echo';
const DEFAULT_TRANSPORT_NAME = getString(
'DEFAULT_TRANSPORT_NAME',
ECHO_TRANSPORT_NAME
);
const DEFAULT_SMTP_TRANSPORT_NAME = getString('DEFAULT_SMTP_TRANSPORT_NAME');
const DEFAULT_SMS_TRANSPORT_NAME = getString('DEFAULT_SMS_TRANSPORT_NAME');
const DEFAULT_PUSH_TRANSPORT_NAME = getString('DEFAULT_PUSH_TRANSPORT_NAME');
const {
DIRECTION_INBOUND,
DIRECTION_OUTBOUND,
DIRECTIONS,
TYPE_SMS,
TYPE_EMAIL,
TYPE_PUSH,
TYPES,
MIME_TEXT,
MIME_HTML,
MIMES,
PRIORITY_LOW,
PRIORITY_NORMAL,
PRIORITY_MEDIUM,
PRIORITY_HIGH,
PRIORITY_CRITICAL,
PRIORITIES,
SEND_MODE_PULL,
SEND_MODE_PUSH,
SEND_MODES,
STATE_RECEIVED,
STATE_UNKNOWN,
STATE_SENT,
STATE_QUEUED,
STATE_DELIVERED,
STATE_FAILED,
STATES,
} = require('./common');
/* load transports */
const transports = {
'infobip-sms': require('./transports/sms.infobip'),
'tz-ega-sms': require('./transports/sms.tz.ega'),
smtp: require('./transports/smtp'),
'fcm-push': require('./transports/push.fcm'),
smssync: require('./transports/smssync'),
echo: require('./transports/echo'),
};
/* model name for the message */
const CAMPAIGN_MODEL_NAME = getString('CAMPAIGN_MODEL_NAME', 'Campaign');
const MODEL_NAME = getString('MESSAGE_MODEL_NAME', 'Message');
/* collection name for the message */
const COLLECTION_NAME = getString('MESSAGE_COLLECTION_NAME', 'messages');
/* schema options */
const SCHEMA_OPTIONS = {
timestamps: true,
emitIndexErrors: true,
collection: COLLECTION_NAME,
};
/* message hash fields */
const HASH_FIELDS = [
'type',
'direction',
'bulk',
'sender',
'to',
'transport',
'body',
'priority',
'createdAt',
];
/**
* @name MessageSchema
* @description message schema
* @type {Schema}
* @author lally elias <lallyelias87@gmail.com>
* @version 0.1.0
* @since 0.1.0
* @private
*/
const MessageSchema = new Schema(
{
/**
* @name role
* @description campaign underwhich a message belongs.
*
* @since 0.1.0
* @version 0.1.0
* @instance
*/
campaign: {
type: ObjectId,
ref: CAMPAIGN_MODEL_NAME,
index: true,
default: undefined,
},
/**
* @name type
* @description message type i.e SMS, e-mail, push etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
type: {
type: String,
enum: TYPES,
trim: true,
default: TYPE_EMAIL,
index: true,
searchable: true,
fake: true,
},
/**
* @name mime
* @description message mime type i.e text/plain, text/html etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
mime: {
type: String,
enum: MIMES,
trim: true,
default: MIME_TEXT,
index: true,
searchable: true,
fake: true,
},
/**
* @name direction
* @description message direction i.e received or sending
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
direction: {
type: String,
enum: DIRECTIONS,
trim: true,
default: DIRECTION_OUTBOUND,
index: true,
searchable: true,
fake: true,
},
/**
* @name state
* @description message state i.e Received, Sent, Queued etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
state: {
type: String,
enum: STATES,
trim: true,
default: STATE_UNKNOWN,
index: true,
searchable: true,
fake: true,
},
/**
* @name mode
* @description message transport send mode i.e Pull or Push etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
mode: {
type: String,
enum: SEND_MODES,
trim: true,
default: SEND_MODE_PUSH,
index: true,
searchable: true,
fake: true,
},
/**
* @name bulk
* @description unique identifier used to track group messages which have been
* send together.
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
bulk: {
type: String,
trim: true,
index: true,
searchable: true,
fake: {
generator: 'random',
type: 'uuid',
},
},
/**
* @name sender
* @description sender of the message
* i.e e-mail sender, message sender etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
sender: {
// TODO: use contact schema
type: String,
trim: true,
required: true,
index: true,
searchable: true,
fake: {
generator: 'internet',
type: 'email',
},
},
/**
* @name to
* @description receiver(s) of the message
* i.e e-mail receiver, message receiver etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
to: {
// todo must me a receiver
type: [String],
required: true,
index: true,
searchable: true,
fake: {
generator: 'internet',
type: 'email',
},
},
/**
* @name cc
* @description receiver(s) of the carbon copy of the message
* i.e e-mail cc receiver
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
cc: {
// TODO: move to campaign
type: [String],
index: true,
searchable: true,
fake: {
generator: 'internet',
type: 'email',
},
},
/**
* @name bcc
* @description receiver(s) of the blind carbon copy of the message
* i.e e-mail cc receiver
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
bcc: {
// TODO: move to campaign
type: [String],
index: true,
searchable: true,
fake: {
generator: 'internet',
type: 'email',
},
},
/**
* @name replyTo
* @description actual sender of the message i.e e-mail sender etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
replyTo: {
// TODO: move to campaign
type: String,
trim: true,
index: true,
searchable: true,
fake: {
generator: 'internet',
type: 'email',
},
},
/**
* @name subject
* @description subject of the message
* i.e email title etc
* e.g Hello
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
subject: {
type: String,
trim: true,
index: true,
searchable: true,
fake: {
generator: 'lorem',
type: 'sentence',
},
},
/**
* @name body
* @description content of the message to be conveyed to receiver(s)
* e.g Hello
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
body: {
type: String,
trim: true,
// index: true,
// searchable: true,
fake: {
generator: 'lorem',
type: 'sentence',
},
},
/**
* @name queuedAt
* @description time when message was queued successfully for sending.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
queuedAt: {
type: Date,
default: null,
index: true,
fake: {
generator: 'date',
type: 'past',
},
},
/**
* @name sentAt
* @description time when message was send successfully to a receiver.
*
* If message send succeed, set the result and update sent time.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
sentAt: {
type: Date,
default: null,
index: true,
fake: {
generator: 'date',
type: 'past',
},
},
/**
* @name failedAt
* @description latesst time when message sned to receiver(s) failed.
*
* If message send failed just set the result and set failed time.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
failedAt: {
type: Date,
default: null,
index: true,
fake: {
generator: 'date',
type: 'recent',
},
},
/**
* @name deliveredAt
* @description latest time when message delivered to receiver(s).
*
* If message delivered just set the result and set delivery time.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
deliveredAt: {
type: Date,
default: null,
index: true,
fake: {
generator: 'date',
type: 'recent',
},
},
/**
* @name readAt
* @description latest time when receiver read the message delivered.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
readAt: {
type: Date,
default: null,
index: true,
fake: {
generator: 'date',
type: 'recent',
},
},
/**
* @name result
* @description message send result i.e success or failure response
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
result: {
type: Mixed,
fake: {
generator: 'helpers',
type: 'createTransaction',
},
},
/**
* @name transport
* @description method used to actual send the message. It must be set-ed
* by a transport used to send message.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
transport: {
type: String,
trim: true,
default: DEFAULT_TRANSPORT_NAME,
index: true,
searchable: true,
fake: true,
},
/**
* @name priority
* @description message sending priority
* @see {@link https://github.com/Automattic/kue#job-priority}
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
priority: {
type: String,
enum: PRIORITIES,
trim: true,
default: PRIORITY_NORMAL,
index: true,
searchable: true,
fake: true,
},
/**
* @name hash
* @description unique message hash that is set by a transport.
*
* It allow for a transport to uniquely identify a message.
*
* A quick scenarion is when sms is received and you dont want to receive
* a message previous received from a transport.
*
* You can use transport hash to check for sms existance or upserting
* a message.
*
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
hash: {
type: String,
trim: true,
required: true,
unique: true,
searchable: true,
fake: {
generator: 'random',
type: 'uuid',
},
},
/**
* @name tags
* @description additional tags(or labels) used to identified sent message
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
tags: {
type: [String],
index: true,
searchable: true,
fake: {
generator: 'address',
type: 'city',
},
},
/**
* @name options
* @description additional message sending options(or extra transport options)
* i.e push sending options, email sending options etc
* @type {Object}
* @since 0.1.0
* @version 1.0.0
* @instance
*/
options: {
type: Mixed,
fake: {
generator: 'helpers',
type: 'createTransaction',
},
},
/**
* @name metadata
* @description Application specific additional information for the message.
* @type {Object}
* @since 0.18.0
* @version 1.0.0
* @instance
*/
metadata: {
type: Mixed,
fake: {
generator: 'helpers',
type: 'createTransaction',
},
},
// template: String|Predefine, //template used for sending
},
SCHEMA_OPTIONS
);
/*-----------------------------------------------------------------------------
Hooks
------------------------------------------------------------------------------*/
/**
* @name onPreValidate
* @description message schema pre validate hook
* @since 0.1.0
* @version 0.1.0
* @private
*/
MessageSchema.pre('validate', function onPreValidate(next) {
this.preValidate(next);
});
/*-----------------------------------------------------------------------------
Instance
------------------------------------------------------------------------------*/
/**
* @name preValidate
* @function preValidate
* @description run logics before message validation
* @param {Function} done a callback to invoke on success or failure
* @return {Message|Error} an instance of message or error
* @since 0.1.0
* @version 0.1.0
* @private
*/
MessageSchema.methods.preValidate = function preValidate(next) {
// TODO set bulk from campaign
//
//ensure `to` field is in array format
if (this.to && _.isString(this.to)) {
this.to = [].concat(this.to);
}
//ensure `cc` field is in array format
if (this.cc && _.isString(this.cc)) {
this.cc = [].concat(this.cc);
}
//ensure `bcc` field is in array format
if (this.bcc && _.isString(this.bcc)) {
this.bcc = [].concat(this.bcc);
}
//ensure `tags` field is in array format
if (this.tags && _.isString(this.tags)) {
this.tags = [].concat(this.tags);
}
//ensure message hash if not set by a transport
if (!this.hash || _.isEmpty(this.hash)) {
this.createdAt = this.createdAt || new Date();
const _hash = _.pick(this, HASH_FIELDS);
this.hash = hashOf(_hash);
}
//set mime type
if (isHtml(this.body)) {
this.mime = MIME_HTML;
} else {
this.mime = MIME_TEXT;
}
//compact and lowercase to, cc, bcc & tags
this.to = _.map(_.uniq(_.compact(this.to)), _.toLower);
this.cc = _.map(_.uniq(_.compact(this.cc)), _.toLower);
this.bcc = _.map(_.uniq(_.compact(this.bcc)), _.toLower);
this.tags = _.map(_.uniq(_.compact(this.tags)), _.toLower);
//ensure state to be unknown for poll transport
if (this.mode === SEND_MODE_PULL && _.isEmpty(this.state)) {
this.state = STATE_UNKNOWN;
}
//ensure transport
if (_.isEmpty(this.transport)) {
// TODO: ensure transport name per default transports per message type
// TODO: i.e DEFAULT_PUSH_TRANSPORT, DEFAULT_EMAIL_TRANSPORT etc
// TODO: last fallback to DEFAULT_TRANSPORT_NAME
this.transport = DEFAULT_TRANSPORT_NAME;
}
next(null, this);
};
/**
* @name isHtml
* @function isHtml
* @description check if message body is html
* @return {Boolean} true or false
* @since 0.1.0
* @version 0.1.0
* @instance
*/
MessageSchema.methods.isHtml = function _isHtml() {
return this.mime === MIME_HTML || isHtml(this.body);
};
/**
* @name _send
* @function _send
* @description send this message using actual transport
* @param {Function} done a callback to invoke on success or failure
* @return {Message|Error} an instance of message or error
* @since 0.1.0
* @version 0.1.0
* @private
*/
MessageSchema.methods._send = function (done) {
//this refer to Message instance context
try {
//obtain actual message transport
const transport = transports[this.transport];
//NOTE! poll transport should return state on the result
//cause they will later pick messages for sending
waterfall(
[
function send(next) {
//this refer to Message instance context
//set send data
this.sentAt = new Date();
this.state = STATE_SENT;
//update message with transport and country details
// this.country = transport.toObject().countryName;
// this.transport = transport.toObject();
//send message via transport
transport.send(
this,
function (error, result) {
//prepare result
let _result = _.merge(
{},
{
success: true,
},
result
);
//handle error
if (error) {
//set failed date
this.failedAt = new Date();
this.state = STATE_FAILED;
//obtain error details
if (error instanceof Error) {
_result = _.merge(
{},
{
success: false,
},
{
message: error.message,
code: error.code,
status: error.status,
}
);
}
}
// ensure unknown state for pull transports
if (this.mode === SEND_MODE_PULL) {
this.state = STATE_UNKNOWN;
this.sentAt = null;
}
//update message sending details
if (!this.failedAt && this.mode !== SEND_MODE_PULL) {
this.deliveredAt = this.deliveredAt || new Date();
this.state = STATE_DELIVERED;
}
this.result = _result;
next(null, this);
}.bind(this)
);
}.bind(this),
function update(message, next) {
message.put(next);
},
],
done
);
} catch (error) {
done(error);
}
};
/**
* @name send
* @function send
* @description send this message using actual transport or debug it
* @param {Function} done a callback to invoke on success or failure
* @return {Message|Error} an instance of message or error
* @since 0.1.0
* @version 0.1.0
* @instance
* @example
*
* message.send(cb);
*
*/
MessageSchema.methods.send = function send(done) {
//this refer to Message instance context
/* @todo format <to> based on message type */
/* @todo format <to> as e164 phone numbers */
// check for debug flags
const DEBUG = getBoolean('DEBUG', false);
// handle debug message sending
if (DEBUG) {
// update message
this.sentAt = this.sentAt || new Date();
this.deliveredAt = new Date();
this.result = {
success: true,
};
this.state = STATE_DELIVERED;
// persist message
return this.put(done);
}
//send message using actual transport
else {
return this._send(done);
}
};
/**
* @name queue
* @function queue
* @description queue message for later send
* @events job error, job success
* @fire {Message|Error} an instance of queued message or error
* @since 0.1.0
* @instance
* @example
*
* message.queue();
*
*/
MessageSchema.methods.queue = function queue(done) {
//this refer to Message instance context
// normalize arguments
const cb = _.isFunction(done) ? done : _.noop;
// update message details
if (this.mode === SEND_MODE_PULL) {
this.state = STATE_UNKNOWN;
this.sentAt = null;
this.queuedAt = null;
} else {
this.queuedAt = new Date();
this.state = STATE_QUEUED;
}
//persist message
this.save(function (error, message) {
//notify error
if (error) {
worker.queue.emit('job error', error);
return cb(error);
}
//notify message queued successfully
//since a poll transport will later pull the message
//for actul send
else if (message.mode === SEND_MODE_PULL) {
worker.queue.emit('job queued', message);
return cb(null, message);
}
//queue message for later send
//push transport are notified in their worker to send the message
else {
//prepare job details
const jobType = message.type || getString('KUE_DEFAULT_JOB_TYPE');
const title = message.subject || jobType;
const jobDefaults = {
method: 'send',
title: title,
type: jobType,
};
const jobDetails = _.merge({}, jobDefaults, message.toObject());
const job = message.runInBackground(jobDetails);
//ensure message has been queued
return job.save(function (error) {
if (error) {
worker.queue.emit('job error', error);
return cb(error);
} else {
worker.queue.emit('job queued', message);
return cb(null, message);
}
});
}
});
};
/*-----------------------------------------------------------------------------
Statics
------------------------------------------------------------------------------*/
/* schema options*/
MessageSchema.statics.MODEL_NAME = MODEL_NAME;
MessageSchema.statics.COLLECTION_NAME = COLLECTION_NAME;
/* message directions */
MessageSchema.statics.DIRECTION_INBOUND = DIRECTION_INBOUND;
MessageSchema.statics.DIRECTION_OUTBOUND = DIRECTION_OUTBOUND;
MessageSchema.statics.DIRECTIONS = DIRECTIONS;
/* message types */
MessageSchema.statics.TYPE_SMS = TYPE_SMS;
MessageSchema.statics.TYPE_EMAIL = TYPE_EMAIL;
MessageSchema.statics.TYPE_PUSH = TYPE_PUSH;
MessageSchema.statics.TYPES = TYPES;
/* message mime types */
MessageSchema.statics.MIME_TEXT = MIME_TEXT;
MessageSchema.statics.MIME_HTML = MIME_HTML;
MessageSchema.statics.MIMES = MIMES;
/* mesage priorities */
MessageSchema.statics.PRIORITY_LOW = PRIORITY_LOW;
MessageSchema.statics.PRIORITY_NORMAL = PRIORITY_NORMAL;
MessageSchema.statics.PRIORITY_MEDIUM = PRIORITY_MEDIUM;
MessageSchema.statics.PRIORITY_HIGH = PRIORITY_HIGH;
MessageSchema.statics.PRIORITY_CRITICAL = PRIORITY_CRITICAL;
MessageSchema.statics.PRIORITIES = PRIORITIES;
/* transaport sending mode */
MessageSchema.statics.SEND_MODE_PULL = SEND_MODE_PULL;
MessageSchema.statics.SEND_MODE_PUSH = SEND_MODE_PUSH;
MessageSchema.statics.SEND_MODES = SEND_MODES;
/* message states */
MessageSchema.statics.STATE_RECEIVED = STATE_RECEIVED;
MessageSchema.statics.STATE_UNKNOWN = STATE_UNKNOWN;
MessageSchema.statics.STATE_SENT = STATE_SENT;
MessageSchema.statics.STATE_QUEUED = STATE_QUEUED;
MessageSchema.statics.STATE_DELIVERED = STATE_DELIVERED;
MessageSchema.statics.STATES = STATES;
/**
* @name unsent
* @function unsent
* @description obtain unsent message(s)
* @param {Object} [criteria] valid mongoose query criteria
* @param {Function} done a callback to invoke on success or failure
* @return {Message[]|Error} collection of unsent messages
* @since 0.1.0
* @version 0.1.0
* @public
* @static
* @example
*
* Message.unsent(cb);
* Message.unsent(criteria, cb);
* Message.unsent().exec(cb);
* Message.unsent(criteria).exec(cb);
*
*/
MessageSchema.statics.unsent = function unsent(criteria, done) {
//this refer to Message static context
let _criteria = criteria;
let _done = done;
//normalize arguments
if (criteria && _.isFunction(criteria)) {
_done = criteria;
_criteria = {};
}
//ensure unsent criteria
_criteria = _.merge(
{},
{
sentAt: { $eq: null },
},
_criteria
);
//find unsent messages
return this.find(_criteria, _done);
};
/**
* @name sent
* @function sent
* @description obtain already sent message(s)
* @param {Object} [criteria] valid mongoose query criteria
* @param {Function} done a callback to invoke on success or failure
* @return {Message[]|Error} collection of already sent message(s)
* @since 0.1.0
* @version 0.1.0
* @public
* @static
* @example
*
* Message.sent(cb);
* Message.sent(criteria, cb);
* Message.sent().exec(cb);
* Message.sent(criteria).exec(cb);
*
*/
MessageSchema.statics.sent = function sent(criteria, done) {
//this refer to Message static context
let _done = done;
let _criteria = criteria;
//normalize arguments
if (criteria && _.isFunction(criteria)) {
_done = criteria;
_criteria = {};
}
//ensure sent criteria
_criteria = _.merge(
{},
{
sentAt: {
$ne: null,
},
},
_criteria
);
//find sent message
return this.find(criteria, _done);
};
/**
* @name resend
* @function resend
* @description re-send all failed message(s) based on specified criteria
* @param {Object} [criteria] valid mongoose query criteria
* @param {Function} done a callback to invoke on success or failure
* @return {Message[]|Error} collection of resend message(s)
* @since 0.1.0
* @version 0.1.0
* @public
* @example
*
* Message.resend(criteria, cb);
* Message.resend(cb);
*
*/
MessageSchema.statics.resend = function resend(criteria, done) {
//this refer to Message static context
/* @todo use stream */
let _done = done;
let _criteria = criteria;
//normalize arguments
if (criteria && _.isFunction(criteria)) {
_done = criteria;
_criteria = {};
}
//reference Message
const Message = this;
//resend fail or unsent message(s)
waterfall(
[
function findUnsentMessages(next) {
Message.unsent(_criteria, next); /* @todo also failedAt is set */
},
function resendMessages(unsents, next) {
//check for unsent message(s)
if (unsents) {
/* @todo make use of parallelism */
//prepare send work
unsents = _.map(unsents, function (unsent) {
return function (_next) {
unsent.send(_next); /* @todo spies test */
};
});
parallel(_.compact(unsents), next);
} else {
next(null, unsents);
}
},
],
done
);
};
/**
* @name requeue
* @description requeue all failed message(s) based on specified criteria
* @param {Object} [criteria] valid mongoose query criteria
* @type {Function}
* @events job error, job success
* @fire {Message[]|Error} collection of requeued messages or error
* @since 0.1.0
* @version 0.1.0
* @public
* @static
* @example
*
* Message.requeue();
* Message.requeue(criteria);
*
*/
MessageSchema.statics.requeue = function (criteria) {
//this refer to Message static context
/* @todo use stream */
//merge criteria
let _criteria = _.merge({}, criteria);
//reference Message
const Message = this;
//find all unsent message(s) for requeue
Message.unsent(_criteria, function (error, unsents) {
//there is no message queue
if (!worker.queue) {
if (error) {
throw error;
}
}
//there is message queue
else {
//fire requeue error
if (error) {
worker.queue.emit('job error', error);
}
//re-queue all unsent message(s)
else {
//fire requeue success
worker.queue.emit('job success', unsents);
//re-queue unsent message
_.forEach(unsents, function (unsent) {
unsent.queue();
});
}
}
});
};
/*-----------------------------------------------------------------------------
Plugins
------------------------------------------------------------------------------*/
MessageSchema.plugin(actions);
MessageSchema.plugin(exportable);
MessageSchema.plugin(runInBackground, {
types: TYPES,
});
const Message = model(MODEL_NAME, MessageSchema);
/**
* export message model
* @type {Model}
* @private
*/
exports.Message = Message;
/**
* export email factory
* @type {Model}
* @private
*/
exports.Email = function Email(payload) {
const _payload = mergeObjects(payload, {
transport: DEFAULT_SMTP_TRANSPORT_NAME,
type: TYPE_EMAIL,
});
return new Message(_payload);
};
/**
* export SMS factory
* @type {Model}
* @private
*/
exports.SMS = function SMS(payload) {
const copyOfpayload = mergeObjects(
{ transport: DEFAULT_SMS_TRANSPORT_NAME },
payload,
{ type: TYPE_SMS }
);
return new Message(copyOfpayload);
};
/**
* export Push factory
* @type {Model}
* @private
*/
exports.Push = function Push(payload) {
const _payload = mergeObjects(
{ transport: DEFAULT_PUSH_TRANSPORT_NAME },
payload,
{ type: TYPE_PUSH }
);
return new Message(_payload);
};