Recently I came across to a challenge of consuming GraphQL API through SAP PO 7.5. Obviously i though somebody must have had written GraphQL API Call’s through SAP PO, but to my surprise, i was not able to find any single blog on it.
Also Read: SAP PO Certification Preparation Guide
Currently, when industries are moving towards becoming more agile, api based approach, it is obvious that all SAP customer must be looking to integrate their ERP system to various different systems with latest API based solution to make it more secure and reduce the cost of each transactions. As per the new API guidelines, it looks like GraphQL have an edge on rest best API’s.
Before I started with my custom developments, I asked SAP Support help below questions –
- Do we have an adapter or addons which I can use to complete my GraphQL api calls? – Answer was NO.
- Do we have a GraphQL enhancement in any upcoming SAP Patches/Releases? Answer was NO.
- Does SAP have GraphQL in SAP Roadmap? Answer was NO.
So now big question here is – How to consume GraphQL API using SAP PO if it is not supported till now in SAP?
First step to answer this question is to have a basic understanding of GraphQL structure. Please read it from below link.
What is GraphQL?
As per wikipedia –
“GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL was developed internally by Facebook in 2012 before being publicly released in 2015.”
In simple words (My definition) – A graphql enhances rest calls to include query parameters (Mutations/Query) to retrieve exact set of data you need.
To learn more about GraphQL API’s, please follow below link –
https://graphql.org/
A GraphQL API response is json response. It will help you understand that you need to use rest adapter to parse response JSON structure to XML and send it to SAP for below scenario.
Lets consume GraphQL API from SAP PO!
Scenario for this was –
One of the cloud based Order Management System which we bought, deprecated all their Rest Based API’s and moved to GraphQL api’s for their own benefits. We had obvious requirement to connect to this system and complete all kind of transactions, master data exchange, and loading various data to their system. Communication protocol for this system was only GraphQL API.
In this scenario, we need to create a location in Fluent OMS system and send the response back to SAP for further processing. To create a location in Fluent OMS, it needs an access token to authenticate the request and set it up in receiver adapter’s HTTP Header.
Below is the scenario diagram –
Adapter to be used for this scenario –
SAP to SAP PO proxy communication – SOAP Adapter with XI 3.0 message protocol
SAP PO to Fluent OMS GraphQL API – Receiver Rest Adapter
For SAP PO to Fluent Lookup – Receiver Rest Adapter
How to develop above scenario?
- A Rest Lookup to be done through Java Mapping
- A Java Mapping to read response from Rest Lookup and dynamically assigning it to Receiver Rest adapter
- Reading Source XML and preparing GraphQL API Request and posting it to Receiver Rest Adapter in Java Mapping
- Setting of Receiver Rest Adapter
I am not going to show PI developments as it is same till Service Interface creation of a synchronous scenario. Java Mapping code can be found at the end of the blog.
Also, as per step 3, we will be mapping our Source XML to Target using Java Mapping, so we will not need Request Message Mapping. We will only create Response Message Mapping which will be executed once we get response from GraphQL API/Server.
Receiver Rest Adapter configurations are same as all rest API calls as well as dynamic configuration is similar to normal rest adapter configurations.
One Important step to note is the structure of GraphQL API request –
It doesn’t matter whether it is mutation or a query, structure for request should look like below structure-
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
I hope it will help many people who want to consume GraphQL API’s from SAP PO. Reach out to me if you have any confusion through comment section.
Below is the Java Mapping Code which will complete above 3 steps –
package com.xxx.fluentLocationCreate;
import com.sap.aii.mapping.api.*;
import java.io.*;
import java.util.*;
import java.util.Map;
import com.sap.aii.mapping.lookup.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//import org.json.JSONObject;
//import org.json.XML;
import com.sap.aii.mapping.lookup.Channel;
import com.sap.aii.mapping.lookup.LookupService;
import com.sap.aii.mapping.lookup.Payload;
import com.sap.aii.mapping.lookup.SystemAccessor;
import com.sap.aii.mappingtool.tf7.rt.Container;
@SuppressWarnings("deprecation")
public class FluentLocationCreate implements StreamTransformation {
String access_token ="";
String Authorization = "";
private Map param = null;
private MappingTrace trace = null;
/*Defining graphql Location structure fixed fields*/
String GQLqueryMutation = "{\"query\":\"mutation createLocation ($retailerId: ID!, $locationReference: String!, $locationName: String!, $locationType: String!, $locationAddressRef: String!, $locationLatitude: Float!, $locationLongitude: Float!, $locationAddressStreet: String, $locationAddressCity: String, $locationAddressState: String, $locationAddressPostCode: String, $locationAddressRegion: String, $locationAddressCountry: String,$monStart: Int!,$monEnd: Int!,$tueStart: Int!,$tueEnd: Int!,$wedStart: Int!,$wedEnd: Int!,$thuStart: Int!,$thuEnd: Int!,$friStart: Int!,$friEnd: Int!,$satStart: Int!,$satEnd: Int!,$sunStart: Int!,$sunEnd: Int!) {\\n createLocation (input: {\\n ref: $locationReference,\\n name: $locationName,\\n retailer: { id: $retailerId },\\n type: $locationType,\\n primaryAddress: {\\n ref: $locationAddressRef,\\n street: $locationAddressStreet,\\n city: $locationAddressCity,\\n state: $locationAddressState,\\n postcode: $locationAddressPostCode,\\n region: $locationAddressRegion,\\n country: $locationAddressCountry,\\n latitude: $locationLatitude,\\n longitude: $locationLongitude\\n },\\n openingSchedule: {\\n allHours: true,\\n monStart: $monStart,\\n monEnd: $monEnd,\\n tueStart: $tueStart,\\n tueEnd: $tueEnd,\\n wedStart: $wedStart,\\n wedEnd: $wedEnd,\\n thuStart: $thuStart,\\n thuEnd: $thuEnd,\\n friStart: $friStart,\\n friEnd: $friEnd,\\n satStart: $satStart,\\n satEnd: $satEnd,\\n sunStart: $sunStart,\\n sunEnd: $sunEnd\\n }\\n }) {\\n id\\n ref\\n status\\n }\\n}\",";
String GQLvariables = "\"variables\":{\"retailerId\":";
private Map map;
/* method setParamters is required, but we do not anything with it*/
public void setParameter(Map param)
{
map = param;
}
public void execute(InputStream in, OutputStream out) throws StreamTransformationException
{
String sysName = (String) System.getProperty("SAPSYSTEMNAME");
System.out.println("Systemname:" + sysName);
System.out.println("Source XML Payload" + in);
Payload payload = LookupService.getXmlPayload(in);
InputStream inp = payload.getContent();
/* Call to Fluent API to get Access Tokens which will be passed in receiver adapter in headers */
getAccessTokenFromFluent();
/* Set Access Token Variable - It will be passed in Rest adapter header for each mutation API Call */
Authorization = "Bearer"+" "+access_token;
System.out.println("Authorization Header Value:" + Authorization);
System.out.println("Source XML Payload" + in.toString());
//Setup GraphQL Variable tag's values - Actual Values coming from Source System - SAP
String retailerId = ""; String locationReference = "";
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(inp);
System.out.println("Input XML:" + doc);
NodeList retailerId1 = doc.getElementsByTagName("retailer");
Node retID = retailerId1.item(0); System.out.println("Retailer ID:"+ doc.getElementsByTagName("retailer"));
if (retID != null)
{
retID = retID.getFirstChild();
if (retID != null)
{
retailerId = retID.getNodeValue(); //RetailerID value from Incoming XML
System.out.println(retailerId);
}
}
NodeList locationReference1 = doc.getElementsByTagName("ref");
Node locRef = locationReference1.item(0);
if (locRef != null)
{
locRef = locRef.getFirstChild();
if (locRef != null)
{
locationReference = locRef.getNodeValue(); //locationReference value from Incoming XML
System.out.println(locationReference);
}
}
NodeList locationName1 = doc.getElementsByTagName("name");
Node locName = locationName1.item(0);
String locationName = "";
if (locName != null)
{
locName = locName.getFirstChild();
if (locName != null)
{
locationName = locName.getNodeValue(); //locationName value from Incoming XML
System.out.println(locationName);
}
}
NodeList locationType1 = doc.getElementsByTagName("type");
Node locType = locationType1.item(0);
String locationType = "";
if (locType != null)
{
locType = locType.getFirstChild();
if (locType != null)
{
locationType = locType.getNodeValue(); //locationType value from Incoming XML
System.out.println(locationType);
}
}
NodeList locationAddressRef1 = doc.getElementsByTagName("locationAddressRef");
Node locAddRef = locationAddressRef1.item(0);
String locationAddressRef = "";
if (locAddRef != null)
{
locAddRef = locAddRef.getFirstChild();
if (locAddRef != null)
{
locationAddressRef = locAddRef.getNodeValue(); //locationType value from Incoming XML
System.out.println(locationAddressRef);
}
}
NodeList locationAddressStreet1 = doc.getElementsByTagName("street");
Node locAddstr = locationAddressStreet1.item(0);
String locationAddressStreet = "";
if (locAddstr != null)
{
locAddstr = locAddstr.getFirstChild();
if (locAddstr != null)
{
locationAddressStreet = locAddstr.getNodeValue(); //locationType value from Incoming XML
System.out.println(locationAddressStreet);
}
}
NodeList locationAddressCity1 = doc.getElementsByTagName("city");
Node locAddcity = locationAddressCity1.item(0);
String locationAddressCity = "";
if (locAddcity != null)
{
locAddcity = locAddcity.getFirstChild();
if (locAddcity != null)
{
locationAddressCity = locAddcity.getNodeValue(); //locationAddressCity value from Incoming XML
System.out.println(locationAddressCity);
}
}
NodeList locationAddressState1 = doc.getElementsByTagName("state");
Node locAddState = locationAddressState1.item(0);
String locationAddressState = "";
if (locAddState != null)
{
locAddState = locAddState.getFirstChild();
if (locAddState != null)
{
locationAddressState = locAddState.getNodeValue(); //locationAddressState value from Incoming XML
System.out.println(locationAddressState);
}
}
NodeList locationAddressPostCode1 = doc.getElementsByTagName("postcode");
Node locAddpostcode = locationAddressPostCode1.item(0);
String locationAddressPostCode = "";
if (locAddpostcode != null)
{
locAddpostcode = locAddpostcode.getFirstChild();
if (locAddpostcode != null)
{
locationAddressPostCode = locAddpostcode.getNodeValue(); //locationAddressPostCode value from Incoming XML
System.out.println(locationAddressPostCode);
}
}
NodeList locationAddressRegion1 = doc.getElementsByTagName("region");
Node locAddpostReg = locationAddressRegion1.item(0);
String locationAddressRegion = "";
if (locAddpostReg != null)
{
locAddpostReg = locAddpostReg.getFirstChild();
if (locAddpostReg != null)
{
locationAddressRegion = locAddpostReg.getNodeValue(); //locationAddressRegion value from Incoming XML
System.out.println(locationAddressRegion);
}
}
NodeList locationAddressCountry1 = doc.getElementsByTagName("country");
Node locAddctry = locationAddressCountry1.item(0);
String locationAddressCountry = "";
if (locAddctry != null)
{
locAddctry = locAddctry.getFirstChild();
if (locAddctry != null)
{
locationAddressCountry = locAddctry.getNodeValue(); //locationAddressCountry value from Incoming XML
System.out.println(locationAddressCountry);
}
}
NodeList locationLatitude1 = doc.getElementsByTagName("latitude");
Node loclat = locationLatitude1.item(0);
String locationLatitude = "";
if (loclat != null)
{
loclat = loclat.getFirstChild();
if (loclat != null)
{
locationLatitude = loclat.getNodeValue(); //locationLatitude value from Incoming XML
System.out.println(locationLatitude);
}
}
NodeList locationLongitude1 = doc.getElementsByTagName("latitude");
Node loclong = locationLongitude1.item(0);
String locationLongitude = "";
if (loclong != null)
{
loclong = loclong.getFirstChild();
if (loclong != null)
{
locationLongitude = loclong.getNodeValue(); //locationLongitude value from Incoming XML
System.out.println(locationLongitude);
}
}
NodeList monStart1 = doc.getElementsByTagName("monStart");
Node mStart = monStart1.item(0);
String monStart = "";
if (mStart != null)
{
mStart = mStart.getFirstChild();
if (mStart != null)
{
monStart = mStart.getNodeValue(); //monStart value from Incoming XML
System.out.println(monStart);
}
}
NodeList monEnd1 = doc.getElementsByTagName("monEnd");
Node mEnd = monEnd1.item(0);
String monEnd = "";
if (mEnd != null)
{
mEnd = mEnd.getFirstChild();
if (mEnd != null)
{
monEnd = mEnd.getNodeValue(); //monEnd value from Incoming XML
System.out.println(monEnd);
}
}
NodeList tueStart1 = doc.getElementsByTagName("tueStart");
Node tuStart = tueStart1.item(0);
String tueStart = "";
if (tuStart != null)
{
tuStart = tuStart.getFirstChild();
if (tuStart != null)
{
tueStart = tuStart.getNodeValue(); //tueStart value from Incoming XML
System.out.println(tueStart);
}
}
NodeList tueEnd1 = doc.getElementsByTagName("tueEnd");
Node tuEnd = tueEnd1.item(0);
String tueEnd = "";
if (tuEnd != null)
{
tuEnd = tuEnd.getFirstChild();
if (tuEnd != null)
{
tueEnd = tuEnd.getNodeValue(); //tueEnd value from Incoming XML
System.out.println(tueEnd);
}
}
NodeList wedStart1 = doc.getElementsByTagName("wedStart");
Node wStart = wedStart1.item(0);
String wedStart = "";
if (wStart != null)
{
wStart = wStart.getFirstChild();
if (wStart != null)
{
wedStart = wStart.getNodeValue(); //wedStart value from Incoming XML
System.out.println(wedStart);
}
}
NodeList wedEnd1 = doc.getElementsByTagName("wedEnd");
Node wEnd = wedEnd1.item(0);
String wedEnd = "";
if (wEnd != null)
{
wEnd = wEnd.getFirstChild();
if (wEnd != null)
{
wedEnd = wEnd.getNodeValue(); //wedEnd value from Incoming XML
System.out.println(wedEnd);
}
}
NodeList thuStart1 = doc.getElementsByTagName("thuStart");
Node thStart = thuStart1.item(0);
String thuStart = "";
if (thStart != null)
{
thStart = thStart.getFirstChild();
if (thStart != null)
{
thuStart = thStart.getNodeValue(); //thuStart value from Incoming XML
System.out.println(thuStart);
}
}
NodeList thuEnd1 = doc.getElementsByTagName("thuEnd");
Node thEnd = thuEnd1.item(0);
String thuEnd = "";
if (thEnd != null)
{
thEnd = thEnd.getFirstChild();
if (thEnd != null)
{
thuEnd = thEnd.getNodeValue(); //thuEnd value from Incoming XML
System.out.println(thuEnd);
}
}
NodeList friStart1 = doc.getElementsByTagName("friStart");
Node frStart = friStart1.item(0);
String friStart = "";
if (frStart != null)
{
frStart = frStart.getFirstChild();
if (frStart != null)
{
friStart = frStart.getNodeValue(); //friStart value from Incoming XML
System.out.println(friStart);
}
}
NodeList friEnd1 = doc.getElementsByTagName("friEnd");
Node frEnd = friEnd1.item(0);
String friEnd = "";
if (frEnd != null)
{
frEnd = frEnd.getFirstChild();
if (frEnd != null)
{
friEnd = frEnd.getNodeValue(); //friEnd value from Incoming XML
System.out.println(friEnd);
}
}
NodeList satStart1 = doc.getElementsByTagName("satStart");
Node saStart = satStart1.item(0);
String satStart = "";
if (saStart != null)
{
saStart = saStart.getFirstChild();
if (saStart != null)
{
satStart = saStart.getNodeValue(); //satStart value from Incoming XML
System.out.println(satStart);
}
}
NodeList satEnd1 = doc.getElementsByTagName("satEnd");
Node saEnd = satEnd1.item(0);
String satEnd = "";
if (saEnd != null)
{
saEnd = saEnd.getFirstChild();
if (saEnd != null)
{
satEnd = saEnd.getNodeValue(); //satEnd value from Incoming XML
System.out.println(satEnd);
}
}
NodeList sunStart1 = doc.getElementsByTagName("sunStart");
Node suStart = sunStart1.item(0);
String sunStart = ""; //sunStart value from Incoming XML
if (suStart != null)
{
suStart = suStart.getFirstChild();
if (suStart != null)
{
sunStart = suStart.getNodeValue(); //sunStart value from Incoming XML
System.out.println(sunStart);
}
}
NodeList sunEnd1 = doc.getElementsByTagName("sunEnd");
Node suEnd = sunEnd1.item(0);
String sunEnd = "";
if (suEnd != null)
{
suEnd = suEnd.getFirstChild();
if (suEnd != null)
{
sunEnd = suEnd.getNodeValue(); //sunEnd value from Incoming XML
System.out.println(sunEnd);
}
}
// Initial GQLvariables value is "\"variables\":{\"retailerId\":"
GQLvariables = GQLvariables + retailerId +","+"\"locationReference\":\""+locationReference+"\""+",\"locationName\":\""+locationName+"\""+",\"locationType\":\""+locationType+"\""+",\"locationAddressRef\":\""+locationAddressRef+"\""+",\"locationAddressStreet\":\""+locationAddressStreet+"\""+",\"locationAddressCity\":\""+locationAddressCity+"\""+",\"locationAddressState\":\""+locationAddressState+"\""+",\"locationAddressPostCode\":\""+locationAddressPostCode+"\""+",\"locationAddressRegion\":\""+locationAddressRegion+"\""+",\"locationAddressCountry\":\""+locationAddressCountry+"\""+",\"locationLatitude\":"+locationLatitude+",\"locationLongitude\":"+locationLongitude
+",\"monStart\":"+monStart+",\"monEnd\":"+monEnd+",\"tueStart\":"+tueStart+",\"tueEnd\":"+tueEnd+",\"wedStart\":"+wedStart+",\"wedEnd\":"+wedEnd+",\"thuStart\":"+thuStart+",\"thuEnd\":"+thuEnd+",\"friStart\":"+friStart+",\"friEnd\":"+friEnd+",\"satStart\":"+satStart+",\"satEnd\":"+satEnd+",\"sunStart\":"+sunStart+",\"sunEnd\":"+sunEnd+"}}";
System.out.println(GQLvariables);
//Write Dynamic Configuration logic here to update dynamic header in rest receiver adapter
DynamicConfigurationKey KEY = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/REST", "access_token");
DynamicConfiguration conf = (DynamicConfiguration)map.get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);
conf.put(KEY,access_token);
System.out.println("After Dynamic Config");
GQLqueryMutation = GQLqueryMutation+GQLvariables;
System.out.println(GQLqueryMutation);
out.write(GQLqueryMutation.getBytes());
}
catch (Exception e) {
e.printStackTrace();
}
}
private void getAccessTokenFromFluent()
{
try
{
AbstractTrace atTrace = null;
atTrace = (AbstractTrace) map.get(StreamTransformationConstants.MAPPING_TRACE);
Channel channel = LookupService.getChannel("BC_Fluent","CC_REST_FluentAuthenticateLookup_Receiver");
String SystemName = (String) System.getProperty("SAPSYSTEMNAME");
System.out.println("Systemname:" + SystemName);
System.out.println(channel);
SystemAccessor accessor = null;
accessor = LookupService.getSystemAccessor(channel);
System.out.println(accessor);
System.out.println("Lookupservice accesor value :" + LookupService.getSystemAccessor(channel).toString());
String authorizationxml = "{}";
System.out.println("Authoriation XML:"+authorizationxml);
atTrace.addWarning("******* authorizationxml created *******");
atTrace.addWarning("Authorizationxml:"+authorizationxml);
InputStream inputStream = new ByteArrayInputStream(authorizationxml.getBytes());
System.out.println("After GetBytes:" + inputStream.toString());
Payload payload = LookupService.getXmlPayload(inputStream);
System.out.println("After getXMLPayload and Value of Payload" + payload.getContent() + payload.toString() + payload);
Payload RestOutPayload = null;
try {
System.out.println("Before making accessor.call");
RestOutPayload = accessor.call(payload);
System.out.println("After making accessor.call");
}
catch (LookupException e)
{
e.printStackTrace();
}
//System.out.println(SOAPOutPayload);
atTrace.addWarning("******* get content *******");
InputStream inp = RestOutPayload.getContent();
System.out.println("RestOutpayload value" + RestOutPayload.getContent());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
System.out.println("INP Value:" + inp);
Document document = builder.parse(inp);
NodeList access_token1 = document.getElementsByTagName("access_token");
System.out.println("Access Token value:" + document.getElementsByTagName("access_token"));
System.out.println("access_token:"+ access_token1);
Node node = access_token1.item(0);
if (node != null)
{
node = node.getFirstChild();
if (node != null)
{
access_token = node.getNodeValue();
System.out.println("access_token:" + access_token);
}
}
else
access_token = "WrongAccessCode";
}
catch(Exception e)
{
e.printStackTrace();
}
}
}