Taking a Closer Look at the BI RESTful API
Author Leo Gannon, Senior Consultant, NTT DATA Business Solutions UK.
Can’t somebody else do it?
The SAP BusinessObjects Business Intelligence Suite delivers a number of specific user experiences, the Launchpad (classic or Fiori) for end users and Central Management Console (CMC) for Administrators. These are useful tools for creating report objects and managing their use and lifecycle.
Sometimes, however we may want to perform actions outside of the core applications. You may want to change a large number of objects that are in a number of different folders in one go or maybe schedule a set of reports that have been created as part of a newly completed project. Perhaps you want to find all the reports created by a particular use and move them to a new location, or apply a new style to a particular set of documents.
One option is to assign this task to someone on the team with spare capacity (or someone who loves repetitive tasks). An alternative is to build your own scripts or applications that interface with the BusinessObjects back end. The solution to this used to be an SDK which required JAVA or .NET knowledge as well as the necessary libraries and tools to develop and test, however there is an alternative…
BI RESTful Services
The BI RESTful API is a collection of web services accessed via URLs in order to use features of the BI toolset.
The RESTful API has replaced some deprecated elements of the JAVA and .NET SDK and may be used in a similar way.
The benefit of RESTful API is that it provides a standardised method of accessing BI functionality outside of the usual tools (e.g. WebI or CMC). This is achieved through calling the functionality via a URL and providing input via XML or JSON. This can be called from any scripting language (e.g. VBScript1, C#, Python) that supports http requests since the input is essentially manipulated text. This is made easier if the script language also supports XPath or XML/JSON encoding of files/data.
A further benefit is that it requires very little additional software on a client and no additional software on the server. This includes libraries or IDE tools. The code required can be easily written in notepad if required.
Business Application
Whilst the RESTful Services can be used for single actions they are particularly useful for automating bulk tasks for instance if there is a requirement to schedule a large number of reports, the details of each schedule (report name, prompt values, destination and schedule timing) could be included in a single file that is then looped through programmatically and the reports scheduled quickly instead of a user having to schedule each one individually through the BI or CMC Web Interface.
Alternatively it could be used to make bulk changes to the object repository, adding or moving reports, for example.
The REST service could even be used for creating ad hoc queries against a universe and retrieving the results which could be embedded into web applications.
Principle
The essential principle is that the RESTfull web service URL is called in an HTTP Request where the request type is set (GET, POST, PUT or DELETE) as applicable and the body of the request is set using either XML or JSON content.
In practice a single Web Service is not enough to complete a complete task (e.g. scheduling a report) since other actions are required (e.g. generating a logon token, identifying the report to be scheduled, setting prompt values) each of which require a separate RESTful Web Service request. To enable development the SAP documentation provides some templates of suggested sequences to be used when developing applications. These can be found in the REST API Workflows section of the SAP BusinessObjects RESTful Web Service SDK User Guide
Request Path
The path for the request is a URL and is defined in the API Specification documentation. The URL may be either HTTP or HTTPS depending on how the server has been configured similiarly the port used may be the standard HTTP port (80), Tomcat Port (8080), default BI defined web services request port (6405) or a custom port defined within CMS against the service. If you do not know which port to use, check with your systems administrators which port to use and that it is not blocked by any firewall software your organisation my have in place.
Request Header
The request header is used to pass information to the server about the content type being sent and being expected in return as well as other information that is not suited to the body, such as a logon token to authenticate the request user.
The content of header fields is provided by the application making the request however the header fields themselves are defined by the RESTful service and cannot be altered on the client side. Any Header requirements are detailed in the request documentation. (see above)
Request Body
The request body is either XML or JSON (the precise type needs to be specified as a part of the request so it can be recognised and interpreted correctly). The request body may be constructed as a text string and attached to the request (the syntax is provided in the documentation) however a simple method of building the request body is to send a GET request first which generally returns a template request body template (in some cases this may be prepopulated with default values). This template can then be manipulated using XPath2 for XML3.
It is important to note that the order of elements in the request body may be important (for example in the SAP BI Scheduling REST API ordering of elements is important, the element: useSpecificName, in the schedule request must be included under the element: destination and before the element: filesystem, an order that differs from that of the documentation will cause a schedule request to fail) as such it is advisable to stick with the ordering shown in documentation and templates.
In order to get the format and content of the Request body correct it is often easiest to send a GET request first as the response often contains the expected content structure (along with some default values preset).
XPath
XPath is used to identify nodes and elements within XML documents/code which may then be used to add/amend/remove values or additional elements in the document/code. They rely on the structured nature of XML code to identify hierarchies of parent-child relationships.
JSON
The JSON format is essentially a dictionary of dictionaries (i.e. Key-Value pairs) and most scripting languages provide tools for navigating and amending these (if all else fails you can always iterate over and through them).
Whilst this does not have functionality like XPath readily available some users may find it simpler to navigate and set, it is hard to ague against in the case of shallow nested structures such as Authentication however more complex structures such as those required for scheduling are harder to navigate without high degrees of iteration.
Putting it into practice
As a very basic example we can log in, generate and apply a logon token to a request header before logging out again using the following code:
Python:
import requests
from lxml import etree
class SAPRESTful(): ”’ This class is a very basic demo of how you can use Python to log into a Business Objects platform using its REST API. The class provides methods for logging into a system generating a logon token and logging back out to end the session. ”’ def __init__(self, protocol=’http’, host=’localhost’, port=None, content_type=’application/xml’): if port: base_url = protocol + ‘://’ + host + ‘:’ + port else: base_url = protocol + ‘://’ + host
self.bip_url = base_url + ‘/biprws’ self.webi_url = self.bip_url + ‘/raylight/v1’ self.sl_url = self.bip_url + ‘/sl/v1’ self.headers = { ‘Accept’ : content_type, ‘Content-Type’ : content_type }
def _get_auth_info(self): return requests.get(self.bip_url + ‘/logon/long’, headers=self.headers)
def _send_auth_info(self, username, password, auth): ”’Helper function to retrieve a log in token”’ root = etree.fromstring(self._get_auth_info().text)
finduserName = etree.XPath(‘//*[@name=”userName”]’) finduserName(root)[0].text = username
findPassword = etree.XPath(‘//*[@name=”password”]’) findPassword(root)[0].text = password
findAuthentication = etree.XPath(‘//*[@name=”auth”]’) findAuthentication(root)[0].text = auth
return requests.post(self.bip_url + ‘/logon/long’, headers=self.headers, data=etree.tostring(root))
def set_logon_token(self, username, password, auth=’secEnterprise’): ”’Function to create logon token and set in headers currently requires a valid user id and password”’ resp = self._send_auth_info(username, password, auth) findToken = etree.XPath(‘//*[@name=”logonToken”]’) if resp.status_code == 200: root = etree.fromstring(resp.text) token = findToken(root) # Set logon token in headers self.headers[‘X-SAP-LogonToken’] = token[0].text else: # Crude exception handling raise Exception(“Could not log on and set the logon token! Code: ” + str(resp.status_code))
def logoff(self): ”’Function to logoff the existing session”’ resp = requests.post(self.bip_url + ‘/v1/logoff’, headers=self.headers) if resp.status_code == 200: print(‘Log off successful’) else: # Crude exception handling raise Exception(“Could not log off!”)
|
With Python, if HTTPS is being used, SSL authentication needs to be enabled by installing a package that sets up the path to the certificates folders on Windows:
pip install python-certifi-win32 |
This package does not need to be referenced further in the code.
An Explanation
With this Python code we create a class (SAPRESTful) with class attributes for the various base urls:
bip_url – a String Type object that contains the BI Platform REST URL base
webi_url – a String Type object that contains the WebI REST URL base
sl_url – a String Type object that contains the Semantic Layer base url
As well as a class attribute to hold the request header
Header – a Dictionary Type object, this is set on initiation to contain values for Accept and
Content – Type both of which are set on initiation
There are two private class methods:
_get_auth_info – this uses the bip_url class attribute and a GET request to obtain the body required for an Authentication request
_send_auth_info – this sends a POST request with the body created from the _get_auth_info response using Xpath methods to populate the username, password and auth elements with values suppled to the method parameters
It returns the full response which contains the Logon Token string.
There are also two public class methods:
set_logon_token – this calls the _send_auth_info method passing through the credentials and authentication method and extracts the logon token (using an XPath expression) from the response before adding it to the headers class attribute for use in later requests.
logoff – this method uses a POST request to log off and so end the user session. This requires the Logon Token to be provided in the header class attribute otherwise the request will fail since there will be no indication which session is to be terminated.
It is worth noting that the logoff method is important because BI4 systems may be set to limit the number of active sessions a user can have running concurrently so with out closing the sessions this number will be exceeded and access will not be granted even if valid credentials are supplied.
Python code to use the class created above.
import SAPRESTful
parser = SAPRESTful.SAPRESTful(‘https’, ‘your.server.url’) logonResp = parser.set_logon_token(‘user1′,’user1password’) parser.logoff() |
With a non-default port and authentication Method this would be
import SAPRESTful
parser = SAPRESTful.SAPRESTful(‘https’, ‘your.server.url’, ‘6405’) logonResp = parser.set_logon_token(‘user1′,’user1password’,’secWinAD’) parser.logoff() |
The test code firstly imports the newly created class before instantiating a new instance of the object.
The new object instance is then used to login to a SAP BI system.
This can be shown to be working by running the set_logon_token method logging in to CMC and viewing active sessions.
The Sessions section will show an active session for the user(user1 in this case) and additional session could be created by instantiating a new instance of SAPRESTful and running the set_logon_token again.
The logoff method is then called to end the session (again this can be monitored in CMC).
Other Requests may be included as additional class methods in order to make fuller use of this code and the available functionality such as creating lists of objects or scheduling reports.
The same can be achieved with vbscript using:
Dim strArgCMSUser Dim strArgCMSPassword Dim strArgCMSServer Dim strArgCMSAuthentication Dim strRESTfulServer Dim restReq Dim logonToken
strRESTfulServer = “http://your.server.url/biprws“ strArgCMSUser = “user1” strArgCMSPassword = “user1Password” strArgCMSServer = “CMSServerName” strArgCMSAuthentication = “secEnterprise”
‘==================================================================== ‘ RESTful Service Logon ‘==================================================================== logonToken = GetLogonToken(strArgCMSPassword, _ strArgCMSUser, _ strRESTfulServer, _ strArgCMSAuthentication)
‘==================================================================== ‘ RESTful Service Logoff ‘==================================================================== RESTfulLogoff strRESTfulServer, logonToken
‘==================================================================== ‘Log on to RESTful service and return logonToken ‘==================================================================== Function GetLogonToken(strPassword, strUser, strRESTfulServer, strAuth) Dim restReq Dim url Dim authenticationXML
Set restReq = CreateObject(“MSXML2.ServerXMLHTTP”)
‘ Logon and get Logon Token
url = strRESTfulServer & “/logon/long”
restReq.open “GET”, url, false restReq.send
set authenticationXML = restReq.responseXML
authenticationXML.selectSingleNode(“//attrs/attr[@name=’password’]”).text = strPassword authenticationXML.selectSingleNode(“//attrs/attr[@name=’userName’]”).text = strUser authenticationXML.selectSingleNode(“//attrs/attr[@name=’auth’]”).text = strAuth
restReq.open “POST”, url, false
restReq.setRequestHeader “Content-Type”, “application/xml” restReq.setRequestHeader “Accept”, “application/xml” restReq.send authenticationXML.xml
GetLogonToken = restReq.responseXML.selectSingleNode(“//entry/content/attrs/attr[@name=’logonToken’]”).text
set restReq = nothing set authenticationXML = nothing
End Function
‘==================================================================== ‘ RESTful LOGOFF session ‘==================================================================== sub RESTfulLogoff(strRESTfulServer, logonToken)
Dim restReq Set restReq = CreateObject(“MSXML2.ServerXMLHTTP”)
url = strRESTfulServer & “/v1/logoff”
restReq.open “POST”, url, false restReq.setRequestHeader “Content-Type”, “application/xml” restReq.setRequestHeader “Accept”, “application/xml” restReq.setRequestHeader “X-SAP-LogonToken”, logonToken
restReq.send set restReq = nothing
End Sub |
Conclusion
As you can see the concept and process is reasonably straight forward, particularly once you have the basics of retrieving templates and manipulating the XML or JSON to set required values. Once you gain familiarity with these concepts it is a really useful and efficient tool for completing repetitive tasks.
Next Steps
It is well worth reviewing your existing maintenance and development processes in conjunction with the SAP BusinessObjects RESTful Web Service SDK User Guide to identify existing tasks that could be replaced. Please get in touch if you would like assistance with this or to explore other services we may be able to offer.
If you would like to discuss any of the areas covered in this blog please contact us
Notes
1 VBScript is probably not an especially good choice of scripting language as it is no longer supported by Microsoft so may present issues for System and Security Administrators. It is however still used in a number of organisations in legacy applications that may have previously used SDK components.
2 Other text manipulation tools could technically be used e.g. Regular Expression based toolsets however in practice this is not an elegant or efficient solution since it can get complicated quickly and difficult to follow once released when maintenance is required.
3 JSON specific equivalents of XPath exist however there does not appear to be a standard for these (JSONPath seems popular)