bpmn-elements
Version:
Executable workflow elements based on BPMN 2.0
1,030 lines (1,029 loc) • 32.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _ActivityExecution = _interopRequireDefault(require("./ActivityExecution.js"));
var _shared = require("../shared.js");
var _Api = require("../Api.js");
var _EventBroker = require("../EventBroker.js");
var _MessageFormatter = require("../MessageFormatter.js");
var _messageHelper = require("../messageHelper.js");
var _Errors = require("../error/Errors.js");
var _outboundEvaluator = require("./outbound-evaluator.js");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const kActivityDef = Symbol.for('activityDefinition');
const kConsuming = Symbol.for('consuming');
const kConsumingRunQ = Symbol.for('run queue consumer');
const kCounters = Symbol.for('counters');
const kEventDefinitions = Symbol.for('eventDefinitions');
const kExec = Symbol.for('exec');
const kExecuteMessage = Symbol.for('executeMessage');
const kExtensions = Symbol.for('extensions');
const kFlags = Symbol.for('flags');
const kFlows = Symbol.for('flows');
const kFormatter = Symbol.for('formatter');
const kMessageHandlers = Symbol.for('messageHandlers');
const kStateMessage = Symbol.for('stateMessage');
const kActivated = Symbol.for('activated');
var _default = exports.default = Activity;
function Activity(Behaviour, activityDef, context) {
const {
id,
type = 'activity',
name,
behaviour = {}
} = activityDef;
const {
attachedTo: attachedToRef,
eventDefinitions
} = behaviour;
this[kActivityDef] = activityDef;
this.id = id;
this.type = type;
this.name = name;
this.behaviour = {
...behaviour,
eventDefinitions
};
this.Behaviour = Behaviour;
this.parent = activityDef.parent ? (0, _messageHelper.cloneParent)(activityDef.parent) : {};
this.logger = context.environment.Logger(type.toLowerCase());
this.environment = context.environment;
this.context = context;
this[kCounters] = {
taken: 0,
discarded: 0
};
const isForCompensation = !!behaviour.isForCompensation;
let attachedToActivity, attachedTo;
if (attachedToRef) {
attachedTo = attachedToRef.id;
attachedToActivity = context.getActivityById(attachedToRef.id);
}
const {
broker,
on,
once,
waitFor,
emitFatal
} = (0, _EventBroker.ActivityBroker)(this);
this.broker = broker;
this.on = on;
this.once = once;
this.waitFor = waitFor;
this.emitFatal = emitFatal;
const inboundSequenceFlows = context.getInboundSequenceFlows(id);
const inboundAssociations = context.getInboundAssociations(id);
let inboundTriggers;
if (attachedToActivity) {
inboundTriggers = [attachedToActivity];
} else if (isForCompensation) {
inboundTriggers = inboundAssociations.slice();
} else {
inboundTriggers = inboundSequenceFlows.slice();
}
const outboundSequenceFlows = context.getOutboundSequenceFlows(id);
const isParallelJoin = activityDef.isParallelGateway && inboundSequenceFlows.length > 1;
this[kFlows] = {
inboundSequenceFlows,
inboundAssociations,
inboundTriggers,
outboundSequenceFlows,
outboundEvaluator: new _outboundEvaluator.OutboundEvaluator(this, outboundSequenceFlows),
...(isParallelJoin && {
inboundJoinFlows: new Set(),
inboundSourceIds: new Set(inboundSequenceFlows.map(({
sourceId
}) => sourceId))
})
};
this[kFlags] = {
isEnd: !outboundSequenceFlows.length,
isStart: !inboundTriggers.length && !behaviour.triggeredByEvent,
isSubProcess: activityDef.isSubProcess,
isMultiInstance: !!behaviour.loopCharacteristics,
isForCompensation,
attachedTo,
isTransaction: activityDef.isTransaction,
isParallelJoin,
isThrowing: activityDef.isThrowing,
lane: activityDef.lane?.id
};
this[kExec] = new Map();
this[kMessageHandlers] = {
onInbound: isParallelJoin ? this._onJoinInbound.bind(this) : this._onInbound.bind(this),
onRunMessage: this._onRunMessage.bind(this),
onApiMessage: this._onApiMessage.bind(this),
onExecutionMessage: this._onExecutionMessage.bind(this)
};
this[kEventDefinitions] = eventDefinitions?.map((ed, idx) => new ed.Behaviour(this, ed, context, idx));
this[kExtensions] = context.loadExtensions(this);
this[kConsuming] = false;
this[kConsumingRunQ] = undefined;
}
Object.defineProperties(Activity.prototype, {
counters: {
get() {
return {
...this[kCounters]
};
}
},
execution: {
get() {
return this[kExec].get('execution');
}
},
executionId: {
get() {
return this[kExec].get('executionId');
}
},
extensions: {
get() {
return this[kExtensions];
}
},
bpmnIo: {
get() {
const extensions = this[kExtensions];
return extensions?.extensions.find(e => e.type === 'bpmnio');
}
},
formatter: {
get() {
let formatter = this[kFormatter];
if (formatter) return formatter;
formatter = this[kFormatter] = new _MessageFormatter.Formatter(this);
return formatter;
}
},
isRunning: {
get() {
if (!this[kConsuming]) return false;
return !!this.status;
}
},
outbound: {
get() {
return this[kFlows].outboundSequenceFlows;
}
},
inbound: {
get() {
return this[kFlows].inboundSequenceFlows;
}
},
isEnd: {
get() {
return this[kFlags].isEnd;
}
},
isStart: {
get() {
return this[kFlags].isStart;
}
},
isSubProcess: {
get() {
return this[kFlags].isSubProcess;
}
},
isTransaction: {
get() {
return this[kFlags].isTransaction;
}
},
isMultiInstance: {
get() {
return this[kFlags].isMultiInstance;
}
},
isThrowing: {
get() {
return this[kFlags].isThrowing;
}
},
isForCompensation: {
get() {
return this[kFlags].isForCompensation;
}
},
triggeredByEvent: {
get() {
return this[kActivityDef].triggeredByEvent;
}
},
attachedTo: {
get() {
const attachedToId = this[kFlags].attachedTo;
if (!attachedToId) return null;
return this.getActivityById(attachedToId);
}
},
lane: {
get() {
const laneId = this[kFlags].lane;
if (!laneId) return undefined;
const parent = this.parentElement;
return parent.getLaneById && parent.getLaneById(laneId);
}
},
eventDefinitions: {
get() {
return this[kEventDefinitions];
}
},
parentElement: {
get() {
return this.context.getActivityParentById(this.id);
}
}
});
Activity.prototype.activate = function activate() {
if (this[kActivated]) return;
this[kActivated] = true;
return this.addInboundListeners() && this._consumeInbound();
};
Activity.prototype.deactivate = function deactivate() {
this[kActivated] = false;
const broker = this.broker;
this.removeInboundListeners();
broker.cancel('_run-on-inbound');
broker.cancel('_format-consumer');
};
Activity.prototype.init = function init(initContent) {
const id = this.id;
const exec = this[kExec];
const executionId = exec.has('initExecutionId') ? exec.get('initExecutionId') : (0, _shared.getUniqueId)(id);
exec.set('initExecutionId', executionId);
this.logger.debug(`<${id}> initialized with executionId <${executionId}>`);
this._publishEvent('init', this._createMessage({
...initContent,
executionId
}));
};
Activity.prototype.run = function run(runContent) {
const id = this.id;
if (this.isRunning) throw new Error(`activity <${id}> is already running`);
const exec = this[kExec];
const executionId = exec.get('initExecutionId') || (0, _shared.getUniqueId)(id);
exec.set('executionId', executionId);
exec.delete('initExecutionId');
this._consumeApi();
const content = this._createMessage({
...runContent,
executionId
});
const broker = this.broker;
broker.publish('run', 'run.enter', content);
broker.publish('run', 'run.start', (0, _messageHelper.cloneContent)(content));
this[kConsuming] = true;
this._consumeRunQ();
};
Activity.prototype.getState = function getState() {
const status = this.status;
const exec = this[kExec];
const execution = exec.get('execution');
const executionId = exec.get('executionId');
const brokerState = this.broker.getState(true);
if (!brokerState && this.environment.settings.disableTrackState) return;
return {
id: this.id,
type: this.type,
...(status && {
status
}),
executionId,
stopped: this.stopped,
counters: this.counters,
broker: brokerState,
...(execution && {
execution: execution.getState()
})
};
};
Activity.prototype.recover = function recover(state) {
if (this.isRunning) throw new Error(`cannot recover running activity <${this.id}>`);
if (!state) return;
this.stopped = state.stopped;
this.status = state.status;
const exec = this[kExec];
exec.set('executionId', state.executionId);
this[kCounters] = {
...this[kCounters],
...state.counters
};
if (state.execution) {
exec.set('execution', new _ActivityExecution.default(this, this.context).recover(state.execution));
}
this.broker.recover(state.broker);
return this;
};
Activity.prototype.resume = function resume() {
if (this[kConsuming]) {
throw new Error(`cannot resume running activity <${this.id}>`);
}
if (!this.status) return this.activate();
this.stopped = false;
this._consumeApi();
const content = this._createMessage();
this.broker.publish('run', 'run.resume', content, {
persistent: false
});
this[kConsuming] = true;
this._consumeRunQ();
};
Activity.prototype.discard = function discard(discardContent) {
if (!this.status) return this._runDiscard(discardContent);
const execution = this[kExec].get('execution');
if (execution && !execution.completed) return execution.discard();
this._deactivateRunConsumers();
const broker = this.broker;
broker.getQueue('run-q').purge();
broker.publish('run', 'run.discard', (0, _messageHelper.cloneContent)(this[kStateMessage].content));
this[kConsuming] = true;
this._consumeRunQ();
};
Activity.prototype.addInboundListeners = function addInboundListeners() {
const triggers = this[kFlows].inboundTriggers;
if (triggers.length) {
const onInboundEvent = this._onInboundEvent.bind(this);
const triggerConsumerTag = `_inbound-${this.id}`;
for (const trigger of triggers) {
if (trigger.isSequenceFlow) {
trigger.broker.subscribeTmp('event', 'flow.#', onInboundEvent, {
noAck: true,
consumerTag: triggerConsumerTag
});
} else if (this.isForCompensation) {
trigger.broker.subscribeTmp('event', 'association.#', onInboundEvent, {
noAck: true,
consumerTag: triggerConsumerTag
});
} else {
trigger.broker.subscribeTmp('event', 'activity.#', onInboundEvent, {
noAck: true,
consumerTag: triggerConsumerTag
});
}
}
}
return triggers.length;
};
Activity.prototype.removeInboundListeners = function removeInboundListeners() {
const triggerConsumerTag = `_inbound-${this.id}`;
for (const trigger of this[kFlows].inboundTriggers) {
trigger.broker.cancel(triggerConsumerTag);
}
};
Activity.prototype.stop = function stop() {
if (!this[kConsuming]) return this.broker.cancel('_run-on-inbound');
return this.getApi(this[kStateMessage]).stop();
};
Activity.prototype.next = function next() {
if (!this.environment.settings.step) return;
const stateMessage = this[kStateMessage];
if (!stateMessage) return;
if (this.status === 'executing') return false;
if (this.status === 'formatting') return false;
const current = stateMessage;
stateMessage.ack();
return current;
};
Activity.prototype.shake = function shake() {
this._shakeOutbound({
content: this._createMessage()
});
};
Activity.prototype.evaluateOutbound = function evaluateOutbound(fromMessage, discardRestAtTake, callback) {
return this[kFlows].outboundEvaluator.evaluate(fromMessage, discardRestAtTake, callback);
};
Activity.prototype.getApi = function getApi(message) {
const execution = this[kExec].get('execution');
if (execution && !execution.completed) return execution.getApi(message);
return (0, _Api.ActivityApi)(this.broker, message || this[kStateMessage]);
};
Activity.prototype.getActivityById = function getActivityById(elementId) {
return this.context.getActivityById(elementId);
};
Activity.prototype._runDiscard = function runDiscard(discardContent) {
const exec = this[kExec];
const executionId = exec.get('initExecutionId') || (0, _shared.getUniqueId)(this.id);
exec.set('executionId', executionId);
exec.delete('initExecutionId');
this._consumeApi();
const content = this._createMessage({
...discardContent,
executionId
});
this.broker.publish('run', 'run.discard', content);
this[kConsuming] = true;
this._consumeRunQ();
};
Activity.prototype._discardRun = function discardRun() {
const status = this.status;
if (!status) return;
const execution = this[kExec].get('execution');
if (execution && !execution.completed) return;
let discardRoutingKey = 'run.discard';
switch (status) {
case 'executed':
{
discardRoutingKey = 'run.discarded';
break;
}
case 'end':
case 'executing':
case 'error':
case 'discarded':
return;
}
this._deactivateRunConsumers();
const stateMessage = this[kStateMessage];
if (this.extensions) this.extensions.deactivate((0, _messageHelper.cloneMessage)(stateMessage));
const broker = this.broker;
broker.getQueue('run-q').purge();
broker.publish('run', discardRoutingKey, (0, _messageHelper.cloneContent)(stateMessage.content), {
correlationId: stateMessage.properties.correlationId
});
this[kConsuming] = true;
this._consumeRunQ();
};
Activity.prototype._shakeOutbound = function shakeOutbound(sourceMessage) {
const message = (0, _messageHelper.cloneMessage)(sourceMessage);
message.content.sequence = message.content.sequence || [];
message.content.sequence.push({
id: this.id,
type: this.type
});
const broker = this.broker;
this.broker.publish('api', 'activity.shake.start', message.content, {
persistent: false,
type: 'shake'
});
if (this[kFlags].isEnd) {
return broker.publish('event', 'activity.shake.end', message.content, {
persistent: false,
type: 'shake'
});
}
for (const flow of this[kFlows].outboundSequenceFlows) flow.shake(message);
};
Activity.prototype._consumeInbound = function consumeInbound() {
if (!this[kActivated]) return;
if (this.status || !this[kFlows].inboundTriggers.length) return;
const inboundQ = this.broker.getQueue('inbound-q');
const onInbound = this[kMessageHandlers].onInbound;
if (this[kFlags].isParallelJoin) {
return inboundQ.assertConsumer(onInbound, {
consumerTag: '_run-on-inbound',
prefetch: 1000
});
}
return inboundQ.assertConsumer(onInbound, {
consumerTag: '_run-on-inbound'
});
};
Activity.prototype._onInbound = function onInbound(routingKey, message) {
message.ack();
const broker = this.broker;
broker.cancel('_run-on-inbound');
const content = message.content;
const inbound = [(0, _messageHelper.cloneContent)(content)];
switch (routingKey) {
case 'association.take':
case 'flow.take':
case 'activity.restart':
case 'activity.enter':
return this.run({
message: content.message,
inbound
});
case 'flow.discard':
case 'activity.discard':
{
let discardSequence;
if (content.discardSequence) discardSequence = content.discardSequence.slice();
return this._runDiscard({
inbound,
discardSequence
});
}
}
};
Activity.prototype._onJoinInbound = function onJoinInbound(routingKey, message) {
const {
content
} = message;
const {
inboundJoinFlows,
inboundSourceIds
} = this[kFlows];
let alreadyTouched = false;
const touched = new Set();
let taken;
for (const msg of inboundJoinFlows) {
const sourceId = msg.content.sourceId;
touched.add(sourceId);
if (sourceId === content.sourceId) {
alreadyTouched = true;
}
}
inboundJoinFlows.add(message);
if (alreadyTouched) return;
const remaining = inboundSourceIds.size - touched.size - 1;
if (remaining) {
return this.logger.debug(`<${this.id}> inbound ${message.content.action} from <${message.content.id}>, ${remaining} remaining`);
}
const inbound = [];
for (const im of inboundJoinFlows) {
if (im.fields.routingKey === 'flow.take') taken = true;
im.ack();
inbound.push((0, _messageHelper.cloneContent)(im.content));
}
const discardSequence = new Set();
if (!taken) {
for (const im of inboundJoinFlows) {
if (!im.content.discardSequence) continue;
for (const sourceId of im.content.discardSequence) {
discardSequence.add(sourceId);
}
}
}
inboundJoinFlows.clear();
this.broker.cancel('_run-on-inbound');
if (!taken) return this._runDiscard({
inbound,
discardSequence: [...discardSequence]
});
return this.run({
inbound
});
};
Activity.prototype._onInboundEvent = function onInboundEvent(routingKey, message) {
const {
fields,
content,
properties
} = message;
const inboundQ = this.broker.getQueue('inbound-q');
switch (routingKey) {
case 'activity.enter':
case 'activity.discard':
{
if (content.id === this[kFlags].attachedTo) {
inboundQ.queueMessage(fields, (0, _messageHelper.cloneContent)(content), properties);
}
break;
}
case 'flow.shake':
{
return this._shakeOutbound(message);
}
case 'association.take':
case 'flow.take':
case 'flow.discard':
return inboundQ.queueMessage(fields, (0, _messageHelper.cloneContent)(content), properties);
}
};
Activity.prototype._consumeRunQ = function consumeRunQ() {
this[kConsumingRunQ] = true;
this.broker.getQueue('run-q').assertConsumer(this[kMessageHandlers].onRunMessage, {
exclusive: true,
consumerTag: '_activity-run'
});
};
Activity.prototype._pauseRunQ = function pauseRunQ() {
if (!this[kConsumingRunQ]) return;
this[kConsumingRunQ] = false;
this.broker.cancel('_activity-run');
};
Activity.prototype._onRunMessage = function onRunMessage(routingKey, message, messageProperties) {
switch (routingKey) {
case 'run.outbound.discard':
case 'run.outbound.take':
case 'run.next':
return this._continueRunMessage(routingKey, message, messageProperties);
case 'run.resume':
{
return this._onResumeMessage(message);
}
}
const preStatus = this.status;
this.status = 'formatting';
return this.formatter.format(message, (err, formattedContent, formatted) => {
this.status = preStatus;
if (err) {
return this.emitFatal(err, message.content);
}
if (formatted) message.content = formattedContent;
this._continueRunMessage(routingKey, message, messageProperties);
});
};
Activity.prototype._continueRunMessage = function continueRunMessage(routingKey, message) {
const isRedelivered = message.fields.redelivered;
const content = (0, _messageHelper.cloneContent)(message.content);
const correlationId = message.properties.correlationId;
const id = this.id;
const step = this.environment.settings.step;
this[kStateMessage] = message;
switch (routingKey) {
case 'run.enter':
{
this.logger.debug(`<${id}> enter`, isRedelivered ? 'redelivered' : '');
this.status = 'entered';
if (!isRedelivered) {
this[kExec].delete('execution');
if (this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(message));
this._publishEvent('enter', content, {
correlationId
});
}
break;
}
case 'run.discard':
{
this.logger.debug(`<${id}> discard`, isRedelivered ? 'redelivered' : '');
this.status = 'discard';
this[kExec].delete('execution');
if (this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(message));
if (!isRedelivered) {
this.broker.publish('run', 'run.discarded', content, {
correlationId
});
this._publishEvent('discard', content);
}
break;
}
case 'run.start':
{
this.logger.debug(`<${id}> start`, isRedelivered ? 'redelivered' : '');
this.status = 'started';
if (!isRedelivered) {
this.broker.publish('run', 'run.execute', content, {
correlationId
});
this._publishEvent('start', content, {
correlationId
});
}
break;
}
case 'run.execute.passthrough':
{
const execution = this[kExec].get('execution');
if (!isRedelivered && execution) {
if (execution.completed) return message.ack();
this[kExecuteMessage] = message;
return execution.passthrough(message);
}
}
case 'run.execute':
{
this.status = 'executing';
this[kExecuteMessage] = message;
if (isRedelivered && this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(message));
const exec = this[kExec];
let execution = exec.get('execution');
if (!execution) {
execution = new _ActivityExecution.default(this, this.context);
exec.set('execution', execution);
}
this.broker.getQueue('execution-q').assertConsumer(this[kMessageHandlers].onExecutionMessage, {
exclusive: true,
consumerTag: '_activity-execution'
});
return execution.execute(message);
}
case 'run.end':
{
this.logger.debug(`<${id}> end`, isRedelivered ? 'redelivered' : '');
if (isRedelivered) break;
this[kCounters].taken++;
this.status = 'end';
return this._doRunLeave(message, false, () => {
this._publishEvent('end', content, {
correlationId
});
if (!step) message.ack();
});
}
case 'run.error':
{
this._publishEvent('error', {
...content,
error: isRedelivered ? (0, _Errors.makeErrorFromMessage)(message) : content.error
}, {
correlationId
});
break;
}
case 'run.discarded':
{
this.logger.debug(`<${content.executionId} (${id})> discarded`);
this[kCounters].discarded++;
this.status = 'discarded';
content.outbound = undefined;
if (!isRedelivered) {
return this._doRunLeave(message, true, () => {
if (!step) message.ack();
});
}
break;
}
case 'run.outbound.take':
{
const flow = this._getOutboundSequenceFlowById(content.flow.id);
message.ack();
return flow.take(content.flow);
}
case 'run.outbound.discard':
{
const flow = this._getOutboundSequenceFlowById(content.flow.id);
message.ack();
return flow.discard(content.flow);
}
case 'run.leave':
{
this.status = undefined;
if (this.extensions) this.extensions.deactivate((0, _messageHelper.cloneMessage)(message));
if (!isRedelivered) {
this.broker.publish('run', 'run.next', content, {
persistent: false
});
this._publishEvent('leave', content, {
correlationId
});
}
break;
}
case 'run.next':
message.ack();
this._pauseRunQ();
return this._consumeInbound();
}
if (!step) message.ack();
};
Activity.prototype._onExecutionMessage = function onExecutionMessage(routingKey, message) {
const executeMessage = this[kExecuteMessage];
const content = (0, _messageHelper.cloneContent)({
...executeMessage.content,
...message.content,
executionId: executeMessage.content.executionId,
parent: {
...this.parent
}
});
const {
correlationId
} = message.properties;
this._publishEvent(routingKey, content, message.properties);
const broker = this.broker;
switch (routingKey) {
case 'execution.outbound.take':
{
return this._doOutbound(message, false, (err, outbound) => {
message.ack();
if (err) return this.emitFatal(err, content);
broker.publish('run', 'run.execute.passthrough', (0, _messageHelper.cloneContent)(content, {
outbound
}));
return this._ackRunExecuteMessage();
});
}
case 'execution.error':
{
this.status = 'error';
broker.publish('run', 'run.error', content, {
correlationId
});
broker.publish('run', 'run.discarded', content, {
correlationId
});
break;
}
case 'execution.cancel':
case 'execution.discard':
this.status = 'discarded';
broker.publish('run', 'run.discarded', content, {
correlationId
});
break;
default:
{
this.status = 'executed';
broker.publish('run', 'run.end', content, {
correlationId
});
}
}
message.ack();
this._ackRunExecuteMessage();
};
Activity.prototype._ackRunExecuteMessage = function ackRunExecuteMessage() {
if (this.environment.settings.step) return;
const executeMessage = this[kExecuteMessage];
executeMessage.ack();
};
Activity.prototype._doRunLeave = function doRunLeave(message, isDiscarded, onOutbound) {
const {
content,
properties
} = message;
const correlationId = properties.correlationId;
if (content.ignoreOutbound) {
this.broker.publish('run', 'run.leave', (0, _messageHelper.cloneContent)(content), {
correlationId
});
return onOutbound();
}
return this._doOutbound((0, _messageHelper.cloneMessage)(message), isDiscarded, (err, outbound) => {
if (err) {
return this._publishEvent('error', {
...content,
error: err
}, {
correlationId
});
}
this.broker.publish('run', 'run.leave', (0, _messageHelper.cloneContent)(content, {
...(outbound.length && {
outbound
})
}), {
correlationId
});
onOutbound();
});
};
Activity.prototype._doOutbound = function doOutbound(fromMessage, isDiscarded, callback) {
const outboundSequenceFlows = this[kFlows].outboundSequenceFlows;
if (!outboundSequenceFlows.length) return callback(null, []);
const fromContent = fromMessage.content;
let discardSequence = fromContent.discardSequence;
if (isDiscarded && !discardSequence && this[kFlags].attachedTo && fromContent.inbound?.[0]) {
discardSequence = [fromContent.inbound[0].id];
}
let outboundFlows;
if (isDiscarded) {
outboundFlows = outboundSequenceFlows.map(flow => (0, _outboundEvaluator.formatFlowAction)(flow, {
action: 'discard'
}));
} else if (fromContent.outbound?.length) {
outboundFlows = outboundSequenceFlows.map(flow => (0, _outboundEvaluator.formatFlowAction)(flow, fromContent.outbound.filter(f => f.id === flow.id).pop()));
}
if (outboundFlows) {
this._doRunOutbound(outboundFlows, fromContent, discardSequence);
return callback(null, outboundFlows);
}
return this.evaluateOutbound(fromMessage, fromContent.outboundTakeOne, (err, evaluatedOutbound) => {
if (err) return callback(new _Errors.ActivityError(err.message, fromMessage, err));
const outbound = this._doRunOutbound(evaluatedOutbound, fromContent, discardSequence);
return callback(null, outbound);
});
};
Activity.prototype._doRunOutbound = function doRunOutbound(outboundList, content, discardSequence) {
if (outboundList.length === 1) {
this._publishRunOutbound(outboundList[0], content, discardSequence);
} else {
const targets = new Map();
for (const outboundFlow of outboundList) {
const prevTarget = targets.get(outboundFlow.targetId);
if (!prevTarget) {
targets.set(outboundFlow.targetId, outboundFlow);
} else if (outboundFlow.action === 'take' && outboundFlow.action !== prevTarget.action) {
targets.set(outboundFlow.targetId, outboundFlow);
}
}
for (const outboundFlow of targets.values()) {
this._publishRunOutbound(outboundFlow, content, discardSequence);
}
}
return outboundList;
};
Activity.prototype._publishRunOutbound = function publishRunOutbound(outboundFlow, content, discardSequence) {
const {
id: flowId,
action,
result
} = outboundFlow;
this.broker.publish('run', 'run.outbound.' + action, (0, _messageHelper.cloneContent)(content, {
flow: {
...(result && typeof result === 'object' && result),
...outboundFlow,
sequenceId: (0, _shared.getUniqueId)(`${flowId}_${action}`),
...(discardSequence && {
discardSequence: discardSequence.slice()
})
}
}));
};
Activity.prototype._onResumeMessage = function onResumeMessage(message) {
message.ack();
const stateMessage = this[kStateMessage];
const fields = stateMessage.fields;
if (!fields.redelivered) return;
switch (fields.routingKey) {
case 'run.enter':
case 'run.start':
case 'run.discarded':
case 'run.end':
case 'run.leave':
break;
default:
return;
}
if (this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(stateMessage));
this.logger.debug(`<${this.id}> resume from ${message.content.status}`);
return this.broker.publish('run', fields.routingKey, (0, _messageHelper.cloneContent)(stateMessage.content), stateMessage.properties);
};
Activity.prototype._publishEvent = function publishEvent(state, content, properties) {
this.broker.publish('event', `activity.${state}`, (0, _messageHelper.cloneContent)(content, {
state
}), {
...properties,
type: state,
mandatory: state === 'error'
});
};
Activity.prototype._onStop = function onStop(message) {
const running = this[kConsuming];
this.stopped = true;
this[kConsuming] = false;
const broker = this.broker;
this._pauseRunQ();
broker.cancel('_activity-api');
broker.cancel('_activity-execution');
broker.cancel('_run-on-inbound');
broker.cancel('_format-consumer');
if (this.extensions) this.extensions.deactivate((0, _messageHelper.cloneMessage)(message));
if (running) {
this._publishEvent('stop', this._createMessage(), {
persistent: false
});
}
};
Activity.prototype._consumeApi = function consumeApi() {
const executionId = this[kExec].get('executionId');
if (!executionId) return;
const broker = this.broker;
broker.cancel('_activity-api');
broker.subscribeTmp('api', `activity.*.${executionId}`, this[kMessageHandlers].onApiMessage, {
noAck: true,
consumerTag: '_activity-api',
priority: 100
});
};
Activity.prototype._onApiMessage = function onApiMessage(routingKey, message) {
switch (message.properties.type) {
case 'discard':
{
return this._discardRun(message);
}
case 'stop':
{
return this._onStop(message);
}
case 'shake':
{
return this._shakeOutbound(message);
}
}
};
Activity.prototype._createMessage = function createMessage(override) {
const {
name,
status,
parent
} = this;
const result = {
...override,
id: this.id,
type: this.type,
...(name && {
name
}),
...(status && {
status
}),
...(parent && {
parent: (0, _messageHelper.cloneParent)(parent)
})
};
for (const [flag, value] of Object.entries(this[kFlags])) {
if (value) result[flag] = value;
}
return result;
};
Activity.prototype._getOutboundSequenceFlowById = function getOutboundSequenceFlowById(flowId) {
return this[kFlows].outboundSequenceFlows.find(flow => flow.id === flowId);
};
Activity.prototype._deactivateRunConsumers = function _deactivateRunConsumers() {
const broker = this.broker;
broker.cancel('_activity-api');
this._pauseRunQ();
broker.cancel('_activity-execution');
this[kConsuming] = false;
};