Friday 28 August 2020

SAP Analytics Cloud Custom Widget REST API with OAuth 2.0 and SAP HANA XSA

We’ll learn how to create a SAP Analytics Cloud custom widget REST API with OAuth 2.0 and SAP HANA XSA.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep


Background


We have a scenario where user needs to get the scoring result from the R Plumber service. User will enter the partner number and expect to get the score result from SAC screen.

The R plumber service is a HTTP GET request without authentication. As we need to protect this service, one of the possible solution is to use OAuth 2 authentication between SAC and R plumber service. The HANA XSA comes in to the picture for the OAuth 2 authentication.

To fulfill the above requirements, we will write a code for the following parts:

◉ SAC custom widget, restAPI.
◉ SAP HANA XSA (NodeJS and Web module), zrestapi.

The R plumber service is out of scope of this blog.

SAC Custom Widget


We will create an user interface like below. There are two UI elements, textarea and button. The textarea under Result is coming from the actual SAC widget.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

Everything is happening under onButtonPress() function. The first thing is to get the access token with the client_id and client_secret. We will get these values from HANA XSA later. Once we got the token, then we can perform the HTTP Post request to get the scoring result.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

onButtonPress: function(oEvent) {
    var this_ = this;

    var partnernumber = oView.byId("input").getValue(); 
    console.log(partnernumber);
    this_.wasteTime();

    var CLIENT_ID_str = 'REPLACE_WITH_CLIENT_ID';
    var CLIENT_SECRET_str = 'REPLACE_WITH_CLIENT_SECRET';

    $.ajax({
        type: 'POST',
        url: "https://REPLACE_WITH_TOKEN_URL/uaa-security/oauth/token",
        contentType: 'application/x-www-form-urlencoded; charset=utf-8',
        crossDomain: true,
        cache: true,
        dataType: 'json',
        data: {
            client_id: CLIENT_ID_str,
            client_secret: CLIENT_SECRET_str,
            grant_type: 'client_credentials',
        },

        success: function(data) {
            console.log(data);

            var access_token = data.access_token;

            $.ajax({
                url: restAPIURL,
                type: 'POST',
                headers: {
                    "Authorization": "Bearer " + access_token,
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                data: $.param({
                    "partnernumber": partnernumber
                }),
                async: true,
                timeout: 0,
                contentType: 'application/x-www-form-urlencoded',
                success: function(data) {
                    this_.runNext();
                    console.log(data);
                    _score = data;

                    that._firePropertiesChanged();
                    this.settings = {};
                    this.settings.score = "";

                    that.dispatchEvent(new CustomEvent("onStart", {
                        detail: {
                            settings: this.settings
                        }
                    }));

                },
                error: function(e) {
                    this_.runNext();
                    console.log("error: " + e);
                    console.log(e);
                }
            });

        },
        error: function(e) {
            this_.runNext();
            console.log(e.responseText);
        }
    });
},

We’ll get the Client ID & Secret and Token URL from HANA XSA.

SAP HANA XSA


◉ Create SAP Cloud Platform Business Application in SAP HANA XSA, zrestapi.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Create the NodeJS module. Replace the server.js with the code below.

/*eslint no-console: 0, no-unused-vars: 0, no-undef:0, no-process-exit:0*/
/*eslint-env node, es6 */
"use strict";
const port = process.env.PORT || 3000;
const server = require("http").createServer();

//Initialize Express App for XSA UAA and HDBEXT Middleware
const xsenv = require("@sap/xsenv");
const passport = require("passport");
const xssec = require("@sap/xssec");
const express = require("express");
global.__base = __dirname + "/";

const https = require('https');
const http = require('http');
const cors = require('cors');
const querystring = require('querystring');

//logging
var logging = require("@sap/logging");
var appContext = logging.createAppContext()

//Initialize Express App for XS UAA and HDBEXT Middleware
var app = express();

app.use(cors());
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from    
    res.header('Access-Control-Allow-Methods: OPTIONS,GET,PUT,POST,DELETE');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

var bodyParser = require('body-parser');
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({
    extended: true
})); // support encoded bodies

//Compression
app.use(require("compression")({
    threshold: "1b"
}));

//Helmet for Security Policy Headers
const helmet = require("helmet");
// ...
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    styleSrc: ["'self'", "sapui5.hana.ondemand.com"],
    scriptSrc: ["'self'", "sapui5.hana.ondemand.com"]
  }
}));
// Sets "Referrer-Policy: no-referrer".
app.use(helmet.referrerPolicy({ policy: "no-referrer" }));

passport.use("JWT", new xssec.JWTStrategy(xsenv.getServices({
uaa: {
tag: "xsuaa"
}
}).uaa));
app.use(logging.middleware({
appContext: appContext,
logNetwork: true
}));
app.use(passport.initialize());
app.use(
passport.authenticate("JWT", {
session: false
})
//xsHDBConn.middleware(hanaOptions.hana)
);

var corsOptions = {
   origin: '*',
   optionsSuccessStatus: 200 
}


// Redirect any to service root
app.get("/node", (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/html'
    });
    res.write('OK');
    res.end();
});

app.post('/score', function(req, res) {
req.setTimeout(0);
    console.log(req.body.partnernumber);

    async function getPartnerNumber() {
        let response = await doRequest(req.body.partnernumber);
        console.log(response);
        res.status(200).send(response)
    }
    getPartnerNumber();
})

function doRequest(partnernumber) {
console.log();
    return new Promise((resolve, reject) => {

        var post_data = querystring.stringify({
            'partnernumber': partnernumber
        });

        // An object of options to indicate where to post to
        var post_options = {
            host: 'R_PLUMBER_SERVER',
            port: 'R_PLUMBER_PORT',
            path: '/score',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(post_data)
            }
        };

    var body = '';
        var post_req = http.request(post_options, function(res) {
            res.setEncoding('utf8');
            res.on('data', function(chunk) {
                console.log('Response: ' + chunk);
body += chunk;
            });
res.on('end', function() {
resolve(body);
});
        });

        post_req.write(post_data)
        post_req.end();
    });
}

//Start the Server 
server.on("request", app);

//Start the Server 
server.listen(port, function () {
console.info(`HTTP Server: ${server.address().port}`);
});
◉ Create Web module and other artifacts like xs-app.json and xs-security.json.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Run the both NodeJS and Web modules.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ If you try to click the link of the NodeJS server URL, you will get unauthorized message, which is correct.  We need to have the access token first as mentioned earlier.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Now let’s get the client ID, secret and token URL in order to generate the token. Go to Tools > SAP HANA XS Advanced Cockpit.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Search the app and select the Environment Variables. Under VCAP_SERVICES, get the clientid, clientsecret and url.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Update the code in SAC custom widget with clientid, clientsecret and tokenurl.

var CLIENT_ID_str = 'REPLACE_WITH_CLIENT_ID';
var CLIENT_SECRET_str = 'REPLACE_WITH_CLIENT_SECRET';

url: "https://REPLACE_WITH_TOKEN_URL/uaa-security/oauth/token"​

Usage


◉ Insert the custom widget restAPI.
◉ Create the layout in SAC Analytic Application.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ On the onStart() event, put the below code. This is to get the scoring value and print in TextArea_1.

var score = restAPI_1.getScore();
console.log("score: " + score);
TextArea_1.setValue(score);​

◉ Under styling of restAPI widget, fill in the REST API URL with the SAP HANA XSA NodeJS server URL and put any name under Widget Name.

SAP HANA XSA, SAP Analytics Cloud, SAP HANA Exam Prep, SAP HANA Tutorial and Material, SAP HANA Learning, SAP HANA Prep

◉ Save and run the application.

Demo Video


No comments:

Post a Comment