Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
MioYasutake
Active Contributor

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


2 Comments
Labels in this area