BOL URL API (or RESTless BOL API)

In this article, I investigate the possibility of developing a URL-based API for the Business Object Layer (BOL) that could be used to develop an alternative to the standard CRM Web UI.

For my current client, I am implementing a CRM Web UI application with a difference. Due to the project requirements, which include many Web 2.0 features and the fact that it is largely a front-end for a bespoke third-party application, I embarked on writing a Javascript/HTML/CSS application hosted in a BSP WD component which makes XHR requests for which the server returns JSON responses.

Having gone through this exercise, I started wondering what it would take to implement a complete alternative to the standard CRM Web UI that utilizes the BOL, but replaces the Web UI framework with a completely different web UI framework, not utilizing any of the provided BSP tag libraries, like CHTMLB or THTMLB, on which the CRM Web UI framework is built.

This in turn led me to wonder how easy it would be to implement a RESTful API over the Business Object Layer (BOL). The short answer of course, is that this is not really possible. To familiarize yourself with REST, take a look at this Wikipedia article: http://en.wikipedia.org/wiki/Representational_state_transfer. Basically we are going to manipulate BOL objects (resources) using a set of HTTP requests. The server differentiates between GET, PUT, POST and DELETE requests as verbs (actions) to perform on resources (objects), where the URL points to a specific resource, such as a business partner or one-order object.

The BOL itself does not lend itself to RESTful web service URLs, due to the fact that objects cannot be accessed via standardized unique ids, but have to be accessed through search objects. Below, we will consider whether we can implement a RESTful web service over a specific BOL object, e.g. Business Partner (BP), but as we will see, we will run into certain limitations that make a RESTful interface to the BOL not a viable option. In this exercise, my quest was to find a generic and dynamic way to expose the BOL through a URL API that could be used for any BOL objects from a web page making XHR requests.

Such an API could form the basis of an entirely standalone Web UI framework where the exchange of data between the web page and the SAP server does not require constant page refreshes, and could result in a significantly better performing, more easily customizable and stylable web application. As the title of this post suggests, the resulting URL API does not of course conform to the REST architecture, by virtue of the fact that it is very much session-bound and therefore not stateless.

Basis for a BOL URL API

The best way to explain what I have done so far is probably to launch into an example. (To start with, I already have a library of functions to transform ABAP data objects into a valid JSON string, which can be returned to the browser, based on the work I have done for the client application. In addition, you should note that I have created a new service in the ICF. Making use of a BSP controller is not possible in this context). So, let’s say that you have a page to allow the user to search for business partners. When the user perform the search, the XHR function accesses the following URL with a GET request:

brd/bp/BuilHeaderSearch?PARTNER=100000001

Let’s break that down:

  1. This is the service that handles the BOL API requests. (BRD stands for BOL Request Dispatcher. Whether that is a good name, I am not yet sure).
  2. The second portion identifies the BOL component we are working with. (This is the same as the component you would specify in transaction GENIL_BOL_BROWSER; it is translated to upper case by the service and then loaded into the BOL core.
  3. The next portion specifies the BOL object, which in this case is a search object. The service, by querying the BOL model, knows what type of object (root, search, dependent etc) is being requested.
  4. The last portion is a set of parameters which will be used to populate the BOL object structure. In this case, any combination of fields from the BuilHeaderSearch object could be specified, and in this case, could come from a form on the web page.

The result of the above GET request will be a JSON response containing an array of result objects. So passing for example CREATION_USER=MARTINC in the parameter list would return all the business partners that I created. So lets say we passed parameters PARTNER=100001153. We could get a response as follows:

[
    [
        [
            "BuilHeader",
            "FF060201010280003431303300000000010400000...(shortened for brevity)"
        ],
        {
            "FIRSTNAME": "Mariam",
            "LASTNAME": "TITUS",
            "INITIALS": "M",
            "SEX": "1",
            "FULLNAME": "Mrs Mariam M TITUS",
            "TITLE_KEY": "Z012",
            "BP_GUID": "E103C6FC69073CF19F4B005056AF0039",
            "BP_NUMBER": "0100001153",
            "BP_CATEGORY": "1",
            "BP_GROUP": "Z1",
            "BP_DESCRIPTION": "Mariam M TITUS",
            "CREATIONDATE": "20111031",
            "CREATIONTIME": "154818",
            "CREATIONUSER": "MARTINC",
            "PERS_NO": "0000026105",
            "ACCOUNT_DESCRIPTION": "Mariam M TITUS",
            "ACCOUNT_NAME": "Mariam M TITUS"
        }
    ]
]

The first portion of the response is a key consisting of the BOL object name and ID (which is used by the BOL object manager to track objects in the current session). Because this design is not RESTful and relies on application statefulness, we expect that the client browser must store this key for future requests to GET or PUT the resource, so that the service can use this key to pick up the object in question from the BOL object manager. To access the same object within our session, we could then use the following URL with a GET request, which contains the object name and ID we got above:

GET /brd/bp/BuilHeader/FF060201010280003431303300000000010400000...(shortened for brevity)

To update the partner, we would issue a PUT request to this URL and append a set of parameters with the fields we want to update and the values we want to set them to. Similarly, issuing a DELETE request should allow us to delete the business partner.

To create a business partner, we could issue a POST request to the following URL:

/brd/bp/BuilHeader?BP_CATEGORY=1&BP_GROUP=Z001...

Here we would be passing a set of parameters for the BuilHeader object to create a new BP factory. Based on the fact that we issue a POST, the service will know to use a factory object to create an instance of the BOL object in question.

So far so good. Up to now, we are able to manipulate BOL root objects in a very generic way, because all the information about the BOL objects and the component to use are contained in the URL. Things get more involved when we want to start maintaining dependent objects, but we will look at that in a moment.

Trying to make it RESTful

Before looking at dependent objects, let’s look for a minute at how we might approach the above actions with a RESTful service.  If we wanted to go the RESTful route, we could start by assuming that we can use a pretty URL such as this one:

In this case:

  1. bp is now our resource collection representing business partners
  2. the next portion is the unique identifier, in this case the partner number

This would of course work fine, but it means that you would have to nominate a particular search object and field to represent the unique ID for looking up the object, which could be BuilHeaderSearch and PARTNER in this case, and you would have to specify somewhere that this is applicable to a named resource collection “bp”. If, however, you wanted to access one-order objects, you would have to again nominate a different search object and field and map a different names resource collection, so you could for example have a URL like /brd/1o/100018272. This could be fairly easily implemented by creating a mapping table in the database where we specify a named resource collection, and associate it with the given details.

In terms of the server implementation, the main difference between the RESTful approach, as oppsed to the RESTless approach described above, would be that instead of relying on the client for keeping a reference to the key (name and ID) of the object in the session, we always load the object via a BOL search with each request. This adds a lot of overhead to the application, but it would make the design much cleaner.

Again, this gets us to the point of manipulating BOL root objects, but not dependent objects. So now, let us take a look at how we would expect to be able to access dependent objects using such an API.

Running into problems

If, for example, we wanted to get all the addresses for a specific BP using the BOL, we must make use of the BuilAddressRel relationship. Thus, if we were to extend our RESTful URL above (because it is significantly shorter), we could expect to use the following URL:

/brd/bp/100001153/BuilAddressRel

In this instance the last portion which specifies the relationship name would be pointing to a collection of addresses belonging to the business partner identified in the URL. While that is still OK, it becomes more tricky if you now want to address a specific address in this collection. It would be reasonable to assume that appending the address number to the URL as the next portion should return the address. Again, we would need to provide some sort of mapping to say that for the BuilAddressRel relationship object, the field ADDRESS_NUMBER must represent the unique ID. If we wanted to go further down and get all phone numbers for the address using the BuilAddressPhoneRel relationship, we could expect that the following URL would lead us to that collection:

Here we run into a problem: The phone number is not really identifiable by any key exposed by the BOL, only by an internal GUID. In this case, the only reliable way of manipulating such a resource is to have a handle on its name and ID as identified by the BOL during the session, which means we must first get all the resources in the collection, then identify the one we need to work with. This seems to be a design approach in the BOL; it is geared toward user intervention from a GUI, although arguably, in the case of the phone numbers, it is the result of the underlying design of the relational database tables. Whatever the reason, there is no clean way to access such resources in a RESTful way, and as a result, if we need to first access a collection for any object to be able to choose one to manipulate, we may end up choosing the RESTless design described above to give us an API with which we can build screens for a user, as the user will most likely manipulate the resources in by making specific selections in the UI.

So, taking everything into account, it might make more sense to implement a stateful API where we address objects based on the session-based ID. Given that, let’s go through an example scenario to explain how we would expect to interact with such an API.

A sample session with our RESTless API

As we have already described how we could access and manipulate root objects, let’s imagine that we carry on from there. (For brevity, I am using a short, imaginary ID, though the actual ID for the BOL object in the session is 154 characters).

First, let’s find all business partner’s created by myself:

GET /brd/bp/BuilHeaderSearch?CREATION_USER=MARTINC

We take the results from this request (which look like the JSON earlier in the article) and populate a table on the web page. Now the user selects a record and calls up all the addresses for that BP:

GET /brd/bp/BuilHeader/12345/BuilAddressRel

We use the response to populate another table on the page. The user chooses one of the addresses and chooses to view the telephone numbers.

GET /brd/bp/BuilAddress/43123/BuilAddressPhoneRel

Again, we populate a table with the results. Now the user updates one of the telephone numbers:

PUT /brd/bp/BuilAddressPhone/81716?TELEPHONE=0817262512

Additionally, the user adds a telephone number:

POST /brd/bp/BuilAddress/43123/BuilAddressPhoneRel?TELEPHONE=0128726251...

The user may choose to delete the entire address:

DELETE /brd/bp/BuilAddress/43123

The only thing missing now is a way to update the BOL and save and commit once we are done. To do this, one would have to come up with a meaningful convention for our URLs.

Conclusion

In this article, we looked at how it may be possible to implement a URL API for the Business Object Layer and took an (incomplete) look what such an API might look like. Based on what we have seen, a RESTful API is not viable, as we would have to make use of a stateful web application. However, having a URL API would certainly prove useful for building alternative web applications that take advantage of the dynamic nature of the BOL. In fact, I think it would be possible to leverage the power of a Javascript framework such as Dojo (of which I am a great fan) to build enterprise-class user interfaces and leverage its XHR functions to perform the calls on the BOL API.

 

Tags: ,

  • Scott

    Have you seen CL_CRM_BOL_CORE methods GET_ROOT_ENTITY and GET_ACCESS_ENTITIES? They should help avoid use of search objects.

    There is also BPATH, which I’ve not had cause to use and am now rusty, but I believe it can be used to query an access object’s properties, either directly or for relationships (dependent or otherwise).

    SB