Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos

Introduction

As per the OData V2 specification, an OData Entry being created may contain links to other Entries in the service. If that is the case, the server is expected to create the inline Entry and the appropriate Links. For example, to create a new product in the catalog that is associated with the category Entry, you would execute a POST request against the OData.svc/Products collection with a product Entry containing a link to the Category Entry (using any URI that resolves to that resource).

Alternatively you can create and link an Entry to a related Entry by leveraging the addressing scheme if the server supports addressing related items. For example, if a server implements the OData URI conventions, the address …/Categories(10)/Products points at all the products in the specified category. When a POST request is issued against that products collection (instead of the top-level products collection), the server will create the new product Entry and automatically link it to the parent category.

You may combine the above two methods to create an Entry that is related to another one implicitly through the relationship implied in the URL, and related to other Entries by explicitly-specified Links in the request body. When you need to create multiple related Entries, you can do so as independent operations or perform a single POST with a tree of Entries (if the Links between Entries allow it structurally). The tree is formed by using inline expansion. All expanded Entries are considered new. Servers process a request with inline Entries by creating individual Entries and then linking them in the same way as linking would have happened in an independent request. The related Entry or collection of Entries is represented as the child element of an element as an or respectively.

For additional details refer the OData V2 links below:

http://www.odata.org/documentation/odata-version-2-0/operations/

http://www.odata.org/documentation/odata-version-2-0/uri-conventions/

http://www.odata.org/documentation/odata-version-2-0/atom-format/


Scenario

In SAP Mobile Platform (3.0 SP09 onwards), an OData service exposing a SOAP data source should be able to support deep insert operation on an entity in such a way that if the corresponding entity belonging to the SOAP service data model has a parent child relationship with another entity, then this should also get persisted along with the parent entity. There are certain prerequisites that defines the structural contract for the OData model and the SOAP service data model, which should be met and strictly followed to achieve the deep insert use case.

Prerequisites

  • There should be an association property defined between two OData entities
  • There should be a one to many relationship cardinality defined between the two OData entities. This is also applicable for entities belonging to SOAP service data model.
  • There should be a referential constraint defined between the two OData entities
  • The web service API exposed for create operation should adhere to a nested inline structure format and return the parent entity structure as shown below:

Note: Deep Insert can be done up to any level, however the example below is a reference for only up to one level

Web Service API Input/Request Payload XML Format:


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"

    xmlns:sei="http://sei.soap.service.metering.rt.gw.sap.com/">

    <soapenv:Header />

    <soapenv:Body>

        <sei:createSubscription>

            <!--Optional: -->

            <arg0>

                <!--Optional: -->

                <services>

                    <!--Zero or more repetitions: -->

                    <service>

                        <!--Optional: -->

                        <serviceID>1</serviceID>

                        <!--Optional: -->

                        <serviceName>getStockUpdates</serviceName>

                        <!--Optional: -->

                        <userID>1</userID>

                    </service>

                    <service>

                        <!--Optional: -->

                        <serviceID>2</serviceID>

                        <!--Optional: -->

                        <serviceName>purchaseStocks</serviceName>

                        <!--Optional: -->

                        <userID>1</userID>

                    </service>

                </services>

                <!--Optional: -->

                <user>

                    <!--Optional: -->

                    <address>Bangalore</address>

                    <!--Optional: -->

                    <contactNo>67867867</contactNo>

                    <!--Optional: -->

                    <email>user1@Sap.com</email>

                    <!--Optional: -->

                    <userId>1</userId>

                    <!--Optional: -->

                    <userName>user1</userName>

                </user>

            </arg0>

        </sei:createSubscription>

    </soapenv:Body>

</soapenv:Envelope>

Web Service API Output /Response Payload XML Format:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   <soap:Body>

      <ns2:createSubscriptionResponse xmlns:ns2="http://sei.soap.service.metering.rt.gw.sap.com/">

         <return>

            <address>Bangalore</address>

            <contactNo>67867867</contactNo>

            <email>user1@Sap.com</email>

            <userId>1</userId>

            <userName>user1</userName>

         </return>

      </ns2:createSubscriptionResponse>

   </soap:Body>

</soap:Envelope>

OData Modelling

Request/Response Mapping

Custom Scripts

/**

  Function processRequestData will be called just after the Request

  flow is triggered.

  Implement processRequestData for additional functionalities, such as

  business validations e.g. Filter some request data.

  The sample code provided prevents read of entries which have IDs < 200.

*/

function processRequestData(message) {

    importPackage(java.util);

    importPackage(com.sap.gateway.ip.core.customdev.logging);

    importPackage(org.apache.olingo.odata2.api.ep.feed);

    // Get the body from exchange

    log.logErrors(LogMessage.TechnicalError, " input for processRequestData: "+message.getBody());

    var newData = newLinkedHashMap();

    var newList = newArrayList();

    var payloadMap = message.getBody();

    var keys = payloadMap.keySet();

    var itr = keys.iterator();

    while(itr.hasNext()){

        var key = itr.next();

        var val = payloadMap.get(key);

        log.logErrors(LogMessage.TechnicalError, "Key: "+key+" Val: "+val);

        if(!(val instanceofString)){

            var inlineEntryList = val.getEntries();

            for(var k = 0;k < inlineEntryList.size();k++){

                var inlineMap = newLinkedHashMap();

                var odataEntry = inlineEntryList.get(k);

                log.logErrors(LogMessage.TechnicalError, "odataEntry: "+odataEntry.getProperties());

                inlineMap.put("service",odataEntry.getProperties());

                newList.add(inlineMap);

            }

            newData.put("services",newList);

        }else{

            newData.put(key,val);

        }

    }

    log.logErrors(LogMessage.TechnicalError, "newData: "+newData);

    message.setBody(newData);

    message.setHeader("InlineEntry", newData);

    log.logErrors(LogMessage.TechnicalError, " output from processRequestData: "+message.getBody());

    returnmessage;

    }

/**

  Gets the SOAP request XML here. This method can be used if extra headers

  need to be added to the XML request.

*/

function processRequestXML(message) {

    importPackage(com.sap.gateway.ip.core.customdev.logging);

    importPackage(java.lang);

    importPackage(java.util);

    log.logErrors(LogMessage.TechnicalError, "processRequestXML: "+message.getBody());

    var payload = message.getBody().toString();

    var newReqXML = newStringBuffer();

    var oldReqXML = newStringBuffer();

    var tokens = payload.split("(?=<)|(?<=>)");

    var inlineEntryMap = message.getHeaders().get("InlineEntry");

    var keys = inlineEntryMap.keySet();

    var itr = keys.iterator();

    log.logErrors(LogMessage.TechnicalError, "InlineEntry: "+inlineEntryMap);

    while(itr.hasNext()){

        var key = itr.next();

        var val = inlineEntryMap.get(key);

        if(!(val instanceofString)){

            log.logErrors(LogMessage.TechnicalError, "Key: "+key+" Val: "+val);

            newReqXML.append("<"+key+">");

            for(var k=0;k<val.size();k++){

                var inlineMap = val.get(k);

                var inlineMapKey = inlineMap.keySet();

                var inlineMapItr  = inlineMapKey.iterator();

                while(inlineMapItr.hasNext()){

                    //service

                    var inKey = inlineMapItr.next();

                    newReqXML.append("<"+inKey+">");

                    //map

                    var inAttribMap = inlineMap.get(inKey);

                    var inlineAttribMapKey = inAttribMap.keySet();

                    var inlineAttribMapItr  = inlineAttribMapKey.iterator();

                    while(inlineAttribMapItr.hasNext()){

                        var attribKey = inlineAttribMapItr.next();

                        var attribVal = inAttribMap.get(attribKey);

                        newReqXML.append("<"+attribKey+">"+attribVal+"</"+attribKey+">");

                    }

                    newReqXML.append("</"+inKey+">");

                }

            }

            newReqXML.append("</"+key+">");

        }

    }

    log.logErrors(LogMessage.TechnicalError, "newReqXML: "+newReqXML.toString());

    for(var i=0;i<tokens.length;i++) {

        log.logErrors(LogMessage.TechnicalError, "tokens: "+tokens[i]);

        if(tokens[i].contains("</user>")){

            oldReqXML.append(tokens[i]).append(newReqXML.toString());

        }else{

            oldReqXML.append(tokens[i]);

        }

    }

    log.logErrors(LogMessage.TechnicalError, "oldReqXML: "+oldReqXML.toString());

    message.setBody(oldReqXML.toString());

    returnmessage;

}

/**

  Gets the web service response XML. If the web service returns values which are not mapped to Edm types,

  those values can be filtered out here eg :- Inline count, eTag, etc.

*/

function processResponseXML(message) {

    importPackage(com.sap.gateway.ip.core.customdev.logging);

    log.logErrors(LogMessage.TechnicalError, "processResponseXML: "+message.getBody());

    returnmessage;

}

/**

  Implement processResponseResult to modify the response returned from the

  Web service. The XML response returned by the Web Service will get converted to a HashMap

  when this method is invoked. The user can access the HashMap through the message body.

*/

function processResponseData(message) {

    importPackage(com.sap.gateway.ip.core.customdev.logging);

    log.logErrors(LogMessage.TechnicalError, "processResponseData: "+message.getBody());

    returnmessage;

}

OData Request Payload

<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/MTR4;v=1/">

    <id>

        https://localhost:8083/gateway/odata/SAP/MTR4;v=1/UserSet('3')

    </id>

    <title type="text">UserSet</title>

    <updated>2015-09-11T19:14:22.678+05:30</updated>

    <category term="EXPAND.User"

        scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

    <link href="UserSet('3')" rel="edit" title="User" />

    <link href="UserSet('3')/ServiceSet"

        rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ServiceSet"

        title="ServiceSet" type="application/atom+xml;type=feed">

        <m:inline>

            <feed xml:base="https://localhost:8083/gateway/odata/SAP/MTR4;v=1/">

                <id>

                    https://localhost:8083/gateway/odata/SAP/MTR4;v=1/ServiceSet

                </id>

                <title type="text">ServiceSet</title>

                <updated>2015-09-11T19:14:22.68+05:30</updated>

                <author>

                    <name />

                </author>

                <link href="UserSet('3')/ServiceSet" rel="self" title="ServiceSet" />

                <entry>

                    <id>

                        https://localhost:8083/gateway/odata/SAP/MTR4;v=1/ServiceSet(serviceID='5')

                    </id>

                    <title type="text">ServiceSet</title>

                    <updated>2015-09-11T19:14:22.681+05:30</updated>

                    <category term="EXPAND.Service"

                        scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

                    <link href="ServiceSet(serviceID='5')" rel="edit" title="Service" />

                    <content type="application/xml">

                        <m:properties>

                            <d:serviceID>5</d:serviceID>

                            <d:userID>3</d:userID>

                            <d:serviceName>getStockHolderNames</d:serviceName>

                        </m:properties>

                    </content>

                </entry>

                <entry>

                    <id>

                        https://localhost:8083/gateway/odata/SAP/MTR4;v=1/ServiceSet(serviceID='6')

                    </id>

                    <title type="text">ServiceSet</title>

                    <updated>2015-09-11T19:14:22.682+05:30</updated>

                    <category term="EXPAND.Service"

                        scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

                    <link href="ServiceSet(serviceID='6')" rel="edit" title="Service" />

                    <content type="application/xml">

                        <m:properties>

                            <d:serviceID>6</d:serviceID>

                            <d:userID>3</d:userID>

                            <d:serviceName>getNetWorth</d:serviceName>

                        </m:properties>

                    </content>

                </entry>

            </feed>

        </m:inline>

    </link>

    <content type="application/xml">

        <m:properties>

            <d:UserID>3</d:UserID>

            <d:UserName>user3</d:UserName>

            <d:ContactNo>324-5443-4353</d:ContactNo>

            <d:Email>user3@Sap.com</d:Email>

            <d:Address>Chennai</d:Address>

        </m:properties>

    </content>

</entry>

OData Response Payload

<?xml version="1.0" encoding="utf-8"?>

<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="http://localhost:8080/com.sap.gateway.core.gwcoreip.web/odata/SAP/MTR4;v=1/">

    <id>http://localhost:8080/com.sap.gateway.core.gwcoreip.web/odata/SAP/MTR4;v=1/UserSet('3')</id>

    <title type="text">UserSet</title>

    <updated>2015-09-24T13:23:02.103+05:30</updated>

    <category term="EXPAND.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"></category>

    <link href="UserSet('3')" rel="edit" title="User"></link>

    <link href="UserSet('3')/ServiceSet" rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ServiceSet" title="ServiceSet" type="application/atom+xml;type=feed"></link>

    <content type="application/xml">

        <m:properties>

            <d:UserID>3</d:UserID>

            <d:UserName>user3</d:UserName>

            <d:ContactNo>324-5443-4353</d:ContactNo>

            <d:Email>user3@Sap.com</d:Email>

            <d:Address>Chennai</d:Address>

        </m:properties>

    </content>

</entry>