Technical Articles
OData API in SAP Cloud Integration for Beginners
This blog post covers how to expose an OData API in SAP Cloud Integration with CRUDQ operations.
Introduction
In SAP Cloud Integration, You can develop OData API that exposes existing data sources, such as SOAP, OData, ODC, and REST as OData endpoints. In this blog post, I will be exposing an existing OData Service with some modifications in SAP Cloud Integration.
Official Documentation
Prerequisite
- You have to know OData protocol and how to use an OData Service for Create, Read, Update, Delete operations. Help
- You have to know how to model Integration Flows and know how to use Message mappings.
- You have to know the difference between OData Service project and Integration Flow with OData sender.
- How to use Postman Rest client to perform HTTP calls.
What this blog doesn’t cover
- How to create the request payload for Create, update and $batch operations.
Help with Olingo
SAP Cloud Integration uses Olingo library to serialize/deserialize the request-response and URI.
- Olingo UriInfo stores the request URI information.
- This git contains java code to getUriInfo object with olingo library.
OData Receiver Service
OData Service that I will be using is the reference service from https://www.odata.org/odata-services/
https://services.odata.org/V2/(S(ODataSenderCPIDemo))/OData/OData.svc/
TIP: In the above service URL, (ODataSenderCPIDemo) represents a unique session for the manipulation of records. i.e., Entities created, updated, deleted will reflect only in that session. You can have your own session with any string within S() for your testing.
X-CSRF-Token support
OData API exposed is CSRF enabled. SAP Cloud Integration expects its clients to send a valid CSRF token in the HTTP header X-CSRF-Token
with every request, except for GET
If you don’t, you will receive an error response with the status code 403
- Fetch a CSRF-token
Send a GET-request to your service root with the appropriateAuthorization
header and theX-CSRF-Token
header with valueFetch
. - Send the CSRF-Token with every request
Now, you can send your payload-requests with the headerX-CSRF-Token
set to the value received in the response header of step 1. If your session or token expires, you will get a 403-error again. Just repeat the steps above.
OData Sender Behavior
- OData sender converts the incoming OData Request payload (atom+xml / json) to simple XML.
- For OData Operations which has response body (create, read), the final step in the IFlow has to be a simple XML which represents the Entities defined in OData sender Edmx.
- The URI of the OData Sender can have OData query parametersa and other custom parameters. The URI parameters have to be retrieved using Olingo UriInfo which is available in message header “UriInfo”.
OData Sender Updates
- OData sender version 1.2 onwards: Request URI, QueryString and HTTP Method are stored in exchange header CamelHttpUri, CamelHttpQuery, and CamelHttpMethod respectively. In order to get these headers, it has to be added in ‘Allowed Headers’ in integration flow Runtime configuration as below.
- The exception message will not be returned to the client. Refer KBA
Contents
- Create an OData Sender Artifact
- Deploying an OData Service
- Invoking an OData Service
- Editing an OData Model
- Read
5.1 Read with Navigation - Query
6.1 $expand - Create
7.1 Deep Insert - Update
- Delete
- FunctionImport
- Set Custom Error Code and Response Body for Error
1. Create an OData API Artifact
- Create an OData Sender project by following the documentation up to step 7.
- Download the metadata from existing OData service from postman
https://services.odata.org/V2/(S(ODataSenderCPIDemo))/OData/OData.svc/$metadata
TIP: Use your own session
- Open the created artifact in Edit mode, choose Import Model Wizard
- Select Data Source Type as ODATA, Browse the Edmx file saved in step 2
- Choose the entities that you want to expose in the API from SAP Cloud Integration. I have chosen Products and Suppliers (without Address fieds)
- Choose the fields that will be key in each entity. I have chosen ID and ReleaseDate to be the Key of Products.
- Click Finish.
- Now the OData Service is ready with no operations configured. In order to deploy an OData API, at least one operation has to be configured. Click on bind link under Actions.
- Choose the Entity and the End Point to your OData Receiver Service.
- An Iflow template for OData Read operation will be generated. Now the OData Service is ready for Deploy.
2. Deploying an OData API
- Deploy the service by following the documentation.
3. Invoking an OData API
- Copy the endpoint URI by choosing the copy icon.
- From Postman, fire the request for the endpoint with the user having the role ESBMessaging.send
- The OData API has 2 entities ProductSet and SupplierSet. Copy the EDMX URL and trigger it from Postman.
The OData Receiver Service had Products and Suppliers, which is exposed in SAP Cloud Integration as ProductSet and SupplierSet. Similarly, you can see that the ProductSet has 2 Key property that was configured previously.
4. Editing an OData Model
- Follow the steps from the documentation.
- Rename the ID of ProductSet to ProductID and ID of SupplierSet to SupplierID.
- Save and deploy. $metadata call will reflect the changes.
The following operations are supported when developing an OData service in the SAP Cloud Integration. Let’s configure and test each operation one by one.
5. Read
- Configure the Read operation for ProductSet. In Configure OData Data Source, make sure to give the same endpoint for all operations (with the same session)
- Navigate to IFlow Editor.
- A predefined IFlow will be created. The script step of the IFlow will have the template to add $top and $skip to add to message header “odataURI”.
- Modify the groovy script to set the key parameter as message header with key “Key_<propertyName>”
The URI parameters has to be retrieved using Olingo UriInfo which is available in message header “UriInfo”.This git contains java code to get UriInfo object. Since you cannot debug a script, you can use the java class to understand the UriInfo and derive the key property values from request URI.
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import org.apache.olingo.odata2.api.uri.UriInfo; import com.sap.gateway.ip.core.customdev.logging.*; def Message processData(Message message) { def uriInfo = message.getHeaders().get("UriInfo"); def keyPredList = uriInfo.getKeyPredicates(); def k=0; for(item in keyPredList) { message.setHeader("Key_"+item.getProperty().getName(),item.getLiteral()); } return message; }
- The Receiver OData is configured to get the key value from the header set from the script.
- The Response from OData receiver has to be mapped as per the entities in sender Edmx.
- Perform the Read request from postman https://<serviceRoot>/ProductSet(ProductID=1,ReleaseDate=datetime’1995-10-01T00:00:00′)
5.1 Read with Navigation
- Edit the OData Model such that ProductsSet has navigation to SupplierSet and vice versa.
<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" xmlns:sap="http://www.sap.com/Protocols/SAPData" Version="1.0"> <edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="2.0"> <Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Namespace="S1"> <EntityContainer Name="EC1" m:IsDefaultEntityContainer="true"> .... <AssociationSet Name="Products_Supplier_Suppliers" Association="S1.Product_Supplier_Supplier_Products"> <End Role="Product_Supplier" EntitySet="ProductSet"/> <End Role="Supplier_Products" EntitySet="SupplierSet"/> </AssociationSet> </EntityContainer> <EntityType Name="Product"> ..... <NavigationProperty Name="Supplier" Relationship="S1.Product_Supplier_Supplier_Products" FromRole="Product_Supplier" ToRole="Supplier_Products"/> </EntityType> <EntityType Name="Supplier"> ..... <NavigationProperty Name="Products" Relationship="S1.Product_Supplier_Supplier_Products" FromRole="Supplier_Products" ToRole="Product_Supplier"/> </EntityType> <Association Name="Product_Supplier_Supplier_Products"> <End Role="Product_Supplier" Type="S1.Product" Multiplicity="*"/> <End Role="Supplier_Products" Type="S1.Supplier" Multiplicity="0..1"/> </Association> </Schema> </edmx:DataServices> </edmx:Edmx>
- The navigation ProductsSet to Supplier is of cardinality * to 1. Hence the request ProductSet(ProductID=1,ReleaseDate=datetime’1995-10-01T00:00:00′)/Supplier will trigger the Read operation of SupplierSet. Configure Read for SupplierSet with the initial script containing the below code.
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import org.apache.olingo.odata2.api.uri.UriInfo; import com.sap.gateway.ip.core.customdev.logging.*; def Message processData(Message message) { def uriInfo = message.getHeaders().get("UriInfo"); def keyPredList = uriInfo.getKeyPredicates(); def k=0; for(item in keyPredList) { log.logErrors(LogMessage.TechnicalError, (++k) + " Key Predicate value for property "+item.getProperty().getName()+" is: "+ item.getLiteral()); message.setHeader("Key_"+item.getProperty().getName(),item.getLiteral()); } def targetEntityName = uriInfo.getTargetEntitySet().getName(); def startEntityName = uriInfo.getStartEntitySet().getName(); //Handle Navigation request if(!targetEntityName.equals(startEntityName)){ log.logErrors(LogMessage.TechnicalError, "Navigation request from source "+ startEntityName + " to target " +targetEntityName); //Do your own processing to decide which Supplier ID to retrive. message.setHeader("SupplierID","0"); }else{ //Handle SupplierSet Read } return message; }
TIP : In the script above, log.logErrors are used to print the values of my variable in logs. These logs appear in System Log file of type CP default trace as below.
…..[Gateway][TECHNICAL][TechnicalError]:Entity Name::CategorySet| - Do the required response mapping. The navigation query from postman https://<serviceRoot>/ProductSet(ProductID=1,ReleaseDate=datetime’1995-10-01T00:00:00′)/Supplier will return Supplier of ID 0.
6. Query
- Add the initial script step to hold the query options Filter, top, skip, orderby, expand as below. Query options are retrieved using Olingo UriInfo which is available in message header “UriInfo”.
[OR]
Refer point 1 in OData Sender Updates to get the query option.
$select, $count doesn’t need to be handled in the script. This git contains java code to get UriInfo object. Since you cannot debug a script, you can use the java class to understand the UriInfo and derive each of the OData query options like $top $filter. -
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import org.apache.olingo.odata2.api.uri.UriInfo; import com.sap.gateway.ip.core.customdev.logging.*; def Message processData(Message message) { def uriInfo = message.getHeaders().get("UriInfo"); def odataURI = new StringBuilder(); def urlDelimiter = "&"; def urlConcat = "?"; def entityName = uriInfo.getTargetEntitySet().getName(); log.logErrors(LogMessage.TechnicalError, "Entity Name::"+entityName); if (uriInfo.getTop() != null){ def top = uriInfo.getTop(); if(odataURI.size()!=0) odataURI.append(urlDelimiter); odataURI.append("\$top=").append(top); log.logErrors(LogMessage.TechnicalError, "Top value:"+top); } if (uriInfo.getSkip() != null){ def skip = uriInfo.getSkip(); if(odataURI.size()!=0) odataURI.append(urlDelimiter); odataURI.append("\$skip=").append(skip); log.logErrors(LogMessage.TechnicalError, "Skip value:"+skip); } if(uriInfo.getFilter() != null){ def filterValue = uriInfo.getFilter().getUriLiteral(); filterValue = filterValue.replace("ProductID","ID"); //The receiver has property names as ID and not ProductID if(odataURI.size()!=0) odataURI.append(urlDelimiter); odataURI.append("\$filter=").append(filterValue); log.logErrors(LogMessage.TechnicalError, "Filter value: "+filterValue); } if(uriInfo.getOrderBy() != null){ def orderBy = uriInfo.getOrderBy().getExpressionString(); if(odataURI.size()!=0) odataURI.append(urlDelimiter); odataURI.append("\$orderby=").append(orderBy); log.logErrors(LogMessage.TechnicalError, "orderby value: "+orderBy); } if(uriInfo.getExpand() != null){ def expandList = uriInfo.getExpand(); def expandValue; log.logErrors(LogMessage.TechnicalError, "expandList size: "+expandList.size()); if(expandList.size()!=0){ odataURI.append(urlDelimiter); for(item in expandList){ if(item.size() > 0){ for(navSegments in item){ expandValue = navSegments.getNavigationProperty().getName(); //TO DO : Multiple expand values to be handled } } } odataURI.append("\$expand=").append(expandValue); log.logErrors(LogMessage.TechnicalError, "expand value: "+expandValue); } } log.logErrors(LogMessage.TechnicalError, "URI value:"+ odataURI.toString()); message.setHeader("odataEntity",entityName); message.setHeader("odataURI",odataURI.toString()); return message; }
- The receiver OData has to be configured to get the system query option from the header ${header.odataURI}
- Map the response from receiver to Sender Edmx. For $expand scenario the expanded entity has to be mapped. The Response mapping template will generate source and target with only root entity.
I have created the XSD for source and target containing the Supplier child and used that XSd for the mapping.
TIP: You can use the OData receiver query modeler to generate the XSD. For the XSD to have Entity and its navigation, choose ‘Create’ with sub levels.
- From Postman, perform Query with various query options like $select, $top, $skip, $filter, $expand.
- To enable serverside pagination with next link in response, set the message header ‘skipToken’ with the number of records that have been returned. Eg, skipToken : 20 will add the next Link in response as <link href=”Products?$skiptoken=20″ rel=”next”/>
- $inlinecount=allpages will return the count in response only if the message property InlineCount is set with an Integer value.
7. Create
The Response of Create should have a response body of the created entity with the key properties.
- Configure create for ProductSet.
- Request from the Sender has to be mapped to the Receiver edmx.
- Response from the receiver has to be mapped to Sender Edmx.
- Perform Create operation from postman
7.1 Deep Insert
The Response of Deep Insert should have a response body with only the root Entity and not the navigation entities.
UPDATE: Software increment 2101 allows deepinsert response body to have navigaton entities too. You have to map the response structure with the root entity and the navigation entity.
- In case of deep insert, the sender generates the nested structure of the deep insert payload and the same has to be handled in the iflow as per the use case.
- The request mapping with nested Supplier has to be done.
Refer this blog for detailed steps to get the XSD.
- The response of the deep insert should have only the root entity for the OData sender. Please note in below response mapping, only the root Entity ProductSet is mapped.
- The OData deepinsert request will be converted to XML by OData sender whcih has to be handled as per your business logic.
I have enabled trace and triggered deepinsert and the equivalent XML is as below.
- Since the OData receiver is capable of handling the deep insert, I have just mapped the incoming payload to the receiver as shown in step 2.
- DeepInsert triggered from the postman.
- I verify that the Supplier with SupplierID 6543 is created by querying on the OData receiver URL configured.
https://services.odata.org/V2/(S(ODataSenderCPIDemo))/OData/OData.svc/Suppliers(6543)
8. Update
- URI handling: Similar to Read operation, The ID has to be correctly passed to the receiver.
- Payload: The request payload has to be mapped just like Create.
9. Delete
- URI handling: similar to Read operation, The ID has to be correctly passed to the receiver.
- The is no need for request and response mapping.
10. FunctionImport
- Edit the Iflow model to have a FunctionImport defined. I have defined GetProductsGTRating which will fetch all the Products greater than the rating given in input.
- Configure the FunctionImport.
- Modify the initial script step to store the function import parameters into the message header.
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import org.apache.olingo.odata2.api.uri.UriInfo; import com.sap.gateway.ip.core.customdev.logging.*; def Message processData(Message message) { def uriInfo = message.getHeaders().get("UriInfo"); def funcImpParams = uriInfo.getFunctionImportParameters(); if(funcImpParams != null && !funcImpParams.isEmpty()){ log.logErrors(LogMessage.TechnicalError, "FunctionImport"+funcImpParams); def k=0; for(item in funcImpParams) { log.logErrors(LogMessage.TechnicalError, "Functionimport Param "+(++k)+" : "+ item.getKey()+" = "+item.getValue().getLiteral()); message.setHeader(item.getKey(),item.getValue().getLiteral()); } } return message; }
- Model the Iflow to handle the defined function Import. The OData receiver is configured to get all the Products with rating greater than the header value ‘rating’.
- Do the required response mapping.
- Trigger the functionimport from the postman, <service root>/GetProductsGTRating?rating=2.
The response will contain all the Products with rating greater than 2.
11. Set Custom Error Code and Response Body for Error
- The exception message will not be returned to the client. Refer point 2 in OData Sender Updates .
- Perform a Create operation on ProductSet without the Key Property in payload as below.
{ "ReleaseDate": "/Date(694224000123)/", "Rating": 4, "Price": "2.5" }
- Below is the response that will be seen.500 status code with default error.
- This response can be customized with custom response code and response message.
- Add an exception subprocess in create Iflow. Add a content modifier.
- Set the message header “CamelHttpResponseCode” to the status code you want to propagate to Sender.
- Save and deploy the artifact and trigger the create operation again. The status code will be 400 instead of 500.
- The Iflows resources has an XSD named Error. Set the message body with the error message as per the XSD.
- Trigger the create without key property. The response will be custom status code and message set in exception subprocess.
OData Sender related blogs
- Introduction to Creating OData Service in HANA Cloud Integration
- OData Service Project vs Integration Project when to use what in Cloud Platform Integration
- OData Service in $batch Mode
- https://blogs.sap.com/2016/06/10/deep-insert-suport-in-odata-provisioning-in-hci/
- https://blogs.sap.com/2017/06/05/cloud-integration-how-to-setup-secure-http-inbound-connection-with-client-certificates/
- https://blogs.sap.com/2017/07/17/odata-service-project-vs-integration-project-when-to-use-what-in-cloud-platform-integration/
- https://blogs.sap.com/2018/09/16/sap-cloud-platform-integration-odata-v2-conditional-update/
Hi Saranya, Very helpful article indeed.
I was trying to do the same with Success Factors odata api. However while importing EDMX , it is giving errors.
"Element 'tagcollection' is not valid at this position Namespace of attribute 'label' not found Namespace of attribute 'creatable' not found Namespace of attribute 'updatable' not found Namespace of attribute 'sortable' not found Namespace of attribute 'filterable' not found Namespace of attribute 'visible' not found"
Can you please guide how to download edmx in case of SF APIs or if any modification is needed.
Thanks
Hi Ashuthosh,
SF OData Edmx is annotated with sap namespace for properties as below which is not understood by the OData Sender GUI.
<Property Name="addressLine1" Type="Edm.String" Nullable="true" sap:required="false" sap:creatable="true" sap:updatable="true" sap:upsertable="true" sap:visible="true" sap:sortable="false" sap:filterable="false" MaxLength="255" sap:field-control="userPermissionsNav/addressLine1" sap:label="Address Line 1"/>
However, instead of importing the EDMX, you could go to the Edmx Editor and paste the complete EDMX. This will show the lines with error and you could fix them one by one. I have copied the EDMX of user Entity from SF, and I have commented the line 17 to 20 to fix the validation errors.
Hi Saranya,
Great piece of work.
Can you comment if we can integrate SAP Gateway oData services(v2) in Cloud platform iFlow ? Also is it possible to reuse the same session from the cloud platform for all the oData api calls to the SAP backend ?
Note: oData services in the backend are stateful.i.e., soft state enabled.
Regards
Prabha
Hi Prabha,
Thanks for your comments.
Yes, you can integrate SAP Gateway OData services. If it is on-premise service, you will have to use a cloud connector.
Yes, you can reuse HTTP sessions from CPI. Here is a blog for the same https://blogs.sap.com/2017/07/17/cloud-integration-how-to-configure-session-handling-in-integration-flow/
BR
Saranya
Thanks Saranya for your great blog!
It has helped me a lot!
Trinidad.
Hi Saranya,
Thanks for the blog. For deeply nested structures on the request side (For Ex: Supplier inside Products), do we have always have to create the xsd manually? Although the edmx file has NavigationProperty specified correctly, there is no option during step 2 "Select Structure" to select the nested structure. It only displays fields of individual "Entity Types" separately without displaying the fields of the referenced Entity Type(s). Please clarify.
Regards,
Malgi
Hi Malgi,
OData Sender adapter doesn't support XSD generation. Hence you have to manually create one.
Whereas the OData receiver supports the XSD generation for deep structure.
BR
Saranya
Hi Saranya,
I am stuck in step 5.6
I am never able to save the mapping. I access the generated mapping and it has both entities without any mapping I then add mapping to all fields and then save and it says mapping saved.
Then when I try to access it again the mapping is gone.
I tried creating a new mapping but once I do it and finish the mapping it gives an error that the mapping file that I just created is not available.
Not sure what I am doing wrong.
Thank you in advance
Regards,
Amin
Seems to have been a temporary issue. its now saving.
Excellent Blog //
Regards,
Krushi Nenavath
Hello Saranya,
Thanks for the helpful documentation.
But I am having a hard time while implementing 5.READ operation. I believe I am doing something wrong in receiver configuration.
I've added this as a query option;
ContactOriginData(ContactOrigin=${header.Key_ContactOrigin},ContactID=${header.Key_ContactID})
My URL looks like this ;
gw/odata/SAP/SCPYMKTODATA;v=1/ContactOriginDataSet(ContactOrigin='SAP_CRM_BUPA',ContactID='0008793186')
And getting error like this;
Help is very much appreciated.
Hi,
The best way to identify the issue is to enable trace on the artifact. And check the trace in the message monitoring and see where exactly the error occurs.
Hello Again Saranya,
Thanks for kind reply. This is the trace I get from message monitoring but could not find any solution.
Hi Mert Can Geyik,
The error occurs from the OData receiver. Please refer to this KBA https://launchpad.support.sap.com/#/notes/2852998 to troubleshoot the receiver error.
Thank you for your documentation.
Can I ask you some questions? Can I custom response body and response header for successful creation(post method)?(same as error response which you mentioned as above. (11. Set Custom Error Code and Response Body for Error)
Hi Sawanee,
As of now, setting a custom response for a successful operation is not possible. OData spec has a defined status codes and response body structure for each OData operation. And our library sets them. It cannot be customized.
I suspect that when I upsert odata at successfactor, I will get response like this
“<?xml version=”1.0″ encoding=”utf-8″?>
when I call post method to custom odata service in CPI
You will have to follow the same, however as per specification: FunctionImport cannot have a request body. This is not adhered to by SuccessFactors upsert.
Thank you so much
hi Mam,
Blog is very useful
We are struct from one month can you please help me how to configure data source entity type .
Our C4C is not able to provide which entity we need to select currently we have 11 iflow .
Here how you select productsets..how can I identify from my c4c which entity I need to select
Please do the need ful
Hi Sree Harsha,
Your question is confusing. As an OData service developer, you have to decide which entity do you want to expose via CPI. You have to select that appropriate entity from your C4C. This is as per your business use case.
Hi Saranya,
We couldn't implemented the query option $count in CPI. What we are trying to do is we need to get all the marketing contacts count from marketing system. But unable to add the query parameter into request URL. Have you ever done this before? If so, can you help?
Hi Mert,
$count doesn't need any implementation. As far as Query is configured, $count will work. The /$count parameter should appear immedietly after the EntitySet name in the URI.
For Example:
/gw/odata/SAP/ODATASAMPLE;v=1/ProductSet/$count
gw/odata/SAP/ODATASAMPLE;v=1/ProductSet/$count?$filter=ProductID gt 6
gw/odata/SAP/ODATASAMPLE;v=1/ProductSet/$count?$filter=ProductID gt 6&$top=1
Hi Saranya,
Yes it is working fine when I give a $top parameter. But without $top parameter, it does not give me the total number. But when I work directly with the endpoint it does give me a proper response.
So;
/gw/odata/SAP/ODATASAMPLE;v=1/ProductSet/$count >> this does not work
I am getting 400 response and adding $count at the end of the url does not effect the REQUEST_URL.
Help is much appreciated
Thanks
Hi,
400 Bad Request must be returned from the OData receiver adapter. Please check the trace thoroughly and analyze the response from OData receiver.
Hello Saranya,
Thank you so much for this blog.
We are getting 400 Bad request error in CREATE operation when we are sending a field in request payload which has property creatable="false".
The error can be elaborately seen as a attachment ODataV2_Adapter_Response_Body when we enable the traces.
But this error needs to be sent back to the requesting client. Could you please guide how should we achieve that.
Regards,
Shikha
Hi Saranya,
Few questions from my end. Please help answer the same
For Ex:
<Products>
<Product>
<integrationKey>test</integrationKey>
<responseStatus>success</responseStatus>
<responseMsg>Message 1</responseMsg>
</Product>
<Product>
<integrationKey>test</integrationKey>
<responseStatus>Success</responseStatus>
<responseMsg>Message 2 </responseMsg>
</Product>
</Products>
Currently we receive java.util.ArrayList cannot be cast to java.util.HashMap error while processing multiple responses.
Regards,
Malgi
Hi Malgi,
Answers for your question.
Hello Saranya,
How can we disable X-CSRF protection for Odata sender as there is no checkbox to enable/disable CSRF in Odata sender adapter?
Regards.
Smit
Hi Smit,
Today disabling the CSRF protection is not possible. We have a development request for the same.
Hi Saranya,
Thank you for the response.
Can you let me know when it will be available?
-Smit
Hi Smit,
Unfortunately I'm not the right person to communicate about the availability of any feature.
Look out for 'OData Sender Updates' section wherein I shall update the new features.
Hi Saranya,
Do you have an example or have suggestions to convert standard REST service to a OData service? SAC can integrate only with OData but I have a service that has REST endpoints. Want to integrate SAC with this REST service and was thinking of a OData wrapper if possible.
Thanks
-ravi
Hi Ravi,
I do not have an example to connect to REST. Refer the documentation https://help.sap.com/viewer/368c481cd6954bdfa5d0435479fd4eaf/Cloud/en-US/e0103db1c7214156bd0612850bafa36d.html
Instead of OData Receiver, you will have an HTTP Receiver.
The final step in the Integration Flow has to have an XML that is compatible with OData Sender. You may have to do some additional steps for that.
Hi Saranya,
First of all, this is such a great blog post. Thanks for putting everything together.
I have a question on query filter. What is the correct way to use query filter. I am getting invalid query filter error on postman. Any idea?
Thanks
Jonas
Hi Jonas,
The ProductID is of type Edm.Int so the filter has to be without the ''. So could refer odata documentation to know how to use the OData filters.
Thanks for your reply. It appears to be working without the ''.
Hi Saranya,
I am trying to send CREATE request while testing the API but it is resulting in 403 forbidden error whereas when I am sending READ request, it works totally fine.
I took the trace in both cases but for CREATE request, it does not reach CPI
Could you please help me out on this.
Regards,
Akshay
Hi Akshay,
Please refer X-CSRF-Token support section in this blog post.
Hello dears. I am sending the following character string in the URI tag to MKT, but when I send the material with the space it gives me an error, is there another way to send it with a space in the material? in mkt it must be replicated with space
Hi Alexandra,
Try %20 instead of space in the <uri>.
%20 is the percent-encoding for space.
Hi Saranya,
great blog. I call my oData service
getTaskSet?$skip=0&$top=100&$filter=to%20eq%20%27tizio.caio%40gmail.com%27%
I read the filter value with
def uriInfo = message.getHeaders().get("UriInfo");
if(uriInfo.getFilter() != null){myLogic...}
But now uriInfo.getFilter() is always null. I read about some update on OData Service in CPI.
Do you have any idea about this problem?
Regard,
Massimiliano
Hi Massimiliano,
Ideally uriInfo.getFilter() should return the filter parameter. Can you verify the filter contains valid filter expression? Also, try with another filter expression. There is no reason for this to return null as long as the expression is valid.
Hi Saranya,
thanks for your reply, it's seem work fine now. I don't touch anything.
Maybe a little server downtime.
BR.
Massimiliano
Hi Saranya,
Thank you for your documentation. Very useful. Only, i have a question of your documentation, please:
I am currently implementing the deep entity scenario. First, I edit the metadata.edmx (Result of the import of OData example metadata) to meet associations.
I did all the steps indicated in the point of Deep Entity (Create Operation/Create Receiver dummy/Mapping the Channel in dummy and real receiver). However in the request mapping, on the left side I associate it with the xsd file generated with the OData Dummy (Sender edmx) and on the right side I associate with the xml metadata from services.odata.org.
But, on the left side, you can see the associations without problems. On the other side, the associations as such are still not visualized.
What would I be missing? It should be noted that placing the xsd of the Receiver Channel (Also edit it according to what you commented in the guide) instead of the metadata of the ODataDemo if the fields are shown, but it generates an error when executing the service
I hope you can help me please. Thank you very much in advance for your answer.
Regards,
Manuel.
Hello Jenibalet Hentry ,
I am also facing same issue . Were you all to solve this issue and how ?
Regards
Tisha
Hello Saranya Baskaran ,
First of all,congratulations for the blog. This is really so very helpful being quite elaborate too.
Now here I am facing some issue in $expand function in the query.
We are dealing with multiple entities and a deep structure on which we have to create a query. So I have used the exact script as you have mentioned and while for me expand operation is working for a single entity but where we have to call multiple entities, it isnt.
Example queries not working:
The error in POSTMAN tool that it is giving is:
"
"
And when I enable traces:
As you can see, the odataURI created takes only 1 last entity from the CamelHTTPQuery.
Also, though its not giving any responce in POSTMAN , it shows as successful status in Message Monitoring of CPI.
And seems to be successfully returning the response as per odataURI but even this incomplete response never reaches back to POSTMAN.
____________________________________________________
2. https://xxxx-iflmap.hcisbt.xxx.hana.ondemand.com/gw/odata/SAP/ODATA_REGISTERED_PRODUCT;v=1/RegisteredProductCollection?$filter=ID eq '2424'&$expand=RegisteredProductDescription,RegisteredProductPartyInformation/RegisteredProductOtherPartyContactParties
3.https://xxxx-iflmap.hcisbt.xxx.hana.ondemand.com/gw/odata/SAP/ODATA_REGISTERED_PRODUCT;v=1/RegisteredProductCollection?$filter=ID eq '2424'&$expand=RegisteredProductPartyInformation/RegisteredProductOtherPartyContactParties
Even in this case odataURI remains the same as above and the message failed unable to find the requested entity: RegisteredProductOtherPartyContactParties
Error:
"Could not find property with name: 'RegisteredProductOtherPartyContactParties'."
Could you please help here. Seems the expand list is taking last in only.
Thanks in advance.
Regards,
Shikha
Need some code adjustment in groovy, then it will work fine. Hope you had fix the issue already.
Regards,
Souvik
Hi Saranya,
Very well articulated blog.
I was able to develop the oData service to consume oData web service. When I did the same to consume SOAP web service, it is giving the error “Requested entity could not be found”. I have also posted as a question in the forum. Can you please check and let me know where I am wrong?
https://answers.sap.com/questions/13116501/develop-odata-service-in-scpi-to-consume-soap-web.html#
Regards,
Anand…
UPDATE: I forgot to add the SET at the end of the url. Also, configured the READ operation. Then it worked.
Hi Saranya,
Thank you for the detailed blog which has been very useful for us.
We are using Sybase ASE Database. SAP UI5 Portal required OData queries to be executed and hence same was implemented with the help of groovy as per this blog.
However, we found that OData Services are supported in Sybase On Premise and OData queries can be executed directly in OnPremise Sybase ASE as per below blogs.
https://blogs.sap.com/2014/07/22/odata-services-in-ase/
https://archive.sap.com/documents/docs/DOC-44412
We wanted to check if Sybase ASE Cloud Database can also support the same, we had raised incident but didn't find the answer. Please let us know if you have any idea on the same.
Thanks and Regards,
Karthik
Hi Saranya ,
Is it possible to use in the same scenario an OData that comes from ECC System ?
The big picture is to receive some information saved in a custom table in ECC System to the mapping process inside the iFlow .
Best regards
Arnold
Hi
I have done message mapping even then system is not considering it as deep insert . How to solve this issue ?
Regards
Tisha
Hi ,
How to handle this in message mapping ?
"The Response of Deep Insert should have a response body with only the root Entity and not the navigation entities"
Regards
Tisha
Hi all & Saranya Baskaran ,
I've got a problem with the $count parameter. The iFlow runs for several minutes since it caches all the values of the EntitySet and does the "count" only after getting all the entries and thus having a massive payload. Is this working as designed or might I have a configuration error within my iFlow? I've got the header ${header.odataURI} in the query options. The odataURI ist not filled with the $count parameter.
The blog states "$select, $count doesn’t need to be handled in the script." but does it mean that it needs to catch all entries of an odata entity and send them to CPI before counting?
BR,
Dominik
Hi Saranya Baskaran
Thank you for sharing.
BR
Hi Saranya Baskaran
I am trying to expose my existing OData service as API following this blog (Using a Trial account).
Unfortunately, when I am trying to access the API portal, the screen showing as
"Your account is being provisioned. Please wait"
Even after 2 days, still, I am getting the same message and busy loader.
The blog I followed to set up integration suite in my Trial account: https://developers.sap.com/tutorials/cp-starter-isuite-onboard-subscribe.html
Any suggestions or help are appreciated.
Hi Saranya,
Could anyone please let us know the exact URL you configured for READ and create operations please if the get operation is working?
I'm trying to execute UPDATE and POST method for API but getting 405 error https://services.odata.org/V3/Northwind/Northwind.svc/Products
Could you let me know how to pass the ID while executing?
Thanks,
Dinesh
Hello Saranya,
thank you for providing this great blog post.
I am trying to create an ODATA API in the Cloud Integration using SOAP as data source.
I am facing with the issue that I can not find UriInfo in the message headers and therefore I can not use your script to get UriInfo object. When tracing my request, I get the following headers back:
Looking forward to your help.
Best regards,
Timo
How is the $format handled in the query section? even though it is not mentioned in the script, it works when tested.. But how?
Hi Saranya,
I`m not able to generate an OData flow in integration suite. Metadata are loaded correctly, I`m able to select Entity Set within the new Binding (simple Query), but when pressing OK to generate flow error message "Unable to generate integration flow" is thrown and I`m not able to continue. Does anyone else noticed similar issue?
Thank's
Greate blog!
Hi Saranya Baskaran ,
I don't want to keep the mapping generated in the iFlow but when i am removing it,its throwing me error like "Entity Set not found in the edmx".What should i do to resolve the error?
It would be really great if anyone could help me with this.
Thanks & Regards,
Keerthana
Hi Saranya Baskaran ,
How to enable inline count query in the odata.Please help me with this.
Regards,
Keerthana
Hello Saranya Baskaran ,
Hope you are doing good.
Thanks for sharing the details blog. I have couple of queries for SuccessFactors related Integration use cases.
Q1. Is it a good options to expose the SuccessFactors ODATA API using ODATA API flow in CPI? If yes is it possible to use Server Snapshot based pagination instead of client side pagination?
Q2. How to read parameters like: paging, customPageSize, fromDate, toDate query parameters for SF Integration use cases.
Expecting a response from you.
Regards,
Souvik