UNPKG

bpmn-elements

Version:

Executable workflow elements based on BPMN 2.0

178 lines (143 loc) 5.79 kB
import getPropertyValue from '../getPropertyValue.js'; import { brokerSafeId } from '../shared.js'; import { cloneContent, shiftParent } from '../messageHelper.js'; const kCompleted = Symbol.for('completed'); const kMessageQ = Symbol.for('messageQ'); const kExecuteMessage = Symbol.for('executeMessage'); export default function LinkEventDefinition(activity, eventDefinition) { const { id, broker, environment, isThrowing } = activity; const { type = 'LinkEventDefinition', behaviour } = eventDefinition; this.id = id; this.type = type; const reference = (this.reference = { linkName: behaviour.name, referenceType: 'link', }); this.isThrowing = isThrowing; this.activity = activity; this.broker = broker; this.logger = environment.Logger(type.toLowerCase()); this[kCompleted] = false; if (!isThrowing) { const messageQueueName = `${reference.referenceType}-${brokerSafeId(id)}-${brokerSafeId(reference.linkName)}-q`; this[kMessageQ] = broker.assertQueue(messageQueueName, { autoDelete: false, durable: true }); broker.bindQueue(messageQueueName, 'api', `*.${reference.referenceType}.#`, { durable: true }); } else { broker.subscribeTmp('event', 'activity.discard', this._onDiscard.bind(this), { noAck: true, consumerTag: '_link-parent-discard', }); } } Object.defineProperty(LinkEventDefinition.prototype, 'executionId', { get() { return this[kExecuteMessage]?.content.executionId; }, }); LinkEventDefinition.prototype.execute = function execute(executeMessage) { return this.isThrowing ? this.executeThrow(executeMessage) : this.executeCatch(executeMessage); }; LinkEventDefinition.prototype.executeCatch = function executeCatch(executeMessage) { this[kExecuteMessage] = executeMessage; this[kCompleted] = false; const executeContent = executeMessage.content; const { executionId, parent } = executeContent; const parentExecutionId = parent.executionId; this[kMessageQ].consume(this._onCatchLink.bind(this), { noAck: true, consumerTag: `_api-link-${executionId}`, }); if (this[kCompleted]) return; const broker = this.broker; const onApiMessage = this._onApiMessage.bind(this); broker.subscribeTmp('api', `activity.stop.${parentExecutionId}`, onApiMessage, { noAck: true, consumerTag: `_api-parent-${executionId}`, }); broker.subscribeTmp('api', `activity.#.${executionId}`, onApiMessage, { noAck: true, consumerTag: `_api-${executionId}`, }); this._debug(`expect link ${this.reference.linkName}`); const waitContent = cloneContent(executeContent, { executionId: parentExecutionId, link: { ...this.reference }, }); waitContent.parent = shiftParent(parent); broker.publish('event', 'activity.wait', waitContent); }; LinkEventDefinition.prototype.executeThrow = function executeThrow(executeMessage) { const executeContent = executeMessage.content; const { executionId, parent } = executeContent; const parentExecutionId = parent && parent.executionId; this.logger.debug(`<${executionId} (${this.activity.id})> throw link ${this.reference.linkName}`); const broker = this.broker; const linkContent = cloneContent(executeContent, { executionId: parentExecutionId, message: { ...this.reference }, state: 'throw', }); linkContent.parent = shiftParent(parent); broker.publish('event', 'activity.link', linkContent, { type: 'link', delegate: true }); return broker.publish('execution', 'execute.completed', cloneContent(executeContent)); }; LinkEventDefinition.prototype._onCatchLink = function onCatchLink(routingKey, message) { if (getPropertyValue(message, 'content.message.linkName') !== this.reference.linkName) return; if (message.content.state === 'discard') return this._discard(); return this._complete('caught', message.content.message); }; LinkEventDefinition.prototype._onApiMessage = function onApiMessage(routingKey, message) { const messageType = message.properties.type; switch (messageType) { case 'discard': { return this._discard(); } case 'stop': { this._stop(); break; } } }; LinkEventDefinition.prototype._complete = function complete(verb, output) { this[kCompleted] = true; this._stop(); this._debug(`${verb} link ${this.reference.linkName}`); const executeContent = this[kExecuteMessage].content; const parent = executeContent.parent; const catchContent = cloneContent(executeContent, { link: { ...this.reference }, message: { ...output }, executionId: parent.executionId, }); catchContent.parent = shiftParent(parent); const broker = this.broker; broker.publish('event', 'activity.catch', catchContent, { type: 'catch' }); return broker.publish('execution', 'execute.completed', cloneContent(executeContent, { output, state: 'catch' })); }; LinkEventDefinition.prototype._discard = function discard() { this[kCompleted] = true; this._stop(); return this.broker.publish('execution', 'execute.discard', cloneContent(this[kExecuteMessage].content)); }; LinkEventDefinition.prototype._stop = function stop() { const broker = this.broker, executionId = this.executionId; broker.cancel(`_api-link-${executionId}`); broker.cancel(`_api-parent-${executionId}`); broker.cancel(`_api-${executionId}`); this[kMessageQ].purge(); }; LinkEventDefinition.prototype._onDiscard = function onDiscard(_, message) { this.broker.publish( 'event', 'activity.link.discard', cloneContent(message.content, { message: { ...this.reference }, state: 'discard', }), { type: 'link', delegate: true } ); }; LinkEventDefinition.prototype._debug = function debug(msg) { this.logger.debug(`<${this.executionId} (${this.activity.id})> ${msg}`); };