UNPKG

iamqp

Version:

RabbitMQ wrapper for NodeJS with several produce(reply)/consume scenarios

154 lines (143 loc) 5.1 kB
const UUIDv4 = require('../uuid_v4') const MESSAGES = require('../internal').MESSAGES const _isNull = require('lodash/isNull') const _get = require('lodash/get') /** * RPC client. Init example: * <pre> * const iamqp = require('iamqp'); * const amqpUri = 'amqp://localhost'; * const rpcChannel = 'rpc-channel-a'; * let rpcClient = new iamqp.RPCClient(amqpUri, rpcChannel); * * // This needs to be done or the errors will bubble up. * rpcClient.getEventer().on('error', (err) => { * console.log('RPC client error:' + err.message); * }); * * // For when the connection is established. * rpcClient.getEventer().on('connected', () => { * console.log('RPC client connected'); * }); * * rpcClient.getEventer().on('reply', (reply, correlationId) => { * console.log('RPC client got a reply (' + correlationId + '): ' + reply); * }); * * // Open the connection - this takes some time and is not sync * rpcClient.openConnection(); * * // ... * * // Call the RPC server with some data. * // The correlation ID strings are to be used to distinguish replies to answers. * let correlationIdA = rpcClient.callRemote({'a': 5}); * let correlationIdB = rpcClient.callRemote({'b': 5}); * if (correlationIdA === false) { * console.log('call not performed on A'); * } else { * console.log('correlationID on A: ' + correlationIdA); * } * * if (correlationIdB === false) { * console.log('call not performed on B'); * } else { * console.log('correlationID on B: ' + correlationIdB); * } * * // ... * * // Close the connection * if (rpcClient.closeConnection()) { * console.log('RPC client connection closing ...'); * } * </pre> * ... */ class RpcClient extends require('../common') { /** * @param {AmqpURI} amqpUri * @param {ChannelName} channelName * @param {Configuration} [configuration] */ constructor (amqpUri, channelName, configuration) { super(amqpUri, channelName, configuration) this._uidGenerator = new UUIDv4() this._correlationIDStorage = {} this._name = `RPC-CLIENT: ${this._configuration.channelName}` this._messageDispatcherWrapper('info', MESSAGES.INIT) this._E.on('connected', () => { this._createChannel() }) } /** * Perform the "server" call with some data. * @param {number | string | boolean | Object | Array} contentToPublish * @returns {boolean | string} the correlation ID of the call. * The user can use this as to distinguish between multiple answers. * If something went wrong, false will be returned. */ callRemote (contentToPublish) { const data = this._commonPublishPreparations(contentToPublish) if (!_isNull(data)) { const thisCorrelationId = this._uidGenerator.generate() this._correlationIDStorage[thisCorrelationId] = true this._amqp.channel.sendToQueue(this._configuration.channelName, data, { replyTo: this._amqp.queue, correlationId: thisCorrelationId }) return thisCorrelationId } return false } /** * Create the channel. * @private */ _createChannel () { this._amqp.connection.createChannel((channelCreatingErr, createdChannel) => { if (channelCreatingErr) { this._emitAndDispatchError(`${MESSAGES.CHANNEL_ERROR} -> ${channelCreatingErr.message}`) return } this._messageDispatcherWrapper('info', MESSAGES.CHANNEL_READY) this._amqp.channel = createdChannel this._assertQueue() }) } /** * Assert the queue and bind the consumer function. * @private */ _assertQueue () { this._amqp.channel.assertQueue('', { exclusive: true, arguments: _get(this._configuration, 'configuration.queueArguments', {}) }, (channelAssertErr, asserted) => { if (channelAssertErr) { this._emitAndDispatchError(`${MESSAGES.CHANNEL_ERROR} -> ${channelAssertErr.message}`) } else { this._messageDispatcherWrapper('info', MESSAGES.CONSUMING) this._amqp.queue = asserted.queue this._amqp.channel.consume(this._amqp.queue, (data) => { if ( !data || !data.properties || !data.properties.correlationId || !Object.prototype.hasOwnProperty.call(this._correlationIDStorage, data.properties.correlationId) ) { this._emitAndDispatchError(MESSAGES.INVALID_DATA) return } this._logData(data.content, 'consume') const replied = this._parseConsumedMessage(data) delete this._correlationIDStorage[data.properties.correlationId] this._E.emit('reply', replied, data.properties.correlationId) }, { noAck: true }) } }) } } module.exports = RpcClient