Exercise: Adding an External Service

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Add and consume an external service

Add an External Service

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:

  1. Live Environment – using the following instructions provided, you can perform the tasks in the SAP BTP Free Tier account
  2. Platform Simulation – follow the step-by-step instructions within the simulation
Note
We are strongly recommending that you first perform the tasks in the live environment.

Live Environment

In this exercise you will perform the following steps:

  1. Use the external API in the Project.
  2. Connect your app with the Business Partner API Sandbox Environment of the SAP API Business Hub.
  3. Consume an External Service in your UI application.
  4. 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

  1. Use the external API in the Project.

    1. 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
      The API_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.

    2. In your project, open the db/schema.cds file and enter the code listed below at the very and of the file.

      Code snippet
      
      // 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,
      }
      
      Expand

      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, and FirstName - so that your projection is using a subset of everything the original service has to offer.

    3. In schema.cds uncomment the BusinessPartner Association In the Risks Entity.

      The Risks entity should look like the following:

      Code snippet
      
      entity 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;
      }
      
      Expand

    4. Uncomment the entity BusinessPartners line or replace the entire file with the following snippet.

      Code snippet
      
      using {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;
      }
      
      Expand
  2. 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, the CSN 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 your risks and mitigations 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.
    1. Go back to the SAP Business Accelerator Hub page in your browser.

    2. Make sure you are logged in. If you are not logged in, select the Log On button on the upper right.

    3. Navigate to the Business Partner API (SAP S/4HANA CloudBusiness Partner (A2X)).

    4. In the upper-right corner, choose Show API Key to see your API key.

    5. Copy the API key.

    6. Go to your project in the SAP Business Application Studio, rename the file .env-example in the root 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 stopping cds 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.

    7. Open the package.json file and add the credentials configuration to the API_BUSINESS_PARTNER configuration.

      Code snippet
      
      "credentials": {
                "url": "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER/"
              }
      
      Expand

      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.

    8. Open the risk-service.js file and insert the following lines to the service implementation wrapper function:

      Code snippet
      
          // 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,
                  },
              });
          });
      
      Expand
      It should look like the following:

      You've now created a custom handler for your service. This time it called on for the READ event.

      The handler is invoked when your BusinessPartner service is called for a READ, 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 a where clause to the request, which selects only business partners where the first and last name is set.

    9. Save the file. Stop cds watch (to load the set environment variables) in your terminal and start it again.

    10. In your browser, open the BusinessPartners link to see the data.

  3. Consume the External Service in Your UI Application.

    In this task, you incorporate the external service into the UI application.

    1. Open the riskmanagement-Risks.csv file in your db/data folder.

    2. Replace the content with the new content below which additionally includes the BusinessPartner ID at the very end (last column)

      Code snippet
      ID;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
      Expand
    3. 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.

  4. Add the Business Partner Field to the List Report and Object page.

    1. Open the Page Map for the risk-management application (right click "app/risk-management" folder and choose Show Page Map.

    2. Edit the List Report Page.

    3. Add a new contact column.

    4. Update the column name and enable localization.

    5. Navigate back to the main page map and edit the Object page.

    6. Add the BusinessPartner as the Contact column.

    7. Update the column name and enable localization.

  5. 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 (the bp association and also provide the api-key there. You will add this logic in the next step.
    1. Open the srv/risk-service.js file.

    2. Insert the following lines to the service implementation wrapper function:

      Code snippet
      
          // 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;
          });
      
      Expand

      It should be inserted here:

      You have added another custom handler. This one is called on a READ of the Risks service. It checks whether the request includes a so-called expand 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 the Risks 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.

    3. Save the file.

    4. In your tab with the application, go back to the launchpad.html page and press refresh.

    5. 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.

Log in to track your progress & complete quizzes