ihub-framework-js
Version:
Legacy version of iHub Framework written in Javascript
144 lines (121 loc) • 4.36 kB
JavaScript
const amqp = require('amqplib/callback_api');
const log = require('winston');
const {
RABBIT_USER = '',
RABBIT_PASS = '',
RABBIT_HOST = 'rabbit',
RABBIT_PORT = '',
RABBIT_VHOST_NAME = '',
RABBIT_HEARTBEAT = '',
} = process.env;
if (!RABBIT_HOST) {
throw new Error('RABBIT_HOST variable cannot be empty');
}
// https://www.rabbitmq.com/uri-spec.html
let connectionUri = 'amqp://';
if (RABBIT_USER) {
connectionUri += RABBIT_USER;
if (RABBIT_PASS) {
connectionUri += `:${RABBIT_PASS}`;
}
connectionUri += '@';
}
connectionUri += RABBIT_HOST;
if (RABBIT_PORT) {
connectionUri += `:${RABBIT_PORT}`;
}
if (RABBIT_VHOST_NAME) {
connectionUri += `/${RABBIT_VHOST_NAME}`;
}
if (RABBIT_HEARTBEAT) {
connectionUri += `?heartbeat=${RABBIT_HEARTBEAT}`;
}
module.exports = apm => (
new Promise((resolve, reject) => {
amqp.connect(connectionUri, (connectError, conn) => {
if (connectError) {
reject(connectError);
return;
}
let cleanUri = connectionUri;
if (RABBIT_PASS) {
cleanUri = cleanUri.replace(`:${RABBIT_PASS}`, '');
}
if (RABBIT_USER) {
cleanUri = cleanUri.replace(`${RABBIT_USER}@`, '');
}
/* Debug */
log.debug(`Rabbit connected at ${cleanUri} with user '${RABBIT_USER}'`);
conn.createChannel((channelError, ch) => {
if (channelError) {
reject(channelError);
return;
}
/* Debug */
log.debug('Rabbit channel connected');
// TODO: Make each task a channel for better use of the prefetch
ch.prefetch(parseInt(process.env.TASKS_PREFETCH, 10) || 1);
/* Send message to exchange abstraction */
const send = (exchange, routeKey, message, options = {}) => {
let apmTransaction = false;
if (apm) apmTransaction = apm.startTransaction(`${exchange} - ${routeKey}`, 'Rabbit');
// Check if it's an object, if true convert to json
const parsedMsg = (typeof message === 'object' && !Array.isArray(message)) ? JSON.stringify(message) : message;
ch.assertExchange(exchange, options.type || 'direct', { durable: true });
// Sends the message to the queue
const status = ch.publish(exchange, routeKey, Buffer.from(parsedMsg));
if (apmTransaction) apmTransaction.end();
return status;
};
/* Listen to messages in the queue abstraction */
const job = (exchange, routeKey, queue, prefetch, callback, options = {}) => {
// Assert that this exchange exists
ch.assertExchange(exchange, options.type || 'direct', { durable: true });
// Assert that the queue exists
ch.assertQueue(queue);
// Bind the queue in the exchange by the routing key
ch.bindQueue(queue, exchange, routeKey);
// prefetch
if (prefetch) {
ch.prefetch(prefetch);
}
// Consumes it
ch.consume(queue, (msg) => {
let apmTransaction = false;
// Check if APM is enabled to track the transaction
if (apm) apmTransaction = apm.startTransaction(queue, 'Rabbit');
// Lets threat the message
const stringMsg = msg.content.toString();
let parsedMsg;
// Tries to parse to object
try {
parsedMsg = JSON.parse(stringMsg);
} catch (e) {
// If it fails send as a string
parsedMsg = stringMsg;
}
/**
* We send back to the user only a callback with the payload content and a done fuction
* so he can ack the message and possibly end the transaction.
* @return {type} Description.
*/
callback(parsedMsg, (nack, requeue = true, allUpTo = false) => {
// Check if APM is enabled to end the trasaction treacking
if (apmTransaction) apmTransaction.end();
if (typeof nack !== 'undefined') {
ch.nack(msg, allUpTo, requeue);
return;
}
// Sends the acknolage to the message at Rabbit
ch.ack(msg);
});
});
};
resolve({
send,
job,
});
});
});
})
);