Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 

Using SAP Data Services 4.2 we will walk through an example of consuming a REST web services via a Web Service REST Datastore.

To get a basic idea of what we will do, you can read through the SAP DS Integrator Guide, specifically the REST portions of these three sections:

Consuming external web services in SAP Data Services

>> Accessing a web service using the designer

>> Adding web service calls to a job

Also the processes is essentially the same as consuming as SOAP web service for which Saurav Mitra has posted an excellent article over at dwbi.org

A summary of the steps we will take:

1. Procure the WADL file for the REST web service we want to consume

2. Create the Datastore and function from the WADL file

3. Create our Dataflow that will consume the function (REST web service)

The REST service we will use in our example is Google's Geocoding API.  I'm not advocating using this as a way to mass geocode addresses in Data Services, it simply is a nice open API to use for demoing the REST capabilities.

Here's an example Call:

http://maps.googleapis.com/maps/api/geocode/xml?address=1600%20Amphitheatre%20Parkway,%20Mountain%20...

In a browser you can see the results as:

Let's get started!

1. Procuring the WADL

This can be one of the most difficult part of this effort.  Sometimes REST service publishers do make a WADL available and sometimes they do not.  The WADL is what defines the REST service call for Data Services, similar to the WSDL for a SOAP service.  You will very likely have to create your own WADL, or use the help of some tools to do so.

If you want to skip this step you can simply download the attached WADL file to your PC with DS Designer on it, and save the file without the .txt extension (GoogleGeocodeAPI.wadl).  Note: if Google changes their API after this blog is posted, you may have to tweak or recreate these. Also, passing in different address types to Google's API can yield different XML results, meaning the schema we are tying into here isn't necessarily complete.  For the sake of demo we will use some addresses that yield similar results

One free tool that can help us generate the WADL is SoapUI by Smart Bear.

If you download the free version of SoapUI you can

a. Create a new REST Project (File menu --> New REST Project)

b. Enter the service URI when prompted, and click OK

http://maps.googleapis.com/maps/api/geocode/xml?address=1600%20Amphitheatre%20Parkway,%20Mountain%20...

   

c. In your new project window, you can test the service by clicking the submit button and viewing the result pane.

d. To generate the WADL, right click on the service in the project navigator and choose "Export WADL"

Note: wherever you save it, it will be called "_1.wadl"


e. If you open the file in your favorite XML editor, you'll notice that it contains information on the parameters to pass to the service (outlined in green), but no details about the response format (outlined in red).  Note: the geocode api has more parameters than we show here, but because we only specified these parameters in our sample call in SoapUI, they are the only ones present in the generated WADL.


f. To address the missing response info we need to add a <grammars> section just after the first <doc /> tag

g. Within the grammars section we need to add the schema of the response.  To do this, copy the XML Response from the SoapUI test we did, and paste into an XML to XSD generator.  For example here is one.

h. Copy the schema output into the grammars section


i. Finally we have to tie the response to the schema.  Add the following attributes to the response <representation /> tag:

element="GeocodeResponse" xmlns:xs="http://www.w3.org/2001/XMLSchema"

j. Save the WADL file.... preferably as something other than _1.wadl



2. Creating the Datastore from the WADL file

a. Launch SAP Data Services Designer

b. On the Datastore tab of the Object Library view, right click on a blank area and choose New

c. Give your data store a name, select type: Web Service REST, and in the Web Service URI browse to the WADL file we created in step 1, and click OK to create.

If the create was successful, GREAT JOB!  You will see your new Datastore listed.  If it was not successful, there is something wrong with your WADL file, designer will indicate the line and column where it had the issue, try to find and correct the related issue and then try to recreate the Datastore.  We worked through several xml validation issues until we got it right!

d. Next we need to import the function call.  In the Datastore browser, double-click on the f(x) Functions node.  This will open the Datastore explorer.  Expand the tree until you see the XML node.  Right-click the XML node and select Import.  (note: this was another area where we had to work through some XML validation issues originally).

e. You should now see the function listed!

3. Creating a Dataflow that will consume the function (REST web service)

Next we will walk through a simple data flow where we consume a CSV file that contains our parameters for the service call (address and sensor), calls the service, and writes the the address along with geo coordinates out to another CSV.

CSV (address, sensor) --> REST service --> (address, latitude, longitude).

a. In designer we've created a test project with a batch job and a new dataflow.

b. Next we'll create a test CSV file to use as the source of our parameters to the REST call.  The file as two columns, address and sensor.  A copy of this file is attached (googleresttest.txt).


address, sensor
"1600 Amphitheatre Parkway, Mountain View, CA",false
"3999 West Chester Pike, Newtown Square, PA",false




c. To use our file we'll create a new flat file format.  Here are the settings used to create it

d. Add the Flat file as a source to our data flow, and then add a query in which we will call the function module, and link the two together.

e. Inside the Query we add the function call by right clicking the target schema and choosing "new function call..."

f. Then we select our function

g. Next we see the parameter mapping screen and notice we are unable to map because of the nested structure that the function takes.  We'll take care of that in the next step, just hit finish for now.

You should see this in your target schema of the query.

h. Let's go back and add an additional query step in between our flat file and our REST call.  We will use this to structure, or nest, the data in the format needed by the function.  We also need to add a vanilla Row Generator feeding into the nest query, without which we would end up with only one address getting geocoded.

i. In our nest query, right click on the target schema, and choose "New Output Schema...", and give fill in a new schema name.

j. in the target schema, make sure the PARAMETERS sub-schema is select, then in the source schema, select the address and sensor fields, right click and choose map to output.

k. Next with PARAMETERS still selected ad the Schema Out, we need to hook in the row generator by checking the Row_Generation in the From tab, and unchecking our file format.

l. Now we go back to the REST function call query, where we can see our new incoming structure.  Next right click on the function call and choose "Modify Function Call...".  Click on the new PARAMETERS sub-schema and drag it down to the URI_Parameters box. Then click finish.

m. Now let's add a new Query after the REST function query to unnest the results.  Map the GeocodeResonse to the output, then right click the QRY_UNEST and choose "Unnest with Sub-shemas"

n. Now we'll add another query after the unnest, and map the columns we want to dump to a file.  However after you map the columns, you'll notice if we run a validation that it comes up with an error about each column.  I'm not sure if this is a bug in how DS maps unnested columns, but you can correct by removing the original subschema names from the new mapping.  The other thing we need to do is mark the select "distinct rows" option because of the format of the response, the unnesting can result in multiple rows per address.

o. Lastly we can take the result and dump it out to a file, by generating a file format from our final query, and making it a target.

p. Now if we execute our test, the resulting file contains:


formatted_address,lat,lng
"1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",37.4224607000,-122.0842702000
"3999 West Chester Pike, Newtown Square, PA 19073, USA",39.9880211000,-75.4153618000

We hope this example proves useful. Again, the Google geocode API has many variations that are not all accounted for in this example, so if you try other addresses, don't be surprised if it breaks due to the different elements in the response.  Also, there are probably other better ways to mass geocode addresses, again we are just using it as an easy example.  Thanks!

31 Comments
Former Member
0 Kudos

Great Post Perry!

Unfortunetley I can't get it to work.

When I execute the Job the error " QRY_GEOCODE_REST-Function1, HTTP-Client error:<7>:<Couldn't connect to server>." occurs.

I am not sure why this is not working. Calling the WS from SOAP UI and via Browser is working fine (my Laptop). It is also possible to call the WS with Internet Explorer on the Data Services server.

I was able to create the data store and import the function, so at least from the designer the connection to the server was possible, right?

Do you have any idea?

Former Member
0 Kudos
Thanks Perry for posting step by step instruction.
I am able to implement the same now.
I have another task where we need to consume JSON response from REST web service call.
But I am stuck with capturing response,in DS Designer I get only EMPTYBODY as reply schema when I imported the function module and getting NULL output....I think this is to do with WADL file defination.
Do you have similar post to deal with(properly construct) JSON based WADL file

Regards
Anirban
0 Kudos
Hi Anirban Haldar,

Where you able to get it to work? I have a similar need to read JSON response in DS. keep posted on your findings.

 

thnx,

Saravanan
Former Member
0 Kudos
Hi,

After doing all this i am not able to consume this in BODS as datastore as it throws an error. Does anyone has a working WADL file with above URL?

 

Thanks

Pankaj
former_member410528
Discoverer
0 Kudos
Hello,

I would like to use the POST method, is the parameter passing in the same way?

Thanks

Christèle

 

 
Former Member
0 Kudos
Hi, I am also facing same error  HTTP-Client error:<7>:<Couldn’t connect to server>.

What were the steps u performed to get it worked?

 

Best Regards

Praveen
0 Kudos
I was able to complete till step f. On step g, I have below issue.

Under URI_Parameters in BODS I see Address and Sensor duplicated. How do I remove the 2 extra ones?
sshen-illumiti
Explorer
0 Kudos
Can you have an example of using HCI-DS REST/JSON (GET/POST) using WADL?

 
Former Member
0 Kudos
Hi, like other users I have the same problem as them, the HTTP client error:<7>:<Couldn't connect to server>. I checked the proxy and nothing is solved, a part of it, I checked the AL_ERROR_NUM that gets a number 3, checking the guides the AL_ERROR_NUM = 3 is because: The call to the server failed. Possible causes include:

  • invalid URL

  • malformed URL

  • server does not exist

  • SSL certificate is missing

  • proxy or firewall does not allow Data Services to communicate with the URL

  • no internet communication

  • resource can't be found

  • lack of privileges to access the resource


 

So I don't know where the error can be, if someone that was able to do it please reply, I will appreciate.

 

Regards.

 

Mike.
Former Member
0 Kudos
Thanks! Great blog.
naveenkurmadas
Explorer
Hi Saravanan,

 

Could you get it working for JSON response? I'm stuck with the same.

 

Regards,

Naveen
naveenkurmadas
Explorer
0 Kudos
Hello Anirban,

 

Hi Saravanan,

 

Could you get it working for JSON response? I’m stuck with the same issue. I could see the JSON response in the trace, but this response in not mapped to my output parameters. They are blank.

 

Regards,

Naveen

 
Former Member
0 Kudos
Also got the the HTTP client error:<7>. Solution was to explicitly specify the Proxy host and Proxy port in the Datastore configuration. I simply took these from the wpad script on the server using Chrome. Now works flawless for JSON REST.
0 Kudos

Great blog.

Yes, POST method have similar passing way for the parameters. (basically you need to do step g twice)

0 Kudos
I don't find the wadl file? where i can download it?

 

Thanks.
Staceyl
Explorer
Great post.  Thanks for providing this.

Taking it a bit further, I'm just wondering if anyone has had a the requirement to retrieve a batch of records through an API (all than 2018-01-01 for example)?  This call could return multiple records in a single return message, based on a single input parameter/filter.  So how are we able to write each record from the return message back to the target?  The current example is based on 1 to 1 relationship between the parameter and records returned where the row generator handles the subsequent read/writes.

Thanks.

Stacey
former_member391431
Discoverer
0 Kudos
Thanks for the post. It worked for me mostly. I had to put the Param into the representation, which differs from your sample.

In my case I was not able to solve one problem:

I try to send the request in the request body and not as a param in the URL.

My WADL definition looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
<doc xml:lang="en" title="https://myservice"/>
<grammars>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="uuid" type="xs:string"/>
</xs:schema>
</grammars>
<resources base="https://myservice">
<resource path="path/v0/api/dataservice/endpoint" id="endpointname">
<doc xml:lang="en" title="endpointname"/>
<method name="PUT" id="endpointname">
<doc xml:lang="en" title="endpointname"/>

<request>
<representation mediaType="application/json">
<param name="input_json" type="xs:string" style="query" default="sometestval" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
</representation>
</request>
<response status="200">
<representation mediaType="application/json; charset=utf-8" element="uuid" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
</response>
<response status="404">
<representation mediaType="text/html; charset=utf-8"/>
</response>
<response status="">
<representation mediaType="application/json"/>
</response>
</method>
</resource>

The above WADL works for PARAM, but I don't see a way to attach anything into the request body.

Any suggestion how to specify the WADL the way that Data Service understands in the function to asign to the body?

 

Thanks,

Mansur
0 Kudos
Hi.... Thanks for the nice blog.

 

I'm working on REST API's .Is it required to use XSD file if the response of API is in JSON format?

Imported functions but the content of the function was EMPTYBODY.

Please post remedy for this issue.

 

Thanks!

 
sulvaran
Discoverer
0 Kudos
I had the same problem with the Rest api when the answer comes in json format and there are several data records. Has anyone been able to solve this problem?
The data flow passes the parameters correctly but the api response returns the empty structure to me.
Can someone give me lights on this problem?

 

 
richiew03
Explorer
0 Kudos
Hi this blog is great but has anyone got this working with certificates?  My web service requires me to pass the client certificate and key PEM files.  I have added the location to the REST Datastore however when executing the job receiveERROR:  HTTP client error:<60>:<Peer certificate cannot be authenticated with given CA certificates>.

SAP note 234560 describes this error but I have already maintained the location of the PEM file in the SSL PEM File parameter of the datastore.  any thoughts? Thanks

 
former_member642525
Discoverer
0 Kudos

Hello everyone.

I saw that everyone was asking, but could not find an answer

I am trying to make a json scheme for wadl.

Does not work.
Can you tell me what the problem is?

example

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
<doc xml:lang="en" title="https://TITLE.kz"/>
<grammars>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="element" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:int" name="ATTEMPT_ID"/>
<xs:element type="xs:string" name="DATE_START"/>
<xs:element type="xs:string" name="ID_1C"/>
<xs:element type="xs:string" name="PASSED"/>
<xs:element type="xs:string" name="POSITION"/>
<xs:element type="xs:byte" name="SCORE"/>
<xs:element type="xs:string" name="STORE"/>
<xs:element type="xs:int" name="STUDENT_ID"/>
<xs:element type="xs:short" name="TEST_ID"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</grammars>
<resources base="https://TITLE.kz">
<resource path="webservices/students_tests_attempts.php" id="Students_tests_attempts.php">
<doc xml:lang="en" title="Students_tests_attempts.php"/>
<param name="DATE_START" default="01.01.2010" type="xs:string" required="false" style="query" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
<param name="DATE_END" default="25.04.2022" type="xs:string" required="false" style="query" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
<method name="GET" id="Students_tests_attempts.php">
<doc xml:lang="en" title="Students_tests_attempts.php"/>
<request/>
<response status="200">
<representation mediaType="application/json" element="root" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
</response>
</method>
</resource>
</resources>
</application>

former_member417037
Discoverer
My case is JSON as input for REST webservice, I don't know how to setup it. Is any sample for reference? Thanks.
former_member654837
Discoverer
0 Kudos
Hi Mansur,

 

Have you found how to pass request body values. I generated WADL using SOAP UI and the header details are copied to WADL but the body details are not copied.  I have created Datastore and was able to generate the function.Is there a way to send the request body details(like from date, to date, secret key).Thanks.

 

Regards

Suresh
vivekkrishnan
Active Participant
0 Kudos
I am getting this error.

Error: Query <Transform2> either selects from no object at all, or its SELECT produces <1> columns while the query output schema specifies <0> columns
former_member702624
Discoverer
0 Kudos
I have a similar requirement, need to read json format comig as output from webservice and consume in Data services. Can anyone help me for extact steps I need to do ? will only pyhton code not help??
ayaatmohammed
Member
0 Kudos
Hi ,

I trying to import the function from the datastore and it is throwing this error.


Please check


i am using the same url and everything .

 
Sjoof
Explorer
0 Kudos

Change "Xml 1" Into "Xml_1". Spaces are not allowed

0 Kudos
Hello Maria,

Did you progress on your development? Please suggest, i am kind of in same boat.

Have an error with the response tag on xml.

 

Thank you,

Swetha
0 Kudos
<response> missing.
0 Kudos
Remove the second <doc xml:lang="en" title="blahblah"/>
0 Kudos
I have everything running successfully except it renders the last record. When traced, the query transform does render the number of records requested but the Unnest Query transform only reads the last record. Does anyone have a solution for this? Please suggest.
Labels in this area