Skip to Content

In previous blog ESR Objects I explained how to get the details of the objects imported or exported through a CTS+ transport. In this blog I will explain about how to get the DIR object details from Imported/Exported CTS+ transport. We can create a script to load these objects into text search engines like Elastic Search.

This blog is very similar to my previous blog with some minor differences to get ID Objects. Main objective of getting all these object details is to run automatic tests for these objects.

Again used Postman for this process. You can download and find documentation about postman at Get Postman . Using hmi_iflow_transport_service_log service to get the transport details.

Step# 1:

Service end-point/URI for Transport Service – https://{{system}}/dir/hmi_iflow_transport_service_log/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">78598a115e9c11e88c48025041000001</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.0</value></attribute><attribute isleave="true" leave_typeid="string" name="SupportPackage"><value index="0" isnull="false">*</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">enhancedLogAndDetails</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">true</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">import</value></attribute></instance></value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="RequestId"><value index="0" isnull="false">78598a125e9c11e894ef025041000001</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="false"></value></attribute><attribute isleave="true" leave_typeid="string" name="ServiceId"><value index="0" isnull="false">hmi_iflow_transport_service_log</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 next request to read the objects under each of these transport id’s.

var triggerNext = false;
//get and set messageId
if(responseCode.code == 200)
{
    var jsonData = xml2Json(responseBody);

    // console.log(jsonData);
    var messageDetails = jsonData["instance"]["attribute"];
    console.log(messageDetails);
    messageDetails.forEach(function (obj){
    //   console.log(obj);
      var message = obj["$"]["name"]
      if(message == "MethodOutput"){
          var output = obj["value"]["instance"]["attribute"];
          output.forEach(function (obj1){
              var name = obj1["$"]["name"];
            //   console.log(name);
              if(name == "Return"){
                    var mapReturn = obj1["value"]["_"];
                //   console.log(mapReturn);
                    var outputJson = xml2Json(mapReturn);
                    console.log("outputJson: " + outputJson);
                    var changeList = outputJson["Response"]["Transport"];
                    var guids = [];
                    // console.log(changeList);
                    //  console.log(changeList.length);
                    if (changeList !== undefined){
                    if(changeList.length > 1){
                        changeList.forEach(function (obj2){
                            var changeGuid = obj2["$"]["id"];
                            // console.log(changeGuid);
                            guids.push(changeGuid);
                            triggerNext = true;
                            tests["Transport Id: " + changeGuid] = 1 === 1;
                        })
                    }else {
                        var changeGuid = changeList["$"]["id"];
                        guids.push(changeGuid);
                        triggerNext = true;
                        tests["Transport Id: " + changeGuid] = 1 === 1;
                         console.log(changeGuid);
                    }
              }
                    pm.globals.set("guids", guids);
                    pm.globals.set("loopCount", 0);
              }
          })
      }
    })        
}
 pm.globals.unset("toDate");
 pm.globals.unset("fromDate");

if(Boolean(triggerNext)){
    postman.setNextRequest("Get Objects of Change List using ChangeList Guid");
}else{
    postman.setNextRequest(null);
}

Once the request created as above and trigger the request in postman, it should get a result as below with the transport id’s in the data range specified in Pre-request script section.

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}}/dir/hmi_iflow_transport_service_log/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">78598a115e9c11e88c48025041000001</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.0</value></attribute><attribute isleave="true" leave_typeid="string" name="SupportPackage"><value index="0" isnull="false">*</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">enhancedLogAndDetails</value></attribute><attribute isleave="true" leave_typeid="string" name="Value"><value index="0" isnull="false">true</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">import</value></attribute></instance></value></attribute></instance></value></attribute><attribute isleave="true" leave_typeid="string" name="RequestId"><value index="0" isnull="false">78598a125e9c11e894ef025041000001</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="false"></value></attribute><attribute isleave="true" leave_typeid="string" name="ServiceId"><value index="0" isnull="false">hmi_iflow_transport_service_log</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.

var toDate = Date.now();
//Substract time in milli seconds to get a range
var fromDate = toDate - 3592000000;

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 objects in each of the transport id. It also stores the object details into a global variable to use in test scripts

var triggerNext = false;
var transportDetails = [];

if(responseCode.code == 200)
{
    // Parse xml response to JSON
    var jsonData = xml2Json(responseBody);

    // console.log(jsonData);
    var messageDetails = jsonData["instance"]["attribute"];
    console.log(messageDetails);
    messageDetails.forEach(function (obj){
    //   console.log(obj);
      var message = obj["$"]["name"]
      // Get Transport service method output and read the return attribute values to get the transport objects details
      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);
                    console.log("outputJson: " + outputJson);
                    var changeList = outputJson["Response"]["Transport"];
                    var guids = [];
                    // Save transport details to a variable to add them to global variable. This global variable will be used in the testing script to get all the changes imported through transports
                    if (changeList !== undefined){
                    if(changeList.length > 1){
                        changeList.forEach(function (obj2){
                            var changeGuid = obj2["$"]["id"];
                            // console.log(changeGuid);
                            guids.push(changeGuid);
                            transportDetails.push({"transportName" : "ID Transport", 
                                                   "transportId": changeGuid,
                                                   "user": obj2["user"]
                                                    });
                            triggerNext = true;
                            tests["Transport Id: " + changeGuid] = 1 === 1;
                        })
                        console.log(guids);
                    }else {
                        var changeGuid = changeList["$"]["id"];
                        guids.push(changeGuid);
                        transportDetails.push({"transportName" : "ID Transport", 
                                                   "transportId": changeGuid,
                                                   "user": changeList["user"]
                                                    });
                        triggerNext = true;
                        tests["Transport Id: " + changeGuid] = 1 === 1;
                         console.log(guids);
                    }
              }
                    pm.globals.set("guids", guids);
                    pm.globals.set("loopCount", 0);
                    // Set global variable with Transported objects details to be used in subsequent scripts
                    pm.globals.set("transportDetails", transportDetails);
              }
          })
      }
    })        
}

// Clear from and todate from the memory
 pm.globals.unset("toDate");
 pm.globals.unset("fromDate");

// Loop through get object details request if still have further transport id's otherwise set it to null to complete the process
if(Boolean(triggerNext)){
    postman.setNextRequest("Get ID Objects of Change List using ChangeList Guid");
}else{
    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 ID objects exported.

JSON file to create this collection in postman added to this Git repository

Import the file from git repo and import it into Postman to create the collection. Create an Environment with variables System and ServerApplicationId. After that it should be ready to run in Postman runner.

Main objevctive of getting these object details is to run automatic tests for these objects. We planned to run tests for only Operation Mappings and ICO/IFlows. For any other ESR/ID objects changed we will do a where used list to find the related operation mappings and IFlows to run tests for it.

In next blog I will write about running tests for Operation mappings from Postman. For ICO/IFlow testing we will use the blog from Michael Send test message to AEX directly and use postman to help with generating some dynamic values.

We can also submit these objects for code review automatically by generating a task in MS Planner or JIRA etc.

 

Thanks to Chris Ortloff for working with me on this.

 

Disclaimer: Details in this blog are tested in PO 7.5 SP10. Also don’t know if SAP is going to support this API in future or not. 

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Daniel Graversen

    Hi  Srini,

    It is an interesting approach for doing a regression testing. Can you also see if a message mapping has been transported or only directory objects.

    Do you need to check all systems to see what is imported to that system.

    For our test application (Figaf IRT) we are currently looking for if the runtime artifacts change on the system. Using the directory API and simple query to the repository.

    But we should probably also start connecting it to the transports as it will provide an extra layer of validation.

     

    Daniel

    (0) 
    1. Srini Reddy Post author

      Hi Daniel Graversen,

      We can get the mapping or any other ESR objects also as shown in this blog – ESR Objects.

      We came across these APIs when we are trying to automate the testing and code review of SAP PI Transports. With the help of some npm modules (newman, request etc..) we are able to make some progress.

       

      Thanks,

      Srini

      (0) 

Leave a Reply