bpmn-elements
Version:
Executable workflow elements based on BPMN 2.0
189 lines (185 loc) • 6.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = ConditionalEventDefinition;
var _messageHelper = require("../messageHelper.js");
var _Errors = require("../error/Errors.js");
var _condition = require("../condition.js");
const kExecuteMessage = Symbol.for('executeMessage');
function ConditionalEventDefinition(activity, eventDefinition, _context, index) {
const {
id,
broker,
environment
} = activity;
const {
type = 'ConditionalEventDefinition',
behaviour = {}
} = eventDefinition;
this.id = id;
this.type = type;
this.behaviour = behaviour;
this.activity = activity;
this.environment = environment;
this.broker = broker;
this.logger = environment.Logger(type.toLowerCase());
this.condition = this.getCondition(index);
}
Object.defineProperty(ConditionalEventDefinition.prototype, 'executionId', {
get() {
return this[kExecuteMessage]?.content.executionId;
}
});
ConditionalEventDefinition.prototype.execute = function execute(executeMessage) {
this[kExecuteMessage] = executeMessage;
if (!this.condition) return this._setup(executeMessage);
this.evaluate(executeMessage, (err, result) => {
this.evaluateCallback(err, result);
if (!err && !result) {
this._setup(executeMessage);
}
});
};
ConditionalEventDefinition.prototype._setup = function setup(executeMessage) {
const broker = this.broker;
const executeContent = executeMessage.content;
const {
executionId,
parent
} = executeContent;
const parentExecutionId = parent.executionId;
const onApiMessage = this._onApiMessage.bind(this);
broker.subscribeTmp('api', `activity.#.${executionId}`, onApiMessage, {
noAck: true,
consumerTag: `_api-${executionId}`
});
broker.subscribeTmp('api', `activity.#.${parentExecutionId}`, onApiMessage, {
noAck: true,
consumerTag: `_parent-signal-${executionId}`
});
broker.subscribeTmp('api', '#.signal.*', this._onDelegateApiMessage.bind(this), {
noAck: true,
consumerTag: `_api-delegated-${executionId}`
});
const waitContent = (0, _messageHelper.cloneContent)(executeContent, {
executionId: parentExecutionId,
...(this.condition && {
condition: this.condition.type
})
});
waitContent.parent = (0, _messageHelper.shiftParent)(parent);
broker.publish('event', 'activity.wait', waitContent);
};
/**
* Evaluate condition
* @param {import('types').ElementBrokerMessage} message
* @param {CallableFunction} callback
*/
ConditionalEventDefinition.prototype.evaluate = function evaluate(message, callback) {
const condition = this.condition;
if (!condition) {
this._debug(`condition is empty <${condition}>`);
return callback();
}
condition.execute(message, callback);
};
/**
* Handle evaluate result or error
* @param {Error|null} err Condition evaluation error
* @param {any} result Result from evaluated condition, completes execution if truthy
*/
ConditionalEventDefinition.prototype.evaluateCallback = function evaluateCallback(err, result) {
const broker = this.broker;
const executeMessage = this[kExecuteMessage];
const executeContent = executeMessage.content;
if (err) {
return broker.publish('execution', 'execute.error', (0, _messageHelper.cloneContent)(executeContent, {
error: new _Errors.ActivityError(err.message, executeMessage, err)
}, {
mandatory: true
}));
}
this._debug(`condition evaluated to ${!!result}`);
this.broker.publish('event', 'activity.condition', (0, _messageHelper.cloneContent)(this[kExecuteMessage].content, {
conditionResult: result
}));
if (!result) return;
this._stop();
return broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeContent, {
output: result
}));
};
/**
* Get condition
* @param {number} index Eventdefinition sequence number, used to name registered script
* @returns {ExpressionCondition|ScriptCondition|null}
*/
ConditionalEventDefinition.prototype.getCondition = function getCondition(index) {
const behaviour = this.behaviour;
if (behaviour.script) {
const {
language,
body,
resource
} = behaviour.script;
const scriptId = `${this.id}/${index}`;
const script = this.environment.scripts.register({
id: scriptId,
type: this.type,
environment: this.environment,
behaviour: {
scriptFormat: language,
...(body && {
script: body
}),
...(resource && {
resource
})
}
});
if (script) {
return new _condition.ScriptCondition(this, script, language);
}
} else if (behaviour.expression) {
return new _condition.ExpressionCondition(this, behaviour.expression);
}
};
ConditionalEventDefinition.prototype._onDelegateApiMessage = function onDelegateApiMessage(routingKey, message) {
if (message.content.message?.id === this.id) {
this._onApiMessage(routingKey, message);
}
};
ConditionalEventDefinition.prototype._onApiMessage = function onApiMessage(routingKey, message) {
const messageType = message.properties.type;
switch (messageType) {
case 'signal':
{
if (!this.condition) break;
return this.evaluate(message, (err, result) => this.evaluateCallback(err, result));
}
case 'discard':
{
this._stop();
this._debug('discarded');
return this.broker.publish('execution', 'execute.discard', (0, _messageHelper.cloneContent)(this[kExecuteMessage].content, {
state: 'discard'
}));
}
case 'stop':
{
this._stop();
return this._debug('stopped');
}
}
};
ConditionalEventDefinition.prototype._stop = function stop() {
const executionId = this.executionId;
const broker = this.broker;
broker.cancel(`_api-${executionId}`);
broker.cancel(`_parent-signal-${executionId}`);
broker.cancel(`_api-delegated-${executionId}`);
};
ConditionalEventDefinition.prototype._debug = function debug(msg) {
this.logger.debug(`<${this.executionId} (${this.activity.id})> ${msg}`);
};