diff --git a/demo/App.jsx b/demo/App.jsx index 047a855..97a1253 100644 --- a/demo/App.jsx +++ b/demo/App.jsx @@ -218,6 +218,10 @@ function App() { .then(response => response.json()); }; + const cancelProcessInstance = async (processInstanceKey) => { + return fetch(`/api/cancelProcessInstance/${processInstanceKey}`, { method: 'POST' }); + }; + const { current: onConfigChanged } = useRef(debounce(config => setConfig(config), 300)); // eslint-disable-next-line no-undef @@ -246,7 +250,8 @@ function App() { startInstance, getProcessInstance, getProcessInstanceVariables, - getProcessInstanceIncident + getProcessInstanceIncident, + cancelProcessInstance } } config={ config } onConfigChanged={ onConfigChanged } diff --git a/demo/server.mjs b/demo/server.mjs index 9566ee8..f434f16 100644 --- a/demo/server.mjs +++ b/demo/server.mjs @@ -165,6 +165,26 @@ app.get('/api/getProcessInstanceIncident/:processInstanceKey', async (req, res) } }); +app.post('/api/cancelProcessInstance/:processInstanceKey', async (req, res) => { + try { + if (!camunda) { + return res.json({ success: false, error: 'Camunda environment not configured' }); + } + + const { processInstanceKey } = req.params; + + const client = camunda.getCamundaRestClient(); + + const response = await client.cancelProcessInstance({ + processInstanceKey + }); + + res.json({ success: true, response }); + } catch (err) { + res.status(500).json({ success: false, error: err.message, source: err.source }); + } +}); + const PORT = process.env.PORT || 3000; app.listen(PORT, () => { diff --git a/lib/TaskExecution.js b/lib/TaskExecution.js index b8f65f6..640f48c 100644 --- a/lib/TaskExecution.js +++ b/lib/TaskExecution.js @@ -39,6 +39,8 @@ export default class TaskExecution extends EventEmitter { /** @type {TaskExecutionStatus} */ this._status = 'idle'; + this._processInstanceKey = null; + const eventBus = injector.get('eventBus'); eventBus.on([ 'selection.changed', 'commandStack.changed' ], () => { @@ -99,7 +101,7 @@ export default class TaskExecution extends EventEmitter { return; } - const processInstanceKey = getProcessInstanceKey(startInstanceResult.response); + const processInstanceKey = this._processInstanceKey = getProcessInstanceKey(startInstanceResult.response); if (!processInstanceKey) { this._emitError('Failed to retrieve process instance key from start instance response'); @@ -157,6 +159,8 @@ export default class TaskExecution extends EventEmitter { const isCompleted = [ 'COMPLETED', 'TERMINATED', 'CANCELED' ].includes(state); if (isCompleted || hasIncident) { + this._processInstanceKey = null; + const getProcessInstanceVariablesResult = await this._api.getProcessInstanceVariables(processInstanceKey); if (/** @type {TaskExecutionStatus} */ (this._status) === 'idle') { @@ -199,6 +203,11 @@ export default class TaskExecution extends EventEmitter { clearInterval(this._interval); } + if (this._processInstanceKey) { + this._api.cancelProcessInstance(this._processInstanceKey); + this._processInstanceKey = null; + } + this._changeStatus('idle'); } diff --git a/lib/types.d.ts b/lib/types.d.ts index 1b3e56e..9aba8be 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -78,6 +78,7 @@ export type TaskExecutionApi = { getProcessInstance: (processInstanceKey: string) => Promise; getProcessInstanceVariables: (processInstanceKey: string) => Promise; getProcessInstanceIncident: (processInstanceKey: string) => Promise; + cancelProcessInstance: (processInstanceKey: string) => Promise; }; export type TaskExecutionStatus = diff --git a/test/TaskExecution.spec.js b/test/TaskExecution.spec.js index 6597e9a..ea047e8 100644 --- a/test/TaskExecution.spec.js +++ b/test/TaskExecution.spec.js @@ -30,7 +30,8 @@ describe('TaskExecution', function() { startInstance: sinon.stub().resolves({ success: false, error: 'Not implemented' }), getProcessInstance: sinon.stub().resolves({ success: false, error: 'Not implemented' }), getProcessInstanceVariables: sinon.stub().resolves({ success: false, error: 'Not implemented' }), - getProcessInstanceIncident: sinon.stub().resolves({ success: false, error: 'Not implemented' }) + getProcessInstanceIncident: sinon.stub().resolves({ success: false, error: 'Not implemented' }), + cancelProcessInstance: sinon.stub().resolves({ success: false, error: 'Not implemented' }) }; taskExecution = new TaskExecution(injector, api); @@ -73,6 +74,7 @@ describe('TaskExecution', function() { expect(api.getProcessInstance).to.have.been.calledOnce; expect(api.getProcessInstanceVariables).to.have.been.calledOnce; expect(api.getProcessInstanceIncident).to.not.have.been.called; + expect(api.cancelProcessInstance).to.not.have.been.called; expect(finishedSpy).to.have.been.calledWithMatch({ 'incident': null, @@ -107,6 +109,7 @@ describe('TaskExecution', function() { expect(api.getProcessInstance).to.have.been.calledTwice; expect(api.getProcessInstanceVariables).to.have.been.calledOnce; expect(api.getProcessInstanceIncident).to.not.have.been.called; + expect(api.cancelProcessInstance).to.not.have.been.called; expect(finishedSpy).to.have.been.calledWithMatch({ 'incident': null, @@ -417,6 +420,7 @@ describe('TaskExecution', function() { expect(errorSpy).to.not.have.been.called; expect(api.deploy).to.have.been.calledOnce; expect(api.startInstance).to.have.been.calledOnce; + expect(api.cancelProcessInstance).to.have.been.calledOnce; expect(api.getProcessInstance).to.not.have.been.called; expect(api.getProcessInstanceVariables).to.not.have.been.called; expect(api.getProcessInstanceIncident).to.not.have.been.called;