diff --git a/CHANGELOG.md b/CHANGELOG.md index ee3a802..9c43692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 [`moddle-context-serializer@4.2`](https://github.com/paed01/moddle-context-serializer/blob/master/CHANGELOG.md) - Minor bump [`bpmn-elements@13.2.0`](https://github.com/paed01/bpmn-elements/blob/master/CHANGELOG.md) diff --git a/README.md b/README.md index 2a74224..6ce2cc3 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/API.md b/docs/API.md index 728954f..b1ffc5f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,6 +1,6 @@ -# 20.0.0 API Reference +# 20.0.1 API Reference diff --git a/package.json b/package.json index 556f063..533fdd4 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/src/index.js b/src/index.js index 645a170..ae0ed35 100644 --- a/src/index.js +++ b/src/index.js @@ -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; } } @@ -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) { diff --git a/test/feature/engine-output-feature.js b/test/feature/engine-output-feature.js new file mode 100644 index 0000000..3e713ab --- /dev/null +++ b/test/feature/engine-output-feature.js @@ -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 = ` + + + + + + + `; + + 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 = ` + + + + + + + + + + + + + + `; + + 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 = ` + + + + + + + `; + + 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' }); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8201c9f --- /dev/null +++ b/tsconfig.json @@ -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"] + } + } +} diff --git a/types/bpmn-engine.d.ts b/types/bpmn-engine.d.ts index 69e1786..403a424 100644 --- a/types/bpmn-engine.d.ts +++ b/types/bpmn-engine.d.ts @@ -16,6 +16,7 @@ import { ILogger, EnvironmentState, IScripts, + Script, } from 'bpmn-elements'; declare module 'bpmn-engine' {