bpmn-elements
bpmn-elements copied to clipboard
Export internal Behaviour classes
Hi
I'm using bpmn-engine to execute the whole process flow in my application, but the execution of any script expressions (e.g. the code of a ScriptTask or the condition of a SequenceFlow) needs to be executed by my application. Furthermore, I need a bit more control over how activities are generally executed.
As a proof of concept, I have therefore copied & pasted the implementation of ScriptTaskBehaviour and used my custom implementation in the engine, which is working well so far. Now I'm not a big fan of copying and pasting and would like to do this in a cleaner way by extending the default ScriptTaskBehaviour rather than copying it over.
For this to work, the package would however need to export the Behaviour classes, which it currently doesn't. Would you be open to changing this minor detail?
For reference, what I'm currently doing:
const engine = Engine({
source,
listener,
elements: {
ScriptTask: MyScriptTask,
}
})
// -------------------------
import * as BpmnElements from 'bpmn-elements'
export function MyScriptTask(activityDefinition: any, context: any) {
return new (BpmnElements as any).Activity(MyScriptTaskBehaviour, activityDefinition, context)
}
class MyScriptTaskBehaviour {
loopCharacteristics
constructor(private activity, private context) {
this.loopCharacteristics = activity.behaviour.loopCharacteristics && new activity.behaviour.loopCharacteristics.Behaviour(this.activity, activity.behaviour.loopCharacteristics);
}
async execute(executeMessage) {
const { content } = executeMessage
const { id, type, broker, behaviour } = this.activity
const { environment } = this.context
if (this.loopCharacteristics && content.isRootScope) {
return this.loopCharacteristics.execute(executeMessage);
}
try {
await myCustomFunctionProvidedByMyApplication()
broker.publish('execution', 'execute.completed', { ...content })
} catch(err) {
// handle error according to https://github.com/paed01/bpmn-engine/issues/182
}
}
}
and what I'd like to do instead:
const engine = Engine({
source,
listener,
elements: {
ScriptTask: MyScriptTask,
}
})
// -------------------------
import { Activity, ScriptTaskBehaviour } from 'bpmn-elements' // Activity is already exported but not included in the .d.ts type declarations
export function MyScriptTask(activityDefinition: any, context: any) {
return new Activity(MyScriptTaskBehaviour, activityDefinition, context)
}
class MyScriptTaskBehaviour extends ScriptTaskBehaviour {
async execute(executeMessage) {
// same code as in the previous example
}
}
PS: I know that there's little code reuse in the above example as ScriptTaskBehaviour doesn't contain much more than the execute method, but I'll also need to override the behaviours of a few other similar cases where it's not as straight-forward (e.g. StartEventBehaviour).
Thanks for your support and the great work you've already put into this!
I see your point. I'll attempt to achieve this in an elegant way.
I have published the new version as rc, npm i bpmn-elements@rc
.
Struggled with the type definitions so I don't dare to make it latest yet.
Exposed the behaviors categorised by type of activity, e.g:
import { ScriptTaskBehaviour } from 'bpmn-elements/tasks';
import { EndEventBehaviour } from 'bpmn-elements/events';
import { ParallelGatewayBehaviour } from 'bpmn-elements/gateways';
Thank you very much for these changes :-), this now works much better for my use case. Special thanks for creating all these TS declarations that truly help understand the internals of this library and use it much more easily. The only remaining place I currently have to monkey-patch something is the Activity; as this is exported as interface and not as class, I'm using this slightly nasty hack:
import * as BpmnElements from 'bpmn-elements'
import { Activity, IActivityBehaviour } from 'bpmn-elements/types'
// a bit of hacky type manipulation...
const Activity = (BpmnElements as any).Activity as new (Behaviour: IActivityBehaviour, activityDef: any, context: any) => Activity
export function MyScriptTask(activityDefinition: any, context: any) {
// ... so that I don't have any hacks here
return new Activity(MyScriptTaskBehaviour, activityDefinition, context)
}
A quick win could be to turn Activity from interface into a class and add a constructor (ideally with better type annotations for activityDef/context):
declare class Activity extends Element<Activity> {
constructor(Behaviour: IActivityBehaviour, activityDef: any, context: any)
...
}
So much for my inputs :-). Again, I'm super happy and thankful for your changes!
The new version should now expose Activity
as a class - npm i bpmn-elements@latest
. Feel free to try it out.
Any luck with the type definitions?