Skip to Content
Technical Articles

SAP Analytics Cloud REST API Custom Widget

In this blog, we’ll learn how to build a simple REST API custom widget in SAP Analytics Cloud (SAC). User needs to enter the partner number information and get the scoring result from the REST API.

We will write a custom widget with SAPUI5 components for text-area, button and performs Ajax HTTP Post request.

For the backend, we will write a NodeJS app that performs the HTTP request to R Plumber REST API to get the score value.

Create Custom Widget

Create a WebComponent with SAPUI5 elements TextArea and Button. On button press events onButtonPresscall the Ajax HTTP Post request with parameter partnernumber. You can refer to the blog in the Reference section on how to create the WebComponent for custom widget.

The main code can be found on the UI5() function below.

function UI5(changedProperties, that) {
    var that_ = that;

    div = document.createElement('div');
    widgetName = that._export_settings.name;
    div.slot = "content_" + widgetName;

    var restAPIURL = that._export_settings.restapiurl;
    console.log("restAPIURL: " + restAPIURL);

    if (that._firstConnectionUI5 === 0) {
        console.log("--First Time --");

        let div0 = document.createElement('div');
        div0.innerHTML = '<?xml version="1.0"?><script id="oView_' + widgetName + '" name="oView_' + widgetName + '" type="sapui5/xmlview"><mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns:l="sap.ui.layout" height="100%" controllerName="myView.Template"><l:VerticalLayout class="sapUiContentPadding" width="100%"><l:content><Input id="input"  placeholder="Enter partner number..." liveChange=""/></l:content><Button id="buttonId" class="sapUiSmallMarginBottom" text="Get Score" width="150px" press=".onButtonPress" /></l:VerticalLayout></mvc:View></script>';
        _shadowRoot.appendChild(div0);

        let div1 = document.createElement('div');
        div1.innerHTML = '<div id="ui5_content_' + widgetName + '" name="ui5_content_' + widgetName + '"><slot name="content_' + widgetName + '"></slot></div>';
        _shadowRoot.appendChild(div1);

        that_.appendChild(div);

        var mapcanvas_divstr = _shadowRoot.getElementById('oView_' + widgetName);

        Ar.push({
            'id': widgetName,
            'div': mapcanvas_divstr
        });
        console.log(Ar);
    }

    sap.ui.getCore().attachInit(function() {
        "use strict";

        //### Controller ###
        sap.ui.define([
            "jquery.sap.global",
            "sap/ui/core/mvc/Controller",
            "sap/m/MessageToast",
            'sap/m/MessageBox'
        ], function(jQuery, Controller, MessageToast, MessageBox) {
            "use strict";

            return Controller.extend("myView.Template", {

                onButtonPress: function(oEvent) {

                    var partnernumber = oView.byId("input").getValue(); //"0004540866"
                    console.log(partnernumber);

                    $.ajax({
                        url: restAPIURL,
                        type: 'POST',
                        data: $.param({
                            "partnernumber": partnernumber
                        }),
                        contentType: 'application/x-www-form-urlencoded',
                        success: function(data) {
                            console.log(data);
                            _score = data;

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

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

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

        console.log("widgetName:" + widgetName);
        var foundIndex = Ar.findIndex(x => x.id == widgetName);
        var divfinal = Ar[foundIndex].div;

        //### THE APP: place the XMLView somewhere into DOM ###
        var oView = sap.ui.xmlview({
            viewContent: jQuery(divfinal).html(),
        });

        oView.placeAt(div);

        if (that_._designMode) {
            oView.byId("buttonId").setEnabled(false);
            oView.byId("input").setEnabled(false);
        } else {
            oView.byId("buttonId").setEnabled(true);
            oView.byId("input").setEnabled(true);
        }
    });
}

 

Create NodeJS App

Create a simple HTTP server with endpoint /score to get the score result from the R server with Plumber. I will not cover how to create the R Plumber service, but you can refer to the tutorial here.

"use strict";
const port = process.env.PORT || 3000;
const fs = require('fs');
const options = {
    key: fs.readFileSync('YOUR.key'),
    cert: fs.readFileSync('YOUR.crt')
};
const server = require("https").createServer(options);

const http = require('http');
const express = require("express");
const app = express();
const cors = require('cors');
const querystring = require('querystring');

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


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

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: GET, POST, OPTIONS');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

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

app.post('/score', function(req, res) {
    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) {
    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_SERVER',
            port: 'R_PORT',
            path: '/score',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(post_data)
            }
        };

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

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


//Start the Server 
server.on("request", app);
server.listen(port, function () {
	console.info(`HTTP Server: ${server.address().port}`);
});

I hosted the code in SAP HANA XSA with the minimum configuration (no security or credential checks):

Usage

  • Load the widget onto canvas in Analytic Application, restAPI_1.
  • Create TextArea, TextArea_1 to store the result.
  • Enter the Custom Widget properties: REST API URL and Widget Name.
    Rest API URL is the link to the NodeJS URL endpoint with port, example: https://localhost:3000/score.
    You can put any name for Widget Name.

  • Insert the following code in onStart() events trigger. This is to get the result from getScore() function and print in TextArea, TextArea_1.

  • Click Run Analytic Application to run the app.
  • Enter the partner number and click Get Score. If no error, you will get the result printed in TextArea.

With this reference, now you can add the REST API in your SAC custom widget. Do let me know if you have questions.

References:

Be the first to leave a comment
You must be Logged on to comment or reply to a post.