My Journey with Camunda — Part 4— External Service Task With Golang

Nurul Arif Setiawan
4 min readMay 11, 2019
Don’t buy your ticket yet! We are not done. Photo by Paolo Rosa on Unsplash

This is multipart series where I wrote my journey using Camunda from a non-java developer perspective.

We have deployed our BPMN to Camunda engine. Before we create Golang external service task, let’s see if we can create instance with it.

Something is wrong here. I have deployment, but no definition.

We have deployment, but no definition

I checked Camunda logs, and I found following line

05-May-2019 13:32:50.616 INFO [http-nio-8080-exec-10] org.camunda.commons.logging.BaseLogger.logInfo ENGINE-01002 Ignoring non-executable process with id 'Process_1bvk6g2'. Set the attribute isExecutable="true" to deploy this process.

Turns out that we not set executable property of the BPMN. Let’s go to Modeler and fix this. I think default should be Executable, right?

Check executable checkbox. This is disabled by default.

After we deploy and refresh Camunda Cockpit, we can see now there is definition deployed.

Now, we have our definition

To create an instance, we can use start process instance API.

curl -X POST \
http://localhost:8080/engine-rest/process-definition/key/Process_1bvk6g2/start \
-H 'Content-Type: application/json' \
-d '{
"variables": {
"id" : {
"value" : "sampurple",
"type": "String"
},
"name" : {
"value" : "Sam Purple",
"type": "String"
},
"email" : {
"value" : "sam.purple@journey.com",
"type": "String"
},
"level" : {
"value" : "junior",
"type": "String"
},
"days" : {
"value" : 10,
"type": "Integer"
}
},
"businessKey" : "Leave-123"
}'

And we have our instances

Process instances from our BPMN

If you notice, our instances is waiting at Validate available days service task. It waits until some external service task process then completed the task. Check service task with fetchAndLock API

curl -X POST \
http://localhost:8080/engine-rest/external-task/fetchAndLock \
-H 'Content-Type: application/json' \
-d '{
"workerId":"worker1",
"maxTasks":2,
"usePriority":true,
"topics": [
{
"topicName": "available-leave-days",
"lockDuration": 30000
}
]
}'

After we get tasks for particular topic, we process it and then complete the task.

curl -X POST \
http://localhost:8080/engine-rest/external-task/{{TASK_ID}}/complete \
-H 'Content-Type: application/json' \
-d '{
"workerId": "worker1",
"variables":{
"daysAvailable": {"value": true}
}
}'

After service task is completed, execution will continue to next activity which is Business Rules Task, where it will select assignee. After that, we go to User Task

We arrived at user task

External Service Task With Golang

Now we know that External service task implementation must implement 2 APIs: fetchAndLock and complete. There are more but for now we only use these APIs. I have created simple Golang application to handle Camunda External Task. You can check following repo in Github

It has two main parts:

Periodically fetch tasks with specific topic

c := cron.New()

availableLeaveDays := &camunda.FetchAndLock{
WorkerID: "worker1",
MaxTasks: 1,
Topics: []camunda.Topic{
camunda.Topic{
Name: "available-leave-days",
LockDuration: 10000,
},
},
}

c.AddFunc("@every 5s", call(client, availableLeaveDays, handlers.AvailableLeaveDaysHandler))

c.Start()

Handler to handle specific topic and complete the task

func AvailableLeaveDaysHandler(client *camunda.Client, tasks []camunda.Task) error {

for _, task := range tasks {

// pretend to do something
time.Sleep(2 * time.Second)

// complete
err := client.Complete(task.ID, &camunda.Complete{
WorkerID: "worker1",
Variables: map[string]camunda.Variable{
"daysAvailable": camunda.Variable{
Value: true,
},
},
})
if err != nil {
return err
}
}

return nil
}

Handlers will be implementation specific and for now we just use sleep function.

We are done here. Next, we will see how we can improve Tasklist experience with form fields.

--

--

Nurul Arif Setiawan

software engineer. product design, planning and development.