Wednesday, 13 June 2018

How to implement Function Import in HANA XS

It is all about a feature that I found missing in HANA XSODATA — support for OData Function Imports.  While this feature can be implemented in a custom way through XSJS, I would like to have this custom implementation compliant with OData V2 specification in order for the consumer application (which is SAPUI5) to be able to leverage the existing ODataModel V2 library.  This is my journey for achieving this requirement — by creating a custom function import utility through XSJS.

Overview


I need to develop an OData service on top of SAP HANA XS engine through the XSODATA framework, however, according to this SAP Q&A, Function Imports are not supported by the XSODATA framework and can only be implemented using the XSJS framework.

When implementing an XSJS service, by default, it would need to be called by using an AJAX call.  But when you are using SAPUI5 framework, as a UI5 developer, you don’t normally have to use AJAX in your UI5 application, you would normally use ODataModel V2 instead.  A lot of service handling capabilities is already provided by the standard ODataModel V2 library, so we want to make use of those capabilities as much as possible when consuming the custom XSJS service.

In order to achieve the above requirement, the XSJS service needs to be compliant with the OData V2 specifications.  Therefore, I have created this custom Function Import Utility through XSJS that is compliant with OData V2 spefications.

Prerequisites


◈ Understanding of OData V2 specifications – especially Function Import operation
◈ Understanding of SAP HANA XSJS Development in XSA (the concept should be the same if implemented in XSC)

Function Import Utility


Basically there are a total of 4 XSJS Library files in this utility:

◈ FunctionImportUtility.xsjslib – the main utility file for this framework that enables the support for function import operation
◈ MetadataGenerator.xsjslib – a supporting library file that helps to generate an EDMX file as a response for $metadata query
◈ MessageUtility.xsjslib – a supporting library that handles error message conversions
◈ type.xsjslib – a supporting library that holds constants for EDM types

Notes and limitations:

◈ This utility/framework may be incomplete, it was created to be able to cater the requirements we need
◈ No support for OData $batch functionality

How to use


◈ In the SAP WebIDE for SAP HANA tool, create a new MTA Project
◈ In the new MTA Project, create a Nodejs Module with XSJS support
◈ In the Nodejs Module under the “lib” folder, create a folder called “custom”
◈ In the new “custom” folder, import the Function Import Utility files
◈ In the “lib” folder, create a new XSJS file — SampleFunction.xsjs
◈ Here is a sample code for SampleFunction.xsjs:

"use strict";

$.import("custom", "type");
$.import("custom", "FunctionImportUtility");

// Function Import Utility expects an importing parameter of type
// Object (data definition object) which contains the data definition
// for the function import and its return type
$.custom.FunctionImportUtility.execute({
ComplexType: {
Name: "TotalAmountType",
Property: {
Name: "TotalAmount",
Type: $.custom.type.DECIMAL,
Precision: "15",
Scale: "0"
}
},

FunctionImport: [{
Name: "GetTotalAmount",
// the return type is based on the complex type TotalAmountType
ReturnType: "TotalAmountType",
HttpMethod: "GET",
// the exit function implementation that is similar to the
// exit function in XSODATA framework
ExitFunction: "xsjs:GetTotalAmount.xsjslib::onExecuteAction",
Parameter: [{
Name: "SalesOrderId",
Type: $.custom.type.STRING,
MaxLength: "10"
}]
}]
});

◈ Under the “lib” folder, create a new folder called “xsjs”
◈ Under the “xsjs” folder, create a new file called GetTotalAmount.xsjslib
◈ Here is a sample code for GetTotalAmount.xsjslib:

"use strict";

function _getTotalAmount(sSalesOrderId) {
// implement your logic here ...

// for simplicity of the demo just return a hardcoded value
return (sSalesOrderId === "0100000000") ? 100 : 0;
}

function onExecuteAction(oParameter) {
// oParameter will contain the parameters defined in Function Import
// definition in SampleFunction.xsjs
var sSalesOrderId = oParameter.SalesOrderId;

// Implement your logic to get the TotalAmount based on sSalesOrderId
var iTotalAmount = _getTotalAmount(sSalesOrderId);
 
// Return an object that has properties matching the data definition
// of the Function Import's return type
return {
TotalAmount: iTotalAmount
};
}

That’s it!  You’re all set to test the function import implementation!

Testing the implementation


For this testing, I will just show how to consume the OData Service on a regular chrome browser, just like how a backend developer would normally test his/her OData service.

◈ Execute the Nodejs module and wait until the service URL is generated by the WebIDE
◈ Get the metadata of the OData Function Import implementation by using $metadata

SAP HANA XS, SAP HANA Learning, SAP HANA Certification, SAP HANA Tutorial and Material

◈ Note that the EDMX file is generated based on the Data Model Definition maintained in SampleFunction.xsjs.  If you have been developing SAPUI5 apps, you probably already know that when you instantiate an object of sap.ui.model.odata.V2.ODataModel, this OData model will first call the $metadata to fetch the EDMX and the success of the instantiation depends on the successful fetch of EDMX file.

◈ Now, let’s test the actual Function Import — GetTotalAmount.  We can test the function import the same way we test an OData service created from SAP Gateway — see below:

SAP HANA XS, SAP HANA Learning, SAP HANA Certification, SAP HANA Tutorial and Material

◈ During the implementation, we have hardcoded the response to get the TotalAmount and we wrap the TotalAmount value inside a regular JavaScript Object.
return {
TotalAmount: iTotalAmount
};

◈ However, take note of the result of the Function Import call on the browser, you can see that the response is now wrapped with a fully qualified OData response that is returned by a Function Import.  Because of this behaviour, the object of sap.ui.model.odata.v2.ODataModel will be able to interpret the OData service response correctly.

That is all about it for testing the implementation of a fully qualified OData function import using XSJS service.

Closing


By using a framework concept like this, the consumption of the XSJS service to perform a Function Import operation is simplified since we can now use the SAPUI5 class library sap.ui.model.odata.v2.ODataModel, otherwise, the UI5 developers will need to use the conventional AJAX method call.

Notice how a simple framework can help on generating the EDMX file and making the function import response compliant with OData V2 specification.