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.
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:
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.
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:
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.
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! :smile:
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
In this tutorial, we’ve learned how to implement the CREATE operation in a Groovy script.
We’ve seen that there are no surprises:
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"
Installing SMP Toolkit:
Tutorial for OData provisioning in SMP:
Preparing Eclipse for Groovy scripting: http://scn.sap.com/docs/DOC-61719
Introduction in REST datasource part 1: Understanding the return structure in xml
Introduction in REST data source part 2: Understanding the return structure in json
Introduction in REST data source part 3: Implementing the QUERY operation
Introduction in REST data source part 7: Implementing the READ operation
Overview of all REST blogs
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
21 | |
19 | |
13 | |
10 | |
9 | |
8 | |
8 | |
7 | |
7 |