3. Delegating Requests - SAP-samples/teched2022-AD265 GitHub Wiki

Use Case: Value Lists

In this exercise, you will make a new Customers entity available, so that the user can pick customer data in a value list.

Expose Customers in service

Create a new file srv/mashup.cds and add this snippet, so that Customers appears in the API of the IncidentsService:

using { acme.incmgt, IncidentsService } from './incidents-service';
using { s4 } from './external';

extend service IncidentsService with {
  entity Customers as projection on s4.simple.Customers;
}

Also in the same file, add an association from Incidents to Customers, to allow queries on which incident refers to which customer (like /Incidents('...')/customer):

extend incmgt.Incidents with {
  customer : Association to s4.simple.Customers;
}

Delegate calls to remote system

To make the value help for Customers work, we need to redirect the request to the remote system (or our mock). Otherwise, the framework would read it from a local DB table, which does not exist.

So, in file srv/incidents-service.js, add this snippet inside the existing function:

  const cds = require('@sap/cds');
  const S4bupa = await cds.connect.to('API_BUSINESS_PARTNER')

  // Delegate Value Help reads for Customers to S4 backend
  this.on('READ', 'Customers', (req) => {
    console.log('>> delegating to S4 service...')
    return S4bupa.run(req.query)
  })

Test with Remote System

As a ready-to-use remote service, we use the sandbox system of SAP API Business Hub.

To use your own SAP S/4HANA Cloud system, see this tutorial. You don't need it for this tutorial though.

  1. Create a new file .env in the incidents folder and add environment variables that hold the URL of the sandbox as well as a personal API Key:

    DEBUG=remote
    cds.requires.API_BUSINESS_PARTNER.[sandbox].credentials.url=https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER/
    cds.requires.API_BUSINESS_PARTNER.[sandbox].credentials.headers.APIKey=<Copied API Key>

    Note the [sandbox] segment which denotes a configuration profile named sandbox. The name has no special meaning. You will see below how to use it.

  2. Get an API key:

  3. Add the key to the .env file

    By putting the key in a separate file, you can exclude it from the Git repository (see the .gitignore file).

    Also note how the cds.requires.API_BUSINESS_PARTNER structure in the .env file matches to the package.json configuration.
    To learn about more configuration options for CAP Node.js applications, see the documentation.

Now kill the server with Ctrl+C and run again with the profile activated:

cds watch --profile sandbox

In the server log, you can see that the configuration is effective:

...
[cds] - connect to API_BUSINESS_PARTNER > odata-v2 {
  url: 'https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER/',
  headers: { APIKey: '...' }
}
...

On the application's index page, the mocked service is gone, because it is no longer served in the application. Instead, it is assumed to be running in a remote system. Through the configuration above, the system knows how to connect to it.

Open /incidents/Customers to see the data coming from the remote system.

If you get a 401 error instead, check your API key in the .env file. After a change in the configuration, kill the server with Ctrl+C and start it again.

You can also see something like this in the log (due to the DEBUG=remote variable from the .env file above):

[remote] - GET https://.../API_BUSINESS_PARTNER/A_BusinessPartner
  ?$select=BusinessPartner,BusinessPartnerFullName&$inlinecount=allpages&$top=74&$orderby=BusinessPartner%20asc
...

This is the remote request sent by the framework when S4bupa.run(req.query) is executed. The req.query object is transparently translated to an OData query $select=BusinessPartner,BusinessPartnerFullName&$top=...&$orderby=.... The entire HTTP request (completed by the sandbox URL configuration) is then sent to the remote system with the help of SAP Cloud SDK.

Note how simple the execution of remote queries is. No manual OData query construction needed, no HTTP client configuration like authentication, no response parsing, error handling, issues like hard-wired host names etc.

See the documentation on CQN for more on such queries in general. The service consumption guide details out how they are translated to remote requests.

Finish UI

The UI needs some more annotations to show the changed data.

  1. Put basic annotations that refer to Customers itself in srv/external/index.cds, next to the Customers definition:

    annotate Customers with @UI.Identification : [{ Value:name }];
    annotate Customers with @cds.odata.valuelist;
    annotate Customers with {
      ID   @title : 'Customer ID';
      name @title : 'Customer Name';
    }
  2. Annotations that refer to Incidents or its association to Customers should go to file srv/mashup.cds. Add this snippet there:

    // import annotations from rest of the application
    using from '../app/fiori';
    
    annotate IncidentsService.Incidents with @(
      UI: {
        // insert table column
        LineItem : [
          ...up to { Value: title },
          { Value: customer.name, Label: 'Customer' },
          ...
        ],
    
        // insert customer to field group
        FieldGroup #GeneralInformation : {
          Data: [
            { Value: customer_ID, Label: 'Customer'},
            ...
          ]
        },
      }
    );
    
    // for an incident's customer, show both name and ID
    annotate IncidentsService.Incidents:customer with @Common: {
      Text: customer.name,
      TextArrangement: #TextFirst
    };

    Don't change the ellipsis ... in the cds code above. It's a special syntax for refering to the 'remaining values' of array-valued annotations. The advantage of this syntax is that you do not have to repeat the other table columns. See the documentation for more.

Verify in UI

Now Open Fiori preview for the Incidents entity.

Create a new incident and select a customer using the value help. When pressing Save, watch the console output of the application and see the >> delegating to S4 service... message.

Behind the scenes - SAP Cloud SDK

When you integrate data from SAP S/4HANA, CAP applications use the SAP Cloud SDK underneath. It is a set of libraries that reduce the effort of building applications on the SAP Business Technology Platform (SAP BTP).

Connectivity

SAP Cloud SDK abstracts authentication flows and communication with SAP BTPs connectivity, destination, and XSUAA services. It doesn't matter whether you want to connect against Cloud or On-premise systems.

OData and REST clients

For applications not using CAP, SAP Cloud SDK helps you build your own typed clients for any OData or REST service to easily connect to your and SAP's systems.

Summary

In the next exercise, you will use data replication to display local and remote data in a performant way.

⚠️ **GitHub.com Fallback** ⚠️