TaskId is null for each variable

Dear community,

I am trying to get the variables and their values by task as well the full variable history in case the values have been changed in a loop by an user task.

Below you will see a simplified bpmn pattern:

I use a NodeJS backend to complete a task with an embedded form and the Rest API of Camunda 7.21.0-alpha1.

If I use the Rest APIs endpoint /task({id}/complete, and send my variables as payload, the variables taskId is null.

Furthermore if I set the variable a second time due to the loop, no history is stored, just the value changed and the state and createTime remains with old values

Variable set the 1st time:

{
 "type": "String",
 "value": "test 1",
 "valueInfo": {},
 "id": "fb1d1ec6-ae20-11ee-ba89-005056be16e8",
 "name": "test_variable",
 "processDefinitionKey": "DEV-03-03.2",
 "processDefinitionId": "DEV-03-03.2:23:5e52cb3b-9b2a-11ee-a759-005056be16e8",
 "processInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "executionId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "activityInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "caseDefinitionKey": null,
 "caseDefinitionId": null,
 "caseInstanceId": null,
 "caseExecutionId": null,
 "taskId": null,
 "errorMessage": null,
 "tenantId": null,
 "state": "CREATED",
 "createTime": "2024-01-08T13:25:17.024+0100",
 "removalTime": null,
 "rootProcessInstanceId": "ea606922-ae1f-11ee-ba89-005056be16e8"
}

Variable set the 2nd time:

 {
  "type": "String",
  "value": "test 2",
  "valueInfo": {},
  "id": "fb1d1ec6-ae20-11ee-ba89-005056be16e8",
  "name": "test_variable",
  "processDefinitionKey": "DEV-03-03.2",
  "processDefinitionId": "DEV-03-03.2:23:5e52cb3b-9b2a-11ee-a759-005056be16e8",
  "processInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
  "executionId": "11b5618f-ae20-11ee-ba89-005056be16e8",
  "activityInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
  "caseDefinitionKey": null,
  "caseDefinitionId": null,
  "caseInstanceId": null,
  "caseExecutionId": null,
  "taskId": null,
  "errorMessage": null,
  "tenantId": null,
  "state": "CREATED",
  "createTime": "2024-01-08T13:25:17.024+0100",
  "removalTime": null,
  "rootProcessInstanceId": "ea606922-ae1f-11ee-ba89-005056be16e8"
}

In my application.yml, I set the history-level to FULL but I assume it is the default value anyway, right?

spring:
  datasource:
    platform: mariadb
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://xxx.x.x.x:xxxx/camunda
    username: xxxx
    password: xxxx
camunda.bpm:
  admin-user:
    id: xxxx
    password: xxxx
    firstName: Demo
    lastName: Demo
  filter:
    create: All Tasks
  job-execution:
    max-wait: 10000
  generic-properties:
      properties:
        historyTimeToLive: P36500D
  history-level: FULL
server.port: 8080

Is it possible to set the taskId reference for the variable with the Rest API?
As in this case the variable has a local scope, how to use the value afterwards in my gateways?

Thanks for your help

Regards

Michael

Hi @michael1.
If you want to set a local task variable, have a look at this rest api:
https://docs.camunda.org/rest/camunda-bpm-platform/7.20/#tag/Task-Local-Variable/operation/putTaskLocalVariable
which you would need to call before completing the task.
But to access that later in your process, you would need an out mapping, as local variables are not visible outside of their scope.
Regarding the history, the state of the variable can only take the values “CREATED” or “DELETED”.
If you want to see the changes of the variable, I think you have to use the following REST call
https://docs.camunda.org/rest/camunda-bpm-platform/7.20/#tag/Historic-Detail/operation/getHistoricDetails

Update:
I’ve tried to solve my problem by updating the variables in the global scope.

I was able to update the variables in case they already existed in the process instance. Therefore I used the Rest endpoint /process-instance/{id}/variables/{varName} and the method “PUT”.

{
 "type": "String",
 "value": "test 7",
 "valueInfo": {},
 "id": "fb1d1ec6-ae20-11ee-ba89-005056be16e8",
 "name": "test_variable",
 "processDefinitionKey": "DEV-03-03.2",
 "processDefinitionId": "DEV-03-03.2:23:5e52cb3b-9b2a-11ee-a759-005056be16e8",
 "processInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "executionId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "activityInstanceId": "11b5618f-ae20-11ee-ba89-005056be16e8",
 "caseDefinitionKey": null,
 "caseDefinitionId": null,
 "caseInstanceId": null,
 "caseExecutionId": null,
 "taskId": null,
 "errorMessage": null,
 "tenantId": null,
 "state": "CREATED",
 "createTime": "2024-01-08T13:25:17.024+0100",
 "removalTime": null,
 "rootProcessInstanceId": "ea606922-ae1f-11ee-ba89-005056be16e8"
}

Hello Jan,

Thanks a lot for your support! Unfortunately I was not aware about the the REST endpoint task/{id}/localVariables and I’ve made a more complex approach to solve my problem. But I like to share it with the community, maybe it helps someone for a similar pupose

As my BPMN is nested by a lot of sub processes, I retrieve a started and completed process Instances sharing the same business key

const processInstances = await apiClient({
      method: "get",
      url:
         "/history/process-instance?processInstanceBusinessKey=" + businesskey,
   }).then((response) => {
      return response.data;
   });

then I loop the process Instances and collect all detailed variable information in an array

let processInstance = null;
   const CamundaVariables = [];
   for (processInstance of processInstances) {
      const variableInstance = await apiClient({
         method: "get",
         url: "/history/detail?processInstanceId=" + processInstance.id,
      }).then((response) => {
         return response.data;
      });
      let variable;
      for (variable of variableInstance) {
         //parse the value into a string, to be able to display it in a React table cell
         if (variable.variableType === "Boolean") {
            variable.value = variable.value.toString();
         }
         if (variable.variableType === "Object") {
            variable.value = JSON.stringify(variable.value);
         }
         CamundaVariables.push(variable);
         variable = null;
      }
   }

Now I retrieve all tasks with the same business key and check all revisions of all variables if they where created or updated in the timeframe the task is/was active.

const historicTasks = await apiClient({
      method: "get",
      url: "/history/task?processInstanceBusinessKey=" + businesskey,
   }).then(async (response) => {
      const tasks = response.data;
      let historicTasks = [];
      for (const task of tasks) {
         const taskVariables = CamundaVariables.filter(function (variable) {
            return (
               variable.time >= task.startTime &&
               variable.time <= task.endTime &&
               variable.processInstanceId == task.processInstanceId
            );
         });

.
.
.

As I currently do not have any parallel gateways, this works fine for me. If a parallel gateway will be used in the future I will add an additional check if the executionId is the same.

Find below the full function.

async function getHistoricTasks(businesskey) {
   const processInstances = await apiClient({
      method: "get",
      url:
         "/history/process-instance?processInstanceBusinessKey=" + businesskey,
   }).then((response) => {
      return response.data;
   });

   let processInstance = null;
   const CamundaVariables = [];
   for (processInstance of processInstances) {
      const variableInstance = await apiClient({
         method: "get",
         url: "/history/detail?processInstanceId=" + processInstance.id,
      }).then((response) => {
         return response.data;
      });
      let variable;
      for (variable of variableInstance) {
         //parse the value into a string, to be able to display it in a React table cell
         if (variable.variableType === "Boolean") {
            variable.value = variable.value.toString();
         }
         if (variable.variableType === "Object") {
            variable.value = JSON.stringify(variable.value);
         }
         CamundaVariables.push(variable);
         variable = null;
      }
   }
   const historicTasks = await apiClient({
      method: "get",
      url: "/history/task?processInstanceBusinessKey=" + businesskey,
   }).then(async (response) => {
      const tasks = response.data;
      let historicTasks = [];
      for (const task of tasks) {
         const taskVariables = CamundaVariables.filter(function (variable) {
            return (
               variable.time >= task.startTime &&
               variable.time <= task.endTime &&
               variable.processInstanceId == task.processInstanceId
            );
         });
         //if the process instance is not completed, the durationInMillis is null
         let durationInDays = 0;
         if (task.duration === null) {
            const startTime = new Date(task.startTime);
            const now = new Date();
            const diffTime = Math.abs(now - startTime);
            durationInDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
         } else {
            durationInDays = Math.round(task.duration / 86400000);
         }
         const item = {
            id: task.id,
            timestamp: task.startTime,
            processDefinitionKey: task.processDefinitionKey,
            startTime: new Date(Date.parse(task.startTime)).toLocaleString(
               "de-DE",
               {
                  dateStyle: "short",
                  timeStyle: "short",
               }
            ),
            endTime: task.endTime
               ? new Date(Date.parse(task.endTime)).toLocaleString("de-DE", {
                    dateStyle: "short",
                    timeStyle: "short",
                 })
               : null,
            priority: task.priority,
            name: task.name,
            variables: taskVariables,
            durationInDays: durationInDays,
            description: task.description,
            deleteReason: task.deleteReason,
         };
         historicTasks.push(item);
      }
      // sort the log in descending order
      historicTasks.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
      return historicTasks;
   });
   return historicTasks;
}

Best regards

Michael

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