Closed kstan79 closed 8 months ago
I figure out there is 'hack' way like:
@teacher[$data.teacher._id]
@student[$data.student._id]
@user[$data.createdBy]
seems bpmn-server won't override data with@
, I can manually apply string replace or regex outside.
However it seems not a long term solution. It is still better we have some official way which is endorse by you.
Please send your Bpmn file will investigate Sent from my iPhoneOn Mar 21, 2024, at 6:26 AM, Ks Tan @.***> wrote: I figure out there is 'hack' way like: @Techer[data.teacher._id] Then at bpmn won't override it, I'm manually apply string replace or regex outside. However it seems not a long term solution
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: @.***>
I'm not file a bug report, just wish to ask any "official" way to resolve user from the bpmn user task and that updated information can save into database permanently.
Base on above sample, i created my own way methods to convert teacher (@teacher[$data.teacher._id]). it willl run custom code to find user who have same email with the teacher, then I wish to save the teacher's userid into items. below i gave example which i can replace the run time assignee/candidateusers/groups but cant persist the value into mongodb
this.bpmnServer.listener.on(
'all',
async (event) => await this.applyEventListener(event),
);
async applyEventListener(event){
.. ... many codes
//if is user task event under wait status
const assignee = await this.userResolver(appuser, event.context.item.assignee,data,);
const candidateUsers = await this.userResolver( appuser, event.context.item.candidateUsers, data,);
const candidateGroups = await this.userResolver( appuser,vent.context.item.candidateGroups,data,);
//i cant override this 3 properties and let bpmnserver save into database
event.context.item.assignee = assignee
event.context.item.candidateUsers = candidateUsers
event.context.item.candidateGroups =candidateGroups
//send email notification
//run others codes
}
// sample userids = ['@teacher[$data.teacher._id]','@user[$data.createdBy], ]
async userResolver( appuser: UserContext, userids: string | string[] | undefined, data: any,) {
if (!userids) return undefined;
if (typeof userids == 'string') userids = [userids];
if (!Array.isArray(userids)) return undefined;
const newids: string[] = [];
for (let i = 0; i < userids.length; i++) {
let uid = userids[i].trim();
// require convert teacher/student to real user
if (uid.substring(0, 1) == '@' && uid.includes('[') && uid.includes(']')) {
const regextype = /(?<=\@)(.*?)(?=\[)/;
const regexvalue = /(?<=\[)(.*?)(?=\])/;
const usertype = uid.match(regextype)[0].trim(); //obtain teacher/user
const typevalue = uid.match(regexvalue)[0].trim(); // obtain $data.teacher._id , $data.createdBy
let idvalue = '';
// read $data.teacher._id , $data.createdBy from "data"
if (typevalue.substring(0, 6) == '$data.') {
const fieldpath = typevalue.replace('$data.', '');
idvalue = this.readFieldFromData(fieldpath, data);
}
//convert become real uid using external resolver (such as teacher/student execute different resolver)
uid = await this.userResolverService.resolve(
appuser,
usertype,
idvalue,
data,
);
}
//either convert become user's uuid, or remove it when the teacher cannot resolve
if (uid) newids.push(uid);
}
return newids; //after return, wish to save into mongodb instance's usertask item
}
readFieldFromData(path: string, data: any) {
return path.split('.').reduce((o, i) => o[i], data);
}
here is the xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="errohandle" name="errors handler" isExecutable="false">
<bpmn:startEvent id="StartEvent_1" name="update schedule">
<bpmn:outgoing>Flow_1pg0fnn</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1pg0fnn" sourceRef="StartEvent_1" targetRef="tryerror" />
<bpmn:endEvent id="Event_1o6o4k2">
<bpmn:incoming>Flow_170a071</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_170a071" sourceRef="teacheracknowledge" targetRef="Event_1o6o4k2" />
<bpmn:userTask id="teacheracknowledge" name="teacher acknowledge" camunda:assignee="@teacher[$data.teacher._id]">
<bpmn:incoming>Flow_0i8h61u</bpmn:incoming>
<bpmn:outgoing>Flow_170a071</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0i8h61u" sourceRef="tryerror" targetRef="teacheracknowledge" />
<bpmn:serviceTask id="tryerror" name="try error">
<bpmn:incoming>Flow_1pg0fnn</bpmn:incoming>
<bpmn:outgoing>Flow_0i8h61u</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:boundaryEvent id="Event_1x43azn" attachedToRef="tryerror">
<bpmn:outgoing>Flow_0ujgdj5</bpmn:outgoing>
<bpmn:errorEventDefinition id="ErrorEventDefinition_1jljctq" />
</bpmn:boundaryEvent>
<bpmn:sequenceFlow id="Flow_0ujgdj5" sourceRef="Event_1x43azn" targetRef="errorhandler" />
<bpmn:serviceTask id="errorhandler" name="error handler">
<bpmn:incoming>Flow_0ujgdj5</bpmn:incoming>
<bpmn:outgoing>Flow_0xn22s3</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:endEvent id="Event_1lr7684">
<bpmn:incoming>Flow_0xn22s3</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0xn22s3" sourceRef="errorhandler" targetRef="Event_1lr7684" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="errohandle">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="122" y="102" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="100" y="145" width="81" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1o6o4k2_di" bpmnElement="Event_1o6o4k2">
<dc:Bounds x="632" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1vu7axv_di" bpmnElement="teacheracknowledge">
<dc:Bounds x="400" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0vrmgin_di" bpmnElement="tryerror">
<dc:Bounds x="200" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_12q3140_di" bpmnElement="errorhandler">
<dc:Bounds x="320" y="200" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1lr7684_di" bpmnElement="Event_1lr7684">
<dc:Bounds x="472" y="222" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0nov24y_di" bpmnElement="Event_1x43azn">
<dc:Bounds x="232" y="142" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1pg0fnn_di" bpmnElement="Flow_1pg0fnn">
<di:waypoint x="158" y="120" />
<di:waypoint x="200" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_170a071_di" bpmnElement="Flow_170a071">
<di:waypoint x="500" y="120" />
<di:waypoint x="632" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0i8h61u_di" bpmnElement="Flow_0i8h61u">
<di:waypoint x="300" y="120" />
<di:waypoint x="400" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ujgdj5_di" bpmnElement="Flow_0ujgdj5">
<di:waypoint x="250" y="178" />
<di:waypoint x="250" y="240" />
<di:waypoint x="320" y="240" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0xn22s3_di" bpmnElement="Flow_0xn22s3">
<di:waypoint x="420" y="240" />
<di:waypoint x="472" y="240" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
cause above code happen during doEvent start (or others which before save), so bpmn.engine.assign()
seems is not a solution. Hope where is better interceptor or middleware available, either deligates or etc.
I don't understand why is your start
event handler able to calculate the assignment and set it directly:
item.assignee='user1';
call api from inside the event does not work Sorry about lack of documentation about this
[samples]
xxxxx-yyyyyy-zzzz
E001
E001
[workflow]
change schedule
, new student enroll
, student drops
, ...E001
become xxxxx-yyyy-zzzz
, then immdiately send pending approval notification (such as email) to respective user.xxxxx-yyyyyy-zzzz
I saw example #(services.getSupervisorUser(this.data.requester))
at below link, no luck too.
** I assume getSupervisorUser
is the method under delegates.serviceProvider
https://github.com/bpmnServer/bpmn-server/blob/0c001fc61518da9d4d92692acecd0f88800a909b/docs/userAssignment.md
I think i can conclude my observation as this:
assignee = null
"$" working fine, and "#" doesn't.
since we need async call to read database, thats the reason i keep failing.
sorry, you are having problems with this: as a quick work around you have two options:
I am on the road right now, but will investigate this further in couple of days Thanks
seems during evaluate at ScriptHandler
have error, below is the console error:
error in script execution
var item=this;
var data=this.data;
var input=this.input;
var output=this.output;
var appDelegate=this.token.execution.appDelegate;
var appServices=this.token.execution.servicesProvider;
var appUtils=appDelegate.appUtils;
#(appDelegate.getasyncdata('sss'));
SyntaxError: Invalid or unexpected token
result = Function(js).bind(scope)(); have issue with #(appDelegate.getasyncdata('sss'));
?
ok, seems it is bugs during process #(...)
, i'd send PR #191 to fix it.
Just, I have a few comment:
eval
which create security vulnerability, no doubt it is high efficient but there is some drawback as I mentioned below. Future we may use more restricted but reliable evaluation instead. It also release from javascript pattern#(appDelegate.getUser('teacher',this.data.teacher._id))
. We may have shorter alias in future:
[$this.data.teacher._id]
can shorten become $F{teacher._id} (mean $ for access data only, most of time developer use it)
** We can have multiple kind of prefix like
$F => Field in this.data, dynamic depends on data
$R => static runtimes parameter, like server name, server time, instance properties $R{servertime}
$P => extra input parameter we obtain from somewhere (in your case like something from `vars`,`inputs`)
$V => static variables regarding this process/item such as process owner, how long it wait, when overdue, how many assignees and etc [$V{owner}, $V{expired}]
[#(appDelegate.getUser('teacher',this.data.teacher._id))] can shortern become @.getUser('teacher',$F{teacher._id}), in this case @=access appdeligate (or serviceProvider). I have additional feed back which is:
[$this.instance.context...] Im not recommend to allow bpmn access dangerous runtime. BPMN is advisable run within isolated environment, the bpmn designer not interested in this scope too.
Hope above comment helpful
Thanks for this feedback.
this.data.var
just `data.var'I have to point out that bpmn-server is not using eval but using Function()
https://stackoverflow.com/questions/4599857/are-eval-and-new-function-the-same-thing
Thanks of clarification. I don't have computer to prove now but I would like to say don't create room for execute script at every places. Just allow it on script task. I worry user task able to execute some of below code
$(throw 'error') $(fetch(...send-out-private-data))
The field of bpmn like user task assigned shall only allow access to specific data, or method similar like "getter", give them room to insert js freely maybe harmful
But keep in mind that is code must be in either the bpmn model or appServices and both cases is protected. In other words the end-user has no access to this code nor can modify it
I have plenty of scenario look common in real world but quite challenging to implement viauser task, wish to find solution.
1 example as below: assume I have multiple group of users such as:
I have multiple mongodb collection such as users (real user database who can login the application, cashers, teachers, and branch managers store in this collection) teachers (master list of teachers) student (master list of students) schedules (class schedule, it store student name list, the teacherid)
Assume when class date/time change we wish to notify teachers and students.
refer to above bpmn and image, you can see that
schedule
record consist ofteacher
. However theteacher
is master data, not real user. The only thing can match user and teacher is email, but we won't save email into schedule. So, during prepare bpmn we can't define correct user identity.There is many scenario have similar case such as
students
parents
salesagent
they stored in different collection but consderhuman
, play role in business processes. It is impossible to obtain suitable user info fromdata
cause it too dynamic. Seems I need special user resolver as below strategy:then I can create suitable methods outside to resolve the real user for that teacher, and send the notification/pusher message.
Any advise to implement this?