UNPKG

bpmn-elements

Version:

Executable workflow elements based on BPMN 2.0

139 lines (110 loc) 5.02 kB
import { Broker } from 'smqp'; import { makeErrorFromMessage } from './error/Errors.js'; export function ActivityBroker(activity) { const executionBroker = ExecutionBroker(activity, 'activity'); return executionBroker; } export function ProcessBroker(owner) { const executionBroker = ExecutionBroker(owner, 'process'); executionBroker.broker.assertQueue('api-q', { durable: false, autoDelete: false }); executionBroker.broker.bindQueue('api-q', 'api', '#'); return executionBroker; } export function DefinitionBroker(owner, onBrokerReturn) { return ExecutionBroker(owner, 'definition', onBrokerReturn); } export function MessageFlowBroker(owner) { const eventBroker = new EventBroker(owner, { prefix: 'messageflow', autoDelete: false, durable: false }); const broker = eventBroker.broker; broker.assertExchange('message', 'topic', { durable: true, autoDelete: false }); broker.assertQueue('message-q', { durable: true, autoDelete: false }); broker.bindQueue('message-q', 'message', 'message.#'); return eventBroker; } function ExecutionBroker(brokerOwner, prefix, onBrokerReturn) { const eventBroker = new EventBroker(brokerOwner, { prefix, autoDelete: false, durable: false }, onBrokerReturn); const broker = eventBroker.broker; broker.assertExchange('api', 'topic', { autoDelete: false, durable: false }); broker.assertExchange('run', 'topic', { autoDelete: false }); broker.assertExchange('format', 'topic', { autoDelete: false }); broker.assertExchange('execution', 'topic', { autoDelete: false }); const runQ = broker.assertQueue('run-q', { durable: true, autoDelete: false }); const formatRunQ = broker.assertQueue('format-run-q', { durable: true, autoDelete: false }); const executionQ = broker.assertQueue('execution-q', { durable: true, autoDelete: false }); broker.assertQueue('inbound-q', { durable: true, autoDelete: false }); broker.bindQueue(runQ.name, 'run', 'run.#'); broker.bindQueue(formatRunQ.name, 'format', 'run.#'); broker.bindQueue(executionQ.name, 'execution', 'execution.#'); return eventBroker; } export function EventBroker(brokerOwner, options, onBrokerReturn) { this.options = options; this.eventPrefix = options.prefix; const broker = (this.broker = Broker(brokerOwner)); broker.assertExchange('event', 'topic', options); broker.on('return', onBrokerReturn ? onBrokerReturn.bind(brokerOwner) : this._onBrokerReturnFn.bind(this)); this.on = this.on.bind(this); this.once = this.once.bind(this); this.waitFor = this.waitFor.bind(this); this.emit = this.emit.bind(this); this.emitFatal = this.emitFatal.bind(this); } EventBroker.prototype.on = function on(eventName, callback, eventOptions = { once: false }) { const key = this._getEventRoutingKey(eventName); if (eventOptions.once) return this.broker.subscribeOnce('event', key, eventCallback, eventOptions); return this.broker.subscribeTmp('event', key, eventCallback, { ...eventOptions, noAck: true }); function eventCallback(routingKey, message, owner) { if (eventName === 'error') return callback(makeErrorFromMessage(message)); callback(owner.getApi(message)); } }; EventBroker.prototype.once = function once(eventName, callback, eventOptions) { return this.on(eventName, callback, { ...eventOptions, once: true }); }; EventBroker.prototype.waitFor = function waitFor(eventName, onMessage) { const key = this._getEventRoutingKey(eventName); return new Promise((resolve, reject) => { const consumers = [ this.broker.subscribeTmp('event', key, eventCallback, { noAck: true }), this.broker.subscribeTmp('event', '*.error', errorCallback, { noAck: true }), ]; function eventCallback(routingKey, message, owner) { if (onMessage && !onMessage(routingKey, message, owner)) return; unsubscribe(); return resolve(owner.getApi(message)); } function errorCallback(routingKey, message, owner) { if (!message.properties.mandatory) return; unsubscribe(); return reject(makeErrorFromMessage(message, owner)); } function unsubscribe() { for (const consumer of consumers) { consumer.cancel(); } } }); }; EventBroker.prototype.emit = function emit(eventName, content, props) { this.broker.publish('event', `${this.eventPrefix}.${eventName}`, { ...content }, { type: eventName, ...props }); }; EventBroker.prototype.emitFatal = function emitFatal(error, content) { this.emit('error', { ...content, error }, { mandatory: true }); }; EventBroker.prototype._onBrokerReturnFn = function onBrokerReturnFn(message) { if (message.properties.type === 'error') { const err = makeErrorFromMessage(message); throw err; } }; EventBroker.prototype._getEventRoutingKey = function getEventRoutingKey(eventName) { if (eventName.indexOf('.') > -1) return eventName; switch (eventName) { case 'wait': { return `activity.${eventName}`; } default: { return `${this.eventPrefix}.${eventName}`; } } };