bpmnServer / bpmn-server

BPMN 2.0 server for Node.js , providing modeling, execution, persistence and monitoring for Workflow. along with sample UI. Intended to be developers workbench for BPMN 2.0
MIT License
186 stars 48 forks source link

service task deligate not work #115

Closed kstan79 closed 1 year ago

kstan79 commented 1 year ago

I'm tried delegate service task to notifyhead() however it seems not execute, no matter i define implementation or not:

my version: "bpmn-server": "^1.3.18"

<bpmn2:serviceTask id="Activity_1bqpwop" implementation="notifyhead" name="notifyhead"
      camunda:delegateExpression="notifyhead">
      <bpmn2:documentation>notifyhead doc2</bpmn2:documentation>
    </bpmn2:serviceTask>

I'd define method in both appDelegate and MyServices, seems both not execute and no console print:

notifyhead(){
        console.log("----notifyhead--------")        
    }

part of the console log is here:

Token(0).execute: executing currentNodeId=StartEvent_1 item.seq=0 is done!',
      'Token(0).goNext(): currentNodeId=StartEvent_1 type=bpmn:StartEvent currentItem.status=end',
      'Node(undefined|StartEvent_1).getOutbounds: itemId=17fa414c-a110-4494-a6fc-c5509f44750d',
      'Item:new Item Flow_06q8s14 for token 0 ',
      'Flow(undefined|Flow_06q8s14).run: from=undefined to=notifyhead find action... ',
      'Flow(undefined|Flow_06q8s14).run: going to Activity_1bqpwop action : take',
      'Node(undefined|StartEvent_1).getOutbounds: return outbounds1',
      'Token(0).goNext(): verify outbonds....',
      'Token(0).goNext(): ... outbonds flowItemId=bc308aae-2fc6-45ae-a76f-771ecf9fb71b',
      'Token(0).goNext(): ... currentNodeId(undefined|StartEvent_1) processing  Flow(Flow_06q8s14) to Activity_1bqpwop',
      'Token(0).execute: inputnull',
      'Token(0).goNext(): waiting for num promises 1',
      'Item:new Item Activity_1bqpwop for token 0 ',
      'Token(0).execute: new Item created itemId=dbfa8ed5-4d52-43e2-a755-1d92065771de',
      'Token(0).execute: executing currentNodeId=Activity_1bqpwop',
      'Node(notifyhead|Activity_1bqpwop).execute: item=dbfa8ed5-4d52-43e2-a755-1d92065771de token:0',
      'Node(notifyhead|Activity_1bqpwop).execute: execute enter ...',
      'Node(notifyhead|Activity_1bqpwop).doEvent: executing script for event:enter newStatus:enter',
      'Node(notifyhead|Activity_1bqpwop).enter: item=dbfa8ed5-4d52-43e2-a755-1d92065771de',
      'Node(notifyhead|Activity_1bqpwop).execute: execute start ...',
      'Node(notifyhead|Activity_1bqpwop).doEvent: executing script for event:start newStatus:start',
      'Node(notifyhead|Activity_1bqpwop).start: item=dbfa8ed5-4d52-43e2-a755-1d92065771de',
      'Node(notifyhead|Activity_1bqpwop).startBoundaryEvents: itemId=dbfa8ed5-4d52-43e2-a755-1d92065771de',
      'Node(notifyhead|Activity_1bqpwop).execute: start complete ...token:0 ret:1',
      '..Saving instance c3d042cb-0370-41d3-b2d7-698f6d01ce1d{"caseId":3607}',
      'Node(notifyhead|Activity_1bqpwop).execute: execute run ...token:0',
      'invoking service:notifyhead input:{}',
      'service returned undefined',
      'service ',
      'Node(notifyhead|Activity_1bqpwop).execute: execute continue...',
      'Node(notifyhead|Activity_1bqpwop).continue: item=dbfa8ed5-4d52-43e2-a755-1d92065771de',
      'Node(notifyhead|Activity_1bqpwop).end: item=dbfa8ed5-4d52-43e2-a755-1d92065771de cancel:false',
      'Node(notifyhead|Activity_1bqpwop).doEvent: executing script for event:end newStatus:end',
      'Node(notifyhead|Activity_1bqpwop).end: setting item status to end itemId=dbfa8ed5-4d52-43e2-a755-1d92065771de itemStatus=end cancel: false endedat 2023-09-14T16:26:52.164Z',
      'Node(notifyhead|Activity_1bqpwop).end: finished',
ralphhanna commented 1 year ago

Can you please try it in the latest release.

Can you please attach your bpmn file and your appDelegate source

kstan79 commented 1 year ago

Can you please try it in the latest release.

Can you please attach your bpmn file and your appDelegate source

sorry typo, I'm use "version": "1.3.28", which is latest

the bpmn:

<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.11.1" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="Process_1" name="hod approve" isExecutable="false">
    <bpmn2:startEvent id="StartEvent_1" name="start">
      <bpmn2:outgoing>Flow_06q8s14</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_06q8s14" sourceRef="StartEvent_1" targetRef="Activity_1bqpwop" />
    <bpmn2:sequenceFlow id="Flow_08zhsu5" sourceRef="Activity_1cauerg" targetRef="Activity_17u4z08" />
    <bpmn2:userTask id="Activity_1cauerg" name="hod approve">
      <bpmn2:incoming>Flow_0rqyqdg</bpmn2:incoming>
      <bpmn2:outgoing>Flow_08zhsu5</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:serviceTask id="Activity_17u4z08" name="set doc status">
      <bpmn2:incoming>Flow_08zhsu5</bpmn2:incoming>
      <bpmn2:outgoing>Flow_1c3db1n</bpmn2:outgoing>
    </bpmn2:serviceTask>
    <bpmn2:sequenceFlow id="Flow_1c3db1n" sourceRef="Activity_17u4z08" targetRef="Activity_0xwghq6" />
    <bpmn2:endEvent id="Event_002ziis">
      <bpmn2:incoming>Flow_0zn1isx</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:task id="Activity_0xwghq6" name="notification to user">
      <bpmn2:incoming>Flow_1c3db1n</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0zn1isx</bpmn2:outgoing>
    </bpmn2:task>
    <bpmn2:sequenceFlow id="Flow_0zn1isx" sourceRef="Activity_0xwghq6" targetRef="Event_002ziis" />
    <bpmn2:serviceTask id="Activity_1bqpwop" name="notifyhead" implementation="notifyhead" camunda:delegateExpression="notifyhead">
      <bpmn2:documentation>notifyhead doc2</bpmn2:documentation>
    </bpmn2:serviceTask>
    <bpmn2:sequenceFlow id="Flow_0rqyqdg" sourceRef="Activity_1bqpwop" targetRef="Activity_1cauerg" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNEdge id="Flow_0rqyqdg_di" bpmnElement="Flow_0rqyqdg">
        <di:waypoint x="400" y="210" />
        <di:waypoint x="435" y="210" />
        <di:waypoint x="435" y="120" />
        <di:waypoint x="470" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0zn1isx_di" bpmnElement="Flow_0zn1isx">
        <di:waypoint x="950" y="120" />
        <di:waypoint x="1092" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1c3db1n_di" bpmnElement="Flow_1c3db1n">
        <di:waypoint x="790" y="120" />
        <di:waypoint x="850" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_08zhsu5_di" bpmnElement="Flow_08zhsu5">
        <di:waypoint x="570" y="120" />
        <di:waypoint x="690" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_06q8s14_di" bpmnElement="Flow_06q8s14">
        <di:waypoint x="188" y="120" />
        <di:waypoint x="244" y="120" />
        <di:waypoint x="244" y="210" />
        <di:waypoint x="300" y="210" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Activity_1qxh3do_di" bpmnElement="Activity_1cauerg">
        <dc:Bounds x="470" y="80" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_09ghxvn_di" bpmnElement="Activity_17u4z08">
        <dc:Bounds x="690" y="80" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_002ziis_di" bpmnElement="Event_002ziis">
        <dc:Bounds x="1092" y="102" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0xwghq6_di" bpmnElement="Activity_0xwghq6">
        <dc:Bounds x="850" y="80" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="152" y="102" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="159" y="145" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0oalru2_di" bpmnElement="Activity_1bqpwop">
        <dc:Bounds x="300" y="170" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>
ralphhanna commented 1 year ago

Oh, sorry I didn't notice it first,

Just make sure that the method is async

async notifyhead() { console.log('>>>>>>>>>>appDelegate notifyhead'); }

kstan79 commented 1 year ago

Dear ralphhanna, sorry it doesn't work.

I'd tried many times with different way, it seems does't work. below is the delegation file. I'm using webapp folder example and modify appDelegate.ts, then run npm run dev, npm run compileRun, ts-node app, no one success.


import {  Item, FLOW_ACTION , NODE_ACTION, IExecution  } from './';
import { DefaultAppDelegate } from './index';

const fs = require('fs');

var seq = 1;

class MyAppDelegate extends DefaultAppDelegate{
    winSocket;
    constructor(server) {
        super(server);
        this.servicesProvider = new MyServices();
    }
    /**
    * is fired on application startup
    **/
    async startUp() {
        console.log('myserver started.. checking for incomplete processes');

        var query = { "items.status": "start" };

        var list = await this.server.dataStore.findItems(query);
        if (list.length > 0) {

            this.server.logger.log("...items query returend " + list.length);
            for (var i = 0; i < list.length; i++) {
                let item = list[i];
                console.log('-->',item.processName,item.elementId,item.type,item.startedAt,item.status);
                if (item.type=='bpmn:ScriptTask' || item.type=='bpmn:ServiceTask' )
                {
                    console.log('recovering:',item.elementId);
//                    let response =await this.server.engine.invoke({"items.id":item.id}, {},null, {recover:true});
                }
            }
        }

    }
    async notifyhead() { 
        console.log('>>>>>>>>>>appDelegate notifyhead');        
     }

    sendEmail(to, msg, body) {

        console.log(`Sending email to ${to}`);

        const key = process.env.SENDGRID_API_KEY;

        if (key && (key != '')) {
            const sgMail = require('@sendgrid/mail')
            sgMail.setApiKey(process.env.SENDGRID_API_KEY)

            const email = {
                to: to,
                from: 'ralphhanna@hotmail.com', // Change to your verified sender
                subject: msg,
                text: body,
                html: body
            }

            sgMail
                .send(email)
                .then((response) => {
                    this.server.logger.log('responseCode', response[0].statusCode)
                    this.server.logger.log('responseHeaders', response[0].headers)
                })
                .catch((error) => {
                    console.error('Email Error:' + error)
                })

        }
        else {
            console.log(`email is disabled`);
        }

    }

    async executionStarted(execution: IExecution) {
        await super.executionStarted(execution);
    }

    async executionEvent(context, event) {
        console.log("executionEvent",context, event)
        if (context.item) {

//            console.log(`----->Event: '${event}' for ${context.item.element.type} '${context.item.element.id}' id: ${context.item.id}`);
            if (event == 'wait' && context.item.element.type == 'bpmn:UserTask')
                console.log(`----->Waiting for User Input for '${context.item.element.id}' id: ${context.item.id}`);
        }
 //       else
 //           console.log('----->All:' + event, context.definition.name);

    }
    async messageThrown(messageId, data, matchingQuery, item: Item) {
        await super.messageThrown(messageId, data, matchingQuery,item);
    }
    async signalThrown(signalId, data, matchingQuery, item: Item) {
        await super.signalThrown(signalId, data, matchingQuery, item);
    }
    async serviceCalled(input, context) {
        this.server.logger.log("service called");

    }
}

async function delay(time, result) {
    console.log("delaying ... " + time)
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log("delayed is done.");
            resolve(result);
        }, time);
    });
}
import * as readline from 'readline';
const cl = readline.createInterface(process.stdin, process.stdout);
const question = function (q) {
    return new Promise((res, rej) => {
        cl.question(q, answer => {
            res(answer);
        })
    });
};
class MyServices {
    async promptUser(input, context) {
        console.log('executing prompt user');

        var result = await question("continue?");
        console.log('result:', result);
        return null;

    }
    async serviceTask(input, context) {
        let item = context.item;
        console.log(" Hi this is the serviceTask from appDelegate");
        console.log(item.elementId);
        await delay(5000, 'test');
        console.log(" Hi this is the serviceTask from appDelegate says bye");
    }
    async simulateCrash(input, context) {
        let item = context.item;
        let data = item.token.data;
        if (data['crash']=='Yes')
        {
         data['crash']='No';
         await item.token.execution.save();
         console.log('Will Crash now',item.token.data);
         process.exit(100);
        }
        else
            console.log('no crash');
    }
    async add({ v1, v2 }) {
        console.log("Add Service", v1, v2);

        return Number(v1) + Number(v2);
    }

    async service1(input, context) {
        let item = context.item;
        item.vars = input;
        seq++;
        await delay(5000, 'test');
        item.token.log("SERVICE 1: input: " + JSON.stringify(input)+ item.token.currentNode.id + " current seq: " + seq);

        console.log('appDelegate service1 is now complete input:',input, 'output:',seq,'item.data',item.data);
        return { seq , text: 'test' };
    }
}
export {MyAppDelegate}
bpmn-ts commented 1 year ago

please move the method to MyServices class

Thanks

On Sat, Sep 16, 2023 at 8:44 PM Ks Tan @.***> wrote:

Dear ralphhanna, sorry it doesn't work.

I'd tried many times with different way, it seems does't work. below is the delegation file. I'm using webapp folder example and modify appDelegate.ts, then run npm run dev, npm run compileRun, ts-node app, no one success.

import { Item, FLOW_ACTION , NODE_ACTION, IExecution } from './';import { DefaultAppDelegate } from './index'; const fs = require('fs'); var seq = 1; class MyAppDelegate extends DefaultAppDelegate{ winSocket; constructor(server) { super(server); this.servicesProvider = new MyServices(); } /* is fired on application startup **/ async startUp() { console.log('myserver started.. checking for incomplete processes');

    var query = { "items.status": "start" };

    var list = await this.server.dataStore.findItems(query);
    if (list.length > 0) {

        this.server.logger.log("...items query returend " + list.length);
        for (var i = 0; i < list.length; i++) {
            let item = list[i];
            console.log('-->',item.processName,item.elementId,item.type,item.startedAt,item.status);
            if (item.type=='bpmn:ScriptTask' || item.type=='bpmn:ServiceTask' )
            {
                console.log('recovering:',item.elementId);//                    let response =await this.server.engine.invoke({"items.id":item.id}, {},null, {recover:true});
            }
        }
    }

}
async notifyhead() {
    console.log('>>>>>>>>>>appDelegate notifyhead');
 }

sendEmail(to, msg, body) {

    console.log(`Sending email to ${to}`);

    const key = process.env.SENDGRID_API_KEY;

    if (key && (key != '')) {
        const sgMail = ***@***.***/mail')
        sgMail.setApiKey(process.env.SENDGRID_API_KEY)

        const email = {
            to: to,
            from: ***@***.***', // Change to your verified sender
            subject: msg,
            text: body,
            html: body
        }

        sgMail
            .send(email)
            .then((response) => {
                this.server.logger.log('responseCode', response[0].statusCode)
                this.server.logger.log('responseHeaders', response[0].headers)
            })
            .catch((error) => {
                console.error('Email Error:' + error)
            })

    }
    else {
        console.log(`email is disabled`);
    }

}

async executionStarted(execution: IExecution) {
    await super.executionStarted(execution);
}

async executionEvent(context, event) {
    console.log("executionEvent",context, event)
    if (context.item) {

// console.log(----->Event: '${event}' for ${context.item.element.type} '${context.item.element.id}' id: ${context.item.id}); if (event == 'wait' && context.item.element.type == 'bpmn:UserTask') console.log(----->Waiting for User Input for '${context.item.element.id}' id: ${context.item.id}); } // else // console.log('----->All:' + event, context.definition.name);

}
async messageThrown(messageId, data, matchingQuery, item: Item) {
    await super.messageThrown(messageId, data, matchingQuery,item);
}
async signalThrown(signalId, data, matchingQuery, item: Item) {
    await super.signalThrown(signalId, data, matchingQuery, item);
}
async serviceCalled(input, context) {
    this.server.logger.log("service called");

}}

async function delay(time, result) { console.log("delaying ... " + time) return new Promise(function (resolve) { setTimeout(function () { console.log("delayed is done."); resolve(result); }, time); });}import * as readline from 'readline';const cl = readline.createInterface(process.stdin, process.stdout);const question = function (q) { return new Promise((res, rej) => { cl.question(q, answer => { res(answer); }) });};class MyServices { async promptUser(input, context) { console.log('executing prompt user');

    var result = await question("continue?");
    console.log('result:', result);
    return null;

}
async serviceTask(input, context) {
    let item = context.item;
    console.log(" Hi this is the serviceTask from appDelegate");
    console.log(item.elementId);
    await delay(5000, 'test');
    console.log(" Hi this is the serviceTask from appDelegate says bye");
}
async simulateCrash(input, context) {
    let item = context.item;
    let data = item.token.data;
    if (data['crash']=='Yes')
    {
     data['crash']='No';
     await item.token.execution.save();
     console.log('Will Crash now',item.token.data);
     process.exit(100);
    }
    else
        console.log('no crash');
}
async add({ v1, v2 }) {
    console.log("Add Service", v1, v2);

    return Number(v1) + Number(v2);
}

async service1(input, context) {
    let item = context.item;
    item.vars = input;
    seq++;
    await delay(5000, 'test');
    item.token.log("SERVICE 1: input: " + JSON.stringify(input)+ item.token.currentNode.id + " current seq: " + seq);

    console.log('appDelegate service1 is now complete input:',input, 'output:',seq,'item.data',item.data);
    return { seq , text: 'test' };
}}export {MyAppDelegate}

— Reply to this email directly, view it on GitHub https://github.com/ralphhanna/bpmn-server/issues/115#issuecomment-1722354798, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQL5NQZB2MY63CEQK2N7YJTX2ZBVLANCNFSM6AAAAAA4YM4SQA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

kstan79 commented 1 year ago

finally it work as this:

  1. put method in Myservices class
  2. use context.item.token.log()
    async notifyhead(input, context) { 
        context.item.token.log('>>>>>>>>>>appDelegate notifyhead gogogo');    

     }

Thanks!