I am a bit new to SAP Cloud Integration and I started learning and working on SAP Cloud Integration to integrate Ariba Sourcing via Ariba Network Quote Automation with non SAP ERP.
Initially, I tried to do a lot of research on Ariba adapter provided by SAP but I gave up when I came to know about its limitations. Ariba adapter on SAP CPI is no match to its old counter part of SAP PI. Probably it will be in future, but for now it is not much of a use.
Anyway, my target was to send Quote Request (Sourcing Request) to Ariba Network as MIME Multipart message. I started googling and looking into SAP standard I-Flows, expert blogs and discussions. Out of all,I gave a try to one of the blog where the cXML and attachment were fetched as SFTP files and then fed to MIME Mulitpart encoder. I tried to build the I-Flow in same manner but Ariba Network kept responding error regarding MIME message format. Errors like –
No content allowed before prolog or HTTP 500 error server side error.
Also, as I am getting attachments as base64 encoded string already part of JSON, decoding to attach as attachment object to message and then MIME Multipart encoder encoding again was unnecessary.
Finally, I decided to build the payload using a custom approach just the way Ariba Network wants and send it over standard HTTP receiver adapter.
From customer’s source system, I am getting the RFQ as a JSON payload with attachments as base64 encoded string inside the body.
A test payload from Postman looks like this –
Attachments section is Array in the Quote Request Header section
Here is how my I-Flow looks like this –
- First Step was to convert the incoming JSON file to XML format using XML to JSON converter.
- Next, I used Content Modifier to have configurable exchange properties. Like Buyer AN ID, Ariba AN ID, System ID, Endpoint ID. This information is needed in cXML header section of payload and it is constant information for the customer. Hence I made these as externalized parameters in this content modifier so as to configure production specific values in production tenant.
- The Incoming document, may have or may not have attachments. So using router, I am checking if the attachments section is present in the incoming payload. If not, then default route route1 handles only the main cXML body. If present, then route2 handles payload with attachments.
- Next, via Sequential Multicast, first I am preparing cXML body with the data of quote request document. For this, I added a separate local integration sub flow.
- Generate a unique payload ID and get shared secret of endpoint using groovy script –
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import com.sap.it.api.ITApiFactory; import com.sap.it.api.securestore.*; import com.sap.it.api.keystore.*; def Message processData(Message message) { def map = message.getProperties(); def credAlias = map.get("EndPointCreds"); def service = ITApiFactory.getService(SecureStoreService.class, null); def creds = service.getUserCredential(credAlias); def ss = creds.getPassword(); message.setProperty("SharedSecret", ss); def payloadId = UUID.randomUUID().toString(); message.setProperty("PayloadID", payloadId); return message; }
- I used XSLT mapping to map the incoming fields and externalized parameter values to map to outgoing cXML. Follow the cXML reference guide for exact fields. Only tip here pertaining to this i-flow is, the content ID of the attachment part is needed as in the /QuoteRequestHeader/Comments section. This way the attachment part is connected to main cXML part.
<Attachment> <URL>cid:XXXXX00000000000XXXXXX00000000XXXX</URL> </Attachment> <Attachment> <URL>cid:YYYYY00000000000YYYYYY00000000YYYY</URL> </Attachment>
- Then I added the Multipart boundary to the generated cXML
- Generate a unique payload ID and get shared secret of endpoint using groovy script –
- Once the main sub process is over, I am adding a newline character. This is done so that when join and gather combine the cXML and attachment parts using concatenate, it adds it at new line. If the Quote Request message does not have any attachments, then new line character is not added.
Very simple groovy script to add new line char.import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; def Message processData(Message message) { //Body def body = message.getBody(java.lang.String); message.setBody(body + "n"); return message; }
- In the 2nd branch of sequential multicast, I am extracting attachment section and add it as part of the Multipart message.
- In first sub step, I am using Iterating splitter to handle each attachment(in case of multiple) separately.
Expression Type : XPATH
XPath Expression : /QuoteRequest/QuoteRequestHeader/Attachments - In Content Modifier, add the file attributes and Multipart boundary. In exchange properties, using XPath, I am extracting the file attributes like File Name, Content-Length, File Type etc..
Name Source Type Source Value Data Type Default Value fileContent XPath
/Attachments/Content java.lang.String /Attachments/contentType java.lang.String fileName XPath
/Attachments/fileName java.lang.String /Attachments/contentLength java.lang.String fileContentID XPath
/Attachments/contentID java.lang.String -
Add new line character using groovy script. Only variation here I am using is, if it is a last attachment part, then new line character is not needed. So, I am checking SAP standard property CamelSplitComplete. If it is not last split, then keep on adding new line character. If it is the last split, then do not add new line character. If newline character gets added after end of entire message, Ariba Network throws error like EOF skipping headers
import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; def Message processData(Message message) { //Body def map = message.getProperties(); def last = map.get("CamelSplitComplete"); def tom = 0; if( last == false){ tom = 1; message.setProperty("done",tom ); def body = message.getBody(java.lang.String); message.setBody(body + "n"); } else { message.setProperty("done",tom ); } return message; }
- Gather all the attachment parts using Concatenate aggregation algorithm. Incoming format I kept as Plain Text.
- In first sub step, I am using Iterating splitter to handle each attachment(in case of multiple) separately.
- Join the Sequential multicast branches and gather to concatenate.
- In the step 9 Content Modifier, I added the header parameter
Source Type Source Value Constant Multipart/mixed; boundary=”kdflkajfdksadjfk”;type=”text/xml” - Step 10 was important to convert entire payload as String type other wise the HTTP adapter with HTTP POST call has limitation that makes Ariba Network respond with HTTP 411 Length. required exception.
The link of guided answers from SAP about HTTP receiver adapter asks to convert the payload as byte array/string https://ga.support.sap.com/dtp/viewer/index.html#/tree/2237/actions/28748:45399:41513:41515:44262import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; def Message processData(Message message) { //Body def body = message.getBody(java.lang.String); long msgSize = body.getBytes().length; String payloadSize = msgSize.toString(); message.setBody(body); message.setHeader("Content-Length",payloadSize); return message; }
- Lastly, I used HTTP adapter with Request Reply step to send the final payload to Ariba Network
The response from Ariba Network in CPI trace
Once Ariba Network successfully accepts the cXML mulitpart message, it responds with HTTP 200. This I am storing it to log custom header properties for monitoring purpose. AN’s responds with XML message which I am converting to JSON so that the source system receives the success/failure in the format which they accept.
Also, I have added some exception handling. If there is any exception in message processing, we are triggering email to administrator.
Let me know your views and comments.
Regards,
Yayati Ekbote