Skip to Content
Technical Articles

Exploring BTP Workflow Service APIs with a sample application

While developing the backend for this prototype that uses SAP Workflow Services I had the opportunity to dive deeper into the APIs that this service exposes. In this blog I want to share my learnings, give an overview of its capabilities and present examples of use cases.

This article will also serve as a compilation of resources that I used and might come handy if you seek to develop your own solution with the SAP Business Technology Platform Workflow Service. Special thanks to DJ Adams for building the Workflow building block for the devotoberfest. Really helpful!

Before we jump in, all the code shown here is published on Github more precisely, in the workfklow.js module. Before each image with samples, there is a link for the exact position on the code (I don’t like the code formatting in here 🙂)

Also, you can see a demo of the application, that will help you to understand the business case in the video below:

 

SAP Workflow Services APIs

There are currently 2 sets of workflow APIs, Neo Environment APIs and Cloud Foundry APIs. The former is there for legacy reasons, so don’t get too excited if you are starting a new development. The latter, will be the focus of this post.

Workflow APIs on Cloud Foundry

The list of available endpoints is published on the API Hub, where you can use a sandbox system to try them out. They are also available in the Help Portal with some additional information.

I wanted to use my own Workflow definitions, so the sandbox system wasn’t an option. I needed my own system.

There are several pieces that need to be configured in order for the SAP Workflow Management to work. Luckily, the BTP Boosters make life much easier. With a few clicks everything is ready to roll.

Workflow Management Booster running

The next step would be the creation of the workflow definition, using the Business Application Studio, so we can finally start playing with the API.

Authentication

The workflow service API supports OAuth 2.0 authentication for both Users (Authorization Code Grant) and for Clients (Client Credentials Grant).

The first considers the user context and therefore, the application would require the user’s authentication, using the Authorization and Trust Management Service.

On one side it’s quite interesting to keep logs of which user did what. On another, it adds the complexity of managing user sessions (ideally on a cache system) which does not add value to my prototype.

Start Workflow instance response. User token vs Client Token. Almost the same aside from the user that started it.

For the sake of simplicity, I decided not to pursue this route. Instead, my app is authenticated as a client, as you can see in the snippet below:

There isn’t much to unpack here. We are performing a POST request to the AUTH_URL on the endpoint /oauth/token. AUTH_CLIENT_ID and AUTH_CLIENT_SECRET are the user and password. And last, grand_type parameter is set to “client_credentials”. All of those values are stored on environment variables, following the 12 factor methodology, and were retrieved from the Service Key that the Booster process created for our workflow service instance.

The worfklow APIs are also protected by a Scope, that means a given endpoint needs to be allowed on your workflow service instance, using a command like:

cf update-service <WF SERVICE INSTANCE NAME> -c '{"authorities": ["WORKFLOW_DEFINITION_GET", "WORKFLOW_INSTANCE_START", "WORKFLOW_INSTANCE_GET", "TASK_GET", "TASK_GET_CONTEXT", "TASK_COMPLETE", "TASK_UPDATE"]}'

The name of the scope you need, is listed on the API documentation.

Starting a Workflow Instance

The first step of our food app prototype is the customer to place an order. Behind the scenes our app interacts with the /v1/workflow-instances endpoint. This endpoint is pretty permissive and just requires definitionId and a context to get things moving. You can pass any values on those. The component responsible for parsing them, is the workflow definition, created previously. Here is an example of request.

 

In our implementation is translated like this:

Listing open tasks and context

There is the requirement to list open tasks for a given user. This will be useful on the screens used by the “kitchen” and “delivery” teams. The /v1/task-instances endpoint is just what we need.

It supports several types of parameters as filters making it easy to retrieve the open tasks (in workflow terms, status “READY”) for a giving user. There is one problem, though. The response is something like this:

A lot of metadata, but no context information. There are no details regarding the payload used to create the workflow instance. In our case, the order details, customer name, etc…

This info can be retrieved performing another call, to the same endpoint. But this time passing the task id as parameter.

So once we implement the context retrieval, for a given task id:

We can use it for all open tasks found:

Although satisfied with my approach, there is a scalability concern. Making 1 additional request for every task is not optimal. I wish there was a parameter that would allow retrieving task + context in a single shot.

Later I was told that one can parse the context information in Custom Attributes and those can be retrieved in a single shot, via the instance API, with the parameter $expand=attributes.

Nevertheless, the UI has what it needs:

 

Completing a task and retrieving the next one

I like to compare workflows with state machines and the input that triggers a transition of states are the task completions. Doing that using the API is quite trivial:

The endpoint for doing that is again the but, /v1/task-instances this time with a PATCH request.

Just need to update the status to COMPLETED

This will work great when the kitchen flags the order as “ready for pickup”, which generates a task for a rider to “start delivery”. However, “start delivery” generates another task for it to be confirmed.

The issue here is that once one task is completed, it is not aware of what the next one is (state machine, remember?). And the rider needs that in order to complete that specifically delivery.

The solution, was to implement an optional parameter on my app API when completing tasks. Not only one can provide the taskId, that should be completed (31), but also the workflow instanceId. That will allow us to check for open tasks, on that particular instance. Problem solved, right? Well…. not exactly.

My application API handling both taskId and instanceID

Although everything was worked as expected when running the app on my machine. After pushing the app to Cloud Foundry, I was always getting an error. Checking the logs this is what I found:

That didn’t make any sense, at first. Specially when checking the workflow monitor UI, I could see there was 1 open task (confirm delivery). As it should be. To cut a long story short: There is a minimum delay between a task being completed and a subsequent task being created.

When running the app locally, due to the network latency between my laptop and the Business Technology Platform that was just enough for the app to retrieve the open tasks. But when running everything on the BTP… It was just too fast.

The solution: keep on trying until a task shows up

What a difference 6 milliseconds can make

That was my experience with the Workflow Service API. If you have any comments please put them down below. Thank you for reading, and you can also find me on Twitter. @Ralphive.

3 Comments
You must be Logged on to comment or reply to a post.
  • Thanks Ralph Oliveira ,

    this is a nice post about the APIs for the workflow service. I hope SAP is investing in the workflow services more time & effort, as it still misses some necessary features.

    Your project is showing us why this would be worth it 😉

    The only negative thing ... I'm now hungry and I don know why, but I want some pizza ... :/

     

    Regards,

    Cedric