This  blog is about Integration Gateway in SAP Mobile Platform 3.0 (SMP).

In the previous tutorials, we’ve learned how to deal with REST services as data source for an OData service in SMP.

These tutorials were based on reading data, which is done via QUERY and READ operations.

Since SMP SP07, modifying operations are supported as well.

In this blog, we’ll have a look at the CREATE operation:

We’ll do the implementation in the script and in order to create data, we’ll execute a POST request from a browser-based REST client tool.

If you’re new to the topic, check the Links section below where you can find the tutorials which get you up to speed.

Update:
The sample REST service is now made available, so I’ve updated the tutorial to use it.
Also, the code is attached to this blog.

Prerequisites

I expect that you’ve gone through my previous tutorials, explaining REST data source – QUERY and READ operation – based on XML payload.

Please check the Links section for the relevant blogs.

The prerequisites are:

  • Eclipse with SAP Mobile Platform Tools installed
  • SMP SP07
  • Basic knowledge about OData provisioning using the Integration Gateway component of SMP

Preparation

REST Service

For this tutorial, we need a REST service that supports writing scenario.

I’m using a service that is public available, you only need to sign up, afterwards you can access it with your SCN  user and password.

Please see the following document for details:

Getting started with the SAP Netweaver Gateway Service Consumption System

Finally, you should be able to access it via the following URL:

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

Destination

In your SMP, you need to create an HTTP destination to the following URL:

https://sapes1.sapdevcenter.com

Note:

Furthermore, since the URL is HTTPS, you need to download the certificate

and import it into your SMP keyStore.

Note:

For this Destination, it isn’t possible to do a “Test Connection”, as the server doesn’t send a valid response for the configured URL

As a workaround, you can proceed as follows:

Create a second destination, which is only used to test if the target host can be reached.

This second destination points to a URL that actually can send a valid response.

For example, enter the following URL as destination URL:

https://sapes1.sapdevcenter.com/sap/opu/rest/address/companies

As such, you use this second destination to do the “Test Connection”.

If this succeeds, then the first destination should be fine as well.

This first destination will be used to configure our OData service, that we create in this tutorial.

If you get an error message on connection test, you might consider the following:

Note:

You might need to enter proxy settings in your SMP:

https://localhost:8083/Admin/ -> Settings-> System

Note that you might need to restart the SMP server after changing the proxy settings.

OData Model

Define OData model

The sample REST service that I’m using provides the following sample data:

So for my OData model, I create the following entity type:

Bind data source

For better testing, I create binding and scripts for QUERY and READ operation (please refer to my previous tutorials for description).

The following relative URIs have to be used:

QUERY companies:      /sap/opu/rest/address/companies

READ company:           /sap/opu/rest/address/companies/{ID}

Then I deploy to SMP, create and assign destination and run the service.

Fine.

Now let’s do the binding for the CREATE operation.

How are we supposed to build this relative URL?

As in the previous tutorials, we wonder how the Request URL should look like.

The answer is again: this depends on the backend REST service.

Before implementing the OData service, I’ve checked how a POST request which is fired directly to the backend REST service should look like.

For my sample REST service, it looks as follows:

Request URL

the same URL as for the QUERY operation, the URL that lists the entry resources:

https://<host>:<port>/sap/rest/odata/address/companies



RequestBody

the same that is returned by a READ operation:


<asx:abap version=”1.0″>

  <asx:values>

    <COMPANY>

      <ID>40</ID>

      <NAME>Indian IT Trading Company</NAME>

      <STREET>Nariman Point</STREET>

      <POSTAL_CODE>400021</POSTAL_CODE>

      <CITY>Mumbai</CITY>

      <COUNTRY>IN</COUNTRY>

      <WEB_ADDRESS>http://www.it-trade.in</WEB_ADDRESS>

      <PHONE_NUMBER>4203876954</PHONE_NUMBER>

    </COMPANY>

  </asx:values>

</asx:abap>



Note: Request body

For my sample backend REST service, I don’t have to care about unique ID, as it is computed in the backend

What we learn from this exercise:

1)    The relative URL that we have to enter in the wizard is the same as the one that we enter for the QUERY operation.

       Note: this might be different for other REST services.

2)    The request body has to look like that

So now we can proceed in Eclipse, we do the “Select Data Source” -> REST -> CREATE -> Request URL:

Custom Code

Then we generate the Custom Code for Groovy.

For the CREATE operation, we have to implement both methods,

processRequestData()

and

processResponseData()

Why?

In the processRequestData() method, we have to adapt the POST request, before it is sent to the backend REST service

In the processResponseData() method, we have to adapt the response, because the OData specification requires that the POST operation should return the newly created entry in the response payload.

Implement processRequestData() method

After you’ve understood the QUERY and READ tutorials, you’ll guess what needs to be done in the processRequestData() method for the CREATE operation:

We get the OData request payload and we have to modify it such that it becomes a payload that is understood by the backend REST service.

What we get from the Integration Gateway Framework is not the full OData payload, it is only the data in the usual special structure.

What we have to return, is the full payload, as it is required by the backend REST service.

So everything sounds known to us: it is the same like the READ operation, just the other way round.

What we have to do:

1) The first thing we have to take care, is to set the Content-Type header.

It should be done before other tasks are done.

Why?

Because with this header we specify in which format we want to get the data from Integration Gateway.

If we write:

message.setHeader(“Content-Type”, “application/atom+xml”);

Then we get the OData request payload as a String that contains an XML structure.

If we don’t set this header, we get a JSON structure.

2) Second, modify the request body

When our final OData service is used, a POST request will be executed and it will have the following request Body:

<entry xmlns=”http://www.w3.org/2005/Atom

          xmlns:m=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata

          xmlns:d=”http://schemas.microsoft.com/ado/2007/08/dataservices

          xml:base=”https://localhost:8083/gateway/odata/<myNamespace>/<myService>/”>

  <content type=”application/xml”>

    <m:properties>

      <d:ID>40</d:ID>

      <d:NAME>Indian IT Trading Company</d:NAME>

      <d:STREET>Nariman Point</d:STREET>

      <d:POSTAL_CODE>400021</d:POSTAL_CODE>

      <d:CITY>Mumbai</d:CITY>

      <d:COUNTRY>IN</d:COUNTRY>

      <d:WEB_ADDRESS>http://www.it-trade.in</d:WEB_ADDRESS>

      <d:PHONE_NUMBER>4203876954</d:PHONE_NUMBER>

    </m:properties>

  </content>

</entry>

Note:

As you know, we obtained this body by copy&pasting it from the response body of a READ request.

In our script, we get the relevant data structure as XML, which looks as follows

<Companies>

  <Company>

    <NAME>Indian IT Trading Company</NAME>

    <COUNTRY>IN</COUNTRY>

    <WEB_ADDRESS>http://www.it-trade.in</WEB_ADDRESS>

    <ID>40</ID>

    <CITY>Mumbai</CITY>

    <POSTAL_CODE>400021</POSTAL_CODE>

    <STREET>Nariman Point</STREET>

    <PHONE_NUMBER>4203876954</PHONE_NUMBER>

  </Company>

</Companies>

What we have to do is to transform it to the following XML-string, which is required by our backend REST service:

<asx:abap version=”1.0″>

  <asx:values>

    <COMPANY>

      <NAME>Indian IT Trading Company</NAME>

      <COUNTRY>IN</COUNTRY>

      <WEB_ADDRESS>http://www.it-trade.in</WEB_ADDRESS>

      <ID>40</ID>

      <CITY>Mumbai</CITY>

      <POSTAL_CODE>400021</POSTAL_CODE>

      <STREET>Nariman Point</STREET>

      <PHONE_NUMBER>4203876954</PHONE_NUMBER>

    </COMPANY>

  </asx:values>

</asx:abap>

And this is how my implementation looks like:

def Message processRequestData(message) {

       message.setHeader(“Content-Type”, “application/atom+xml”);

       message.setHeader(“x-requested-with”, “XMLHTTPRequest”);

            // convert OData request body to a string that the backend REST service understands

       String odataRequestBody = message.getBody(String.class);

       odataRequestBody = odataRequestBody.replaceAll(“<Companies>”, “<asx:values>”);

       odataRequestBody = odataRequestBody.replaceAll(“</Companies>”, “</asx:values>”);

       odataRequestBody = odataRequestBody.replaceAll(“<Company>”, “<COMPANY>”);

       odataRequestBody = odataRequestBody.replaceAll(“</Company>”, “</COMPANY>”);

       odataRequestBody = “<asx:abap version=\”1.0\”>” + odataRequestBody +“</asx:abap>”;

       message.setBody(odataRequestBody);

       return message;

}

Note:

If your backend requires additional handling, then here’s the right place to do it.

For example, my backend requires the x-requested-with header to be set.

Note:

I’m doing it with string operations, because I assume that it is easier to see what is happening there.

If you deploy your service after implementing the processRequestData() method, then your service will fail – however, the entry will be created in the backend, because the request is being sent and it is properly implemented.

But we have to implement the processResponseData() method as well, because we have to return the newly created entity.

In this tutorial we assume that the backend REST service returns the newly created entity.

If this is the case, we have to implement the processResponseData() method just the same as we did for the READ operation.

Since you can really just copy&paste the code from the READ script, I’m skipping the explanation here.

def Message processResponseData(message) {

       String bodyString = (String) message.getBody();

       /* CONVERT JUST AS YOU DID IN READ operation */

       convertPayload(bodyString, message);

       return message;

}

After you’ve properly implemented the processResponseData() method, you can proceed with generate&deploy, then configure the service on SMP and invoke the service.

Result

In order to test the CREATE operation, we need to use a REST client tool.

Note:

Such REST clients are available e.g. as add-ons for Firefox (e.g. “RESTClient”) or Chrome (e.g. “Advanced Rest Client”) browsers

Please find below a screenshot of my POST request to the OData service created in the current tutorial.

Please find below the details of this request:

Header 1 Header 2
Request URL https://<yourSMP>:8083/gateway/odata/<yourNameSpace>/<yourService>;v=1/<yourEntitySet>
HTTP verb POST
x-csrf-token header as provided by your SMP (see note below)
Content-Type header
application/atom+xml
Request body

<entry xmlns=”http://www.w3.org/2005/Atom

          xmlns:m=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata

          xmlns:d=”http://schemas.microsoft.com/ado/2007/08/dataservices

          xml:base=”https://localhost:8083/gateway/odata/SAP/RESTPROJECT10_CREATE;v=1/“>

  <content type=”application/xml”>

    <m:properties>

      <d:ID>40</d:ID>

      <d:NAME>Indian IT Trading Company</d:NAME>

      <d:STREET>Nariman Point</d:STREET>

      <d:POSTAL_CODE>400021</d:POSTAL_CODE>

      <d:CITY>Mumbai</d:CITY>

      <d:COUNTRY>IN</d:COUNTRY>

      <d:WEB_ADDRESS>http://www.it-trade.in</d:WEB_ADDRESS>

      <d:PHONE_NUMBER>4203876954</d:PHONE_NUMBER>

    </m:properties>

  </content>

</entry>

The screenshot also shows the response that we get after sending the request.

The HTTP status code should be 201

The response body should display the created entity

The backend REST service should as well display the newly created resource.

If you’ve got this response: congratulations, you’re through! 🙂

If not, I wish you quite some patience with trouble-shooting…

Note:

Regarding the x-csrf-token header:

I assume that you know how to obtain this token?

You have to first send a GET request to a valid URL, e.g. the EntitySet URL

This GET request should be executed with the header

x-csrf-token: fetch

(Name of the header is “x-csrf-token” and the value of the header is “fetch”)

After executing this request in your REST client, you should check the response headers.

You’ll find the header x-csrf-token with a value which is the token

Now copy the value and paste it into the request header, such that you have

x-csrf-token: <value of the token from the response>

Note:

Regarding fetching the header:

If you proceed as described above and don’t get the token then the reason is that it is always sent only once.

In order to force SMP to send it again, just logout from Gateway Management Cockpit and login again

Summary

In this tutorial, we’ve learned how to implement the CREATE operation in a Groovy script.

We’ve seen that there are no surprises:

  • The processResponseData() method is the same as in READ
  • The processRequestData() method is again the same, just the other way round
  • “Content-Type” header is required in the script.
  • The Relative URLin the Eclipse-Binding-Wizard is the same as for the QUERY

Appendix

If you use the attached scripts, you have to add the following Required Bundles section to your manifest.mf file:

Require-Bundle: org.apache.httpcomponents.httpclient;bundle-version=”4

.1.3″,org.apache.httpcomponents.httpcore;bundle-version=”4.1.4″,org.s

lf4j.api;bundle-version=”1.7.2″,org.slf4j.jcl;bundle-version=”[1.7.2,

1.7.3)”,org.slf4j.jul;bundle-version=”[1.7.2,1.7.3)”,org.slf4j.log4j;

bundle-version=”[1.7.2,1.7.3)”,olingo-odata2-api;bundle-version=”2.0.

2″,olingo-odata2-core;bundle-version=”2.0.2″,com.sap.gw.rt.camel.comp

onents.custom-development;bundle-version=”1.7.0″,com.sap.gw.rt.ip.com

mons.camel-commons;bundle-version=”1.7.0″,com.sap.gw.rt.ip.commons.ca

mel-odata-rest;bundle-version=”1.7.0″,org.apache.camel.camel-core;bun

dle-version=”2.12.4″,com.sap.it.commons;bundle-version=”1.11.0″,com.s

ap.it.commons.logging.slf4j;bundle-version=”1.11.0″,com.springsource.

org.apache.commons.io;bundle-version=”1.4.0″

Links

Installing SMP Toolkit:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/08/22/how-to-install-sap-mobile-platform-tools-for-integration-gateway-in-eclipse-kepler

Tutorial for OData provisioning in SMP:

http://scn.sap.com/community/developer-center/mobility-platform/blog/2014/06/10/creating-an-odata-service-based-on-sap-gateway-soap-jdbc-and-jpa-data-sources-ba

Preparing Eclipse for Groovy scripting: http://scn.sap.com/docs/DOC-61719

Introduction in REST datasource part 1: Understanding the return structure in xml

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/10/understanding-rest-data-source-in-integration-gateway-1-query-very-simplified

Introduction in REST data source part 2: Understanding the return structure in json

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/11/integration-gateway-understanding-rest-data-source-2-query–json-very-simplified

Introduction in REST data source part 3: Implementing the QUERY operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/02/12/integration-gateway-understanding-rest-data-source-3-query–xml-standard

Introduction in REST data source part 7: Implementing the READ operation

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/03/06/integration-gateway-understanding-rest-data-source-7-read–xml

Overview of all REST blogs

http://scn.sap.com/community/developer-center/mobility-platform/blog/2015/04/08/integration-gateway-rest-data-source-overview-of-blogs

To report this post you need to login first.

1 Comment

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

Leave a Reply