Skip to content

Commit

Permalink
hoist definition output on error
Browse files Browse the repository at this point in the history
  • Loading branch information
paed01 committed Apr 10, 2024
1 parent 07e304a commit 3f3ee4c
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# 20.0.1

- hoist current definition environment output to engine environment output if run fails
- Major bump `bpmn-moddle@9`
- Minor bump [`[email protected]`](https://github.com/paed01/moddle-context-serializer/blob/master/CHANGELOG.md)
- Minor bump [`[email protected]`](https://github.com/paed01/bpmn-elements/blob/master/CHANGELOG.md)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)

[![Build Status](https://app.travis-ci.com/paed01/bpmn-engine.svg?branch=master)](https://app.travis-ci.com/paed01/bpmn-engine)[![Build status](https://ci.appveyor.com/api/projects/status/670n39fivq1g3nu5?svg=true)](https://ci.appveyor.com/project/paed01/bpmn-engine)[![Coverage Status](https://coveralls.io/repos/github/paed01/bpmn-engine/badge.svg?branch=master)](https://coveralls.io/github/paed01/bpmn-engine?branch=master)
[![Build](https://github.com/paed01/bpmn-engine/actions/workflows/build.yaml/badge.svg)](https://github.com/paed01/bpmn-engine/actions/workflows/build.yaml)[![Build status](https://ci.appveyor.com/api/projects/status/670n39fivq1g3nu5?svg=true)](https://ci.appveyor.com/project/paed01/bpmn-engine)[![Coverage Status](https://coveralls.io/repos/github/paed01/bpmn-engine/badge.svg?branch=master)](https://coveralls.io/github/paed01/bpmn-engine?branch=master)

# Introduction

Expand Down
2 changes: 1 addition & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- version -->

# 20.0.0 API Reference
# 20.0.1 API Reference

<!-- versionstop -->

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bpmn-engine",
"description": "BPMN 2.0 execution engine. Open source javascript workflow engine.",
"version": "20.0.0",
"version": "20.0.1",
"type": "module",
"module": "./src/index.js",
"main": "./lib/index.cjs",
Expand Down Expand Up @@ -54,6 +54,7 @@
"@bonniernews/hot-bev": "^0.4.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@types/bpmn-moddle": "^5.1.6",
"@types/node": "^18.19.31",
"bent": "^7.3.12",
"c8": "^9.1.0",
"camunda-bpmn-moddle": "^7.0.1",
Expand Down
36 changes: 20 additions & 16 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,30 +467,19 @@ Execution.prototype._onChildMessage = function onChildMessage(routingKey, messag

newState = 'idle';
break;
case 'definition.error':
case 'definition.error': {
this._saveOutput(owner.environment.output);
this._teardownDefinition(owner);
newState = 'error';
break;
}
case 'activity.wait': {
if (listener) listener.emit('wait', owner.getApi(message), this);
break;
}
case 'process.end': {
if (!message.content.output) break;
const environment = this.environment;

for (const key in message.content.output) {
switch (key) {
case 'data': {
environment.output.data = environment.output.data || {};
environment.output.data = { ...environment.output.data, ...message.content.output.data };
break;
}
default: {
environment.output[key] = message.content.output[key];
}
}
}
if (message.content.inbound) break;
this._saveOutput(message.content.output);
break;
}
}
Expand Down Expand Up @@ -532,6 +521,21 @@ Execution.prototype._teardownDefinition = function teardownDefinition(definition
definition.broker.cancel('_engine_definition');
};

Execution.prototype._saveOutput = function saveOutput(output) {
if (!output || typeof output !== 'object') return;

const environmentOutput = this.environment.output;

for (const key in output) {
if (key === 'data') {
const data = (environmentOutput.data = environmentOutput.data || {});
environmentOutput.data = { ...data, ...output.data };
} else {
environmentOutput[key] = output[key];
}
}
};

Execution.prototype.getState = function getState() {
const definitions = [];
for (const definition of this.definitions) {
Expand Down
115 changes: 115 additions & 0 deletions test/feature/engine-output-feature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Engine } from '../../src/index.js';

Feature('Engine output', () => {
Scenario('Process completes with output', () => {
let engine;
Given('a process with output', () => {
const source = `<?xml version="1.0" encoding="UTF-8"?>
<definitions id="script-definition" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" targetNamespace="http://bpmn.io/schema/bpmn">
<process id="my-process" isExecutable="true">
<scriptTask id="task" scriptFormat="javascript">
<script><![CDATA[
environment.output.foo = 'bar';
next();
]]>
</script>
</scriptTask>
</process>
</definitions>`;

engine = new Engine({ source });
});

let end;
When('ran', () => {
end = engine.waitFor('end');
engine.execute();
});

Then('run completes', () => {
return end;
});

And('engine state contain hoisted process output', async () => {
expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
});

Given('a process with call activity and a called process', () => {
const source = `<?xml version="1.0" encoding="UTF-8"?>
<definitions id="script-definition" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" targetNamespace="http://bpmn.io/schema/bpmn">
<process id="my-process" isExecutable="true">
<callActivity id="call-activity" calledElement="called-process" />
<sequenceFlow id="to-task" sourceRef="call-activity" targetRef="task" />
<scriptTask id="task" scriptFormat="javascript">
<script><![CDATA[
environment.output.foo = 'bar';
next();
]]>
</script>
</scriptTask>
</process>
<process id="called-process" isExecutable="false">
<scriptTask id="called-task" scriptFormat="javascript">
<script><![CDATA[
environment.output.bar = 'baz';
next();
]]>
</script>
</scriptTask>
</process>
</definitions>`;

engine = new Engine({ source });
});

When('ran', () => {
end = engine.waitFor('end');
engine.execute();
});

Then('run completes', () => {
return end;
});

And('engine state contain hoisted main process output', async () => {
expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
});
});

Scenario('process fails', () => {
let engine;
Given('a process with output', () => {
const source = `<?xml version="1.0" encoding="UTF-8"?>
<definitions id="script-definition" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" targetNamespace="http://bpmn.io/schema/bpmn">
<process id="my-process" isExecutable="true">
<scriptTask id="task" scriptFormat="javascript">
<script><![CDATA[
environment.output.foo = 'bar';
baz();
next();
]]>
</script>
</scriptTask>
</process>
</definitions>`;

engine = new Engine({ source });
});

let errored;
When('ran', () => {
errored = engine.waitFor('error');
engine.execute();
});

Then('run fails', () => {
return errored;
});

And('engine state contain hoisted process output', async () => {
expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
});
});
});
29 changes: 29 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"include": ["src/**/*", "types"],
"compilerOptions": {
"emitDeclarationOnly": true,
"sourceMap": false,
"rootDir": "src",
"lib": ["es2017"],
"target": "es2017",
"outDir": "./tmp/ignore",
"declaration": true,
"noEmitOnError": true,
"noErrorTruncation": true,
"allowJs": true,
"checkJs": false,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"stripInternal": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"typeRoots": ["./node_modules/@types"],
"paths": {
"types": ["./types/bpmn-engine.js"]
}
}
}
1 change: 1 addition & 0 deletions types/bpmn-engine.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ILogger,
EnvironmentState,
IScripts,
Script,
} from 'bpmn-elements';

declare module 'bpmn-engine' {
Expand Down

0 comments on commit 3f3ee4c

Please sign in to comment.