on 2020 Apr 15 12:45 PM
Hello,
In my multitenant CAP app, I got an error when accessing an S/4HANA Cloud via Cloud SDK from my consumer subaccount. It works fine when accessing from my provider. My scenario is the following:
Perhaps it may seem a little weird the own s/4hana cloud system vs the provider subaccount, it is here just for test purposes.
When accessing https://provider-myapp-aa.cfapps.eu10.hana.ondemand.com/mobile/getData(cardTemplate=''), the own s4hc is reached successfully and data is retrieved but doing so from the consumer, https://consumer-myapp-aa.cfapps.eu10.hana.ondemand.com/mobile/getData(cardTemplate=''), I got the following error in the provider app:
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT error calling api Error: Could not find a destination with name "s4n103C1"! Unable to execute request.
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at Object.errorWithCause (/home/vcap/app/node_modules/@sap/cloud-sdk-core/node_modules/@sap/cloud-sdk-util/dist/error.js:14:20)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at /home/vcap/app/node_modules/@sap/cloud-sdk-core/dist/request-builder/request-builder-base.js:125:78
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at process._tickCallback (internal/process/next_tick.js:68:7)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT Caused by:
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT Error: FetchTokenError: Client credentials Grant failed! Request failed with status code 401
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at Object.errorWithCause (/home/vcap/app/node_modules/@sap/cloud-sdk-core/node_modules/@sap/cloud-sdk-util/dist/error.js:14:20)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at accessTokenError (/home/vcap/app/node_modules/@sap/cloud-sdk-core/dist/scp-cf/xsuaa-service.js:166:29)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at /home/vcap/app/node_modules/@sap/cloud-sdk-core/dist/scp-cf/xsuaa-service.js:43:57
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at process._tickCallback (internal/process/next_tick.js:68:7)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT Caused by:
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT Error: Request failed with status code 401
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at createError (/home/vcap/app/node_modules/axios/lib/core/createError.js:16:15)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at settle (/home/vcap/app/node_modules/axios/lib/core/settle.js:17:12)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at IncomingMessage.handleStreamEnd (/home/vcap/app/node_modules/axios/lib/adapters/http.js:237:11)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at IncomingMessage.emit (events.js:203:15)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at endReadableNT (_stream_readable.js:1145:12)
2020-04-15T09:46:27.639+0000 [APP/PROC/WEB/0] OUT at process._tickCallback (internal/process/next_tick.js:63:19)
The destination s4n103C1 is created in the provider subaccount and I have made sure that the fields are well filled out. I tried creating the destination in the consumer subaccount and I got the same error.
Let me here remark that accessing other CAP services from the consumer subaccount (i.e https://consumer-myapp-aa.cfapps.eu10.hana.ondemand.com/mobile/books) works fine, the error occurs when accessing backend destinations via Cloud SDK.
The source code that raises the error is:
var cloud_sdk_core_1 = require("@sap/cloud-sdk-core");
const { serializeEntity } = require("@sap/cloud-sdk-core");
// building the select fields array
....
var jwt = retrieveJwt(req._.req)
// call api
const salesOrder = await SalesOrder
.requestBuilder()
.getAll()
.select(...selectFields)
.filter(filters)
.top(top)
.execute({destinationName: destination, jwt: jwt})
.then(result => result.map(so => serializeEntity(so, SalesOrder)))
.catch(reason => {
console.log('error calling api ', reason)
})
This is my mta.yaml file:
_schema-version: 2.0.0
ID: myapp
version: 1.0.0
modules:
- name: myapp-db
type: hdb
path: db
parameters:
memory: 256M
disk-quota: 256M
requires:
- name: myapp-db-hdi-container
- name: myapp-srv
type: nodejs
path: srv
parameters:
memory: 512M
disk-quota: 512M
provides:
- name: srv_api
properties:
url: ${default-url}
requires:
- name: myapp-db-hdi-container
- name: myapp-uaa
- name: myapp_destination
- name: myapp_connectivity
properties:
SAP_JWT_TRUST_ACL:
- clientid: "*"
identityzone: "*"
DEBUG : "false"
- name: myapp-ui
type: nodejs
path: app
parameters:
memory: 256M
disk-quota: 256M
requires:
- name: myapp-uaa
- name: srv_api
group: destinations
properties:
forwardAuthToken: true
strictSSL: true
name: srv_api
url: ~{url}
properties:
SAP_JWT_TRUST_ACL:
- clientid: "*"
identityzone: "*"
TENANT_HOST_PATTERN: "^(.*)-myapp-ui.cfapps.eu10.hana.ondemand.com"
resources:
- name: myapp-db-hdi-container
type: com.sap.xs.hdi-container
properties:
hdi-container-name: ${service-name}
- name: myapp-uaa
type: org.cloudfoundry.managed-service
parameters:
service-plan: application
service: xsuaa
path: ./xs-security.json
shared: true
config:
xsappname: myapp-${space}
- name: myapp_destination
type: org.cloudfoundry.managed-service
parameters:
service-plan: lite
service: destination
- name: myapp_connectivity
type: org.cloudfoundry.managed-service
parameters:
service-plan: lite
service: connectivity
Any clues with this issue?
Thanks in advance.
Best regards,
Marc
Request clarification before answering.
Hi Marc
as dhem already described, did you implement the getDependencies endpoint?
Here we had problems in our solution when we had not implemented this endpoint.
You can create an AppRouter module (@sap/approuter) and execute the getDependencies callback against this module. The AppRouter includes a middleware (lib/middleware/subscription-middleware.js) which can answer these requests.
For the AppRouter to answer the request correctly, you must bind the service instance of the destination service (maybe also the service instance of the connectivity service, but I'm not sure if this is necessary) to the AppRouter. The AppRouter reads the bound services and uses them to create the payload that is returned.
When the getDependencies callback is executed against the AppRouter, it returns an array containing information about the bound service instance of the destination service (and the connectivity service instance if it is bound).
Following a screenshot of the response of our getDependencies Callback handled by a AppRouter module.
Regards
Simon
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Simon,
if I understood correctly, only with an Approuter module (I already have it) and without coding any additional handler to process the request, I can execute the onboarding and getDependencies callbacks against this module. Did I get it right?
But, how did you configure the route in xs-app.json? What do you set in destination/localDir/service parameters for the route? One of these is mandatory.
{
"source": "^/callback/(.*)$",
"target": "/callback/$1",
"authenticationType": "xsuaa",
"destination": ...
}
I have not found any documentation on how to implement it, just some additional mention that it is possible to do so.
Thanks in advance.
Regards,
Marc
Hi Marc,
you got that right and you don't have to define a route.
All you have to do is create a service instance of the saas-registry service, set the URIs for onSubscription and getDependencies (in many documentation you will find the paths callback/v1.0/tenants/{tenantId} and callback/v1.0/dependencies here, but they can be different) and bind the service instance to your router.
The XSUAA service must be configured correctly and bound to the AppRouter (tenant-mode, grant-as-authority-to-apps, Scope $XSAPPNAME.Callback (Link)) and the AppRouter also needs the TENANT_HOST_PATTERN (Link).
You do not need to configure any routes for the subscription.
Note
The AppRouter does not forward the requests where the path matches the path of the URIs in the saas registry.
Take a look at node_modules/@sap/approuter/lib/middleware/subscription-middleware.js of your AppRouter module. There you will find the middleware.
Regards
Simon
Hi Simon,
thanks for sharing such exhaustive details. I guess I forgot to recreate some service in my first try because the configuration was as you explain. Now it works like a charm.
So, in summary, both approaches works fine, but this one based on Approuter doesn't need any coding and is easier to implement.
Thanks for your support sperstorfer and dhem!
Best regards,
Marc
User | Count |
---|---|
59 | |
8 | |
7 | |
6 | |
5 | |
5 | |
5 | |
4 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.