Skip to content

Commit

Permalink
cron time cycle by overloading parse function
Browse files Browse the repository at this point in the history
  • Loading branch information
paed01 committed Dec 9, 2023
1 parent ab0fce3 commit 860f34f
Show file tree
Hide file tree
Showing 19 changed files with 997 additions and 140 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"shared-node-browser": true,
"es6": true
},
"extends": "eslint:recommended",
"plugins": ["import"],
"extends": ["eslint:recommended", "plugin:import/recommended"],
"rules": {
"brace-style": [2, "1tbs", {
"allowSingleLine": false
Expand Down Expand Up @@ -37,6 +38,7 @@
"eol-last": 2,
"eqeqeq": 2,
"handle-callback-err": 2,
"import/extensions": [2, "always"],
"indent": [2, 2, {
"SwitchCase": 1
}],
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

# 6.0.0

- Require peer dependency `bpmn-elements >= 8`
- Support timer event definition cron time cycle by overloading parse function. Requires `bpmn-elements >= 10`

# 5.0.2

- Correct package module file...
Expand Down
5 changes: 4 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
declare module '@onify/flow-extensions' {
import { SequenceFlow, ElementBase, Context, IExtension } from 'bpmn-elements';
import { SequenceFlow, TimerEventDefinition, ElementBase, Context, IExtension } from 'bpmn-elements';
import { extendFn as extendFunction } from 'moddle-context-serializer';

export class OnifySequenceFlow extends SequenceFlow {}
export class OnifyTimerEventDefinition extends TimerEventDefinition {
readonly supports: string[];
}
export function extensions(element: ElementBase, context: Context): IExtension;
export const extendFn: extendFunction;
}
28 changes: 16 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onify/flow-extensions",
"version": "5.0.2",
"version": "6.0.0",
"description": "Onify Flow extensions",
"type": "module",
"module": "src/index.js",
Expand Down Expand Up @@ -38,26 +38,30 @@
"license": "MIT",
"devDependencies": {
"@aircall/expression-parser": "^1.0.4",
"@babel/cli": "^7.21.5",
"@babel/core": "^7.21.8",
"@babel/preset-env": "^7.21.5",
"@babel/register": "^7.21.0",
"@babel/cli": "^7.23.4",
"@babel/core": "^7.23.5",
"@babel/preset-env": "^7.23.5",
"@babel/register": "^7.22.15",
"bpmn-elements-8-1": "npm:[email protected]",
"bpmn-engine": "^16.0.0",
"bpmn-engine": "^17.1.1",
"bpmn-engine-14": "npm:bpmn-engine@14",
"bpmn-moddle": "^8.0.1",
"c8": "^7.13.0",
"c8": "^8.0.1",
"camunda-bpmn-moddle": "^7.0.1",
"chai": "^4.3.7",
"chronokinesis": "^5.0.2",
"chai": "^4.3.10",
"chronokinesis": "^6.0.0",
"debug": "^4.3.4",
"eslint": "^8.39.0",
"eslint": "^8.55.0",
"eslint-plugin-import": "^2.29.0",
"mocha": "^10.2.0",
"mocha-cakes-2": "^3.3.0",
"moddle-context-serializer": "^3.2.2"
"moddle-context-serializer": "^4.1.1"
},
"peerDependencies": {
"bpmn-elements": ">=8"
},
"dependencies": {
"cron-parser": "^4.8.1"
"cron-parser": "^4.9.0"
},
"files": [
"src",
Expand Down
25 changes: 25 additions & 0 deletions src/OnifyTimerEventDefinition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TimerEventDefinition } from 'bpmn-elements';
import cronParser from 'cron-parser';

export class OnifyTimerEventDefinition extends TimerEventDefinition {
constructor(activity, def) {
super(activity, def);
Object.defineProperty(this, 'supports', {
value: ['cron'],
});
}
parse(timerType, value) {
let cron;
if (timerType === 'timeCycle' && (cron = cronParser.parseString(value))) {
if (cron.expressions?.length) {
// cronParser.parseString expressions disregards seconds, so we have to parse again
const expireAt = cronParser.parseExpression(value).next().toDate();
return {
expireAt,
delay: expireAt - Date.now(),
};
}
}
return super.parse(timerType, value);
}
}
1 change: 1 addition & 0 deletions src/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class FormatActivity {
let timeCycles;
if (activity.eventDefinitions) {
for (const ed of activity.eventDefinitions.filter((e) => e.type === 'bpmn:TimerEventDefinition')) {
if (ed.supports?.includes('cron')) continue;
if (!('timeCycle' in ed)) continue;
timeCycles = timeCycles || [];
timeCycles.push(ed.timeCycle);
Expand Down
17 changes: 6 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import {OnifyProcessExtensions} from './OnifyProcessExtensions.js';
import {OnifyElementExtensions} from './OnifyElementExtensions.js';
import {OnifySequenceFlow} from './OnifySequenceFlow.js';
import { OnifyProcessExtensions } from './OnifyProcessExtensions.js';
import { OnifyElementExtensions } from './OnifyElementExtensions.js';
export { OnifySequenceFlow } from './OnifySequenceFlow.js';
export { OnifyTimerEventDefinition } from './OnifyTimerEventDefinition.js';

export {
extensions,
extendFn,
OnifySequenceFlow,
};

function extensions(element, context) {
export function extensions(element, context) {
if (element.type === 'bpmn:Process') return new OnifyProcessExtensions(element, context);
return new OnifyElementExtensions(element, context);
}

function extendFn(behaviour, context) {
export function extendFn(behaviour, context) {
if (behaviour.$type === 'bpmn:StartEvent' && behaviour.eventDefinitions) {
const timer = behaviour.eventDefinitions.find(({type, behaviour: edBehaviour}) => edBehaviour && type === 'bpmn:TimerEventDefinition');
if (timer && timer.behaviour.timeCycle) Object.assign(behaviour, {scheduledStart: timer.behaviour.timeCycle});
Expand Down
201 changes: 106 additions & 95 deletions test/extensions-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {default as Serializer, TypeResolver} from 'moddle-context-serializer';
import {extendFn} from '../src/index.js';
import {Serializer, TypeResolver} from 'moddle-context-serializer';
import * as flowExtensions from '../src/index.js';
import * as Elements from 'bpmn-elements';
import factory from './helpers/factory.js';
import testHelpers from './helpers/testHelpers.js';
Expand All @@ -10,90 +10,66 @@ describe('extensions', () => {
moddleExtensions = await testHelpers.getModdleExtensions();
});

it('extendFn registers scripts', async () => {
const source = factory.resource('activedirectory-index-users.bpmn');
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), extendFn);

expect(serialized.elements.scripts.length).to.equal(5);

for (const script of serialized.elements.scripts) {
expect(script, script.name).to.have.property('script');
expect(script.script, script.name).to.have.property('type').that.is.ok;
}
describe('exports', () => {
it('has expected export', () => {
expect(flowExtensions).to.have.property('extensions').that.is.a('function');
expect(flowExtensions).to.have.property('extendFn').that.is.a('function');
expect(flowExtensions).to.have.property('OnifySequenceFlow').that.is.a('function');
expect(flowExtensions).to.have.property('OnifyTimerEventDefinition').that.is.a('function');
});
});

it('extendFn registers extension scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="execlisteners" isExecutable="true">
<task id="task">
<extensionElements>
<camunda:executionListener event="start">
<camunda:script scriptFormat="js">next();</camunda:script>
</camunda:executionListener>
<camunda:executionListener event="end">
<camunda:script scriptFormat="js">next();</camunda:script>
</camunda:executionListener>
</extensionElements>
</task>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), extendFn);
describe('extendFn', () => {
it('extendFn registers scripts', async () => {
const source = factory.resource('activedirectory-index-users.bpmn');
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), flowExtensions.extendFn);

expect(serialized.elements.scripts.length).to.equal(2);
expect(serialized.elements.scripts.length).to.equal(5);

for (const script of serialized.elements.scripts) {
expect(script, script.name).to.have.property('script');
expect(script.script, script.name).to.have.property('type', 'camunda:ExecutionListener');
}
});
for (const script of serialized.elements.scripts) {
expect(script, script.name).to.have.property('script');
expect(script.script, script.name).to.have.property('type').that.is.ok;
}
});

it('extendFn registers io scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="process-1" name="Onify Flow" isExecutable="true">
<task id="service">
<extensionElements>
<camunda:inputOutput>
<camunda:inputParameter name="method">
<camunda:script scriptFormat="js">next(null, 'GET');</camunda:script>
</camunda:inputParameter>
<camunda:inputParameter name="url">/my/items/workspace-1</camunda:inputParameter>
<camunda:outputParameter name="result">
<camunda:script scriptFormat="js">next(null, { id: content.id, statuscode });</camunda:script>
</camunda:outputParameter>
</camunda:inputOutput>
</extensionElements>
</task>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), extendFn);
it('extendFn registers extension scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="execlisteners" isExecutable="true">
<task id="task">
<extensionElements>
<camunda:executionListener event="start">
<camunda:script scriptFormat="js">next();</camunda:script>
</camunda:executionListener>
<camunda:executionListener event="end">
<camunda:script scriptFormat="js">next();</camunda:script>
</camunda:executionListener>
</extensionElements>
</task>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), flowExtensions.extendFn);

const scripts = serialized.elements.scripts;
expect(scripts.length).to.equal(2);
expect(scripts[0], scripts[0].name).to.have.property('script');
expect(scripts[0].script, scripts[0].name).to.have.property('type', 'camunda:InputOutput/camunda:InputParameter');
expect(scripts[1], scripts[1].name).to.have.property('script');
expect(scripts[1].script, scripts[1].name).to.have.property('type', 'camunda:InputOutput/camunda:OutputParameter');
});
expect(serialized.elements.scripts.length).to.equal(2);

for (const script of serialized.elements.scripts) {
expect(script, script.name).to.have.property('script');
expect(script.script, script.name).to.have.property('type', 'camunda:ExecutionListener');
}
});

it('extendFn registers connector io scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="process-1" name="Onify Flow" isExecutable="true">
<serviceTask id="service">
<extensionElements>
<camunda:connector>
<camunda:connectorId>onifyApiRequest</camunda:connectorId>
it('extendFn registers io scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="process-1" name="Onify Flow" isExecutable="true">
<task id="service">
<extensionElements>
<camunda:inputOutput>
<camunda:inputParameter name="method">
<camunda:script scriptFormat="js">next(null, 'GET');</camunda:script>
Expand All @@ -103,22 +79,57 @@ describe('extensions', () => {
<camunda:script scriptFormat="js">next(null, { id: content.id, statuscode });</camunda:script>
</camunda:outputParameter>
</camunda:inputOutput>
</camunda:connector>
<camunda:inputOutput>
<camunda:outputParameter name="result">\${content.output.result.statuscode}</camunda:outputParameter>
</camunda:inputOutput>
</extensionElements>
</serviceTask>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), extendFn);
</extensionElements>
</task>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), flowExtensions.extendFn);

const scripts = serialized.elements.scripts;
expect(scripts.length).to.equal(2);
expect(scripts[0], scripts[0].name).to.have.property('script');
expect(scripts[0].script, scripts[0].name).to.have.property('type', 'camunda:InputOutput/camunda:InputParameter');
expect(scripts[1], scripts[1].name).to.have.property('script');
expect(scripts[1].script, scripts[1].name).to.have.property('type', 'camunda:InputOutput/camunda:OutputParameter');
});

it('extendFn registers connector io scripts with type', async () => {
const source = `
<definitions id="def_0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
targetNamespace="http://bpmn.io/schema/bpmn">
<process id="process-1" name="Onify Flow" isExecutable="true">
<serviceTask id="service">
<extensionElements>
<camunda:connector>
<camunda:connectorId>onifyApiRequest</camunda:connectorId>
<camunda:inputOutput>
<camunda:inputParameter name="method">
<camunda:script scriptFormat="js">next(null, 'GET');</camunda:script>
</camunda:inputParameter>
<camunda:inputParameter name="url">/my/items/workspace-1</camunda:inputParameter>
<camunda:outputParameter name="result">
<camunda:script scriptFormat="js">next(null, { id: content.id, statuscode });</camunda:script>
</camunda:outputParameter>
</camunda:inputOutput>
</camunda:connector>
<camunda:inputOutput>
<camunda:outputParameter name="result">\${content.output.result.statuscode}</camunda:outputParameter>
</camunda:inputOutput>
</extensionElements>
</serviceTask>
</process>
</definitions>`;
const moddleContext = await testHelpers.moddleContext(source, moddleExtensions);
const serialized = Serializer(moddleContext, TypeResolver(Elements), flowExtensions.extendFn);

const scripts = serialized.elements.scripts;
expect(scripts.length).to.equal(2);
expect(scripts[0], scripts[0].name).to.have.property('script');
expect(scripts[0].script, scripts[0].name).to.have.property('type', 'camunda:Connector/camunda:InputParameter');
expect(scripts[1], scripts[1].name).to.have.property('script');
expect(scripts[1].script, scripts[1].name).to.have.property('type', 'camunda:Connector/camunda:OutputParameter');
const scripts = serialized.elements.scripts;
expect(scripts.length).to.equal(2);
expect(scripts[0], scripts[0].name).to.have.property('script');
expect(scripts[0].script, scripts[0].name).to.have.property('type', 'camunda:Connector/camunda:InputParameter');
expect(scripts[1], scripts[1].name).to.have.property('script');
expect(scripts[1].script, scripts[1].name).to.have.property('type', 'camunda:Connector/camunda:OutputParameter');
});
});
});
2 changes: 1 addition & 1 deletion test/features/connector-feature.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ck from 'chronokinesis';
import * as ck from 'chronokinesis';
import testHelpers from '../helpers/testHelpers.js';
import factory from '../helpers/factory.js';

Expand Down
2 changes: 1 addition & 1 deletion test/features/engine-feature.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ck from 'chronokinesis';
import * as ck from 'chronokinesis';
import factory from '../helpers/factory.js';
import testHelpers from '../helpers/testHelpers.js';
import {EventEmitter} from 'events';
Expand Down
2 changes: 1 addition & 1 deletion test/features/execution-listener-feature.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {default as Serializer, TypeResolver} from 'moddle-context-serializer';
import {Serializer, TypeResolver} from 'moddle-context-serializer';
import {extendFn} from '../../src/index.js';
import * as Elements from 'bpmn-elements';
import factory from '../helpers/factory.js';
Expand Down
Loading

0 comments on commit 860f34f

Please sign in to comment.