Consuming an External RESTful Web Service with ABAP in Gateway
But when you look under the covers at the system functionality that ABAP exposes in SAP systems, it becomes apparent that the frameworks, libraries, and system tools that you have at your command can be crafted into an engine to power a phenomenal array of projects. Just this morning I put together something that – while admittedly not incredibly useful all by itself – shows some of what you can accomplish if you have some familiarity with the tools SAP gives you in ABAP and Gateway.
Let me show you. It’s not a ridiculously complex build, and it’s just a slice of what you could do.
At a high level what I’ve done is find an external RESTful web service, write some ABAP to consume it from the SAP side, and expose that data back out through Gateway. It’s a little bit contrived, since you could easily call this service without Gateway mediating the connection…but I think there are occasional valid reasons to mediate the service through Gateway. You might have an account with the external service and need to manage your calls to it, or you might want to join the service data with other things from Business Suite services and make it all available in one entity type. Or you’re like me, and you just want to see if it could be done. 🙂
I created a developer account with world weather online, so that I could use its API for free. This lets you use a simple call to get a set of information on weather for a particular location, and in my case I use it to get the day’s forecast with a given zip code. If you sign up, you can use their neat API explorer to test out different ways to use the service.
If I call the main weather service with my home zip code, I get the following structure back (some unnecessary stuff has been trimmed):
<?xml version=“1.0” encoding=“UTF-8”?>
Knowing the structure of what comes back to me, I can build some simple ABAP to do the same thing. I set up a dictionary z-structure to hold the bits of data that I want to use:
I then set up a function module to do pull data from the service and put it into that structure:
*” VALUE(IM_ZIPCODE) TYPE AD_PSTCD1
*” ET_WEATHER STRUCTURE ZWEATHER
DATA: lo_http_client TYPE REF TO if_http_client,
lv_service TYPE string,
lv_result TYPE string,
lo_ixml TYPE REF TO if_ixml,
lo_streamfactory TYPE REF TO if_ixml_stream_factory,
lo_istream TYPE REF TO if_ixml_istream,
lo_document TYPE REF TO if_ixml_document,
lo_parser TYPE REF TO if_ixml_parser,
lo_weather_element TYPE REF TO if_ixml_element,
lo_weather_nodes TYPE REF TO if_ixml_node_list,
lo_curr_node TYPE REF TO if_ixml_node,
lv_value TYPE string,
lv_node_length TYPE i,
lv_node_index TYPE i,
ls_weather TYPE zweather,
lv_node_name TYPE string,
lv_node_value TYPE string.
lv_service = ‘http://api.worldweatheronline.com/free/v1/weather.ashx‘.
lv_service = lv_service && ‘?q=’ && im_zipcode && ‘&format=xml’.
lv_service = lv_service && ‘&key=[use your own!]’.
url = lv_service
client = lo_http_client
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
OTHERS = 4 ).
http_communication_failure = 1
http_invalid_state = 2 ).
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3 ).
“Prepare XML structure
CLEAR lv_result .
lv_result = lo_http_client->response->get_cdata( ).
lo_ixml = cl_ixml=>create( ).
lo_streamfactory = lo_ixml->create_stream_factory( ).
lo_istream = lo_streamfactory->create_istream_string(
lo_document = lo_ixml->create_document( ).
lo_parser = lo_ixml->create_parser(
stream_factory = lo_streamfactory
istream = lo_istream
document = lo_document ).
“This actually makes the XML document navigable
“Navigate XML to nodes we want to process
lo_weather_element = lo_document->find_from_name_ns( ‘weather’ ).
lo_weather_nodes = lo_weather_element->get_children( ).
“Move through the nodes and assign appropriate values to export
lv_node_length = lo_weather_nodes->get_length( ).
lv_node_index = 0.
WHILE lv_node_index < lv_node_length.
lo_curr_node = lo_weather_nodes->get_item( lv_node_index ).
lv_node_name = lo_curr_node->get_name( ).
lv_node_value = lo_curr_node->get_value( ).
REPLACE ALL OCCURRENCES OF ‘-‘ IN lv_node_value WITH ”.
ls_weather–forecast_date = lv_node_value.
ls_weather–high_temp_f = lv_node_value.
ls_weather–low_temp_f = lv_node_value.
ls_weather–wind_speed = lv_node_value.
ls_weather–wind_direction = lv_node_value.
ls_weather–description = lv_node_value.
ls_weather–precipitation = lv_node_value.
ADD 1 TO lv_node_index.
APPEND ls_weather TO et_weather.
I sprinkled some comments in the code to help, but I use the cl_http_client class to do the call to the service (and it can be set up and done in just 3 method calls), and then use a few of the xml library classes to parse the result and put it into the structure. You can see here that though I’ve only made the zip code dynamic in the call, you could actually be pretty dynamic in choosing services to leverage.
Why did I use a function module? It’s actually pretty easy to use a function module as a basis for building services in SEGW on your Gateway system, so it can be convenient to wrap custom functionality into a function module and then just import definitions. That’s what I did to set up the entity and entity set for this simple service:
Note especially that the GetEntity method is mapped with zip code as the incoming parameter:
After this, when I activated the service, it’s as simple as calling the URI with a parameter of some zip code that I want to see :
Like I mentioned before, by itself this isn’t much use. But combine this with some other information and you start to see what you can really pull together and expose through Gateway. It’s pretty awesome to think that anything you could pull from a REST service on the web can also be a resource for your application.