Introduction

“API Management is not SOA”: that also means that an API Management layer should not perform complex data massage. However you may want to enhance your APIs with contextual information.

For instance, a business partner API response could be enriched with geo-coding data, providing the App Developer the useful data to locate a business partner on a map in the UI. But you could also add the weather for a location request, a stock price for a customer request, etc.. Remember that API Management is also about making your customer happy and productive, in this case the App Developer!

So how does that work within SAP API Management?

In this tutorial I will explain how to get geo-coding information from a public REST API (Google) based on the backend response, and add that information into the API Proxy response.

The prerequisite for this tutorial, is that you are acquainted with SAP API Management, and that you have created the GWSAMPLE_BASIC API Proxy based on Holger Bruchelt’s document How to use SAP API Management on HCP Trial

Note that you can stop at the “Create product” chapter, since it will ease our tutorial (no api keys required for instance).

Last but not least: you may want to use a REST tool to test you API call. Postman is a very good and free tool, that has powerful basic functionalities, and can be extended with the purchase of specific licenses. In this tutorial, I will simply use Chrome, assuming not everyone has Postman installed.

Overview

In this tutorial, we will modify the response of the “BusinessPartnerSet” Resource by adding geocoding information to the address object. We will work with JSON objects since they are easy to work with for the App Developer. Note that you could query the OData service with the header “accept” set to “application/json” to get the response back directly in JSON, but for educational purpose, we’ll do that in our API Proxy.

Here is an overview of the proxy we’ll create:

As mentioned above, I am assuming that you already have your GWSAMPLE_BASIC proxy in place in your APIM Portal. If this is not the case, please refer to the following document (until the chapter “Create product”): How to use SAP API Management on HCP Trial

1- Convert the response to JSON

The first thing to understand, is that we will work with the conditional flow “BusinessPartnerSet”. A conditional flow is executed after the pre-flow, and before the post-flow. This pattern applies both the the request and response. Please check thedocumentation or the excellent blog entry from Elijah Martinez for more detailed information.

Log into your Hana Cloud Cockpit, and navigate to the “API Management” Service.

Navigate to the list of your APIs through the “APIs” Tile.

Click on the “GWSAMPLE_BASIC” API proxy.

Click the “Policies” link In the proxy overview to open the Policy Designer.

Click on the “BusinessPartnerSet” conditional flow on the left-hand menu of the Policy Designer.

Note that this conditional flow will apply only if the condition “Flow condition” is met. But also note, that only the first conditional flow encountered will be executed.

Click on the “Edit” link on the right-bottom corner.

Click on the “+” sign of the “XML to JSON” policy, on the right-handed “Policies” panel (you may need to scroll down to see the policy).

Name: XMLtoJSON

EndpointType and FlowType were set for you when you clicked on the conditional flow in the previous step

Stream: Outgoing Response

The “XML to JSON” policy can be configured in several ways to fit your needs. In our case, we do not need to adapt the output so we will simply remove the content of the “Options” tag.

Use the following configuration for your policy:

  1. <!-- This policy converts an XML payload to JSON structure -->
    <XMLToJSON async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'> 
      <Options>
      </Options>
      <!-- The variable to which the converted JSON should be assigned to -->
      <OutputVariable>response</OutputVariable>
      <!-- The variable that we want to convert to JSON -->
      <Source>response</Source>
    </XMLToJSON>

Click on the “Update” link at the right-bottom corner of the PolicyDesigner.

Click on the “Save” link at the right-bottom corner of the API Proxy overview.

Use a browser or Postman to test your API Proxy:<Your_HANA_Cloud_Platform_URL>/GWSAMPLE_BASIC/BusinessPartnerSet

You should get the following response:

Note that this is the list of all partners, however we will pick only one for our code to work.

Add the ID of the partner to the URL:<Your_HANA_Cloud_Platform_URL>/GWSAMPLE_BASIC/BusinessPartnerSet(‘0100000000’)

NB: because the JSON may not be not formatted properly if you are using Chrome, it may not be easy to read. You may want to use an online tool to beautify your JSON:https://jsonformatter.curiousconcept.com/

  1. {
       "entry":{
          "etag":"W\/\"datetime'2016-06-30T21%3A31%3A20.0000000'\"",
          "base":"https:\/\/SAPES4.SAPDEVCENTER.COM:443\/sap\/opu\/odata\/iwbep\/GWSAMPLE_BASIC\/",
          "id":"https:\/\/SAPES4.SAPDEVCENTER.COM:443\/sap\/opu\/odata\/iwbep\/GWSAMPLE_BASIC\/BusinessPartnerSet('0100000000')",
          "title":{
             "type":"text",
             "":"BusinessPartnerSet('0100000000')"
          },
          "updated":"2016-07-01T12:29:41Z",
          "category":{
             "term":"\/IWBEP\/GWSAMPLE_BASIC.BusinessPartner",
             "scheme":"http:\/\/schemas.microsoft.com\/ado\/2007\/08\/dataservices\/scheme"
          },
          "link":[
             {
                "href":"BusinessPartnerSet('0100000000')",
                "rel":"edit",
                "title":"BusinessPartner"
             },
             {
                "href":"BusinessPartnerSet('0100000000')\/ToSalesOrders",
                "rel":"http:\/\/schemas.microsoft.com\/ado\/2007\/08\/dataservices\/related\/ToSalesOrders",
                "type":"application\/atom+xml;type=feed",
                "title":"ToSalesOrders"
             },
             {
                "href":"BusinessPartnerSet('0100000000')\/ToContacts",
                "rel":"http:\/\/schemas.microsoft.com\/ado\/2007\/08\/dataservices\/related\/ToContacts",
                "type":"application\/atom+xml;type=feed",
                "title":"ToContacts"
             },
             {
                "href":"BusinessPartnerSet('0100000000')\/ToProducts",
                "rel":"http:\/\/schemas.microsoft.com\/ado\/2007\/08\/dataservices\/related\/ToProducts",
                "type":"application\/atom+xml;type=feed",
                "title":"ToProducts"
             }
          ],
          "content":{
             "type":"application\/xml",
             "properties":{
                "Address":{
                   "type":"\/IWBEP\/GWSAMPLE_BASIC.CT_Address",
                   "City":"Walldorf",
                   "PostalCode":"69190",
                   "Street":"Dietmar-Hopp-Allee",
                   "Building":"16",
                   "Country":"DE",
                   "AddressType":"02"
                },
                "BusinessPartnerID":"0100000000",
                "CompanyName":"SAP",
                "WebAddress":"http:\/\/www.sap.com",
                "EmailAddress":"do.not.reply@sap.com",
                "PhoneNumber":"0622734567",
                "FaxNumber":"0622734004",
                "LegalForm":"AG",
                "CurrencyCode":"EUR",
                "BusinessPartnerRole":"01",
                "CreatedAt":"2016-06-30T21:31:20.0000000",
                "ChangedAt":"2016-06-30T21:31:20.0000000"
             }
          }
       }
    }

The API callout we will make in the coming steps requires an address in order to provide the geographical coordinates. In the next steps, we will build the address using the “street“, “city” and “country” information from the JSON response (see the objectentry.content.properties.Address above).

To do that, we will now use the “Extract Variable” policy.

2- Extract the partner address

As explained above, we need to get the address information from the JSON payload. We can extract the required information easily, using en “Extract Variable” policy, that will execute some JSONPath expression against the response payload.

To do so, open the Policy Designer by clicking on “Policies” from within your API.

Click on the “Edit” link on the right-bottom corner.

Click on the “BusinessPartnerSet” conditional flow on the left-hand menu of the Policy Designer.

Click on the “+” sign of the “Extract Variables” policy, on the right-handed “Policies” panel.

Name: GetPartnerAddress

EndpointType and FlowType were set for you when you clicked on the “ProxyEndpoint>BusinessPartnerSet” link in the previous step

Stream: Outgoing Response

Click on “Add” to add the policy to the flow.

Use the following code to configure the Extract Policy variable:

  1. <!-- Extract content from the request or response messages, including headers, URI paths, JSON/XML payloads, form parameters, and query parameters -->
    <ExtractVariables async="true" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
        <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
        <JSONPayload>
            <Variable name="city">
                <JSONPath>$.entry.content.properties.Address.City</JSONPath>
            </Variable>
            <Variable name="street">
                <JSONPath>$.entry.content.properties.Address.Street</JSONPath>
            </Variable>
            <Variable name="country">
                <JSONPath>$.entry.content.properties.Address.Country</JSONPath>
            </Variable>
        </JSONPayload>
        <Source>response</Source>
    </ExtractVariables>

Click on the “Update” link at the right-bottom corner of the PolicyDesigner.

Click on the “Save” link at the right-bottom corner of the API Proxy overview.

Some explanations about our “Extract Variable” policy. It will execute the 3 JSONPath expressions on the backend response (set in the element “Source”), and create/populate so called “flow variables”. These will hold the “city”, “street” and “country” information which in turn can be used from any policy within the resting response flow.

To see if this has worked, we’ll look at the execution trace of our proxy through the “Debug” screen.

On your “GWSAMPLE_BASIC” proxy homescreen, click on “Debug” on the right-top side of the screen.

On the bottom-right side of the screen, start the Debug session by hitting the “Start Debug” link.

Now make a call to your API proxy, using your own URL and an existing partner id:<Your_HANA_Cloud_Platform_URL>/GWSAMPLE_BASIC/BusinessPartnerSet(‘0100000000’)

On the bottom-right side of the screen, refresh your screen by hitting the “Refresh” link.

You should now see the trace:

The second policy towards the API Proxy requester, is our Extract Variable policy. Click on it to display the execution details.

Scrolling down the details page, you will see the result of the execution, containing the variables we are setting:

It all worked fine, and we have now 3 variables containing the address information of the partner we requested.

In the next step, we will use these variables to create the geo-coding API callout.

3- Call the geo-coding API

In order to get the geocoded address (latitude and longitude) of our partner, we will use Google’s geocoding service.

It is available for free to anyone, and can be reached over the following URL:

https://maps.googleapis.com/maps/api/geocode/json?address=<address>

For example: https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View

We will set the partner address as parameter to be sent to google in our “Service Callout” policy.

To do so, open the Policy Designer by clicking on “Policies” from within your API.

Click on the “Edit” link on the right-bottom corner.

Click on the “BusinessPartnerSet” conditional flow on the left-hand menu of the Policy Designer.

Click on the “+” sign of the “Service Callout” policy, on the right-handed “Policies” panel.

Name: GetLatLong

EndpointType and FlowType were set for you when you clicked on the “ProxyEndpoint>PreFlow” link in the previous step

Stream: Outgoing Response

Click on “Add” to add the policy to the flow.

In the policy configuration, use the following configuration to make the callout:

  1. <!-- this policy lets you call to an external service from your API flow -->
    <ServiceCallout async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
      <!-- The request that gets sent from the API proxy flow to the external service -->
      <Request/>
      <!-- the variable into which the response from the external service should be stored -->
      <Response>LatLongResponse</Response>
      <!-- The time in milliseconds that the Service Callout policy will wait for a response from the target before exiting. Default value is 120000 ms -->
      <Timeout>30000</Timeout>
      <HTTPTargetConnection>
      <!-- The URL to the service being called -->
      <URL>https://maps.googleapis.com/maps/api/geocode/json?address={street},{city},{country}</URL>
      </HTTPTargetConnection>
    </ServiceCallout>

As you can see within the configuration, we are making a call against Google’s geocoding API dynamically, setting the “address” parameter to be the flow variables we created and populated in the previous steps.

Note that the content of flow variables is referenced in the configuration of policies, using curly brackets: {myVar}.

The response of the service callout will be placed in a custom flow variable called “LatLongResponse”. This custom flow variable is created and populated at runtime, just like the custom flow variables “street”, “city” and “country”.

The response of the service callout looks as follows:

  1. {
       "results":[
          {
             "address_components":[
                {
                   "long_name":"Dietmar-Hopp-Allee",
                   "short_name":"Dietmar-Hopp-Allee",
                   "types":[
                      "route"
                   ]
                },
                {
                   "long_name":"Walldorf",
                   "short_name":"Walldorf",
                   "types":[
                      "locality",
                      "political"
                   ]
                },
                {
                   "long_name":"Karlsruhe",
                   "short_name":"KA",
                   "types":[
                      "administrative_area_level_2",
                      "political"
                   ]
                },
                {
                   "long_name":"Baden-Württemberg",
                   "short_name":"BW",
                   "types":[
                      "administrative_area_level_1",
                      "political"
                   ]
                },
                {
                   "long_name":"Germany",
                   "short_name":"DE",
                   "types":[
                      "country",
                      "political"
                   ]
                },
                {
                   "long_name":"69190",
                   "short_name":"69190",
                   "types":[
                      "postal_code"
                   ]
                }
             ],
             "formatted_address":"Dietmar-Hopp-Allee, 69190 Walldorf, Germany",
             "geometry":{
                "bounds":{
                   "northeast":{
                      "lat":49.2969047,
                      "lng":8.64724
                   },
                   "southwest":{
                      "lat":49.2911259,
                      "lng":8.6389405
                   }
                },
                "location":{ 
                   "lat":49.2939276,
                   "lng":8.6433734
                },
                "location_type":"GEOMETRIC_CENTER",
                "viewport":{
                   "northeast":{
                      "lat":49.2969047,
                      "lng":8.64724
                   },
                   "southwest":{
                      "lat":49.2911259,
                      "lng":8.6389405
                   }
                }
             },
             "place_id":"ChIJ2RBciqS-l0cRNVMdYeMi8jk",
             "types":[
                "route"
             ]
          }
       ],
       "status":"OK"
    }

From this response, we only want to keep the “results.geometry.location.lat” and “results.geometry.location.lng” to be added to our response. Therefore, you may have guessed it by now, we will use an “Extract Variable” policy.

4- Read the results of the geocoding service callout

Now that we have the results of the Google geocoding callout available in a “LatLongResponse” flow variable (see previous step), we will extract the “latitude” and “longitude” to be inserted to the response of our API proxy.

To do so, open the Policy Designer by clicking on “Policies” from within your API.

Click on the “Edit” link on the right-bottom corner.

Click on the “BusinessPartnerSet” conditional flow on the left-hand menu of the Policy Designer.

Click on the “+” sign of the “Extract Variables” policy, on the right-handed “Policies” panel.

Name: AssignLatLong

EndpointType and FlowType were set for you when you clicked on the “ProxyEndpoint>PreFlow” link in the previous step

Stream: Outgoing Response

Click on “Add” to add the policy to the flow.

Use the following code to configure the Extract Policy variable:

  1. <!-- Extract content from the request or response messages, including headers, URI paths, JSON/XML payloads, form parameters, and query parameters -->
    <ExtractVariables async="true" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
        <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
        <JSONPayload>
            <Variable name="lat">
                <JSONPath>$.results[0].geometry.location.lat</JSONPath>
            </Variable>
            <Variable name="long">
                <JSONPath>$.results[0].geometry.location.lng</JSONPath>
            </Variable>
        </JSONPayload>
        <Source>LatLongResponse</Source>
    </ExtractVariables>

As you can see from the configuration above, from the root of the JSON, we are reading the first object of the array “results”, and point to the “geometry.location.lat” and “lng” attributes. The results of these JSONPath expressions are used to create and populate 2 more flow variables, “lat” and “long“.

Note that the source is “LatLongResponse“, ie. the flow variable we created previously to store our service callout response.

To check if this worked properly, and possibly debug any issue, you can use the Debugger as mentioned previously.

You will now have a fourth policy in your response pipe:

When you click on it, and scroll down to see the details of the execution, you should see the following:

Note the 2 last fields, “lat” and “long”, which represent the flow variables “lat” and “long” that we will now add to the API proxy response.

5- Add the geocoding information to the backend response

The last step of our tutorial, is to mash-up the Google geocoding response with the backend response.

What we would like to end up with, is 2 more attributes in the backend response, within the “address”.

For now we have the following:

  1.         "properties":{
                "Address":{
                   "type":"\/IWBEP\/GWSAMPLE_BASIC.CT_Address",
                   "City":"Walldorf",
                   "PostalCode":"69190",
                   "Street":"Dietmar-Hopp-Allee",
                   "Building":"16",
                   "Country":"DE",
                   "AddressType":"02"
                },
                "BusinessPartnerID":"0100000000",
                "CompanyName":"SAP",

And we would like to have the following:

  1.          "properties":{
                "Address":{
                   "type":"\/IWBEP\/GWSAMPLE_BASIC.CT_Address",
                   "City":"Walldorf",
                   "PostalCode":"69190",
                   "Street":"Dietmar-Hopp-Allee",
                   "Building":"16",
                   "Country":"DE",
                   "AddressType":"02",
                   "Latitude": xxxx,
                   "Longitude": yyyy
                },
                "BusinessPartnerID":"0100000000",
                "CompanyName":"SAP",

To achieve this, we will use a “JavaScript Policy“, that lets us code some specific behaviors that is not available in a pre-configured policy. In our case, because we are using the JSON syntax, the javascript code will be very simple.

The actual javascript code is attached to the API proxy as a “Script”.

Therefore, before you add the JavaScript Policy, you will add the corresponding script.

To do so, open the Policy Designer by clicking on “Policies” from within your API.

Click on the “Edit” link on the right-bottom corner.

Click on the “+” sign of the “Scripts” section, on the left-bottom side of the Policy Designer.

Name: addValues

Type: JavaScript

Click on “Add”.

Now click on the “addValues” script in the “Scripts” section. The “Script Resource” is being displayed, and you can add the following code that will enhance your backend response with the geocoding information:

  1. var payload = JSON.parse(context.getVariable("response.content"));
    payload.entry.content.properties.Address.latitude = context.getVariable("lat");
    payload.entry.content.properties.Address.longitude = context.getVariable("long");
    context.setVariable("response.content",JSON.stringify(payload));

From the script above, you can see that we are:

– creating a js variable containing the backend response (payload) as JS object

– setting the JSON payload entry “latitude” to the previously generated flow variable “lat”

– setting the JSON payload entry “longitude” to the previously generated flow variable “long”

– setting the response of the API proxy to the payload variable that we just enhanced

Important: please note that we are not working with strings, but with javascript structures, which allows us to simply set and get entries. This is the reason we first parse the response, and in the end stringify it.

Now that the script resource is available, we can reference it through a policy within the response pipeline.

Click on the “BusinessPartnerSet” conditional flow on the left-hand menu of the Policy Designer.

Click on the “+” sign of the “JavaScript” policy, on the right-handed “Policies” panel.

Name: AddValues

EndpointType and FlowType were set for you when you clicked on the “ProxyEndpoint>PreFlow” link in the previous step

Stream: Outgoing Response

Click on “Add” to add the policy to the flow.

Use the following code to configure the Extract Policy variable:

  1. <!-- this policy allows us to execute java script code during execution of an API Proxy -->
    <Javascript async="false" continueOnError="false" enabled="false" timeLimit="200" xmlns='http://www.sap.com/apimgmt'>
      <!-- contains the name of the main code file -->
      <ResourceURL>jsc://addValues.js</ResourceURL>
    </Javascript>

As you can see, this policy only references the script we have created previously, and will execute it at runtime.

Your proxy should now look like this:

If you test your proxy now, you will see the JSON response containing the latitude and longitude information we requested from Google at runtime.

And here in a more readable format (thanks tohttps://jsonformatter.curiousconcept.com/):

  1. {
       "entry":{
          "etag":"W/\"datetime'2016-07-05T21%3A31%3A29.0000000'\"",
          "base":"https://SAPES4.SAPDEVCENTER.COM:443/sap/opu/odata/iwbep/GWSAMPLE_BASIC/",
          "id":"https://SAPES4.SAPDEVCENTER.COM:443/sap/opu/odata/iwbep/GWSAMPLE_BASIC/BusinessPartnerSet('0100000000')",
          "title":{
             "type":"text",
             "":"BusinessPartnerSet('0100000000')"
          },
          "updated":"2016-07-06T11:46:37Z",
          "category":{
             "term":"/IWBEP/GWSAMPLE_BASIC.BusinessPartner",
             "scheme":"http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"
          },
          "link":[
             {
                "href":"BusinessPartnerSet('0100000000')",
                "rel":"edit",
                "title":"BusinessPartner"
             },
             {
                "href":"BusinessPartnerSet('0100000000')/ToSalesOrders",
                "rel":"http://schemas.microsoft.com/ado/2007/08/dataservices/related/ToSalesOrders",
                "type":"application/atom+xml;type=feed",
                "title":"ToSalesOrders"
             },
             {
                "href":"BusinessPartnerSet('0100000000')/ToContacts",
                "rel":"http://schemas.microsoft.com/ado/2007/08/dataservices/related/ToContacts",
                "type":"application/atom+xml;type=feed",
                "title":"ToContacts"
             },
             {
                "href":"BusinessPartnerSet('0100000000')/ToProducts",
                "rel":"http://schemas.microsoft.com/ado/2007/08/dataservices/related/ToProducts",
                "type":"application/atom+xml;type=feed",
                "title":"ToProducts"
             }
          ],
          "content":{
             "type":"application/xml",
             "properties":{
                "Address":{
                   "type":"/IWBEP/GWSAMPLE_BASIC.CT_Address",
                   "City":"Walldorf",
                   "PostalCode":"69190",
                   "Street":"Dietmar-Hopp-Allee",
                   "Building":"16",
                   "Country":"DE",
                   "AddressType":"02",
                   "latitude":49.2939276,
                   "longitude":8.6433734
                },
                "BusinessPartnerID":"0100000000",
                "CompanyName":"SAP",
                "WebAddress":"http://www.sap.com",
                "EmailAddress":"do.not.reply@sap.com",
                "PhoneNumber":"0622734567",
                "FaxNumber":"0622734004",
                "LegalForm":"AG",
                "CurrencyCode":"EUR",
                "BusinessPartnerRole":"01",
                "CreatedAt":"2016-07-05T21:31:29.0000000",
                "ChangedAt":"2016-07-05T21:31:29.0000000"
             }
          }
       }
    }

Final words

I hope you have enjoyed this tutorial, and that you could get some insights on how powerful SAP API Management can be, once the basic concepts and policies are understood. There is virtually nothing that you can not do in a proxy! In regards to the JavaScript policy, please keep in mind that it’s a best practice to always use the pre-configured policies, before writing your own…

Any comments or questions are welcome, and I can provide the final API proxy per mail if needed.

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Stan Stadelman

    Thanks @Sven, super helpful to see examples of how to use these policies.  Useful for us working on the SAP Cloud Platform SDK for iOS project.

    –Stan

    (1) 

Leave a Reply