Custom Function for Condition Control/Exclusive Gateway

Hi, we have implement bpmn-js for our project and are using bpmn-modeller to build flow xml which is used for AGI calling.
BPMN provides condition control component (also known as Exclusive Gateway), for which a string is saved in sequenceFlow tag which is executed only when the condition is true.
For example:

      <sequenceFlow id="Flow_0zfcb76" sourceRef="Gateway_0z1dp2u" targetRef="Activity_0zkrwq8">
        <conditionExpression xsi:type="tFormalExpression" language="javascript">next(null,this.environment.variables.test1 === "IS_DUPLICATE");</conditionExpression>
      </sequenceFlow>
      <sequenceFlow id="Flow_0bkz9zr" sourceRef="Gateway_0z1dp2u" targetRef="Event_03wrqjm">
        <conditionExpression xsi:type="tFormalExpression" language="javascript">next(null,this.environment.variables.test1 !== "IS_DUPLICATE");</conditionExpression>
      </sequenceFlow>

which is working perfectly fine.

But now I want that instead of writing comparison string, I call a function for the same, which is already present in this.environment.services.
So, after implementing, XML generated is as follows:

    <sequenceFlow id="Flow_1a5xpmo" sourceRef="Gateway_0rubtqe" targetRef="Activity_0009n6k">
      <conditionExpression xsi:type="tFormalExpression" language="javascript">next(null,this.environment.services.compareDate(this.environment.variables.ivrVar,"payment.testingHeader.expiry","&lt;=","new Date()","MMYY"));</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_1ki3yf7" sourceRef="Gateway_0rubtqe" targetRef="Activity_0q4maht">
      <conditionExpression xsi:type="tFormalExpression" language="javascript">next(null,this.environment.services.compareDate(this.environment.variables.ivrVar,"payment.testingHeader.expiry","&gt;","new Date()","MMYY"));</conditionExpression>
    </sequenceFlow>

And the function compareDate:

async function compareDate1(ivrVar, variable, operator, value, dateFormat) {
    console.log("comparing date");
    const keys = variable.split('.');
    // Use reduce to iterate over the keys and access the value dynamically
    let date = keys.reduce((acc, key) => acc[key], ivrVar);
    const formattedDate = DateUtil.format({dateString:date, sourceDateFormat:dateFormat, targetDateFormat:"YYYY-MM-DD"});
    // Convert formattedDate to a Date object to compare
    const formattedDateObj = new Date(formattedDate);
    const currDateObj = eval(value);
    // Compare the two Date objects using the operator
    let result;
    switch (operator) {
        case '<=':
            result = formattedDateObj <= currDateObj;
            break;
        case '>=':
            result = formattedDateObj >= currDateObj;
            break;
        case '>':
            result = formattedDateObj > currDateObj;
            break;
        case '<':
            result = formattedDateObj < currDateObj;
            break;
        case '===':
            result = formattedDateObj === currDateObj;
            break;
        case '!==':
            result = formattedDateObj !== currDateObj;
            break;
        default:
            result = false;  // In case of an invalid operator
    }

    return result;
}

For the first sequence flow, it returns false, so ideally it should move the execution to second sequence flow and same function should be executed again to check the case, but after first sequence flow only the execution moves to the targetRef of first sequence flow.

Current scenario:

  • First sequence flow executes the function compareDate.
  • False is returned from compareDate as per use case.
  • Now the execution goes to targetRef of first sequence flow, despite of the function returning false.

Expected behaviour:

  • First sequence flow executes the function compareDate.
  • False is returned from compareDate as per use case.
  • So the execution should now go for second sequence flow.
  • If the function compareDate returns true now, then the flow should move to targetRef of second sequence flow.

Can someone please assist with this issue?

@philippfromme can you please help, ideally it should work, function is being called , checked via debugger and returning bool value, but still executing the first condtion instead of based on true/false , called function is sync function as well

@philippfromme @barmac can you please help with this issue?

Is this a question about Camunda 7 usage? If so, I’d suggest to ask at Camunda 7 Topics - Camunda Forum
This forum is about bpmn.io, so no execution outside of bpmn-js-token-simulation.

It is a general question about how does bpmn engine executes condition control if we call a function instead of writing expression to compare variables in condition gateway tags, as mentioned in the query above. Why am I not able to execute the second case anyways? The execution always goes to the targetRef of first condition, ignoring the second condition.

The answer to your question depends on the engine you are using. However, the bpmn.io project is about modeling, and does not have any own engine for execution. So what engine do you use?

We are using bpmn-engine for the execution of flow during call.
{ Engine } = require('bpmn-engine')
Link: https://www.npmjs.com/package/bpmn-engine

Thanks for clarification. Please know that bpmn-engine is not developed by bpmn.io team, so please reach out to the engine’s author: GitHub - paed01/bpmn-engine: BPMN 2.0 execution engine. Open source javascript workflow engine.

1 Like

thanks @barmac , we will check but if you can confirm is this allowed even, calling the sync functions like this

Unfortunately, this is the wrong place to ask. You wouldn’t ask a React question in an Angular forum, would you? :wink:

Thanks @philippfromme , got the solution.
Need to resolve the promise before returning, because in bpmn-engine to reject or accept the condition, condition gateway expects a boolean value.

Example XML:

<bpmn:sequenceFlow id="Flow_06o2cg8" sourceRef="Gateway_142v8k3" targetRef="Activity_1to686s">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression" language="javascript">
    function execute() {
          return new Promise(function(resolve, reject) {
              const result = this.environment.services.conditionListVariableCheck(this.environment.variables.ivrVar,"account.status.test","===","INACTIVE");
              resolve(result);
          }).then(function(result) {
              next(null, result);
          }).catch(function(error) {
              next(error);
          });
      }
      execute();</bpmn:conditionExpression>
    </bpmn:sequenceFlow>

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.