Skip to Content
Technical Articles
Author's profile photo Jhodel Cailan

CAP: Consume External Service – Part 2

Updated on 9 November 2022

In my previous blog post Consume External Service – Part 1, I have tested the application using mock and real data. Just by configuring the NorthWind OData service URL in the credentials.url property, I was able to connect to the external service. This approach is only applicable for local development testing. If you try to deploy this into SAP BTP, you will encounter an error stating that this approach is not valid for production use.

The reason behind this is that external service consumption in this CDS framework is meant to use the Destination Service in Cloud Foundry. And this is the topic of this blog — deploying the Node.js project into SAP BTP Cloud Foundry using Destination Service.

 

 

Prerequisites


 

Blog Post Series


 

Setup Destination Configuration


First of all, why do we need to set up a destination? A simple explanation is that it is best practice to avoid hard coding the external services that you use in your application inside your code. It is better for it to be configured separately from your application because as you deploy your application into different environments like DEV, QA, or PROD, you can have a different setup of the destination per environment. Also, it keeps sensitive information like credentials outside of your code.

See Step 8 of Deploy the application to BTP Cloud Foundry

 

 

Deploy the application to BTP Cloud Foundry


Now let’s go back to our Node.js project — if you were able to follow through my previous blog post, then your project will look exactly like the one I have below:

https://github.com/jcailan/cap-samples/tree/blog-es-part1

The next step is to prepare the application for deployment to BTP Cloud Foundry.

  • 1. Generate the mta.yaml file using the command below:
> cds add mta

  • 2. Generate the security descriptor file xs-security.json using the command below:
> cds add xsuaa

 

  • 3. Update mta.yaml file with declaration of xsuaa and destination resources and then binding it to our node module cap-samples-srv.
---
_schema-version: "3.1"
ID: com.jcailan.cap-samples
version: 1.0.0
description: "A simple CAP project."
parameters:
  enable-parallel-deployments: true
build-parameters:
  before-all:
    - builder: custom
      commands:
        - npx -p @sap/cds-dk cds build --production

modules:
  - name: cap-samples-srv
    type: nodejs
    path: gen/srv
    parameters:
      buildpack: nodejs_buildpack
    build-parameters:
      builder: npm-ci
    provides:
      - name: srv-api # required by consumers of CAP services (e.g. approuter)
        properties:
          srv-url: ${default-url}
    requires:
      - name: cap-samples-uaa
      - name: cap-samples-destination

resources:
  - name: cap-samples-uaa
    type: org.cloudfoundry.managed-service
    parameters:
      service: xsuaa
      service-plan: application
      path: ./xs-security.json
      config:
        xsappname: cap-samples-${org}-${space}
        tenant-mode: dedicated

  - name: cap-samples-destination
    type: org.cloudfoundry.managed-service
    parameters:
      service: destination
      service-plan: lite

Note that in order for the destination service consumption to work, an xsuaa service is required to be bound to our application.

  • 4. Update the package.json cds configurations to use the NorthWind destination (we will create the actual destination configuration later after deployment):
	"cds": {
		"requires": {
			"NorthWind": {
				"kind": "odata",
				"model": "srv/external/NorthWind",
				"[backend]": {
					"credentials": {
						"url": "https://services.odata.org/Experimental/OData/OData.svc"
					}
				},
				"[production]": {
					"credentials": {
						"destination": "NorthWind"
					}
				}
			},
			"[production]": {
				"auth": {
					"kind": "xsuaa",
					"restrict_all_services": false
				}
			}
		}
	}
NOTE: The restrict_all_services = false removes authentication to all the provided service endpoints and we are using this for this example just for the sake of simplicity and because the original service endpoint doesn’t have authentication in place. However, in normal cases, you would want to keep the authentication on especially when the app is used productively.
  • 5. Build the MTA Project by using the command:
> mbt build

Make sure you have saved all the file changes we did before doing the build.

An MTA archive file will be generated in the mta_archives folder.

  • 6. Deploy the MTA archive file into BTP Cloud Foundry using the command:
> cf deploy mta_archives/com.jcailan.cap-samples_1.0.0.mtar
  • 7. Once the deployment has been completed, look out for the terminal logs which state the URL of your cap-samples-srv module. In my case, here’s the generated URL:
s0017687913trial-dev-cap-samples-srv.cfapps.eu10.hana.ondemand.com
  • 8. But before proceeding to test the application, make sure to add the configuration for the NorthWind destination. Go to BTP Cockpit and look for the destination service instance cap-samples-destination — then add below configuration:

 

Test the deployed Node.js app service


  • 1. Open the URL we got from the previous step using your favorite browser, the initial page will show, and now click on the Products entity. You should see the result below:

As you can see, it is very easy to deploy the app and use the destination service. We don’t really need to do any additional JavaScript coding, all that we did is just do a little bit of configuration.

 

Testing the Node.js app locally


Now that we have deployed our application into BTP Cloud Foundry, we can test the app locally while still using the destination service in BTP Cloud Foundry. In order to be able to test locally, we need to capture the environment variable assigned to our Node.js module in BTP.

  • 1. Execute the command below to fetch the environment variables bounded to your deployed node module app:
> cf default-env cap-samples-srv
Environment variables for cap-samples-srv written to default-env.json
NOTE: You need to have the CF Plugin called DefaultEnv installed in your machine — see installation instructions here: https://github.com/saphanaacademy/DefaultEnv

 

  • 3. Next thing to do is start the app locally using the command:
> cds watch --profile production
  • 4. Test the app on a browser by using the URL below:
http://localhost:4004/catalog/Products

As you can see we now can test the app locally using real data while also using the destination service of BTP Cloud Foundry.

If by any chance you still want to revert to testing the app using our mock data, you can still do so by executing the command:

> cds watch

 

 

Closing


Now you know how easy it is to consume an external service using CAP Model and use Destination Service to manage the connection configuration. We also tested our application in three different ways:

  • Test the service directly in BTP Cloud Foundry
  • Test the service locally while still using BTP CF Destination Service
  • Test the service locally using mock data

Take note that mocking the data for your service is essential for automated unit testing, but this is another topic better dealt with over another blog.

UPDATE:

Taking this project further into — Unit Testing using Mocha and Chai

 

~~~~~~~~~~~~~~~~

Appreciate it if you have any comments, suggestions, or questions. Cheers!~

 

Assigned Tags

      72 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Roc Antonio
      Roc Antonio

      Hi Jhodel, Im having troubles making the call to the destination it always prints a 500 erro with a 400 status code in the message. Also it says about not finding a proccess env called HTTPS_PROXY and then it fails the service run.

      Don't know whats happening, and I followed your blog perfectly.

      Thanks Jhodel Cailan

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Roc Antonio

      It looks like the issue is in your configuration of default-env.json, make sure it has the right structure. Also, you need to make sure that the destination service and xsuaa services are setup properly.

      Thanks and regards,

      Jhodel

      Author's profile photo Luis Guillermo León Guzmán
      Luis Guillermo León Guzmán

      Hi Roc,

      I has the same problem, have you found the way to resolve this?

      Author's profile photo Roc Antonio
      Roc Antonio

      I was missing the Connectivity Service on the Cap Instance

      Author's profile photo Manu Gupta
      Manu Gupta

      Hi Antonio,

       

      I am also getting exact same error even after using full URL. How did you fixed that issue ?

      I am getting this issue even for northwind odata.

       

      Thanks,

      Manu

      Author's profile photo Luis Guillermo León Guzmán
      Luis Guillermo León Guzmán

      Hi Jhodel,

      Very useful post, I followed your steps and works fine, but now I want to replay this for a S/4HANA  service, but I always get this error:

      {"message":"Attempting to retrieve destination from environment variable.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.076Z","msg":"Attempting to retrieve destination from environment variable.","written_ts":1591723502076,"written_at":"2020-06-09T17:25:02.076Z"}
      {"message":"Could not retrieve destination from environment variable.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.078Z","msg":"Could not retrieve destination from environment variable.","written_ts":1591723502078,"written_at":"2020-06-09T17:25:02.078Z"}
      {"message":"Attempting to retrieve destination from service binding.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.078Z","msg":"Attempting to retrieve destination from service binding.","written_ts":1591723502078,"written_at":"2020-06-09T17:25:02.078Z"}
      {"message":"Unable to find a service binding for given name \"SCP-TO-DEVVIRTUALNEO\"! Found the following bindings: s4cap-s4cap-uaa-S0019246072-workspKL8pDH6LAjq+PI9x, s4cap-conns4cap-S0019246072-workspOeKcqQC3TvHCyLFe, s4cap-s4cap-db-hdi-container-S0019dJBJdRo5DnLNI72P, s4cap-dests4cap-S0019246072-worksp6fvZ7pO3i0qk1mEo.\n ","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.079Z","msg":"Unable to find a service binding for given name \"SCP-TO-DEVVIRTUALNEO\"! Found the following bindings: s4cap-s4cap-uaa-S0019246072-workspKL8pDH6LAjq+PI9x, s4cap-conns4cap-S0019246072-workspOeKcqQC3TvHCyLFe, s4cap-s4cap-db-hdi-container-S0019dJBJdRo5DnLNI72P, s4cap-dests4cap-S0019246072-worksp6fvZ7pO3i0qk1mEo.\n ","written_ts":1591723502079,"written_at":"2020-06-09T17:25:02.079Z"}
      {"message":"Could not retrieve destination from service binding.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.079Z","msg":"Could not retrieve destination from service binding.","written_ts":1591723502079,"written_at":"2020-06-09T17:25:02.079Z"}
      {"message":"If you are not using SAP Extension Factory, this information probably does not concern you.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.079Z","msg":"If you are not using SAP Extension Factory, this information probably does not concern you.","written_ts":1591723502079,"written_at":"2020-06-09T17:25:02.079Z"}
      {"message":"Attempting to retrieve destination from destination service.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.080Z","msg":"Attempting to retrieve destination from destination service.","written_ts":1591723502080,"written_at":"2020-06-09T17:25:02.080Z"}
      {"message":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. The following one will be selected: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. This might produce errors in other parts of the system!","level":"warn","custom_fields":{"package":"core","messageContext":"environment-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.081Z","msg":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. The following one will be selected: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. This might produce errors in other parts of the system!","written_ts":1591723502081,"written_at":"2020-06-09T17:25:02.081Z"}
      {"message":"Sucessfully retrieved destination from destination service.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.365Z","msg":"Sucessfully retrieved destination from destination service.","written_ts":1591723502365,"written_at":"2020-06-09T17:25:02.365Z"}
      {"message":"OnPrem destination proxy settings from connectivity service will be used.","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.365Z","msg":"OnPrem destination proxy settings from connectivity service will be used.","written_ts":1591723502365,"written_at":"2020-06-09T17:25:02.365Z"}
      {"message":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. The following one will be selected: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. This might produce errors in other parts of the system!","level":"warn","custom_fields":{"package":"core","messageContext":"environment-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.366Z","msg":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. The following one will be selected: s4cap-SCPCFQAS91f91a19-69fd-4fcc-95de-a14f3e41a809!t4787. This might produce errors in other parts of the system!","written_ts":1591723502366,"written_at":"2020-06-09T17:25:02.366Z"}
      {"message":"Unable to create \"SAP-Connectivity-Authentication\" header: no JWT found on the current request.\n Continuing without header. Connecting to on-premise systems may not be possible.","level":"warn","custom_fields":{"package":"core","messageContext":"connectivity-service"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.538Z","msg":"Unable to create \"SAP-Connectivity-Authentication\" header: no JWT found on the current request.\n Continuing without header. Connecting to on-premise systems may not be possible.","written_ts":1591723502538,"written_at":"2020-06-09T17:25:02.538Z"}
      {"message":"OnPrem destination proxy settings from connectivity service will be used.","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-06-09T17:25:02.539Z","msg":"OnPrem destination proxy settings from connectivity service will be used.","written_ts":1591723502539,"written_at":"2020-06-09T17:25:02.539Z"}
      [2020-06-09T17:25:02.681Z | ERROR | 1003549]: Request failed with status code 403
      [2020-06-09T17:25:02.681Z | ERROR | 1003549]: Error stacktrace: Error: Request failed with status code 403 at createError (/home/vcap/app/node_modules/axios/lib/core/createError.js:16:15) at settle (/home/vcap/app/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/home/vcap/app/node_modules/axios/lib/adapters/http.js:236:11) at IncomingMessage.emit (events.js:323:22) at endReadableNT (_stream_readable.js:1204:12) at processTicksAndRejections (internal/process/task_queues.js:84:21)

      This is my mta.yaml

      ID: s4cap
      _schema-version: '2.1'
      parameters:
        deploy_mode: html5-repo
      version: 0.0.1
      modules:
        - name: s4cap-approuter
          type: approuter.nodejs
          path: s4cap-approuter
          parameters:
            disk-quota: 256M
            memory: 256M
          requires:
            - name: s4cap_html5_repo_runtime
            - name: dest_s4cap
            - name: conn_s4cap
            - name: s4cap-uaa
            - name: srv_api
              group: destinations
              properties:
                forwardAuthToken: true
                name: srv_api
                url: '~{url}'
        - name: s4cap_ui_deployer
          type: com.sap.html5.application-content
          path: s4cap_ui_deployer
          requires:
            - name: s4cap_html5_repo_host
          build-parameters:
            requires:
              - name: s4capui
                artifacts:
                  - './*'
                target-path: resources/s4capui
        - name: s4cap-db
          type: hdb
          path: db
          parameters:
            memory: 256M
            disk-quota: 512M
          requires:
            - name: s4cap-db-hdi-container
        - name: s4cap-srv
          type: nodejs
          path: srv
          parameters:
            memory: 512M
            disk-quota: 512M
          provides:
            - name: srv_api
              properties:
                url: '${default-url}'
          requires:
            - name: s4cap-db-hdi-container
            - name: s4cap-uaa
            - name: dest_s4cap
            - name: conn_s4cap
        - name: s4capui
          type: html5
          path: s4capui
          build-parameters:
            builder: custom
            commands:
              - npm install
              - npm run build
            supported-platforms: []
            build-result: dist
      resources:
        - name: s4cap_html5_repo_runtime
          parameters:
            service-plan: app-runtime
            service: html5-apps-repo
          type: org.cloudfoundry.managed-service
        - name: s4cap_html5_repo_host
          parameters:
            service-plan: app-host
            service: html5-apps-repo
          type: org.cloudfoundry.managed-service
        - name: s4cap-db-hdi-container
          type: com.sap.xs.hdi-container
          properties:
            hdi-container-name: '${service-name}'
        - name: s4cap-uaa
          type: org.cloudfoundry.managed-service
          parameters:
            service-plan: application
            service: xsuaa
            config:
              xsappname: 's4cap-${space}'
              tenant-mode: dedicated
            path: xs-security.json
        - name: dest_s4cap
          parameters:
            service-plan: lite
            service: destination
          type: org.cloudfoundry.managed-service
        - name: conn_s4cap
          parameters:
            service-plan: lite
            service: connectivity
          type: org.cloudfoundry.managed-service

      and my package.json

      {
      	"name": "s4cap-srv",
      	"description": "Generated from ../package.json, do not change!",
      	"version": "1.0.0",
      	"dependencies": {
      		"@sap/cloud-sdk-core": "^1.17.2",
      		"@sap/hana-client": "^2.4.196",
      		"@sap/xsenv": "^2.2.0",
      		"@sap/xssec": "^2.2.5",
      		"@sap/cds": "^3.34.2",
      		"passport": "^0.4.1",
      		"express": "^4.17.1",
      		"axios": "^0.19.2",
      		"moment": "^2.24.0",
      		"moment-timezone": "^0.5.28",
      		"execution-time": "^1.4.1",
      		"hdb": "^0.17.1",
      		"uuid": "^3.4.0"
      
      	},
      	"engines": {
      		"node": "^10 || ^12"
      	},
      	"devDependencies": {},
      	"scripts": {
      		"postinstall": "npm dedupe && node .build.js",
      		"start": "node ./node_modules/@sap/cds/bin/cds.js serve gen/csn.json",
      		"watch": "nodemon -w . -i node_modules/**,.git/** -e cds -x npm run build",
      		"start:express": "node --inspect express.js"
      	},
      	"private": true,
      	"cds": {
      		"requires": {
      			"db": {
      				"kind": "hana",
      				"model": "gen/csn.json"
      			},
      			"uaa": {
      				"kind": "xsuaa"
      			},
      			"Z_OD_SCP_CORE_0001_SRV": {
      				"kind": "odata",
      				"model": "srv/external/Z_OD_SCP_CORE_0001_SRV",
      				"credentials": {
      					"destination": "SCP-TO-DEVVIRTUALNEO",
      					"requestTimeout": 30000
      				},
      				"pool": {
      					"min": 1,
      					"max": 10
      				}
      			},
      			"Z_OD_SCP_VEHI_SRV": {
      				"kind": "odata",
      				"model": "srv/external/Z_OD_SCP_VEHI_SRV",
      				"credentials": {
      					"destination": "SCP-TO-DEVVIRTUALNEO",
      					"requestTimeout": 30000
      				},
      				"pool": {
      					"min": 1,
      					"max": 10
      				}
      			},
      			"ZCDS_VW_TEST_CDS": {
      				"kind": "odata",
      				"model": "srv/external/ZCDS_VW_TEST_CDS",
      				"credentials": {
      					"destination": "SCP-TO-DEVVIRTUALNEO",
      					"requestTimeout": 30000
      				},
      				"pool": {
      					"min": 1,
      					"max": 10
      				}
      			},
      			"NorthWind": {
      				"kind": "odata",
      				"model": "srv/external/NorthWind",
      				"credentials": {
      					"destination": "NorthWind",
      					"requestTimeout": 30000
      				},
      				"pool": {
      					"min": 1,
      					"max": 10
      				}
      			}
      		},
      		"auth": {
      			"passport": {
      				"strategy": "JWT"
      			}
      		},
      		"odata": {
      			"version": "v4"
      		}
      	}
      }

      The destination is been using without problems by other apps, but with this method using external services in CDS is not working.

      Thanks in advance

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Luis Guillermo León Guzmán

      I saw your post in the questions section, and it is true that you didn't configure the destination with the full URL of the external service. This is currently a limitation in the CDS framework.

      Cheers!

      Jhodel

      Author's profile photo Luis Guillermo León Guzmán
      Luis Guillermo León Guzmán

      Thank´s Jhodel,

       

      Now it works with the full URL.

      Best regards

      Author's profile photo Fadel Cazor
      Fadel Cazor

      Hi Luis,

       

      If I use the URL instead of destination it works fine, but when I use destination it don't work anymore, need to use destination because using URL is not a suitable solution for my.

      Can you share how to solve using destination please?

      Thanks

       

      {"message":"Attempting to retrieve destination from environment variable.","level
      {"message":"Could not retrieve destination from environment variable.","level":"i
      {"message":"Attempting to retrieve destination from service binding.","level":"in
      {"message":"Unable to find a service binding for given name \"HorasHana\"! Found 
      {"message":"Could not retrieve destination from service binding.","level":"info",
      {"message":"If you are not using SAP Extension Factory, this information probably
      {"message":"Attempting to retrieve destination from destination service.","level"
      {"message":"Unable to match a specific XSUAA service instance to the given JWT. T
      {"message":"Successfully retrieved destination from destination service.","level"
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found v
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found v
      {"message":"Proxy settings for https are found in environment variables.","level"
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found v
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found v
      {"message":"Proxy settings for https are found in environment variables.","level"
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found v
      [2020-08-18T17:05:02.129Z | ERROR | 1885843]: Request failed with status code 404
      [2020-08-18T17:05:02.129Z | ERROR | 1885843]: Error stacktrace: Error: Request fa
      
      Author's profile photo DJ Adams
      DJ Adams

      This is a great pair of posts, nice work Jhodel!

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Thanks DJ Adams ! It's an honor to be noticed by you! 😀

      Author's profile photo José Rubí Braña
      José Rubí Braña

      Hi again Jhodel,

      I noticed that you cannot do expands with a remote service like this although you can do filters.

      It's just me or its the same on all people ?

      Thanks for the great post 😉

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Thanks Jose! Yes, it’s the framework issue ?. It is a limitation at the moment. If you really need this feature then you have to code it yourself — without the use of CDS fluent API.

      Cheers! Jhodel

      Author's profile photo vasundhara lella
      vasundhara lella

      Hi Jhodel...

      I am using on-premise s4h service. I want to read child entities by using $expand. without coding can we achieve it right now?

      Author's profile photo Chunyang Xu
      Chunyang Xu

      Thanks Jhodel! Very clear step-by-step instructions, super helpful!

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Happy to know that my post has helped you Chunyang!

      Author's profile photo Karthiheyan Murugesan
      Karthiheyan Murugesan

      Thanks, Excellent Blog, worked perfectly.

      I want to expose few more entities .  I did below changes and it is not working. What should I code more ?

      using {NorthWind as external} from './external/NorthWind.csn';
      service CatalogService {
          @readonly
          entity Products as projection on external.Products {
              key ID, Name, Description, ReleaseDate, DiscontinuedDate, Rating, Price
          };
          @readonly
          entity Productdetails as projection on external.ProductDetails;
      }
      -------------
      const cds = require('@sap/cds');
      module.exports = cds.service.impl(async function() {
          const { Products,Productdetails } = this.entities;
          const service = await cds.connect.to('NorthWind');
          this.on('READ', Products, request => {
              return service.tx(request).run(request.query);
          });
          this.on('READ', Productdetails, request => {
              return service.tx(request).run(request.query);
          });
      });
      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Thanks Karthiheyan !!

      Is it not working because you got an error? or because it doesn't show any data? I checked your code and it looks fine. You should just create a file called NorthWind-ProductDetails.csv which has a similar data like below:

      ProductID;Details
      1;Details of product 1
      2;Details of product 2
      Author's profile photo Karthiheyan Murugesan
      Karthiheyan Murugesan

      Hi,

      It is working locally. But after deploy to CF , it is giving below error,

      Request failed with status code 400  - error after clicking on Entityset in CF link.

       

      Author's profile photo Karthiheyan Murugesan
      Karthiheyan Murugesan

      Hi ,

      It worked after specifying field names like ID,Name

          @readonly
          entity Suppliers as projection on external.Suppliers{
              key ID,Name
          };
      Author's profile photo Lalit Goyal
      Lalit Goyal

      Hi Thanks Jhodel Cailan

      Thanks for the well-written article.

      I tried the steps. It worked fine in local but when deployed it gave me 500 (internal server error).

      Can you please suggest how can I troubleshoot?

      Regards,

      Lalit

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Lalit Goyal

      I assume that you were able to deploy it but when you are testing the service it gave you an error 500 Internal Server Error? If this is a yes, then go back to your terminal and execute below command

      > cf logs <name of the cap service>

      This will connect you to your deployed service and will see logs in real-time. Trigger again the service that is causing errors. Then analyze the logs from your terminal and see if you can find any clue.

      Author's profile photo Lalit Goyal
      Lalit Goyal

      I am attaching the error here.

      2020-07-09T16:34:50.49+0530 [APP/PROC/WEB/0] ERR [2020-07-09T11:04:50.497Z | ERROR | 1884455]: Failed to build
      HTTP request for destination: failed to load destination!
      2020-07-09T16:34:50.49+0530 [APP/PROC/WEB/0] ERR [2020-07-09T11:04:50.497Z | ERROR | 1884455]: Error stacktrace: Error: Failed to build HTTP request for destination: failed to load destination! at Object.errorWithCause (/home/vcap/app/node_modules/@sap-cloud-sdk/util/dist/error.js:6:20) at /home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/http-client/http-client.js:143:38 at process._tickCallback (internal/process/next_tick.js:68:7) Caused by: Error: Unable to get access token for "destination" service! No service instance of type "destination" found. at Object.resolveService (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/environment-accessor.js:190:19) at Object.<anonymous> (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:73:54) at step (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:45:23)
      at Object.next (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:26:53) at /home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:20:71 at new Promise (<anonymous>)
      at __awaiter (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:16:12) at Object.serviceToken (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/token-accessor.js:70:12) at /home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/destination-accessor.js:197:59 at step (/home/vcap/app/node_modules/@sap-cloud-sdk/core/dist/scp-cf/destination-accessor.js:45:23)

      Author's profile photo Lalit Goyal
      Lalit Goyal

      Also, I want to add that I am using a trial account.

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Lalit Goyal

      I just got to know you have responded. Is your destination configuration correct? can you share your destination configuration?

      Author's profile photo Lalit Goyal
      Lalit Goyal

      Hi Jhodel,

      I am attaching the destination configuration screen shot.

       

      Regards,

      Lalit

       

      Destination%20ConfigurationJ

      Destination Configuration

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Lalit Goyal

      It looks correct to me, except that the name of your destination is NorthWind_CLONING. If you named your destination like this, then you need to make sure you use this exact name on your configuration when you want to consume this destination from your CAP Model project.

      Author's profile photo Lalit Goyal
      Lalit Goyal

      Hi Thanks Jhodel Cailan,

      I found out the issue. Well, I didn't restart the application after binding to the destination.

      After restarting the application, it worked. Maybe you can add that step too 😀

      Thanks a lot for your help!

       

      Regards,

      Lalit

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Lalit Goyal

      Glad to know that it is working fine now. There must be some steps that you did in between which requires a restart for your case. For my steps above, the destination was configured first, then build and deploy of MTA. The MTA will be the one who will bind to the service (auto-binding) and the app should be restarted afterward, hence, manual restart is not necessary.

      Well, that's how we learn, which is by doing it! 🙂

      Author's profile photo Abe Dong
      Abe Dong

      I was facing the same issue, but what I can see is that the service instance demo-destination and demo-uaa are not auto binding to the CAP app. after I created the binding manually, the service works as expected.

      Author's profile photo Christian Hanusch
      Christian Hanusch

      Hello,

      I also have a problem getting this setup. Im using business application studio.

      The error I get looks like this:

      {"message":"Attempting to retrieve destination from environment variable.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.991Z","msg":"Attempting to retrieve destination from environment variable.","written_ts":1594907027991,"written_at":"2020-07-16T13:43:47.991Z"}
      {"message":"Could not retrieve destination from environment variable.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.992Z","msg":"Could not retrieve destination from environment variable.","written_ts":1594907027992,"written_at":"2020-07-16T13:43:47.992Z"}
      {"message":"Attempting to retrieve destination from service binding.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.992Z","msg":"Attempting to retrieve destination from service binding.","written_ts":1594907027992,"written_at":"2020-07-16T13:43:47.992Z"}
      {"message":"Service of type destination is not supported! Consider providing your own transformation function when calling destinationForServiceBinding, like this:\n  destinationServiceForBinding(yourServiceName, { serviceBindingToDestination: yourTransformationFunction });","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.993Z","msg":"Service of type destination is not supported! Consider providing your own transformation function when calling destinationForServiceBinding, like this:\n  destinationServiceForBinding(yourServiceName, { serviceBindingToDestination: yourTransformationFunction });","written_ts":1594907027993,"written_at":"2020-07-16T13:43:47.993Z"}
      {"message":"Could not retrieve destination from service binding.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.993Z","msg":"Could not retrieve destination from service binding.","written_ts":1594907027993,"written_at":"2020-07-16T13:43:47.993Z"}
      {"message":"If you are not using SAP Extension Factory, this information probably does not concern you.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.993Z","msg":"If you are not using SAP Extension Factory, this information probably does not concern you.","written_ts":1594907027993,"written_at":"2020-07-16T13:43:47.993Z"}
      {"message":"Attempting to retrieve destination from destination service.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.993Z","msg":"Attempting to retrieve destination from destination service.","written_ts":1594907027993,"written_at":"2020-07-16T13:43:47.993Z"}
      {"message":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: na-9e477645-d9fa-4701-94d2-055247d3f219!t19402. The following one will be selected: na-9e477645-d9fa-4701-94d2-055247d3f219!t19402. This might produce errors in other parts of the system!","level":"warn","custom_fields":{"package":"core","messageContext":"environment-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:47.994Z","msg":"Unable to match a specific XSUAA service instance to the given JWT. The following XSUAA instances are bound: na-9e477645-d9fa-4701-94d2-055247d3f219!t19402. The following one will be selected: na-9e477645-d9fa-4701-94d2-055247d3f219!t19402. This might produce errors in other parts of the system!","written_ts":1594907027994,"written_at":"2020-07-16T13:43:47.994Z"}
      {"message":"Sucessfully retrieved destination from destination service.","level":"info","custom_fields":{"package":"core","messageContext":"destination-accessor"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.296Z","msg":"Sucessfully retrieved destination from destination service.","written_ts":1594907028296,"written_at":"2020-07-16T13:43:48.296Z"}
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Proxy settings for https are found in environment variables.","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Proxy settings for https are found in environment variables.","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Proxy settings for https are found in environment variables.","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Proxy settings for https are found in environment variables.","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}
      {"message":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-07-16T13:43:48.297Z","msg":"Try to fetch https_proxy or HTTPS_PROXY from the process env. Found value is http://127.0.0.1:8887","written_ts":1594907028297,"written_at":"2020-07-16T13:43:48.297Z"}

       

      Thats my MTA.yaml:

      _schema-version: '3.1'
      ID: cmanagement
      description: "Contract Management Application"
      parameters:
        enable-parallel-deployments: true
      version: 0.0.1
      
      
      modules:
      
        - name: cmanagement-db
          type: hdb
          path: db
          parameters:
            memory: 256M
            disk-quota: 256M
          requires:
            - name: cmanagement-db-hdi-container
      
        - name: cmanagement-srv
          type: nodejs
          path: srv
          parameters:
            memory: 512M
            disk-quota: 256M
          requires:
            - name: cmanagement-db-hdi-container
            - name: my_destination_service_instance
            - name: cm_uaa
      
      
      resources:
        - name: cmanagement-db-hdi-container
          type: com.sap.xs.hdi-container
          properties:
            hdi-container-name: ${service-name}
      
        - name: cm_uaa
          type: org.cloudfoundry.existing-service
          parameters:
             path: ./xs-security.json
             service-plan: application
             service: xsuaa
      
        - name: my_destination_service_instance 
          type: org.cloudfoundry.existing-service
          parameters:
             service-name: my_destination_service_instance 
             service-pan: lite
             service: destination
      
      
      

       

      My Apps rootfolders package.json:

      {
        "name": "cmanagement",
        "description": "Contract Management Application",
        "version": "1.4.0",
        "engines": {
          "node": "^12.16.1"
        },
        "dependencies": {
          "@sap/node-jwt": "latest",
          "@sap/cds": "^3.21.x",
          "@sap/cds-dk": "^1.8.5",
          "@sap/cds-odata-v2-adapter-proxy": "^1.4.30",
          "@sap/hana-client": "^2.4.167",
          "express": "^4.17.1",
          "hdb": "^0.17.1",
          "mbt": "^1.0.4",
          "sqlite3": "^4.1.1"
        },
        "devDependencies": {
          "axios": "^0.19.0",
          "eslint": "^6.7.2",
          "mocha": "^6.2.2",
          "nodemon": "^2.0.1"
        },
        "scripts": {
          "build": "cds build/all --clean",
          "start": "cds watch",
          "dev": "node --inspect=localhost:4014 node_modules/.bin/cds run --in-memory",
          "test": "mocha",
          "lint": "eslint .",
          "deploy:cds": "cds deploy",
          "build:mta": "cds build/all && cds deploy && mbt build -p=cf --mtar=cm.mtar",
          "deploy:cf": "npm run build:mta && cf deploy mta_archives/${npm_package_name}_${npm_package_version}.mtar"
        },
        "cds": {
          "requires": {
            "db": {
              "kind": "sqlite",
              "credentials": {
                "database": "cm.db"
              }
            },
            "API_BUSINESS_PARTNER": {
              "kind": "odata",
              "model": "srv/external/API_BUSINESS_PARTNER",
              "credentials": {
                "destination": "Q_BP",
                "requestTimeout": 30000
              }
            }
          },
          "auth": {
            "passport": {
              "strategy": "JWT"
            }
          }
        },
        "files": [
          "db",
          "srv"
        ]
      }
      

       

      And the default-env.json:

      {
      	"VCAP_SERVICES": {
      		"destination": [
      			{
      				"label": "destination",
      				"provider": null,
      				"plan": "lite",
                      "name": "Q_BP",
                      "url": "https://placeholder-api.s4hana.ondemand.com/sap/opu/odata/sap/API_BUSINESS_PARTNER",
                      "username": ...,
                      "password": ...,
                      
      				"tags": [
      					"destination",
      					"conn",
      					"connsvc"
      				],
      				"instance_name": "my_destination_service_instance",
      				"binding_name": null,
      				"credentials": {
      					"uaadomain": "authentication.eu10.hana.ondemand.com",
      					"tenantmode": "dedicated",
      					"clientid": ...,
      					"instanceid": ...,
      					"verificationkey": ...,
      					"xsappname": clone...,
      					"identityzone": ...,
      					"clientsecret": ...,
      					"tenantid": ...,
      					"uri": "https://destination-configuration.cfapps.eu10.hana.ondemand.com",
      					"url": "https://placeholder-aws.authentication.eu10.hana.ondemand.com"
      				},
      				"syslog_drain_url": null,
      				"volume_mounts": []
      			}
      		],
      		"xsuaa": [
      			{
      				"label": "xsuaa",
      				"provider": null,
      				"plan": "application",
      				"name": "cm_uaa",
      				"tags": [
      					"xsuaa"
      				],
      				"instance_name": "cm_uaa",
      				"binding_name": null,
      				"credentials": {
      					"tenantmode": "shared",
      					"sburl": "https://internal-xsuaa.authentication.eu10.hana.ondemand.com",
      					"clientid": ...,
      					"xsappname": ...,
      					"clientsecret": ...,
      					"url": "https://placeholder-aws.authentication.eu10.hana.ondemand.com",
      					"uaadomain": "authentication.eu10.hana.ondemand.com",
      					"verificationkey": ...,
      					"apiurl": "https://api.authentication.eu10.hana.ondemand.com",
      					"identityzone": "placeholder",
      					"identityzoneid": ...,
      					"tenantid": ...
      				},
      				"syslog_drain_url": null,
      				"volume_mounts": []
      			}
      		],
      		"hana": [
      			{
      				"label": "hana",
      				"provider": null,
      				"plan": "hdi-shared",
      				"name": "cmanagement-db-hdi-container",
      				"tags": [
      					"hana",
      					"database",
      					"relational"
      				],
      				"instance_name": "cmanagement-db-hdi-container",
      				"binding_name": null,
      				"credentials": {
      					"host": "z",
      					"port": "",
      					"driver": "com.sap.db.jdbc.Driver",
      					"url": "...",
      					"schema": "...",
      					"hdi_user": "...",
      					"hdi_password": "...",
      					"user": "...",
      					"password": "...",
      					"certificate": "..."
      				},
      				"syslog_drain_url": null,
      				"volume_mounts": []
      			}
      		]
      	}
      }

       

      The destination configuration:

       

      When accessing the entity I get this error:

      <error xmlns="http://docs.oasis-open.org/odata/ns/metadata" ml-update="aware" ml-stage="preload" ml-view="top">
      <code>500</code>
      <message>Request failed with status code 404</message>
      </error>
      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Are you testing the service locally? For local testing you should use the "url" as credentials. The configuration you have in package.json is good for SCP deployment testing.

      Author's profile photo Christian Hanusch
      Christian Hanusch

      Hello Jhodel

      Yes, I want to test locally using "cds watch". I tried doing that, the code looks like this:

            "API_BUSINESS_PARTNER": {
              "kind": "odata",
              "model": "srv/external/API_BUSINESS_PARTNER",
              "url": "https://placeholder-api.s4hana.ondemand.com/sap/opu/odata/sap/API_BUSINESS_PARTNER"    
            }

       

      However now upon calling the entity I get this error:

      <error xmlns="http://docs.oasis-open.org/odata/ns/metadata" ml-update="aware" ml-stage="preload" ml-view="top">
      <code>501</code>
      <message>The entity "API_BUSINESS_PARTNER.A_BusinessPartner" is annotated with "@sap.persistence.skip", please implement a custom handler for it.</message>
      </error>
      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Maybe the error was right? You don’t have a custom handler implemented? Did you read the first part of this blog post? In part 1, I have step there to implement the custom handler .js file.

      Author's profile photo Christian Hanusch
      Christian Hanusch

      I actually had, my cat-service-cds looks like this:

       

      using { my.contractmanagement, sap.common } from '../db/data-model';
      using { API_BUSINESS_PARTNER as ext } from './external/API_BUSINESS_PARTNER.csn';
      
      service CatalogService {
          
          entity TreeItems as projection on contractmanagement.TreeItems;
          entity BusinessPartner @readonly as projection on ext.A_BusinessPartner;
      
      }
      

       

      The cat-service.js like this:

              const { BusinessPartner } = this.entities;
      	    const service = await cds.connect.to('API_BUSINESS_PARTNER');
              this.on('READ', BusinessPartner, request => {
                  return service.tx(request).run(request.query);
              });

       

      What I did, I just changed:

      this.on ('READ', BusinessPartner, async (req) => {
      to

      this.on('READ', BusinessPartner, request => {

      Now this error pops up:

      <error xmlns="http://docs.oasis-open.org/odata/ns/metadata" ml-update="aware" ml-stage="preload" ml-view="top">
      <code>null</code>
      <message>An error occurred during serialization of the entity collection. An error occurred during serialization of the entity at index #0 in the entity collection. Value of a structured property must be an object.</message>
      </error>​
      Author's profile photo Christian Hanusch
      Christian Hanusch

      If I console.log the query, I get a HTML structure returned, having this paragraph:

              <p><strong>Note:</strong> Since your browser does notsupport JavaScript, you must press the Continuebutton once to</p>
      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      It's still tough to see where the issue is. If you can share the actual project you have right now through GitHub (or any other Git versioning tools), that would be great.

      Author's profile photo Manu Gupta
      Manu Gupta

      Hi Christian Hanusch

      I am also getting exact same issue after deploying the app to CF. Were you able to fix ?

      I am getting this issue even for northwind odata.

       

      Thanks,

      Manu

      Author's profile photo Frank Lin
      Frank Lin

      Hi Christian Hanusch

      Any good news ?  I got same issues .

      Author's profile photo Gaurav Karkara
      Gaurav Karkara

      Hi Jhodel,

      Thanks for such wonderful posts! I was able to follow and perform all the steps. Only part i am not able to make work was correct deploy to CF.

      Though deploy is successful and when i open the link after deploy, i even see metadata. But when i click on 'Products', i get 500 internal server error. What could be the issue?

      Gaurav

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Thanks Gaurav Karkara !

      500 internal server errors are usually brought by some problems with the implementation of the service. It's likely that you got some issues with your code, but I can't tell what is it based on the information provided.

      What you can do is go to SCP Cockpit, navigate to the applications section and open the cap service details and extract logs there to view the logs information and try to make sense of what does the error logs tell you. Usually, it will become obvious where is the error from the logs, but if that's not the case, you can share it here so that we can analyze. Cheers!

      Author's profile photo Gaurav Karkara
      Gaurav Karkara

      Thanks Jhodel for replying.

      I have looked at the logs and find the error:

      'Unable to get access token for 'destination' service! No service instance of type 'destination' found.'

      I have followed the exact steps as you have followed and my destination instance and corresponding entries in mta.yaml seems fine.

      It doesn't look like it's because of code. It's something to do with destination instance access. Still trying to find..

      Thanks for your support.

      Gaurav

      Author's profile photo Gaurav Karkara
      Gaurav Karkara

      Worked. had to bind instances to applications. Thanks

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Gaurav Karkara

      Good to know that you found the issue and it is now working! 😀

      Author's profile photo Saurabh Kadu
      Saurabh Kadu

      Awesome Blog and really appreciate it. But I am facing one problem I am trying to test it Locally but I am Facing this issue.

      Can you please help me to resolve this issue?

       

      This is my mta.yaml

      ---
      _schema-version: '3.1'
      ID: dart
      version: 1.0.0
      description: "A simple CAP project."
      parameters:
        enable-parallel-deployments: true
      build-parameters:
        before-all:
          - builder: custom
            commands:
              - npm ci
              - npx -p @sap/cds-dk cds build --production
      modules:
        - name: dart-srv
          type: nodejs
          path: gen/srv
          parameters:
            buildpack: nodejs_buildpack
          build-parameters:
            builder: npm-ci
          provides:
            - name: srv-api # required by consumers of CAP services (e.g. approuter)
              properties:
                srv-url: ${default-url}
          requires:
            - name: dart-db
        - name: dart-db-deployer
          type: hdb
          path: gen/db
          parameters:
            buildpack: nodejs_buildpack
          requires:
            - name: dart-db
      resources:
        - name: dart-db
          type: com.sap.xs.hdi-container
          parameters:
            service: hana # or 'hanatrial' on trial landscapes
            service-plan: hdi-shared
          properties:
            hdi-service-name: ${service-name}
      Author's profile photo Karthiheyan Murugesan
      Karthiheyan Murugesan

      Hi ,

      Any lead on how to use  service.tx(request).run(request.query)  for a post call to S/4 odata. 

      Exact question

      1. How to set header parameter for getting CSRF token in  [ service.tx(request).run(request.query) ] .  I want to set [X-CSRF-TOKEN : Fetch] in a GET request header so i can set back CSRF token in post call.

      Regards,

      Karthi

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      I'm afraid that you are going to code the logic for that on your own. According to the documentation below:

      https://cap.cloud.sap/docs/guides/consuming-services#sending-requests

      the fluent API can cater to do create (POST) request, however, from my experience, it didn't work when it starts to use the service via connectivity proxy. So what I did, is I have coded the solution my self using the axios node module -- it is the same node module that cap for node.js use.

      Author's profile photo Karthiheyan Murugesan
      Karthiheyan Murugesan

      Thanks Jhodel 🙂

      Author's profile photo Marco Moretti
      Marco Moretti

      Hi guys,

       

      is this working on webide or I should deploy to make it work? because I'm trying to access oData service but I receive

       

      {"message":"Unable to create \"SAP-Connectivity-Authentication\" header: no JWT found on the current request.\n Continuing without header. Connecting to on-premise systems may not be possible.","level":"warn","custom_fields":{"package":"core","messageContext":"connectivity-service"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-08-05T18:49:38.260Z","msg":"Unable to create \"SAP-Connectivity-Authentication\" header: no JWT found on the current request.\n Continuing without header. Connecting to on-premise systems may not be possible.","written_ts":1596653378260,"written_at":"2020-08-05T18:49:38.260Z"}
      {"message":"OnPrem destination proxy settings from connectivity service will be used.","level":"info","custom_fields":{"package":"core","messageContext":"proxy-util"},"logger":"sap-cloud-sdk-logger","timestamp":"2020-08-05T18:49:38.261Z","msg":"OnPrem destination proxy settings from connectivity service will be used.","written_ts":1596653378261,"written_at":"2020-08-05T18:49:38.261Z"}
      Error: Failed to build HTTP request for destination: failed to build headers!

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      The steps outlined in this blog post is not meant to be executed in SAP WebIDE. Read the prerequisites section for more information.

      Author's profile photo Fadel Cazor
      Fadel Cazor

      Hi Thanks Jhodel Cailan,

      thanks a lot for this very well article, I’m tring with it since a few days ago. I'm trying to use an external Hana Odata service.

      I achieved to make it work locally both with URL and destination, but in the latter case, for it to work I had to delete the node_modules directory, another case if this directory exists I can't reach the Odata and got an 404 or 500 error. I don’t understand why that happens, I suspect that there is some incompatibility between the npm packages that are in that directory and the components in the CF, or not?

      Is there an option so that when deploying it does not take the npm packages that exist in the node_modules directory and instead uses the ones that exist in the CF?

      Author's profile photo Klaus Kopecz
      Klaus Kopecz

      Hi

      I'm not sure why this should be the root cause of your issue, but you can avoid deploying the node_modules folder to CF in two ways:

      • if you use 'cf push', just delete the node_modules folder before pushing. The node.js buildpack on CF will then install all dependencies during staging.
      • if you use the MTA build tool 'mbt', then you can configure the module build within mta.yaml by using an 'ignore' directive for the node_modules folder. Just go to https://sap.github.io/cloud-mta-build-tool/configuration/ and check the section "Configuring module build artifacts to package into MTA archive".

      Regards

      Author's profile photo Fadel Cazor
      Fadel Cazor

      Hi Klaus,

      thanks for your answer, you are right the command "cf push" does not generate the directory "node_modules", instead it installs the dependencies in the process, however the process fails due to the following errors:

      ERR npx: installed 4 in 1.686s
      ERR command not found: cds
      ERR npm ERR! code ELIFECYCLE
      ERR npm ERR! errno 1
      ERR npm ERR! aprhorasextras@0.0.1 start: `npx cds run`
      ERR npm ERR! Exit status 1
      ERR npm ERR! 
      ERR npm ERR! Failed at the aprhorasextras@0.0.1 start script.
      ERR npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
      ERR npm ERR! A complete log of this run can be found in:
      ERR npm ERR!     /home/vcap/app/.npm/_logs/2020-08-20T15_52_36_426Z-debug.log

      My package.json file is:

          {
          "name": "aprhorasextras",
          "version": "0.0.1",
          "private": true,
          "devDependencies": {
              "@sap/cds": "^4.1.7",
              "@sap/eslint-plugin-ui5-jsdocs": "2.0.x",
              "@sap/ui5-builder-webide-extension": "1.0.x",
              "@sapui5/ts-types": "1.71.x",
              "@ui5/cli": "2.2.6",
              "bestzip": "2.1.4",
              "eslint": "5.16.x",
              "express": "^4",
              "rimraf": "3.0.2"
          },
          "ui5": {
              "dependencies": [
              "@sap/ui5-builder-webide-extension"
              ]
          },
          "scripts": {
              "start": "npx cds run"
          },
          "engines": {
              "node": "^8.0.0 || ^10.0.0",
              "npm": "^6.0.0"
          },
          "cds": {
              "requires": {
              "horasextras": {
                  "kind": "odata",
                  "model": "srv/external/horasextras",
                  "credentials": {
                  "destination": "HorasHana"
                  }
              }
              }
          },
          "dependencies": {
              "@sap/cds-odata-v2-adapter-proxy": "^1.4.42",
              "@sap/xsenv": "^2.2.0",
              "@sap/xssec": "^2.2.5",
              "@types/jquery": "^3",
              "express": "^4",
              "passport": "^0.4.1"
          },
          "auth": {
              "passport": {
              "strategy": "JWT"
              }
          }
          }
      
      Author's profile photo Klaus Kopecz
      Klaus Kopecz

      Hi Fadel,

      I just have a vague idea what happened. Could you check in the log output of npm which npm registry is contacted to download the cds package? If it is still npm.sap.com, then this is a general issue, because cds 4 is not there. cds 4 is only available on npmjs.org. AFAIK, npm.sap.com will not be maintained anymore ...

      Regards

      Author's profile photo Vikas Madaan
      Vikas Madaan

      When i am using destination with basic authentication then it is working fine. But when i am using destination of type OAuth2SAMLBearerAssertion then system is not able to retrieve destination.

       

      cds.connect.to(‘SF’)

      can above statement automatically pas required JWT token. Please help is very urgent.

       

      I am also getting below error:

      Unable to match a specific XSUAA service instance to the given JWT. Is this common?

       

      @Jhodel

      Can you please help here.

       

      System is able to load destination with basic authentication but destination with type OAuth2SAMLBearerAssertion it is not.

       

      Failed to build HTTP request for destination: failed to load destination!

       

      ---new update

      Looks like cds.run and related statements don't support to pass JWT token to destination. Changing all the code to axios.

       

      Author's profile photo Sangita Purkayastha
      Sangita Purkayastha

      Hi Thanks Jhodel Cailan,

      I followed the steps mentioned in your blog to consume an external service in my project using the destinations service instance.

      I was successfully able to test the application locally, but as soon as I deploy the code to CF environment and try to access the service, I get 502 - Bad gateway error. Any idea what could be the reason for this error?

      The cds > requires section in package.json file is as follows:

        "cds": {
          "requires": {
            "MyNumberRange": {
              "kind": "odata",
              "model": "srv/external/MyNumberRange",
              "credentials": {
                "destination": "number-range-dest"
              }
            },
            "db": {
              "kind": "sql",
              "[production]": {
                "kind": "hana"
              }
            }
          }
        },

      Also the destination that was created is a shown below:

      Regards,

      Sangita Purkayastha

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Sangita Purkayastha

      The error 502 is very generic. Based on the information you provided, I can suspect that there can be an issue in your connection to HANA database. I suggest to do some troubleshooting on the deployed app like catching the error in the section of your code and logging the error into the console to get more information about the 502 error.

      Author's profile photo Zipei Chen
      Zipei Chen

      Hi Jhodel @Thanks Jhodel Cailan

      Thank you very much for sharing the knowledge on this topic. I had similar issue here. Could you please kindly provide some hint on how to solve this issue?

      When I tried to "Deploy the application to SCP Cloud Foundry", Using the created URL, i could access the top screen as shown below, but I can not enter the "Products" detail screen" only got 502 Bad Gateway error.

      top top could be shown properly

      Products%20page%20could%20not%20be%20shown

      Products page could not be shown

      Error message is shown as below.
      <error xmlns="http://docs.oasis-open.org/odata/ns/metadata">
      <code>502</code>
      <message>Bad Gateway</message>
      </error>
      I have checked the setting on CF side.
      Destination service is defined as " api-access" and the "destination" is defined as "northwind" (tested as okay). And the service "demo-srv" is automatically binded to "api-access" and " demo-uaa" instance without error.
      Author's profile photo Anubhav Oberoy
      Anubhav Oberoy

      Guys,

       

      Problem lies in your destination in CF, Check the name, It must be NorthWind where W must be caps. I would request to use the attached destination to import directly.

      #
      #Mon Apr 26 15:59:02 UTC 2021
      URL=https\://services.odata.org/Experimental/OData/OData.svc
      Name=NorthWind
      ProxyType=Internet
      Type=HTTP
      Authentication=NoAuthentication
      Description=NorthWind

       

      Author's profile photo THAJUNNIZA M A
      THAJUNNIZA M A

      I am also facing the same issue..Please help me how you resolved it

      Author's profile photo Ahmed Ali Khan
      Ahmed Ali Khan

      Hi Sangita,

       

      I am also facing the same problem when using odata destination which have Basic authentications, With local data its working fine as per Thanks Jhodel Cailan part1 of this series but when using destination its giving "Bad Gateway" error, Are you able to resolve it somehow?

      Author's profile photo Carl Önnheim
      Carl Önnheim

      Hi Thanks Jhodel Cailan

      Thanks for this article, very helpful and works like a charm!

      Is it possible to reuse the external service in the local CAP model once defined? I.e. incorporate the external service built using this guide into another service which is running in the local data model.

      In essence I have this external service (business partners from my backend ERP)

      using businesspartner from './external/businesspartner.csn';
      
      service businessPartnerService @(requires: 'authenticated-user') {
          @readonly entity BusinessPartner as projection on businesspartner.BusinessPartnerCollection;
      }

      Implemented like so:

      const cds = require('@sap/cds');
      
      module.exports = cds.service.impl(async function() {
      	const { BusinessPartner, CurrentDefaultAddressInformation } = this.entities;
      	const service = await cds.connect.to('businesspartner');
      	this.on('READ', BusinessPartner, request => {
      		return service.tx(request).run(request.query);
      	});
      });

      Now I want to utilize that entity from another service, basically like this:

      using { cuid } from '@sap/cds/common';
      using businessPartnerService from './businesspartner-service';
      
      service projectService @(requires: 'authenticated-user') {
          @readonly entity BusinessPartner as projection on businessPartnerService.BusinessPartner;
          entity ProjectDraft : cuid
          {
              title : String;
              customer : Association to businessPartnerService.BusinessPartner;
          }
      }
      

      This compiles fine and publishes the services. But if I try to navigate to a BusinessPartner entity from within the "projectService" I get:

      Entity "projectService.BusinessPartner" is annotated with "@sap.persistence.skip" and cannot be served generically.

      Can you shed some light on what the intended design pattern is here? I would like to avoid duplicating the code implementing the external service (and doing that causes other issues since the cds.connect.to('service') part only returns the actual service on the first shot).

      Again, thanks for the guide and hoping for some input on this scenario.

      Regards

      //Carl

      Author's profile photo Tháyra Pedroso
      Tháyra Pedroso

      Hi Carl,

       

      Did you solve this problem?

      Author's profile photo Carl Önnheim
      Carl Önnheim

      Hi,

      No. I think it is doable but we also had issues with the external service being oData v2 and managing transposal between v2 and v4 became cumbersome. We decided to set up replication of the source objects into the CAP data model instead.

      Regards

      //Carl

      Author's profile photo Fedor Shestov
      Fedor Shestov

      Hi. When I'm deploying the file to BTP I  get the error "In production mode it is required to set `options.destination`". How can I set it up? Thanks

      Author's profile photo Manikandan Kannan
      Manikandan Kannan

      Hello Fedor,

       

      I am getting the same issue. Did you fix this?

      Author's profile photo Fedor Shestov
      Fedor Shestov

      Hi. If you create an app from template then do not forget to add flag node start - productive

      Author's profile photo Simone Chiappin
      Simone Chiappin

      Hi Fedor,

      Where did you insert this flag? I am getting the same issue

      Author's profile photo Fedor Shestov
      Fedor Shestov

      in mta

      build-parameters:
        before-all:
          - builder: custom
            commands:
              - npm install --production
              - npx cds-dbm build --production
      Author's profile photo Israel Garcia
      Israel Garcia

      hello,

      I am getting the same issue. but i have the meta configuration with --production

      build-parameters:
        before-all:
         - builder: custom
           commands:
            - npm install --production
            - npx -p @sap/cds-dk cds build --production

      any idea ?

       

      Author's profile photo Rajdeep BHUVA
      Rajdeep BHUVA

      Hi Jhodel Cailan,

       

      Great Blog post!!

      How can i consume non odata services into CAP ?

       

      Thanks,

      Rajdeep Bhuva

       

      Author's profile photo Ahmed Ali Khan
      Ahmed Ali Khan

      Awesome Blog! Appreciate it a lot, Just one problem on the final step when i am trying to test it locally, i am getting this error, Can anyone please suggest some solution?

      error%20local%20test

       

      Also I can clearly see that binding of XSUAA is already there
      bindings

      bindings

      my mta looks like this(confused about build params if they are correct or something more required)

      ## Generated mta.yaml based on template version 0.4.0
      ## appName = demo-Northwind
      ## language=nodejs; multitenant=false
      ## approuter=
      _schema-version: '3.1'
      ID: demo-Northwind
      version: 1.0.0
      description: "A simple CAP project."
      parameters:
        enable-parallel-deployments: true
         
      build-parameters:
        before-all:
         - builder: custom
           commands:
            - npm i
            - npx cds build
            - npm ci
            - npx -p @sap/cds-dk cds build --production

      modules:
       # --------------------- SERVER MODULE ------------------------
       - name: demo-Northwind-srv
       # ------------------------------------------------------------
         type: nodejs
         path: gen/srv
         properties:
           EXIT: 1  # required by deploy.js task to terminate 
         requires:
           - name: demo-destination
           - name: demo-Northwind-uaa
      resources:
       - name: demo-destination
         type: org.cloudfoundry.existing-service
         parameters:
           service-name: demo-destination

       - name: demo-Northwind-uaa
         type: org.cloudfoundry.managed-service
         parameters:
           path: ./xs-security.json
           service: xsuaa
           service-plan: application