Usage Scenario
In this exercise, you will extend your CAP service with the consumption of an external Business Partner service.
Exercise Options
You can perform this exercise in two ways:
- Live Environment – using the following instructions provided, you can perform the tasks in the SAP BTP Free Tier account
- Platform Simulation – follow the step-by-step instructions within the simulation
Live Environment
In this exercise you will perform the following steps:
- Use the external API in the Project.
- Connect your app with the Business Partner API Sandbox Environment of the SAP API Business Hub.
- Consume an External Service in your UI application.
- Add the Business Partner Field to the UI.
Prerequisite
Before proceeding with the exercise, make sure that you have added custom business logic to your extension.
Steps
Use the external API in the Project.
Navigate into the
srv/external
folder.The CAP importer created the
API_BUSINESS_PARTNER.csn
file out of the API specification. Core Schema Notation (CSN) is a compact representation of CDS). This basically contains all the schema definitions of the external service.Note
TheAPI_BUSINESS_PARTNER.csn
file has been imported by the course authors and was part of the starter template that you have cloned from GitHub. You can go toapi.sap.com (SAP Business Accelerator Hub) to download the specifications there.In your project, open the
db/schema.cds
file and enter the code listed below at the very and of the file.Code snippetExpand// using an external service from SAP S/4HANA Cloud using { API_BUSINESS_PARTNER as external } from '../srv/external/API_BUSINESS_PARTNER.csn'; entity BusinessPartners as projection on external.A_BusinessPartner { key BusinessPartner, BusinessPartnerFullName as FullName, }
With this code, you create a projection for your new service. Of the many entities and properties in these entities that are defined in the
API_BUSINESS_PARTNER
service, you just look at one of the entities (A_BusinessPartner
) and just three of its properties -BusinessPartner
,LastName
, andFirstName
- so that your projection is using a subset of everything the original service has to offer.In
schema.cds
uncomment the BusinessPartner Association In the Risks Entity.The Risks entity should look like the following:
Code snippetExpandentity Risks : cuid, managed { title : String(100); owner : String; prio : Association to Priority; descr : String; miti : Association to Mitigations; impact : Integer; bp : Association to BusinessPartners; // <-- uncomment this line virtual criticality : Integer; virtual PrioCriticality : Integer; }
Uncomment the
entity BusinessPartners
line or replace the entire file with the following snippet.Code snippetExpandusing {riskmanagement as rm} from '../db/schema'; @path: 'service/risk' service RiskService { entity Risks as projection on rm.Risks; annotate Risks with @odata.draft.enabled; entity Mitigations as projection on rm.Mitigations; annotate Mitigations with @odata.draft.enabled; // BusinessPartner @readonly entity BusinessPartners as projection on rm.BusinessPartners; }
Connect your app with the Business Partner API Sandbox Environment of the SAP Business Accelerator Hub (formerly SAP API Business Hub).
At this point, you have a new service exposed with a definition based on the imported
SCN
file. However, theCSN
file only contains the schema (in schema notation), but doesn't have any connectivity to a backend, so, there’s no data yet. In this case, you do not create local data as with yourrisks
andmitigations
entities, but you connect your service to the Sandbox environment of the SAP Business Accelerator Hub for the Business Partner API that you want to use. To use the API Business Hub Sandbox APIs, you require an API key. This key will authenticate your application with the API endpoint.Note
Pay special attention to credentials such as API keys. They are like passwords and should be treated accordingly.Go back to the SAP Business Accelerator Hub page in your browser.
Make sure you are logged in. If you are not logged in, select the Log On button on the upper right.
Navigate to the Business Partner API (SAP S/4HANA Cloud → Business Partner (A2X)).
In the upper-right corner, choose Show API Key to see your API key.
Copy the API key.
Go to your project in the SAP Business Application Studio, rename the file
.env-example
in theroot
of the project to.env
. Replace<YOUR-API-KEY>
with the API key that you copied in the previous step.apikey=<YOUR-API-KEY>
The result should look like the following:
The
.env
file is an environment file providing values into the runtime environment of your CAP service. You are going to use the API key to call the Business Partner API of the sandbox system provided through the SAP Business Accelerator Hub.Note
When you change or add variables in the.env
file, you have to restart the service. In this case, you can do it by stoppingcds watch
and starting it again.Note
By mentioning the.env
file in the.gitignore
file, you make sure that when you are using git as a version-management-system for your project, no credentials get accidentally leaked into your potentially public git repository.The
.env
file is already added to the.gitignore
file.Open the
package.json
file and add thecredentials
configuration to theAPI_BUSINESS_PARTNER
configuration.Code snippetExpand"credentials": { "url": "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER/" }
It should look like the following:
Now that you have set all the configurations for the external call, you will implement a custom service handler for the external BusinessPartner service in the next step.
Open the
risk-service.js
file and insert the following lines to the service implementation wrapper function:It should look like the following:Code snippetExpand// connect to remote service const BPsrv = await cds.connect.to("API_BUSINESS_PARTNER"); /** * Event-handler for read-events on the BusinessPartners entity. * Each request to the API Business Hub requires the apikey in the header. */ this.on("READ", BusinessPartners, async (req) => { // The API Sandbox returns alot of business partners with empty names. // We don't want them in our application req.query.where("LastName <> '' and FirstName <> '' "); return await BPsrv.transaction(req).send({ query: req.query, headers: { apikey: process.env.apikey, }, }); });
You've now created a custom handler for your service. This time it called
on
for theREAD
event.The handler is invoked when your
BusinessPartner
service is called for aREAD
, so whenever there’s a request for business partner data, this handler is called. It ensures the request for the business partner is directed to the external business partner service. Furthermore, you have added awhere
clause to the request, which selects only business partners where the first and last name is set.Save the file. Stop
cds watch
(to load the set environment variables) in your terminal and start it again.In your browser, open the
BusinessPartners
link to see the data.
Consume the External Service in Your UI Application.
In this task, you incorporate the external service into the UI application.
Open the
riskmanagement-Risks.csv
file in yourdb/data
folder.Replace the content with the new content below which additionally includes the BusinessPartner ID at the very end (last column)
Code snippetExpandID;createdAt;createdBy;title;owner;prio_code;descr;miti_ID;impact;bp_BusinessPartner 20466922-7d57-4e76-b14c-e53fd97dcb11;2019-10-24;SYSTEM;CFR non-compliance;Fred Fish;H;Recent restructuring might violate CFR code 71;20466921-7d57-4e76-b14c-e53fd97dcb11;10000;1000060 20466922-7d57-4e76-b14c-e53fd97dcb12;2019-10-24;SYSTEM;SLA violation with possible termination cause;George Gung;M;Repeated SAL violation on service delivery for two successive quarters;20466921-7d57-4e76-b14c-e53fd97dcb12;90000;9980002245 20466922-7d57-4e76-b14c-e53fd97dcb13;2019-10-24;SYSTEM;Shipment violating export control;Herbert Hunter;L;Violation of export and trade control with unauthorized downloads;20466921-7d57-4e76-b14c-e53fd97dcb13;200000;9980000230
Save the file.
If you check the content of the file, you see numbers like
9980000230
at the end of the lines, representing business partners.
Add the Business Partner Field to the List Report and Object page.
Open the
Page Map
for the risk-management application (right click "app/risk-management" folder and choose Show Page Map.Edit the List Report Page.
Add a new contact column.
Update the column name and enable localization.
Navigate back to the main page map and edit the Object page.
Add the BusinessPartner as the Contact column.
Update the column name and enable localization.
Add another event handler.
Note
If you would preview the application now, you won't receive any BusinessPartner data when you read from the risks entity. We have already implemented the handler providing the api-key for a read event directly on the BusinessPartner entity. However, if you perform a read event on Risks, then you need to expand the OData request (thebp association
and also provide the api-key there. You will add this logic in the next step.Open the
srv/risk-service.js
file.Insert the following lines to the service implementation wrapper function:
Code snippetExpand// Risks?$expand=bp (Expand on BusinessPartner) this.on("READ", Risks, async (req, next) => { /* Check whether the request wants an "expand" of the business partner As this is not possible, the risk entity and the business partner entity are in different systems (SAP BTP and S/4 HANA Cloud), if there is such an expand, remove it */ if (!req.query.SELECT.columns) return next(); const expandIndex = req.query.SELECT.columns.findIndex( ({ expand, ref }) => expand && ref[0] === "bp" ); if (expandIndex < 0) return next(); // Remove expand from query req.query.SELECT.columns.splice(expandIndex, 1); // Make sure bp_BusinessPartner (ID) will be returned if (!req.query.SELECT.columns.find((column) => column.ref.find((ref) => ref == "bp_BusinessPartner") ) ) { req.query.SELECT.columns.push({ ref: ["bp_BusinessPartner"] }); } const risks = await next(); const asArray = x => Array.isArray(x) ? x : [x]; // Request all associated BusinessPartners const bpIDs = asArray(risks).map(risk => risk.bp_BusinessPartner); const busienssPartners = await BPsrv.transaction(req).send({ query: SELECT.from(this.entities.BusinessPartners).where({ BusinessPartner: bpIDs }), headers: { apikey: process.env.apikey, } }); // Convert in a map for easier lookup const bpMap = {}; for (const businessPartner of busienssPartners) bpMap[businessPartner.BusinessPartner] = businessPartner; // Add BusinessPartners to result for (const note of asArray(risks)) { note.bp = bpMap[note.bp_BusinessPartner]; } return risks; });
It should be inserted here:
You have added another custom handler. This one is called
on
aREAD
of theRisks
service. It checks whether the request includes a so-calledexpand
for business partners. This is a request that is issued by the UI when the list should be filled. While it mostly contains columns that directly belong to theRisks
entity, it also contains the business partner. As we have seen in the annotation, instead of showing the ID of the business partner, the FullName of the business partner will be shown. This data is in the business partner and not in the risks entity. Therefore, the UI wants to expand, that is, for each risk the corresponding business partner is also read.As the business partner does not reside in the CAP database but in a remote system instead, a direct expand is not possible. The data needs to be retrieved from the SAP S/4HANA Cloud system.
Save the file.
In your tab with the application, go back to the launchpad.html page and press refresh.
On the launch page that now comes up, choose the Risks tile and then select Go.
You now see the
Risks
application with the business partner data in both the list report and the object page, which is loaded when you select one of the rows in the table:
Result
You have added an external business partner service to your application.