On the 15th of June I will attend the 2nd edition of the Developer Day, with a main focus on the Business Technology Platform Track. I will present a session called “Fantastic CAP Gems & where 2 find them”. This session dedicated to the SAP CAP Hybrid Testing functionality, may be useful to your future CAP development projects.
Even though there is lots of good Hybrid Testing documentation available on SAP CAPire, I had a hard time configuring the Hybrid Testing profiles. The reason for this hard time was lack of advanced examples, which is totally understandable. Nevertheless, I wanted to be able to setup my project correctly and share this setup with you all, to spare you the hunt for this configuration.
I would like to guide you through these configuration steps and explain you the how’s and why’s, so you know exactly what you did and why you did it. Nothing more frustrating than something that works but you don’t know why.
We will be analyzing a CAP application which consumes a cloud (Northwind) and an on-premise (S4/HANA) destination. This CAP application will be accessed through an Approuter application, both using a Hybrid Testing setup.
Prerequisites
Before we dive into the initialization and configuration of our project, we need to fulfill the following prerequisites:
- Access to an SAP BTP (Trial) account (with entitlements to the required services)
- Access to the SAP Business Application Studio (Full Stack Cloud Application workspace)
- Providing the SAP BTP Destinations
- Access to an SAP HANA Cloud database
- Connection to an SAP Cloud Connector (or via docker) from your BTP Subaccount to the S4/HANA system OData Services
The destinations we will be using need to be configured as such:
Northwind:
Type=HTTP
HTML5.Timeout=60000
HTML5.DynamicDestination=true
Authentication=NoAuthentication
WebIDEUsage=odata_abap,dev_abap
Name=Northwind
WebIDEEnabled=true
ProxyType=Internet
URL=https://services.odata.org
S4 (Principal Propagation or Basic Authentication):
Type=HTTP
HTML5.Timeout=60000
HTML5.DynamicDestination=true
Authentication=PrincipalPropagation
WebIDEUsage=odata_abap,dev_abap
Name=S4
WebIDEEnabled=true
ProxyType=OnPremise
URL=http://<your-virtual-host-in-cloud-connector>:8443
sap-client=<client-number>
WebIDESystem=Gateway
Testing destination connectivity
Since we are performing the developments of this blog post in the SAP Business Application Studio, we can test the connectivity to our configured BTP destinations via the terminal or a .http file.
Before testing the connectivity, it is always a good idea to reload the BTP destinations in the SAP Business Application Studio, this by running the following command on the terminal:
curl localhost:8887/reload
Testing connectivity via the terminal and output to a JSON file:
curl "Northwind.dest/v2/northwind/northwind.svc/Orders?%24format=json" > Orders.json
curl "S4.dest/sap/opu/odata/sap/CB_MATERIAL_SRV/Materials?%24format=json" > Materials.json
Testing connectivity via the .http file (for example destinationConnectivity.http):
### Northwind - Orders
GET http://Northwind.dest/v2/northwind/northwind.svc/Orders?$format=json
Content-Type: application/json
### S4 - Materials
GET http://S4.dest/sap/opu/odata/sap/CB_MATERIAL_SRV/Materials?$format=json
Content-Type: application/json
As you may have noticed we are not using the URL of our destination to retrieve the information, but we use the name of our destination followed by .dest to connect and retrieve the results. The SAP Business Application Studio proxy will resolve the name of the destination to the configured destination URL. This feature will become handy in the steps to come!
Initializing the project
When initializing new applications, either UI or Middleware applications, I choose for a TypeScript approach now these days. Typescript with SAP CAP applications is a must in my opinion and lots of cool tips and tricks are all over the place!
I would like to share the final project on my GitHub repository.
This means all configurations are already in place which gives me the opportunity to highlight the important configurations in this project, instead of guiding you through a whole CAP application from scratch.
Analyzing the project
Where does one start explaining/analyzing a project if not building it from scratch? Well, let us loop over the important configuration files in the most logical order possible. The first file might already be a mindblower since you would probably expect it at the end of the loop.
– mta.yaml –
Let’s have a look at the mta.yaml file which is used for the deployment of our application. This file contains some resources configuration which will benefit the Hybrid Testing process later.
- name: hybrid4ever-xsuaa-srv
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service: xsuaa
service-plan: application
service-name: hybrid4ever-xsuaa-srv
service-keys:
- name: hybrid4ever-xsuaa-srv-key
In the above code snippet, you can see that xsuaa resources contains a property named “service-keys” with exactly the same name as the “service-name” property, followed by a suffix “-key”.
This “service-keys” property is configured on every resource in the resources section. (xsuaa, destination, db and connectivity)
The reason that these properties and specific names are configured, is because they will be used by the cds bind command which we will address later in this blogpost.
You could also choose to create these service instances and keys via the Cloud Foundry cli, but why taking the risk of forgetting them.
A second important configuration in the mta.yaml file is the initialization of the destinations inside the destination instance.
- name: hybrid4ever-dest-srv
type: org.cloudfoundry.managed-service
parameters:
config:
HTML5Runtime_enabled: true
init_data:
instance:
destinations:
- Authentication: NoAuthentication
Name: ui5
ProxyType: Internet
Type: HTTP
URL: https://ui5.sap.com
- Authentication: NoAuthentication
HTML5.ForwardAuthToken: true
HTML5.DynamicDestination: true
Name: srv-api
ProxyType: Internet
Type: HTTP
URL: ~{srv-api/srv-url}
existing_destinations_policy: update
The configuration above will create 2 destinations in this case. One is a destination to the SAPUI5 SDK resources and the other one provides a destination to the deployed CAP application service. Providing this configuration allows us during Hybrid Testing to connect to our deployed CAP service and retrieve the data from the cloud instead of our local cap application. More information on how to test this will be addressed later in this blog post.
– package.json –
The package.json file in the root of your CAP project contains all kind of configurations and dependencies, but when consuming external services some extra configurations are required:
{
...,
"cds": {
"requires": {
"[production]": {
"CB_MATERIAL_SRV": {
"kind": "odata-v2",
"model": "srv/external/CB_MATERIAL_SRV",
"credentials": {
"destination": "S4_FLEXSO_ba",
"path": "/sap/opu/odata/sap/CB_MATERIAL_SRV"
}
},
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"credentials": {
"destination": "Northwind",
"path": "/v2/northwind/northwind.svc"
}
}
},
...
},
...
}
}
When you examine the configuration object above, you will notice that the remote services, which are configured as destination (Northwind, S4) in the SAP Business Technology Platform, are appearing in the cds requires section as external services. They were imported in the CAP project using the cds import command. They both contain the destination name and path to the service.
One thing I definitely want to highlight is the fact that both external services are contained in the “[production]” configuration object. This allows us to use this configuration of external services in the deployed version of our application. Without this “[production]” configuration object wrapper the deployed version would still work but issues with Hybrid testing would occur, since they would collide with configurations of our .cdsrc-private.json, which we will address in a moment.
– .cdsrc-private.json –
When talking about SAP CAP Hybrid Testing, the .cdsrc-private.json file is the place to be to set up your Hybrid Testing. But what is this file or what is it replacing? Well, the answer is simple, it replaces your default-env.json file. Isn’t that a relief? This means no more sensitive information stored such as public keys, client-ids and secrets in the VCAP_Services object. You still need to be able to bind your xsuaa, destination, … services to your CAP application. This is still possible and is done via the .cdsrc-private.json file from now on.
Binding those services was never easier, you just use the cds bind command inside your project and the .cdsrc-private.json file is created. I ran the following commands to bind the required services:
cds bind -2 hybrid4ever-xsuaa-srv
cds bind -2 hybrid4ever-dest-srv
cds bind -2 hybrid4ever-conn-srv --kind connectivity
cds bind -2 hybrid4ever-hana-db-srv
As you can see, I did not specify the service-key name to be used. Remember we created the services-keys in the mta.yaml file with the same name as the service-name followed by the “-key” suffix? The reason we named them that way, is so we did not have to provided them in our cds bind command. If you would have named them differently, you could run the command as such:
cds bind -2 <service-name>:<service-key-name>
Since the kind of the connectivity service cannot be auto determined yet, with specify it using the “–kind <name>” argument.
The command binds the specified services and stores the configuration inside the .cdsrc-private.json file.
{
"requires": {
...
"[hybrid]": {
"auth": {
"binding": {
"type": "cf",
"apiEndpoint": "https://api.cf.us10-001.hana.ondemand.com",
"org": "orgIdentifier",
"space": "dev",
"instance": "hybrid4ever-xsuaa-srv",
"key": "hybrid4ever-xsuaa-srv-key",
"resolved": false
},
"kind": "xsuaa",
"vcap": {
"name": "auth"
}
},
...
}
}
}
The configuration of the bindings is stored inside an object called “[hybrid]”, because we did not specify a name for the Hybrid Testing profile when running the cds bind command. If you would like to call your profile differently you can specify it using the “-p <profile-name>” argument in the cds bind command.
As you can see, the information in these service binding configurations is way less sensitive than what you would find in a default-env.json file its VCAP_Services object.
The last configuration inside the .cdsrc-private.json file, and most fancy one in my opinion, is the remote services configuration:
{
"requires": {
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"credentials": {
"url": "https://Northwind.dest",
"path": "/v2/northwind/northwind.svc",
"proxyConfiguration": {
"host": "127.0.0.1",
"port": 8887,
"protocol": "http"
}
}
},
"CB_MATERIAL_SRV": {
"kind": "odata-v2",
"model": "srv/external/CB_MATERIAL_SRV",
"credentials": {
"url": "http://S4.dest",
"path": "/sap/opu/odata/sap/CB_MATERIAL_SRV",
"proxyConfiguration": {
"host": "127.0.0.1",
"port": 8887,
"protocol": "http"
}
}
},
...
}
}
The code snippet above shows that the remote services are configured here as well, only using a little different configuration this time. Do remember that we used the “[production]” configuration inside the package.json file, so that when we Hybrid Test our application, the configuration of the remote services inside the .cdsrc-private.json file is not overruled by the package.json its configuration.
You will notice that this time the credentials object of a remote service does not contain the destination property, but instead holds a “url” property. You might think that you need to specify the URL of your destination in here or the URL to your API, which is not necessary but a config which would work.
Using a syntax as such: http://<destination-name>.dest allows us to just specify the destination name instead of a sensitive URL again. When we use such a configuration, we need to tell the SAP Business Application Studio on how to proxy and resolve this destination name to the respective URL at runtime. That’s where the “proxyConfiguration” objects kick in. By configuring the host, port and protocol, the Business Application Studio resolves the destination name to the BTP destination its URL and the CAP project is ready to connect to the remote services when Hybrid Testing.
– app directory –
The app directory inside your CAP project contains UI applications. But these days not only UI applications are stored in here. You might find an approuter configuration in here, when the need for authenticated access is present. The easiest way to add an approuter to the app directory is by executing the cds add approuter command.
Running this command provides the required configuration files inside the app directory.
There are 2 very important files in this directory at this point.
The xs-app.json which contains the route to your CAP service:
{
"routes": [
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": ".",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/(.*)$",
"target": "$1",
"destination": "srv-api",
"csrfProtection": true
}
]
}
The destination property in the second route (leading to the CAP service) contains the name “srv-api”.
This destination name can also be found in the second important file called “default-env.json”. There is indeed still the need for this file, but only for the approuter, without sensitive information:
{
"destinations": [
{
"name": "srv-api",
"url": "http://localhost:4004",
"forwardAuthToken": true
}
]
}
The approuter its xs-app.json file its route needs info for this destination. This info is provided by the destination array which holds the destination name and forwards the token to the localhost on port 4004.
When running the approuter and CAP application using the configuration above you can consume you cap application locally. This means you can retrieve the information of your local CAP application and debug it as well. If you would remove your default-env.json file, the Hyrbid Testing profile would not use the local destination, but would retrieve the destination in the deployed cloud application and thus no incoming request on your local application, but in your cloud application in the Business Technology Platform:
Do remember the reason why this is possible is because we also defined this destination inside our mta.yaml file. This as a destination on the destination instance level, this is the reason why the application can find the respective URL to retrieve the information from.
Running the project
To run the project you have different possibilities, either you run them via an npm script (see repository package.json “npm run demo”) or via the terminal.
To run the project via Hybrid Testing open 2 terminals and execute the following commands:
Run the CAP application with the Hybrid profile:
cds-ts watch --profile hybrid
Run the Approuter using the Hybrid profile (using the exec param on the cds bind command):
cds bind --profile hybrid --exec – npm start --prefix app
Running the project in Debug Mode
It is also possible to run the CAP application in debug mode using the Hybrid setup.
Just run the approuter via the terminal again and run the following BAS run configuration to start the CAP application in debug mode (add this to your launch.json file in your .vscode directory):
{
"version": "0.2.0",
"configurations": [
{
"command": "cds-ts watch --profile hybrid",
"name": "cds-ts watch hybrid",
"request": "launch",
"type": "node-terminal",
"skipFiles": [
"<node_internals>/**"
]
}
]
}
Conclusion
We can conclude that the Hybrid Testing feature is a total gamechanger. Not only it reduces the project setup significantly, but it also gets rid of sensitive information in configuration files. Running applications locally or using cloud services bindings was never faster or easier. It just takes some time to figure out the full configuration in the beginning in more advanced scenarios, but I hope this blogpost helps to clarify things and helps you in your future developments.
Useful links
I was obviously not born with all this information. 😅
The SAP Community and SAP documentation to the rescue!
By reading, mixing, testing and so much more actions I was able to understand these configurations and write them down in this blog post.
SAP CAPire:
https://cap.cloud.sap/docs/advanced/hybrid-testing
https://cap.cloud.sap/docs/node.js/typescript#using-typescript
https://cap.cloud.sap/docs/advanced/hybrid-testing#cds-bind-usage
SAP Developers:
https://developers.sap.com/tutorials/hcp-create-trial-account.html
https://developers.sap.com/tutorials/appstudio-onboarding.html
https://developers.sap.com/tutorials/cp-cf-create-destination.html
https://developers.sap.com/tutorials/hana-cloud-deploying.html
https://developers.sap.com/tutorials/hana-cloud-mission-extend-08.html
SAP Community:
https://blogs.sap.com/2020/05/26/cap-consume-external-service-part-1/
SAP GitHub IO:
https://sap.github.io/cloud-sdk/docs/js/guides/bas#checking-the-connection
https://sap.github.io/cloud-sdk/docs/js/guides/bas#implementation
SAP Help:
A big thanks to everyone for sharing such informative information and I hope this blogpost can be of use to your future developments!