Recently I was working a project that requires me to consume SAP Build Process Automation API in my app. So when I was looking at our SAP API hub, I found that the APIs for SAP Build are documented quite nicely – the next part is to consume the API. But in the world of JavaScript & cloud, there are many ways to consume an API. One of the popular approach is using Axios, but you might end of doing a lot of manual stuff to connect to the API, especially when you want to use Principal Propagation & connecting to CF.

That’s when I discovered the SAP Cloud SDK, which helps to solve this by providing typed APIs for an Open API or OData service. It also offers multiple ways to connect to the APIs in your SAP BTP subaccount, using Service Binding, Destination and different authentication mechanisms, including OAuth User Token Exchange (Principal Propagation).

In this blog post, I will show how you can connect to the SAP Build Process Automation API using Client Credentials & Principal Propogation. For this, I will create a simple CAP application based on TypeScript using VSCode. You can do the same in a Node JS app or consume other SAP BTP APIs.

Extension app connecting to SAP Build Process Automation using SAP Cloud SDK (User Token Exchange)

Client Credentials Flow

Github

Scenario: [Consuming SAP Build Process Automation: Processes & Workflows] To fetch all the Tasks created for a workflow in the ready state.

Steps:

Assuming a new CAP app was already created using the template, Import the API in JSON format in the resources main folder as shown in this Github repo.

Initializing the project:

Ensure that ts-node is installed globally:

npm i -g typescript ts-node 

 

As this is Open API format, first install the Open API generator using the below command:

npm install @sap-cloud-sdk/openapi-generator

 

Now generate the typed client using the below command:

npx openapi-generator --input resources --outputDir srv/generated --skipValidation

 

[Optional] I also want to generate the cds file for this API, so I can use it as a returning type for my function import.

cds import './resources/SPA_Workflow_Runtime New.json'  --from openapi --as cds

 

add a service.cds file in srv folder and add the below code:

// Below code is from CDS Import from earlier step
using { Processes._.Workflows_types.TaskInstance as TaskInsance } from './external/SPA_Workflow_Runtime New.cds';

// New service to expose a function import
service sapCloudSDKBuildTS {
    function getTaskInstances() returns array of TaskInsance;
}

Two things I did in the above code:

  1. We are loading the types of “TaskInstance” .
  2. We created our service and added a function import, which returns the task instances of type “TaskInstance”.

Consuming the API using SAP Cloud SDK:

There are many ways to consume an SAP BTP API using SAP Cloud SDK. In this example, I will use Service Binding based approach, which only needs service instance created for SAP Build Process Automation service. To test locally, bind the service instance to the app using the below command:

cds bind process-automation --to spateamsint-spa:spateamsint-spa-key --kind process-automation

Here spateamsint-spa is the service instance name and spateamsint-spa-key is the key name.

In the code, first we have to import the TaskInstances API using the below code:

import { UserTaskInstancesApi } from "./generated/SPA_Workflow_Runtime New";

Add a service.ts file in srv folder to write the code return the Task Instances, when the function: getTaskInstances() is called. Check the code to do some copy-pasta (will explain in the coming steps).

As it’s a typed client, you can explore all the available methods but we use getV1TaskInstances to fetch all the available Task Instances.

 const responseData = await UserTaskInstancesApi.getV1TaskInstances({
        definitionId:
          "form_simpleapprovalp_1@****.testworkflow.simpleapproval",
        status: "READY",
      }).execute({
        destinationName: "process-automation",
        serviceBindingTransformFn,
      });

You can also observe that I’ve passed additional parameters like Status, which are part of the typings.

In the destinationName, you need to pass the service name, which in our case is process-automation.

Note: This is the service name, not the service instance name. SAP Cloud SDK will fetch the service instances associated with the service process-automation from VCAP_SERVICES that are loaded locally when we bound our application using CDS BIND for our local testing. You can also do this using an actual SAP BTP Destination, which I will show in the other example after this.

There is also another function that is passed – serviceBindingTransformFn. This is because only few services are by default supported by the SAP Cloud SDK to fetch the URL, Credentials etc., from the service instance automatically, and process-automation service is not one of them 😉

For this reason, we are passing this function, which has the below code:

As you can see I am using executeHttpRequest, part of SAP Cloud SDK to fetch the token. But you can also use Axios to make that code a bit shorter 😉 You can also find the Axios example commented in the github repo.

At the end, you will form an object with the required details and return it, which will be consumed by the SAP Cloud SDK and return our requested data. Run the app using the below command:

cds-ts watch --profile hybrid

Once you call the URL, it will return you all the tasks for the specified workflow.

eg: http://localhost:4004/sap-cloud-sdkbuild-ts/getTaskInstances()

Note: I am only covering the local installation & testing. You need to have mta.yaml with the neccessary resources & modules for it to run in SAP BTP CF environment and also need to adjust the package.json to update the start task. Check the CAP help for TypeScript for more information.

Principal Propagation (User Token Exchange)

Github

Scenario: [Consuming SAP Build Process Automation: Inbox] To fetch all the Workflow Tasks assigned to the logged in user. [Testing done Locally]

Steps:

Assuming a new CAP app was already created using the template, Import the API in EDMX format in the resources main folder as shown in this Github repo.

Initializing the project:

Ensure that ts-node is installed globally:

npm i -g typescript ts-node 

 

As this is an OData API, install the regular generator using the below command:

npm install @sap-cloud-sdk/generator

 

[Optional] Add a serviceMapping.json file with the below content, so you can skip adding the path while executing the requests:

{
  "SPA_Workflow_Inbox": {
    "directoryName": "com-sap-bpm-wfs-tcm-metadata-service",
    "servicePath": "/public/workflow/odata/v1/tcm",
    "npmPackageName": "com-sap-bpm-wfs-tcm-metadata-service"
  }
}

 

Now generate the typed client using the below command. In case if you get any error related to “triple-beam” typings, install it. npm install –save @types/triple-beam

npx generate-odata-client --inputDir resources --outputDir srv/generated --overwrite -s "resources/serviceMapping.json"

 

add a service.cds file in srv folder and add the below code. It’s self-explanatory, and you will have an idea from the previous example.

service sapCloudSDKBuildTS @(requires: 'authenticated-user') {
    // define type {};
    type TaskCollection {
        instanceId: String;
        status:String;
    }
    function getTaskInstances() returns array of TaskCollection;
}

 

Consuming the OData API using SAP Cloud SDK:

In this scenario, I will show how to consume the same using the Destination created in SAP BTP. So you need to bind the destination instance to test it locally.

cds bind destination --to destination-srv-instance:destination-srv-instance-key --kind destination

SAP Cloud SDK also requires XSUAA instance for fetching the destination. So bind it as below: (You can do this in the later steps, in XSUAA hybrid testing)

cds bind xsuaa --to xsuaa-srvins:xsuaa-srvins-key --kind xsuaa

 

Add a service.ts file in srv folder to write the code return the Task Instances for the logged-in user, when the function: getTaskInstances() is called. Check the code to do some copy-pasta.

const { taskApi } = wfApi();
const jwt = retrieveJwt((Request as any).req);
const result = await taskApi
        .requestBuilder()
        .getAll()
        .top(5)
        .execute({ destinationName: "sap_process_automation_service_user_token", jwt:jwt });

Code is pretty straightforward and it’s typed, so it will be easy to figure out all the available methods. The only unique thing here is to fetch the jwt token, which again is provided by SAP Cloud SDK. This will be available once the user is authenticated.

In the destinationName, you must pass the destination that you created in your subaccount. eg:

But how to authenticate the user for local testing? – We will use an approuter to authenticate the user and forward the token to the service call. SAP CAP Documentation for XSUAA hybrid testing – which has explained this clearly

Follow the above documentation and add any missing node libraries (passport & xssec). Run the app router and run the application with the script in the main package.json.

With this, you will be able to fetch all the Workflow tasks assigned to you, and SAP Cloud SDK will perform the user token exchange for us. I am not showing how you can deploy this, as it’s fairly easier with many the blog posts & tutorials talking about deploying a CAP app to SAP BTP CF environment.

In case if you want to do it manually using Postman, download the collection from this github repo and import it in your postman. Then fill up the collection variables, fetch the XSUAA token, perform user token exchange and use the token to fetch the assigned Tasks (TaskCollection).

 

Thant’s it folks, Let me know your thoughts & if you know any better or alternative approach in doing this, please share here 🙂

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x