Closed kstan79 closed 11 months ago
assignment
make sense create
and execution `start'
Cause we need to read data submited by invoke api.
Don't understand, your own app invoke the api ? and provide data to api
Web app: POST action process form data and calls api.engine.invoke(...,data,,) Like here:
router.post('/invokeItem', awaitAppDelegateFactory(async (request, response) => {
let id = request.body.itemId;
let data = {};
Object.entries(request.body).forEach(entry => {
if (entry[0] == 'itemId') { }
else {
data[entry[0]] = entry[1];
}
});
try {
let result = await bpmnAPI.engine.invoke({ "items.id": id }, data, getSecureUser(request));
response.redirect('/instanceDetails?id=' + result.execution.id);
}
catch (exc) {
response.send(exc.toString());
}
}));
To Illustrate some more about events sequence:
Model is: StartEvent_1 Request:user Task boundaryEvent Approve:user Task
Sequence of Execution:
---calling start----- ---Event: --> process.start ---Event: -->StartEvent_1 transformInput ---Event: -->StartEvent_1 enter ---Event: -->StartEvent_1 start ---Event: --> process.saving ---Event: -->StartEvent_1 end ---Event: -->Request enter ---Event: -->Request start* ---Event: -->Request wait ---Event: --> process.wait ---Event: --> process.saving
---calling assign----- ---Event: --> process.restored ---Event: -->Request assignment* ---Event: --> process.saving
---calling invoke----- ---Event: --> process.restored ---Event: --> process.invoke
---Event: -->Request transformInput ---Event: -->Request end*
---Event: -->Approve enter ---Event: -->Approve start ---Event: -->boundaryEvent enter ---Event: -->boundaryEvent start ---Event: -->boundaryEvent wait ---Event: -->Approve wait ---Event: --> process.invoked ---Event: --> process.saving
hm... there is a few comment at the diagram (which is side topic)
request
having 2 incoming, which should connect using gateway ?Come back to topic:
start
and invoke
using same api, with different parametersinvoke
from remote api client (like swagger-ui/postman)
a. since bpmn server hosted at backend, bpmn client only can submit api call together with data. Example post http://localhost:8000/api/invoke/suspendcustomer
b. at server side, we create a some delegate function likedelegateSupervisorApprove
, and wish to bind at user-task:invoke
, show job done to users.
d. at backend, user task involve many flow as below (which i feel task listener and execution redundant with), and I try to give feasible implementation of each type
i) workflow init user task (create) -> log into database, obtain some runtime variables
ii) workflow resolve user/group (assignment) -> resolve dynamic user become real person, send notification to users
iii) after user task invoke by user (complete) -> receive input from user, task completed, but yet to go next step (example we may want to reverse back to previous status due to validation error)
iv) after user task end, no longer under 'wait' (delete), send notification to process initiator say someone proceed to next step successfully
beside direct flow, i think below is special flow?
v) update (not invoke workflow, changing of user task properties only), like swap users, change due date, change priority
vi) timeout (as we know this is use by timer).My opinion: base on observation, seems all above action make sense (not only assign), and correct me if I'm wrong.
assign
you mentioned will trigger (v)? If this is the case seems assign
need rename to avoid confusion.Conclusion:
1. I suggest all the event type (i-vi) just follow same way as service task. put user invoke's `vars/data` & context inside
2. ignore `field injection`, as it is pandora box same like `form`
3. may rename `assign` to `update` if appropriate
Regarding the Diagram above:
Require Information
sequenceFlow, initiate a new instance of the Request
Task
Reminder
has no impact on the workflow, i.e. does not terminate or end, but you can have non-terminating end
event if you like.
Regarding Sequence of logic, currently bpmn-server support the following:
User Task
is reached:
a. Item createdstart
execution delegate is called
d. Item goes into wait
state
e. Workflow is savedSo far, data and assignment are not done, unless created by start
No impact on workflow state
end
execution delegate is called
d. Task is now complete, status =end
e. Workflow proceeds with executionService Task
is reached:
a. Item createdstart
execution delegate is called
d. Service Task is invoked using delegate specified
e. end
execution delegate is called
f. Workflow proceeds with executionassign
to update
, but it needs an delegate as wellvalidate
delegate to be fired after invoke and update?Thanks
I try to understand your reply but i think we are talking different thing. I recommend we shall support all task listener event type, you reply current design pattern.
I notice that invoke
maintain in Engine.ts
, but the actual effected place is Execution.ts
. There is some confusion and naming terminology and design, and I try to propose easiest implementation way I can imagine.
enum ITEM_STATUS {
enter = 'enter',
start = 'start',
wait = 'wait',
end = 'end',
terminated = 'terminated',
cancelled = 'cancelled',
discard = 'discard'
}
enum EXECUTION_EVENT {
node_enter = 'enter', node_start = 'start', node_wait = 'wait', node_end = 'end', node_terminated = 'terminated',
transform_input = 'transformInput', transform_output ='transformOutput',
flow_take = 'take', flow_discard = 'discard',
process_loaded ='process.loaded',
process_start = 'process.start',
process_started = 'process.started',
process_invoke = 'process.invoke',
process_invoked = 'process.invoked',
process_saving = 'process.saving',
process_restored = 'process.restored',
process_resumed = 'process_resumed',
process_wait = 'process.wait',
process_end = 'process.end', process_terminated = 'process.terminated' ,
token_start = 'token.start', token_wait = 'token.wait', token_end = 'token.end', token_terminated = 'token.terminated'
}
create new enum specific for user-task TASKLISTENER
```ts
enum TASKLISTENER_EVENT{
create='create',
assign='assign',
complete='complete',
delete='delete',
update='update',
timeout='timeout' // may not have any place to track timeout yet
}
async doExecutionEvent(process,event) {
//this.item = null;
await this.listener.emit(event, { event, context: this });
switch(event){
case EXECUTION_EVENT.invoke:
await this.listener.emit(event, { TASKLISTENER_EVENT.complete, context: this });
break;
case. EXECUTION_EVENT.invoked:
await this.listener.emit(event, { TASKLISTENER_EVENT.delete, context: this });
break;
}
await this.listener.emit('all', { event, context: this });
}
async doItemEvent(item, event) {
this.item = item;
await this.listener.emit(event, { event, context: this });
// I don't know event is what type, i assume is "ITEM_STATUS"
switch(event){
case. ITEM_STATUS.enter: //correct me if im wrong
await this.listener.emit(event, { TASKLISTENER_EVENT.create, context: this });
break;
case. ITEM_STATUS.start: //correct me if im wrong
await this.listener.emit(event, { TASKLISTENER_EVENT.assign, context: this });
break;
// some others
}
await this.listener.emit('all', { event, context: this });
}
above is just ideal i can think with, However, the issue is I don't know where to obtain user's input
, so it is only an example
engine.start
at the process level not itemengine.assign
at the item level, you can call it update, except it accepts assignment info, has no impact on item statusengine.invoke
at the item level, completes the Taskstart
assignment
end
dueDate
or followUpDate
?I would appreciate your response to these questions. Thanks
delete
: i don't know, in fact after some google i understand wrongly, read here
update
: I think after assign, agree that
timeout
: read here maybe helpful
I am still not clear what is the purpose of delete
, update
and timeout
, I think these are very specific to Camunda complex implementation.
The only one task listeners that I can think of :
assign
fired after engine.assign is called (redundant see below)validate
fired after engine.invoke is called and engine.assign is calledbase on the discussion, seems
[delete]
when we delete/cancel a process, all the tasks within the bpmn will trigger 'delete' event. I think this is make sense, cause it can notify related users who'd initiate/approve/did something about the task.Simple exmaple of truck rentalk process having 1 user task instruct driver go to sides
, if the enire sales suddenly beeen cancel then the task deleted, he been notify no need to go client side.
[timeout]
The timeout event occurs when a Timer, associated with this Task Listener, is due. Note that this requires for a Timer to be defined. The timeout event may occur after a Task has been created, and before it has been completed. It seems it attach a timer to that user task (cause the configuration exactly same like timer), if current time is over the dueDate then it trigger
timeOut
event. It is common practise in industry for escalate work.
Example: sales agent shall issue quotation to customer after enquiry receive in 2 days (due date 2 days), after 2 days if this job not completed it will trigger notification to supervisor. Every day (cycle 1 day) the supervisor receive system msg remind to chase after the sales agent
[update]
The update event occurs when a task property (e.g. assignee, owner, priority, etc.) on an already created task is changed. This includes attributes of a task (e.g. assignee, owner, priority, etc.), as well as dependent entities (e.g. attachments, comments, task-local variables). Note that the initialization of a task does not fire an update event (the task is being created). This also means that the update event will always occur after a create event has already occurred
seen task property changed, but not status change
Cancel
Task is not a valid operation on task, there needs to be another Event defined in the workflow to cancel the entire workflow.
timeout
, yes one can define boundary events as below:
.
No sense of notify the task of timeout, it is the role of the task following the time to take action, in this case , Reminder
The Challenge with that is the attached timer event, it is evaluated and starts at the start of the task, before assignment and dueDate is set. (see my solution below)
update
, would make sense if we allow users to update the task, but we don't. The only options currently is through
engine.assign(..,data,assignment,...)
Which fires the trigger 'assign'
I am keen on wrapping this up to get ready for next release so, here is my proposal:
engine.assign
would be used to change assignment and change dataassign
: Fired only after the engine.assign
validate
: Fired after engine.assign
and 'engine.invoke`validate
delegate would re-evaluate the boundary event timer and re-execute it, this would allow you to escalate issues based on the revised dueDate and followUpDate.Thanks
Cancel
Task is not a valid operation on task, there needs to be another Event defined in the workflow to cancel the entire workflow.
yes there is alternative by draw another another service task, It is not must have
, but with this can reduce number of element in bpmn. We may look into this again future, cause 1 day we use camunda-modeler, 1 day it will remind us bpmn-server not compatible
with bpmn-editor. Future either:
timeout
, yes one can define boundary events as below: No sense of notify the task of timeout, it is the role of the task following the time to take action, in this case ,Reminder
similar as above, not must have
The Challenge with that is the attached timer event, it is evaluated and starts at the start of the task, before assignment and dueDate is set. (see my solution below)
Are you suggesting that bpmn-server fires an timer event automatically at dueDate and followUpDate? Since we use camunda modelere, I always recommend having same behavior with the modeler concept to avoid confusion. In this case, yes I suggest add a timer. However it no high urgency cause next release too much changes, we may scope down and plan it using
milestone
update
, would make sense if we allow users to update the task, but we don't. The only options currently is throughengine.assign(..,data,assignment,...)
Which fires the trigger 'assign'
no problem, may create document of unsupported features/event.
I am keen on wrapping this up to get ready for next release so, here is my proposal:
Changes to current design
engine.assign
would be used to change assignment and change data- The only Task delegates would be
assign
: Fired only after theengine.assign
validate
: Fired afterengine.assign
and 'engine.invoke`
validate
delegate would re-evaluate the boundary event timer and re-execute it, this would allow you to escalate issues based on the revised dueDate and followUpDate.- Please see my previous notes about Notifications
Thanks
I have feed back as below:
process_invoked
? it is complete
anyway. may write in documentation.invoke
(or, before), is it process_invoke
?validate
event, I'm ok update
& assignment
quite similar and we may only use 1 of them, or put update as synonym of assign. After all tidy up we can write in document afterwardThanks for the Feedback, so here is the revised list of sequences
---calling engine.start-----
---Event: -->process.start { option1: 1234 }
---Event: -->transformInput item: StartEvent_1 { option1: 1234 }
---Event: -->enter item: StartEvent_1 { option1: 1234 }
---Event: -->start item: StartEvent_1 { option1: 1234 }
---Event: -->process.saving { option1: 1234 }
---Event: -->end item: StartEvent_1 { option1: 1234 }
---Event: -->enter item: Request { option1: 1234 }
---Event: -->start item: Request { option1: 1234 }
---Event: -->wait item: Request { option1: 1234 }
---Event: -->process.wait { option1: 1234 }
---Event: -->process.saving { option1: 1234 }
---calling engine.assign-----
---Event: -->process.restored { option1: 1234, restored: true }
---Event: -->assign item: Request { option1: 1234, restored: true }
---Event: -->validate item: Request { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
---calling engine.invoke----- for Request
---Event: -->process.restored { option1: 1234, restored: true }
---Event: -->process.invoke { option1: 1234, restored: true }
---Event: -->transformInput item: Request { option1: 1234, restored: true }
---Event: -->validate item: Request { option1: 1234, restored: true }
---Event: -->end item: Request { option1: 1234, restored: true }
---Event: -->enter item: Approve { option1: 1234, restored: true }
---Event: -->start item: Approve { option1: 1234, restored: true }
---Event: -->enter item: Event_1lkpj3z { option1: 1234, restored: true }
---Event: -->start item: Event_1lkpj3z { option1: 1234, restored: true }
---Event: -->wait item: Event_1lkpj3z { option1: 1234, restored: true }
---Event: -->wait item: Approve { option1: 1234, restored: true }
---Event: -->process.invoked { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
---calling engine.invoke----- for Approve
---Event: -->process.restored { option1: 1234, restored: true }
---Event: -->process.invoke { option1: 1234, restored: true }
---Event: -->transformInput item: Approve { option1: 1234, restored: true }
---Event: -->validate item: Approve { option1: 1234, restored: true }
---Event: -->end item: Event_1lkpj3z { option1: 1234, restored: true }
---Event: -->end item: Approve { option1: 1234, restored: true }
---Event: -->enter item: Gateway_1kqewfd { option1: 1234, restored: true }
---Event: -->start item: Gateway_1kqewfd { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
---Event: -->end item: Gateway_1kqewfd { option1: 1234, restored: true }
---Event: -->enter item: Activity_1rx1txe { option1: 1234, restored: true }
---Event: -->start item: Activity_1rx1txe { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
---Event: -->end item: Activity_1rx1txe { option1: 1234, restored: true }
---Event: -->enter item: Event_1qg3mz1 { option1: 1234, restored: true }
---Event: -->start item: Event_1qg3mz1 { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
---Event: -->end item: Event_1qg3mz1 { option1: 1234, restored: true }
---Event: -->process.end { option1: 1234, restored: true }
---Event: -->process.invoked { option1: 1234, restored: true }
---Event: -->process.saving { option1: 1234, restored: true }
Task listeners: only for User Tasks, , fires JavaScripts defined at the model
we can't support delegate too? which able to execute in source code in appDelegate. Javascript editor is really bad implementation for production quality. no syntax highlight, no error checking.
assign: invoked on invoke and assign commands validate: invoked on invoke and assign commands
regarding task listener, it is recommended specific event bind to specific method to prevent confusion (Due to I can't imagine how to bind the event yet). Or, will it have additional property which can tell us what the specific actual event trigged (invoke, invoked, or assign)?
Event listener: is only in the application code (typically appDelegate)
hard to imagine yet. you have sample code? Which will be code to illustrate your deisgn pattern
appUtils
where you have full JS and TS.LeaveApplication.ts
But methods are named approve_start
, approve_validate
, etc.
This can be done by your own appDelegate, if interested, let us start a new discussion, this one is getting too long.i'd went through slightly the documentation. You may proceed what you wish to do. after come out latest version then I try to make it support delegate.
Thanks, I am closing this, lf any issues start another
According BPMN concept.
I wish to ask does bpmn-server support task listener? if yes is the method same like service task? Cause we need to read data submited by invoke api.
For me, invoke user-task happen at client side, task listener for user-task launch at server side. Example: