One of the challenges of data-driven decision making is to present complex and diverse data in a way that is easy to access and understand to act upon. SAP Datasphere delivers seamless and scalable access to mission-critical business data from multiple sources, accessing this data everywhere has the power to ease decision making and speed up business processes. In this context, SAP Build plays vital role to accelerate the apps development and making data accessible on the go. In this blog post, you will learn how to create an SAP Build app that integrates with SAP Datasphere as a centralized data source. This is achieved through the use of SAP Datasphere Consumption oData APIs. SAP Build benefits from SAP Datasphere's authentication model using a centralized Identity Provider (IdP) to grant access to the app and its contents. The blog post will guide you through the following steps:
The most complex and crucial step in integrating these two systems is the authentication process. SAP Datasphere Consumption APIs necessitate a three-legged OAuth2.0 flow with the "Authorization Code" grant type, which needs manual user interaction with the configured Identity Provider (IdP). For more information on the protocol, please refer to our reference page here.
However, SAP Build does not natively support this type of authentication, neither using SAP BTP authentication type. The solution for such problem can accomplished by starting the App login step in a browser based view (aka: WebView) in the App opening to the SAP Datasphere authorization URL. This will initiate communication with the configured IdP in your SAP Datasphere (such as SAP ID, Office 365, etc), displaying its login page. Upon successful provision of user credentials, the OAuth Client redirect URI will be invoked.
For the entire process to operate securely and correctly, the OAuth Client redirect URI must be configured for the backend application API. Within the scope of this blog post, a BTP application will be created for this specific purpose. This backend application will utilize the OAuth Client ID and secret to generate the SAP Datasphere access token, which will then be sent back to the SAP Build Application.
The image bellow illustrates this flow:
This authentication process is inspired on the one described in the SAP Build Samples GitHub. To avoid the Client ID and Secret to be stored on device the "Authorization Backend" was introduced, so sensitive information could be stored into the BTP Destination Service.
The "Authorization Backend" was introduced to ensure sensitive information is securely stored on the server. This blog post will detail the deployment of a new BTP application in NodeJS as an example, but the same steps can be applied to any new or existing backend service running on BTP or in another cloud environment.
The application need to realize 3 main tasks:
Receiving the Redirect URI call in the service requires exposing an unauthenticated route that matches the path used in the OAuth Client configuration (Step 3.1). In this example "/oauth/callback" (<callback-api-path>) will be used.
The authorization code, generated once the IdP login is successful, will be received as the "code" query parameter on the callback route, it will be used later for OAuth token request.
Redirect URL Example:
https://auth-backend.hana.ondemand.com/oauth/callback?code=lxuC1347ZLcNg3gjqFHKjAc6gT4iB0hp
NodeJS Code sample:
[File: index.js]
import express, { Request, Response } from "express";
const port = process.env.PORT || 3000;
const app = express();
app.get("/oauth/callback", async (req: Request, res: Response
try {
const code = req.query.code as string;
} catch (e) {
res.status(500).json(e.message);
}
});
app.listen(port, () => {
console.log("Server listening on " + port);
});
After obtaining the authorization code, the next step in the OAuth protocol is requesting the access token. On this blog post the SAP BTP Destination Service will be used for storing the OAuth Client ID and Secret, as well as for requesting the access token. Some dependencies to this process such as BTP destination and Client ID and Secret will be explained and later created on section 3.
The process for requesting the access token uses the "Find Destination" API from SAP BTP Destination Service. It requires the authorization code received earlier, the destination name (set up as an environment variable), It is crucial that the BTP Application is linked with the destination service. Instructions for these steps are also detailed on the SAP Help pages [1][2][3], used as reference for this blog post.
The outcome of this request is the access and refresh tokens which will be used for authenticating all Consumption API request. The code snippet bellow shows how to configure such request in as part of the App created.
[File: destination-service.js]
import * as xsenv from "@sap/xsenv";
import axios from "axios";
export const findDestination = async (
destinationName: string,
code: string,
redirectHost: string
) => {
const credentials = xsenv.cfServiceCredentials({ tag: "destination" });
const token = await getDestinationServiceToken();
const response = await axios({
method: "get",
url: `${credentials.uri}/destination-configuration/v1/destinations/${destinationName}`,
headers: {
Authorization: `Bearer ${token}`,
"X-code": code,
"X-redirect-uri": `${redirectHost}/oauth/callback`,
},
});
if (!response.data.authTokens || response.data.authTokens.length === 0) {
throw new Error(`Failed to fetch destination, ${response.data}`);
}
const tokenObject = response.data.authTokens[0];
if (tokenObject.error) {
throw new Error(`Failed to fetch destination, ${tokenObject.error}`);
} else if (tokenObject.expires_in !== "0") {
delete tokenObject["http_header"];
return tokenObject;
}
throw new Error(`Failed to fetch destination, invalid token ${tokenObject}`);
};
const getDestinationServiceToken = async () => {
const credentials = xsenv.cfServiceCredentials({ tag: "destination" });
const response = await axios({
method: "post",
url: `${credentials.url}/oauth/token`,
headers: {
Authorization: `Basic ${Buffer.from(
`${credentials.clientid}:${credentials.clientsecret}`
).toString("base64")}`,
"Content-Type": "application/x-www-form-urlencoded",
},
data: {
client_id: credentials.clientid,
grant_type: "client_credentials",
},
});
if (response.status !== 200) {
throw new Error(
`Failed to fetch destination service access token, ${response.data}`
);
}
return response.data.access_token;
};
The last responsibility of the backend application is to return the token back to the client (SAP Build App), which started the authentication flow, in a way it could read and use. In the context of SAP Build Apps which will be using a web browser for all authentication process a straightforward approach is for the server to redirect the request to another URL it has (/oauth/token) in this blog post and provide the access token as part of the query parameters (as implemented in this blog) or within the body.
Some adjustments are required in the index.js file for calling the methods created above
[File: index.js]
import express, { Request, Response } from "express";
const { findDestination } = require("./destination-service");
const port = process.env.PORT || 3000;
const app = express();
app.get("/oauth/callback", async (req: Request, res: Response
try {
const code = req.query.code;
const destinationName = process.env.DESTINATION_NAME;
const redirectUri = `https://${req.hostname}/oauth/callba
const datasphereToken = await findDestination(destinationName,code,redirectUri);
res.redirect(`/oauth/token?access_token=${datasphereToken.value}`);
} catch (e) {
res.status(500).json(e.message);
}
});
app.get("/token", async function (req, res) {
res.status(200).send();
});
This is an example of how a BTP backend application can be developed to serve as authentication middleware between SAP build, functioning as a client, and the destination service in BTP. The only step missing is to deploy the application to BTP and use it - follow BTP guidance on how to deploy an application.
For the next step we will need some information created on this section save for later:
The first thing to be realized for allowing integration with SAP Datasphere Consumption APIs involves creating an OAuth Client within the SAP Datasphere. This action generates the "ClientID" and "Client Secret" necessary for subsequent steps.
For the next step we will need some information created on this section save for later:
The authentication method supported by SAP Datasphere Consumption APIs is the three-legged OAuth2.0 flow with "Authorization Code". However, this authentication method is not supported by BTP Destinations when created via the cockpit, as detailed on this SAP Help Page. In this case, the only viable creation method is through the SAP Destination Service API, as described here. In this blog post we will use a HTTP client (eg: Postman) the request should look like:
URL: https://<destination-service-url>/destination-configuration/v1/subaccountDestinations/
Method: POST
Headers:
Body:
{
"Name": "DWC_Sales_OrdersTypeCode",
"Type": "HTTP",
"URL": "<your-datasphere-url>" //Doesn't matter in this case,
"Authentication": "OAuth2AuthorizationCode",
"ProxyType": "Internet",
"tokenServiceURLType": "Dedicated",
"HTML5.DynamicDestination": "true",
"clientId": "<client-id>",
"clientSecret": "<client-secret>",
"AppgyverEnabled": "true",
"tokenServiceURL": "<datasphere-token-url>",
"WebIDEUsage": "true"
}
Note: <destination-service-url> can be found in the binding between any BTP App and the Destination Service in the BTP cockpit (some more details on this SAP Help Page)
Upon completing the pre-requirements setup in sections 2 and 3, focus can then be shifted to developing the SAP Datasphere view according to individual business needs. This blog post primarily addresses the integration between SAP Datasphere and SAP Build, hence this section will cover a basic setup of a view intended to be accessed by SAP Build.
The most crucial part of creating an app involves understanding the problem that needs solved, as this will drive how data, views and analytic models should be modeled on SAP Dataspehre. In the context of this blog, for example, the SAP Build app will expose a list of countries and the sales value of each of them, also allowing to drill down into each one to check details of the specific product sales in each country.
On this blog a simple view named "CountrySales" containing 2 measures and 3 attributes. The measures will aggregate the data allowing to extract value from it in later in the SAP Build App, the image bellow shows a bit more about the modeling of the view. Also, don't forget to mark your view as "Exposed for Consumption", as required by Consumption APIs and import data into your base local or remote tables, more details can be found here.
After deploying the view it becomes accessible through the Consumption OData APIs of SAP Datasphere. The a catalog request can be used check all available assets for consumption.
Asset Catalog request: https://<your-datasphere-url>/api/v1/dwc/catalog/assets
There are 2 ways of consuming the exposed view via relational or analytical type of consumption, in this blog post we will use the first one due to some limitations on SAP Build to handle analytical consumption type. An example of the consumption URL that will be used later on is defined bellow - it will be references as <country-sales-consumption-api>
https://<your-datasphere-host>/api/v1/dwc/consumption/<mySpace>/CountrySales/
For learning more about SAP Datasphere consumption APIs, checkout Mirko's blog post.
To start configuring the SAP Build App the first task to be executed is setting up the authentication and authorization using the Authorization Code flow within the App. To achieve this, we will create a page containing a webview component for an in-app browser experience. This webview will open the <datasphere-authorization-url> to start the authentication process explained on section 1. Once this flow concludes in the browser, we will add specific logic within SAP Build to capture the returned token and assign it to variables for later use.
https:/your-dwc-tenant.authentication....sap/oauth/authorize?response_type=code&client_id=sb-5a0f6ce9-3ac2-4425-9d27-1d78ce6be785!b13811%7Cclient!b2319
After the login on the IdP is performed a blank screen should be displayed, which is the redirect page developed on section 2.3.
The concept behind the after login logic is to detect when the page displayed on the webview matches the pattern of the redirect URL used on section 2.3, in this case “/oauth/token?access_token=*” . When this happens the access token can be extracted from the URL query parameter, stored into an App variable and the page redirected to the fist content page of the App.
For the logic setup, open the "Logic Canvas" of the "Webview" component and follow the steps, at the end it should look similar to the image:
Start with the event “Component onLocationChange”
Include some Javascript code to detect the URL pattern and extract the access token:
if(inputs.input1.url.includes('/oauth/token?access_token')){
var code = inputs.input1.url.split('access_token=')[1].split('&')[0];
return { code: code, codeAvailable: true }
}else{
return { codeAvailable: false, debug: JSON.stringify(inputs.input1) }
}
Use an "IF" to check if the access token is available
In case it is available:
Set it into an App variable – this will be referenced as <app-var-access-token>
The last step to make the App usable is to create pages and make request to Datasphere Consumption APIs to fetch the data and make it available on the App screen.
For that SAP Build data entities need to be configured for each of the assets of SAP Datasphere that will be required in the App. Once the data models are configured they can be easily bound to list, tables and charts on the App. The steps bellow explain how to configure a data model:
To use the data models created a request for the records need to be added to the life cycle of a page and the authorization token of this request should be set correctly using the <app-var-access-token> as part of the headers.
The steps bellow explain how to create a page, configure the data model and use the filter or parameter capabilities of SAP Datasphere to search for specific data:
"Amount: $" + FORMAT_LOCALIZED_DECIMAL(NUMBER(repeated.current.Weighted_Value), "en", 2)
As “Image Source” set the formula bellow just make the list a bit prettier (It will render country code as colored image:
"https://ui-avatars.com/api/?rounded=true&background=5F308E&&color=FFFFFF&size=64&name=" + ENCODE_FOR_URL(repeated.current.CountryCode)
When the App components have been added in the place they belong on the screen. The missing part is to add behaviour and load data to each of them. First when the page loads the data from the Data Model created on the step before needs to be loaded, also some caveat needs to be added for showing a "spinner" during loading time. Follow the steps bellow for setting this.
This finishes the development of a simple SAP Build app fully integrated with SAP Datasphere via its Consumption APIs, a video of an extended version of the App is available bellow.
To conclude, this blog post serves as an exhaustive guide for the integration of an SAP Build app with SAP Datasphere, while also laying the foundational knowledge for applying these steps to integrate other products and clients that support OAuth authentication with Authorization Code grant type. The elaborate instructions encompass crucial processes such as understanding the authentication flow, making effective use of the Consumption oData APIs, shaping the BTP Authorization Backend, setting up secure authentication, creating an OAuth Client & Destination, and establishing the SAP Build App Data Model.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
24 | |
13 | |
11 | |
10 | |
8 | |
7 | |
6 | |
6 | |
5 | |
5 |