Skip to Content
Technical Articles
Author's profile photo Mio Yasutake

SAP Cloud SDK for JavaScript – Getting started with Workflow API Client

Introduction

SAP Workflow Service provides a set of APIs which allow you to list and manage workflow instances, definitions, and user tasks across recipients. If you consume those APIs directly, you need to write code for handling workflow service URL and authentication on your own.

SAP Cloud SDK for JavaScript provides predefined API Client for SAP Workflow Service in the Cloud Foundry Environment. By using the API Client, you don’t have to write boilerplate code which is mentioned above, so you can quickly start developing your business logic.
In this blog, I will show you how to create a service which consumes Workflow Service API Client and test that service.
The source code is available at GitHub.

Prerequisites

 

Steps

  1. Create a workflow service instance
  2. Create a destination pointing to workflow service rest URL
  3. Create a service that consumes workflow APIs
  4. Test the service

 

1. Create a workflow service instance

 

1.1. Create a service instance

First, let’s create a workflow service instance. The point here is to assign the instance the necessary scopes to execute a certain workflow API. You can find necessary scopes in the API documentation at API Business Hub.

Since I need multiple scopes, I have prepared the following file named “authorities.json” (the name can be anything).

{
    "authorities": [
        "WORKFLOW_INSTANCE_START",
        "WORKFLOW_INSTANCE_GET_CONTEXT",
        "WORKFLOW_INSTANCE_GET",
        "TASK_GET",
        "TASK_COMPLETE", 
        "TASK_UPDATE"
    ]
}

Next, execute the following command to create a service instance. The file prepared in the above step is used to provide parameters to the instance. “wf_cloud_sdk” is the name of the service instance. Again, you can chose your preferred name.

cf create-service workflow lite wf_cloud_sdk -c authorities.json

If you need more scopes at a later point of time, you can update the service instance with the following command.

cf update-service wf_cloud_sdk -c authorities.json

 

1.2. Create a service key

Execute the following command to create a service key.

cf create-service-key wf_cloud_sdk key1

Get the information of the service key by the following command. You’ll need this information in the next step.

cf service-key wf_cloud_sdk key1
{
 "content_endpoint": "https://api.workflow-sap.cfapps.eu10.hana.ondemand.com/workflow-deploy/rest/internal/v1",
 "endpoints": {
  "workflow_odata_url": "https://api.workflow-sap.cfapps.eu10.hana.ondemand.com/workflow-service/odata",
  "workflow_rest_url": "https://api.workflow-sap.cfapps.eu10.hana.ondemand.com/workflow-service/rest"
 },
 "html5-apps-repo": {
  "app_host_id": "1365363a-6e04-4f43-876a-67b81f32306e,1a5b93af-f1af-4acf-aee0-8c6cc8d3f315,8964e911-e35d-4cfd-972e-08e681a2df0f,9ea7410f-80ea-4b19-bbf0-4fca238ef098"
 },
 "portal_content_provider": {
  "instance_id": "b87e14b7-ea72-4866-80b7-fe284e75e83a"
 },
 "saasregistryappname": "workflow",
 "sap.cloud.service": "com.sap.bpm.workflow",
 "uaa": {
  "apiurl": "https://api.authentication.eu10.hana.ondemand.com",
  "clientid": "xxxxxxxxxx",
  "clientsecret": "xxxxxxxxxx",
  "credential-type": "binding-secret",
  "identityzone": "b736177ctrial",
  "identityzoneid": "cf4bec0f-ec63-4d37-8efd-ded0e8f33c58",
  "sburl": "https://internal-xsuaa.authentication.eu10.hana.ondemand.com",
  "subaccountid": "cf4bec0f-ec63-4d37-8efd-ded0e8f33c58",
  "tenantid": "cf4bec0f-ec63-4d37-8efd-ded0e8f33c58",
  "tenantmode": "dedicated",
  "uaadomain": "authentication.eu10.hana.ondemand.com",
  "url": "https://b736177ctrial.authentication.eu10.hana.ondemand.com",
  "verificationkey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxfwBi8D8tOJ61KJ8PJel0ML3oUt+A7k7r7F9gpUAAMon5wqS018oOWErn3gU8VwLCrtDiz9ow8aiia5gxvKXSyMIulMUK/H5QaLMPDFmlPG8hwFR8c3KbxwbzvY4gase7B4Gi2YS6eii+7sNejCbz8tFzpOJJbxia6VX7JCuhs463vFA1svPFDE7Ewa6A5mdpP/uRpW1FQC4zL2npN9dH3T/Osp6b30EKPzL2WcsNes8stxcC8xkMGHDZSBCiAUMhCseD0eei3cwCOMzZj92agyUQiIBr1qgXjIOz8W3xlimkEmRTW8xNlTp7jO972plK1nlVYNnvfA2ObE7cOSqYwIDAQAB-----END PUBLIC KEY-----",
  "xsappname": "clone-52964022-f257-4e19-a621-463a969e3e70!b54386|workflow!b10150",
  "zoneid": "cf4bec0f-ec63-4d37-8efd-ded0e8f33c58"
 }
}

 

2. Create a destination pointing to workflow service rest URL

Go to your BTP Cockpit and create a destination as described here.

  • Name: Workflow-Api
  • Type: HTTP
  • URL: The value of workflow_rest_url
  • Proxy Type: Internet
  • Authentication: OAuth2ClientCredentials
  • Client ID: The value of clientid
  • Client Secret: The value of clientsecret
  • Token Service URL: The value of url appended by /oauth/token?grant_type=client_credentials

 

3. Create a service that consumes workflow APIs

Create a Cloud SDK project and implement methods to call workflow APIs.
The service will provide the following endpoints.

  • Start a new workflow instance
  • Get workflow instances
  • Get a workflow instance by id
  • Get workflow instance context
  • Get user tasks for a specified workflow instance
  • Update a user task

3.1. Create a Cloud SDK project

Initialize your project by the following command.

sap-cloud-sdk init wf_cloud_sdk

Next, create a new module for handling requests related to workflow. Go to the root of the project and use the following Nest CLI for generating necessary artifacts.

nest generate module workflow
nest generate controller workflow
nest generate service workflow

As a result, you’ll get the project structure as below.

 

3.2. Implement the service to call workflow APIs

First you need to add @sap/cloud-sdk-workflow-service-cf to dependency of your project.

npm install @sap/cloud-sdk-workflow-service-cf

Open src/workflow/workflow.service.ts and add the following code. These methods will be called from the controller which we’ll implement in the next step. See only a minimal lines of code is required for executing each API.

import { Injectable } from '@nestjs/common';
import {
  WorkflowInstance,
  WorkflowInstancesApi,
  UserTaskInstancesApi,
  TaskInstance,
} from '@sap/cloud-sdk-workflow-service-cf';

@Injectable()
export class WorkflowService {
  private destination = { destinationName: 'Workflow-Api' };

  startWorkflow(definitionId: string, body: any): Promise<WorkflowInstance> {
    return WorkflowInstancesApi.startInstance({
      definitionId: definitionId,
      context: body,
    }).execute(this.destination);
  }

  getWorkflowInstances(definitionId: string): Promise<WorkflowInstance[]> {
    return WorkflowInstancesApi.queryInstances({
      definitionId: definitionId ? definitionId : '',
      $top: 10,
      $orderby: 'startedAt desc',
    }).execute(this.destination);
  }

  getWorkflowInstance(workflowInstanceID: string): Promise<WorkflowInstance> {
    return WorkflowInstancesApi.getInstance(workflowInstanceID).execute(
      this.destination,
    );
  }

  getWorkflowContext(workflowInstanceID: string): Promise<any> {
    return WorkflowInstancesApi.getInstanceContext(workflowInstanceID).execute(
      this.destination,
    );
  }

  getUserTasks(workflowInstanceID: string): Promise<TaskInstance[]> {
    return UserTaskInstancesApi.queryInstances({
      workflowInstanceId: workflowInstanceID,
    }).execute(this.destination);
  }

  updateUserTask(taskInstanceID: string, body: any): Promise<void> {
    return UserTaskInstancesApi.updateInstance(taskInstanceID, {
      context: body,
      status: 'COMPLETED',
    }).execute(this.destination);
  }
}

Open src/workflow/workflow.controller.ts and add the following code.

import {
  Body,
  Controller,
  Get,
  HttpCode,
  Param,
  Patch,
  Post,
  Query,
} from '@nestjs/common';
import { WorkflowService } from './workflow.service';
import {
  TaskInstance,
  WorkflowInstance,
} from '@sap/cloud-sdk-workflow-service-cf';

@Controller('workflow')
export class WorkflowController {
  constructor(private readonly workflowService: WorkflowService) {}

  @Post('/instance/:definitionId')
  @HttpCode(201)
  startWorkflow(
    @Body() body,
    @Param('definitionId') definitionId,
  ): Promise<WorkflowInstance> {
    return this.workflowService.startWorkflow(definitionId, body);
  }

  @Get('/instance')
  getWorkflowInstances(
    @Query('definitionId') definitionId,
  ): Promise<WorkflowInstance[]> {
    return this.workflowService.getWorkflowInstances(definitionId);
  }

  @Get('/instance/:workflowInstanceID')
  getWorkflowInstance(
    @Param('workflowInstanceID') workflowInstanceID,
  ): Promise<WorkflowInstance> {
    return this.workflowService.getWorkflowInstance(workflowInstanceID);
  }

  @Get('/instance/:workflowInstanceID/context')
  getWorkflowContext(
    @Param('workflowInstanceID') workflowInstanceID,
  ): Promise<any> {
    return this.workflowService.getWorkflowContext(workflowInstanceID);
  }

  @Get('/instance/:workflowInstanceID/usertask')
  getUserTasks(
    @Param('workflowInstanceID') workflowInstanceID,
  ): Promise<TaskInstance> {
    return this.workflowService.getUserTasks(workflowInstanceID);
  }

  @Patch('/usertask/:taskInstanceID')
  @HttpCode(204)
  updateUserTask(
    @Body() body,
    @Param('taskInstanceID') taskInstanceID,
  ): Promise<void> {
    return this.workflowService.updateUserTask(taskInstanceID, body);
  }
}

 

3.3. Deploy the service to the Cloud Foundry

Since this service needs to access BTP destination, you need to bind destination and xsuaa service instances to this service.
First, create a destination service instance.

cf create-service destination lite wf_cloud_sdk-destination

Second, create xs-security.json file with the following content.

{
    "xsappname": "wf_cloud_sdk",
    "tenant-mode": "dedicated"
}

Then, create a xsuaa service instance.

cf create-service xsuaa application wf_cloud_sdk-xsuaa -c xs-security.json

Add the newly created services to the services section of manifest.yml.

applications:
  - name: wf_cloud_sdk
    path: deployment/
    buildpacks:
      - nodejs_buildpack
    memory: 256M
    command: npm run start:prod
    random-route: true
    services:
      - wf_cloud_sdk-destination
      - wf_cloud_sdk-xsuaa

Finally, execute the following command to deploy the service to the Cloud Foundry.

npm run deploy

 

4. Test the service

If you don’t have a workflow definition yet, you can create one by following this tutorial.
The following section assumes you have a workflow definition "onboard", but any workflow definition can be used.

4.1. Get workflow instances

Execute the following request. If you don’t specify ?definitionId=onboard, up to 10 workflow instances will be retrieved in descending order by creation date.

/workflow/instance?definitionId=onboard

 

 

4.2. Get an instance by id

Pick an instance id from the response above. (example: d16bbe10-3363-11eb-8e66-eeee0a979f81)

Execute the following request.

/workflow/instance/<INSTANCE_ID>

 

4.3. Get workflow instance context

Execute the following request.

/workflow/instance/<INSTANCE_ID>/context

 

4.4. Start a new workflow instance

To execute POST requests I use Postman (you can use any API client).
Execute the following request with the body down below.

/workflow/instance/onboard
{
  "managerId": "john.edrich@sapdemo.com",
  "buddyId": "kevin.hart@saptest.com",
  "userId": "cgrant1",
  "empData": {
    "firstName": "Carla",
    "lastName": "Grant",
    "city": "San Mateo",
    "country": "United States",
    "hireDate": "2020-07-11",
    "jobTitle": "General Manager, Industries"
  }
}

You’ll get the result looking like the image below. Copy the instance id in the response for the next text.

 

4.5. Get user tasks for a specified workflow instance

Execute the following request. Use the instance id you’ve got in the previous step.

/workflow/instance/<INSTANCE_ID>/usertask

Copy the id in the response for the next text.

 

4.6. Update a user task

Here again I use Postman for sending a PATCH request.
Execute the following request with the body down below.

/workflow/usertask/<USER_TASK_ID>
{
    "approve": true
}

Now, get the same user task again, and you’ll see the status of the task has been changed from “READY” to “COMPLETED”.

/workflow/instance/<INSTANCE_ID>/usertask

 

Conclusion

In this blog you’ve learned how to create a service using Workflow Service API Client and test that service. With SAP Cloud SDK you can execute workflow APIs with only minimal lines of code, which makes development easier.

References

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Veronica P. Alexander
      Veronica P. Alexander

      It's really useful! I read it all and it's really good information. Thanks for this wonderful article!

      Author's profile photo Cedric Heisel
      Cedric Heisel

      Good job, thx for sharing your journey and your way to use the SDK to make things easier!