Getting PO ESR object details from transports imported or exported
I found lot of useful information in SDN blogs. First time I am trying to post something. Please let me know your suggestions and thoughts on this blog.
I am writing about how to get a list of all the objects imported to a PO system or exported from a PO system. In this blog I am concentrating only on ESR objects. Will write another blog for ID objects and automation of this process.
Used Postman for this process. Not covering details about postman here. You can download and find documentation about postman at Get Postman . You can load these results into Elastic Search or similar tool to keep history of the transports and search capabilities.
Thanks to Chris Ortloff for working with me on this.
In NWDS, SAP is using a transport service API to get the transport log details into Eclipse. We can use same API to create a process to read the transport objects being moved across different systems. It is working for both file and CTS+ transports. I don’t have access to CMS based transports to test it.
We can call transport service API in two steps to get the details of the objects imported into a PO system.
In postman requests, any variables specified in {{ }} can be either populated through Pre-request script or an environment variable.
In postman create a new collection and save below requests into the collection.
Step# 1:
Service end-point/URI for Transport Service – https://{{system}}/rep/transportservice/int?container=any ( {{system}} – Your host name and port or Load balancer URL )
Operation – POST
In postman enter the transport service URI and specify the authorization details. Copy below XML Input into body of the request to get all imported transports. Don’t pretty print the xml. Use it in the same linear format.
<?xml version="1.0" encoding="UTF-8" ?>
<instance typeid="com.sap.aii.util.hmi.core.msg.HmiRequest"><attribute isleave="true" leave_typeid="string" name="ClientId"><value index="0" isnull="false">1baff32c440311e89289025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientLanguage"><value index="0" isnull="false">EN</value></attribute><attribute isleave="false" name="ClientLevel"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.applcomp.ApplCompLevel"><attribute isleave="true" leave_typeid="string" name="Release"><value index="0" isnull="false">7.50</value></attribute><attribute isleave="true" leave_typeid="string" name="SupportPackage"><value index="0" isnull="false">10</value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="ClientPassword"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientUser"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ControlFlag"><value index="0" isnull="false">0</value></attribute><attribute isleave="true" leave_typeid="string" name="HmiSpecVersion"><value index="0" isnull="false">1.0</value></attribute><attribute isleave="true" leave_typeid="string" name="MethodId"><value index="0" isnull="false">read_transport_log</value></attribute><attribute isleave="false" name="MethodInput"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.api.HmiMethodInput"><attribute isleave="false" name="Parameters"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_from_date</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">{{fromDate}}</value></attribute></instance></value><value index="1" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_to_date</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">{{toDate}}</value></attribute></instance></value><value index="2" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_direction</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">import</value></attribute></instance></value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="RequestId"><value index="0" isnull="false">1baff32d440311e8b10e025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="RequiresSession"><value index="0" isnull="false">true</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerApplicationId"><value index="0" isnull="false">{{ServerApplicationId}}</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerLogicalSystemName"><value index="0" isnull="true"></value></attribute><attribute isleave="true" leave_typeid="string" name="ServiceId"><value index="0" isnull="false">transportservice</value></attribute></instance>
In pre-request Script of postman request paste below code and replace – “XXXXXXX” with your system host name.
This code gets the date and time in milliseconds format. toDate is current date and fromDate is 30 days before today’s date. You can change these variables to control the length of tranport history you want to check.
var toDate = Date.now();
//Substract time in milli seconds to get a range
var fromDate = toDate - 2592000000;
pm.globals.set("toDate", toDate);
pm.globals.set("fromDate", fromDate);
//Replace XXXXXXX with your PI system host name
var ServerApplicationId = "XXXXXXX";
Paste below code in tests tab of postman. This code reads the output xml and parses it to read the transport id’s. It stores all these transport id’s in a global variable to use in the second request to read the objects under each of these transport id’s.
var triggerNext = false;
var transportDetails = [];
//get and set messageId
if(responseCode.code == 200)
{
// Convert XML respose to JSON.
var jsonData = xml2Json(responseBody);
// Get all attributes from the response body
var messageDetails = jsonData["instance"]["attribute"];
// Loop through attributes in the response
messageDetails.forEach(function (obj){
var message = obj["$"]["name"]
// Get the read_transport_log method output from MethodOutput attribute
if(message == "MethodOutput"){
var output = obj["value"]["instance"]["attribute"];
output.forEach(function (obj1){
var name = obj1["$"]["name"];
// Get return variable
if(name == "Return"){
var mapReturn = obj1["value"]["_"];
var outputJson = xml2Json(mapReturn);
var changeList = outputJson["Response"]["Transport"];
// If more than one Transport Id is inculded in reponse body then it comes as an array
if(changeList.length > 1){
changeList.forEach(function (obj2){
var changeGuid = obj2["$"]["id"];
var transportName = obj2["transportListName"]
transportDetails.push({"transportName" : obj2["transportListName"],
"transportId": changeGuid,
"user": obj2["user"]
});
triggerNext = true;
tests["Transport Id: " + changeGuid] = 1 === 1;
})
}else {
var changeGuid = changeList["$"]["id"];
triggerNext = true;
tests["Transport Id: " + changeGuid] = 1 === 1;
transportDetails.push({"transportName" : changeList["transportListName"],
"transportId": changeGuid,
"user": changeList["user"]
});
}
pm.globals.set("loopCount", 0);
// Set a global variable with transport details to use in the next request to get the object details
pm.globals.set("transportDetails", transportDetails);
}
})
}
})
}
// If any transports returned for a give date range then trigger the next request to get the object details else end the chain
if(Boolean(triggerNext)){
pm.globals.unset("transportObjects")
postman.setNextRequest("Get Objects of Change List using ChangeList Guid");
}else{
postman.setNextRequest(null);
}
As shown below, It gives you result of all the transport id’s in the date range specified.
This completed first request in the collection.
Step# 2:
In second request we need to get objects using these transport id’s. For this we need to loop through each of the transport id’s exported in step# 1. So to get the correct output we need to run the collection in the runner instead of running them as separate requests.
Service URI for second request – https://{{system}}/rep/transportservice/int?container=any ( {{system}} – Your host name and port or Load balancer URL )
Operation – POST
In postman enter the transport service URI and specify the authorization. Copy below xml and paste it in the body of the request. Don’t pretty print the xml. Use it in the same linear format.
<?xml version="1.0" encoding="UTF-8" ?>
<instance typeid="com.sap.aii.util.hmi.core.msg.HmiRequest"><attribute isleave="true" leave_typeid="string" name="ClientId"><value index="0" isnull="false">3c17dea91c4911e88f00025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientLanguage"><value index="0" isnull="false">EN</value></attribute><attribute isleave="false" name="ClientLevel"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.applcomp.ApplCompLevel"><attribute isleave="true" leave_typeid="string" name="Release"><value index="0" isnull="false">7.50</value></attribute><attribute isleave="true" leave_typeid="string" name="SupportPackage"><value index="0" isnull="false">2</value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="ClientPassword"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientUser"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ControlFlag"><value index="0" isnull="false">0</value></attribute><attribute isleave="true" leave_typeid="string" name="HmiSpecVersion"><value index="0" isnull="false">1.0</value></attribute><attribute isleave="true" leave_typeid="string" name="MethodId"><value index="0" isnull="false">read_transport_log_details</value></attribute><attribute isleave="false" name="MethodInput"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.api.HmiMethodInput"><attribute isleave="false" name="Parameters"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">TransportId</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">{{transportId}}</value></attribute></instance></value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="RequestId"><value index="0" isnull="false">3c17deaa1c4911e8a4ba025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="RequiresSession"><value index="0" isnull="false">true</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerApplicationId"><value index="0" isnull="false">{{ServerApplicationId}}</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerLogicalSystemName"><value index="0" isnull="true"></value></attribute><attribute isleave="true" leave_typeid="string" name="ServiceId"><value index="0" isnull="false">transportservice</value></attribute></instance>
In pre-request Script of postman paste below code and replace – “XXXXXXX” with you system host name.
This loops through the transport id’s exported from previous request and get objects imported through each of these transport id’s.
// Import both the global variables exported from pervious request
var transportDetails = pm.globals.get("transportDetails");
var loopCount = pm.globals.get("loopCount");
// Replace XXXXXXX with your PI system host name
var ServerApplicationId = "XXXXXXX"
//Loop through guids using loopCount
var details = transportDetails[loopCount];
var transportId = details["transportId"];
console.log(transportId);
//Increase the loop count used to read the guids from array
loopCount = loopCount + 1;
pm.globals.set("transportId", transportId);
pm.globals.set("loopCount", loopCount);
pm.globals.set("details", details);
Paste below code in tests tab of postman. This code reads the output xml and parses it to read the objects in each of the transport id.
console.log(pm.globals.get("transportId"));
var transportObjects = pm.globals.get("transportObjects");
if( transportObjects === undefined){
transportObjects = [];
}
if(responseCode.code == 200)
{
var jsonData = xml2Json(responseBody);
var messageDetails = jsonData["instance"]["attribute"];
messageDetails.forEach(function (obj){
var message = obj["$"]["name"]
if(message == "MethodOutput"){
var output = obj["value"]["instance"]["attribute"];
output.forEach(function (obj1){
var name = obj1["$"]["name"];
if(name == "Return"){
var mapReturn = obj1["value"]["_"];
var outputJson = xml2Json(mapReturn);
var changeList = outputJson["Transport"]["Objects"]["Transported"]["Object"];
if(changeList.length > 1){
changeList.forEach(function (obj2){
var objType = obj2["type"];
var objectName = obj2["name"];
var details = pm.globals.get("details");
//Store required details into a JSON array
transportObjects.push({"name" : obj2["name"],
"namespace" : obj2["namespace"],
"swcv" : obj2["swcv"],
"swcvCaption" : obj2["swcvCaption"],
"transportName" : details["transportName"],
"transportId" : details["transportId"],
"userId" : details["user"]
});
tests[objectName] = 1 === 1;
})
}else {
var objType = changeList["type"];
var objectName = changeList["name"];
var details = pm.globals.get("details");
//Store required details into a JSON array
transportObjects.push({"name" : changeList["name"],
"namespace" : changeList["namespace"],
"swcv" : changeList["swcv"],
"swcvCaption" : changeList["swcvCaption"],
"transportName" : details["transportName"],
"transportId" : details["transportId"],
"userId" : details["user"]
});
tests[objectName] = 1 === 1;
}
}
})
}
})
}
//This variable contains all the details about the objects transported (Currently filtering only mapping objects)
console.log(transportObjects);
pm.globals.set("transportObjects", transportObjects);
var loopCount = pm.globals.get("loopCount"),
transportDetails = pm.globals.get("transportDetails")
//Send same request until loopCount reaches length of guids
if(loopCount < transportDetails.length){
postman.setNextRequest("Get Objects of Change List using ChangeList Guid");
}else {
pm.globals.unset("loopCount");
pm.globals.unset("details");
postman.setNextRequest(null);
}
After completing the setup of second request, open postman runner and select the collection created for these two requests and run the collection.
It should show you result as below, with the details of the objects exported.
You can load these object details into some kind of full text search and index tools. We added an extra step in the process to load these results into opensource Elastic Search – Kibana to keep history of these transports and search capabilities.
JSON file to create this collection in postman added to this Git repository
To get object details from the exported transports in Development system, you can replace the body in first request in step# 1 with below xml. All Pre-request script and test will remain same. Second request will also be same.
<?xml version="1.0" encoding="UTF-8" ?>
<instance typeid="com.sap.aii.util.hmi.core.msg.HmiRequest"><attribute isleave="true" leave_typeid="string" name="ClientId"><value index="0" isnull="false">57bb082c1c4411e8801b025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientLanguage"><value index="0" isnull="false">EN</value></attribute><attribute isleave="false" name="ClientLevel"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.applcomp.ApplCompLevel"><attribute isleave="true" leave_typeid="string" name="Release"><value index="0" isnull="false">7.50</value></attribute><attribute isleave="true" leave_typeid="string" name="SupportPackage"><value index="0" isnull="false">2</value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="ClientPassword"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ClientUser"><value index="0" isnull="false">dummy</value></attribute><attribute isleave="true" leave_typeid="string" name="ControlFlag"><value index="0" isnull="false">0</value></attribute><attribute isleave="true" leave_typeid="string" name="HmiSpecVersion"><value index="0" isnull="false">1.0</value></attribute><attribute isleave="true" leave_typeid="string" name="MethodId"><value index="0" isnull="false">read_transport_log</value></attribute><attribute isleave="false" name="MethodInput"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.api.HmiMethodInput"><attribute isleave="false" name="Parameters"><value index="0" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_status</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">ce</value></attribute></instance></value><value index="1" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_from_date</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">{{fromDate}}</value></attribute></instance></value><value index="2" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_to_date</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">{{toDate}}</value></attribute></instance></value><value index="3" isnull="false"><instance typeid="com.sap.aii.util.hmi.core.gdi2.EntryStringString"><attribute isleave="true" leave_typeid="string" name="Key"><value index="0" isnull="false">p_direction</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">export</value></attribute></instance></value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="RequestId"><value index="0" isnull="false">57bb082d1c4411e8af2c025041000001</value></attribute><attribute isleave="true" leave_typeid="string" name="RequiresSession"><value index="0" isnull="false">true</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerApplicationId"><value index="0" isnull="false">{{ServerApplicationId}}</value></attribute><attribute isleave="true" leave_typeid="string" name="ServerLogicalSystemName"><value index="0" isnull="true"></value></attribute><attribute isleave="true" leave_typeid="string" name="ServiceId"><value index="0" isnull="false">transportservice</value></attribute></instance>
In next blog I will show you the process to get these details for Integration Directory objects. Also a script to automate this whole process to run at regular intervals.
Disclaimer: Details in this blog are tested in PO 7.5 SP10. Not yet using in a production environment. Also don’t know if SAP is going to support this API in future or not.
Thanks for the informative post, Srini!
I think SAP should open up APIs for NetWeaver Java applications. In ABAP you can make an RFC or service out of everything! API Hub is another direction for the cloud. NW Java needs APIs.
SAP already has to use some sort of internal APIs for NWDS. If SAP opens up supported APIs, they can make use of that APIs for their own development. And then there will be a lot of tools developed by the community to enhance e.g. SAP Process Orchestration experience.
Best wishes,
Fatih
Thanks Fatih.
For all the operations in NWDS there is system API behind the scenes being called through eclipse plugin java code. If SAP makes a API portal with all these API's then lot of tools can be developed by community. It improves the usefulness of the tool itself.
Regards,
Srini
Hi Expert,
i tried this blog but i am getting error, can someone help me on this?
Thanks &Regards,
Kiran Polani