Skip to Content
Technical Articles
Author's profile photo sushobhan chakrabarti

Build Custom Monitoring Tool in SAP CPI

I have experience of about 8 years in SAP PI/PO/CPI. Currently working as a CPI Developer.

Background

We have a requirement where we were asked to build a monitoring tool for a specific market. We built one html solution in CPI where we will accept the input from the end market users and display the messages that has passed for the Iflow and Date Range selected. We first built one user interface using html and java script and then made a post call with headers as parameters passed to the second Integration process, where we did the CPI Odata API’s call using the inputs from the first Integration Process. We then split the records and append the output in a html format and display it.

The key fields for the individual interfaces were stored in Custom Header using hashmap, which was suggested by my colleague Shwetha Devanand and she also helped in designing another solution via SAP Fiori, where the User Interface is being developed in Fiori and CPI fetches the data via Odata API and sends in Json format to Fiori application.

In the Integration Process 1, we display the User Interface and then make the post call to the other Integration Process.

Iflow%20with%20both%20the%20Integration%20Process

Iflow with both the Integration Process

IntegrationProcess1

IntegrationProcess1

In the Integration Process 1, we have written the code for the display of Initial Home Page and the code is given below:

<!DOCTYPE html>
<html>
<head>
<p><img align=”middle” src=”ImageUrl.png?”
width=”120″ height=”80″>
<b style=”font-size:25px;color:navy”>Lighthouse: API Monitoring</b>
</p>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link href=”https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css” rel=”stylesheet” integrity=”sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1″ crossorigin=”anonymous”>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js”></script>
<script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js”></script>
<script>
function getInputValue(){
var inputVal = document.getElementById(“mySelect”).value;
var inputVal2 = document.getElementById(“mySelect1”).value;
let h = new Headers();
let url = ‘your url to call the integration process’;/* in our case we provided the Axway url and Axway was calling the second integration process.*/
h.append(‘iflowname’,inputVal)  //value selected from interface dropdown list
h.append(‘daterange’,inputVal2)//value selected from daterange dropdown list
fetch(url,{
method:’POST’,
headers:h,
credentials:’include’

}).then(response => response.text())
.then(function (html) {

// Convert the HTML string into a document object
console.log(h);
let parser = new DOMParser();
let doc = parser.parseFromString(html, ‘text/html’);
document.getElementById(“data”).innerHTML += html;

}).catch(function (err) {
// There was an error
console.warn(‘Something went wrong.’, err);
});
}
function refreshPage(){
document.getElementById(“data”).innerHTML = “”;
}
</script>

</head>
<body style=”background-color:powderblue;”>
<form>
<b> Select Interface and Date Range from dropdown list </b>
<select id=”mySelect” onchange=”refreshPage()”>
<option>MATMAS</option>
<option>DESADV</option>
<option>DeliveryNote</option>
<option>MBGMCR</option>
<option>WHSCON</option>
<option>ZINVRECI</option>
<option>SHPCON</option>
<option>WHSORD</option>
<option>SHPORD</option>
<option>ALL</option>
</select>
<select id=”mySelect1″ onchange=”refreshPage()”>
<option>Last24Hours</option>
<option>LastHour</option>
<option>LastWeek</option>
</select>
</form>
<button class=”btn btn-dark” type=”button” onclick=”getInputValue();”>Get Status</button>
<div id = “data”></div>
</body>
</html>

This will Display this page:

Intitial%20Home%20Page

Initial Home Page

In this page we will select the interface and the date range from the drop down list.

InterfaceList

InterfaceList

DateRange

DateRange

 

Now we select the option and click on Get Status, which will call the other integration process with the values selected in the drop down list as Header(we can find that in the above code)

Now the second Integration process gets called from the Integration Process1 as given in the image below:

Integration%20Process

Integration Process

In the first step we build the query based on header received from IntegrationProcess1

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import java.text.SimpleDateFormat ;
import java.util.Date;
import groovy.time.TimeCategory
def Message processData(Message message) {
use(groovy.time.TimeCategory) {
def map = message.getHeaders();
def value = map.get(“iflowname”)
def daterange = map.get(“daterange”)
def time,query
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm:ss.SSS”);
def currentDate = new Date()

//Add increment hour by 1
if(daterange.equals(“LastHour”)){
def lastHour = currentDate – 60.minutes
time = sdf.format(lastHour)
}
if(daterange.equals(“Last24Hours”)){
def last24Hour = currentDate – 24.hour
time = sdf.format(last24Hour)
}
if(daterange.equals(“LastWeek”)){
def lastWeek = currentDate – 1.week
time = sdf.format(lastWeek)
}
if(daterange.equals(“LastMonth”)){
def lastMonth = currentDate – 1.months;
lastMonth = sdf.format(lastMonth)
time = sdf.format(lastMonth)
}

// We can do this in a Value Mapping table also by maintaining the values selected

if(value.equals(“MATMAS”))
value = “CorrespondingIflowname”
if(value.equals(“SHPORD”))
value = “CorrespondingIflowname”
else if(value.equals(“DeliveryNote”))
value = “CorrespondingIflowname”
else if(value.equals(“WHSORD”))
value = “CorrespondingIflowname”
else if(value.equals(“MBGMCR”))
value = “CorrespondingIflowname”
else if(value.equals(“SHPCON”))
value = “CorrespondingIflowname”
else if(value.equals(“WHSCON”))
value = “CorrespondingIflowname”
else if(value.equals(“ZINVRECI”))
value = “CorrespondingIflowname”
else if(value.equals(“DESADV”))
value = “CorrespondingIflowname”

if(value.equals(“ALL”)) /* mention all the iflowname for the option ALL*/
query = “=IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”or IntegrationArtifact/Name eq”+”‘”+”Iflowname”+”‘”+”and LogEnd gt datetime”+”‘”+time+”‘”+”&”
else
query = “=IntegrationArtifact/Name eq”+”‘”+value+”‘”+”and LogEnd gt datetime”+”‘”+time+”‘”+”&”
message.setProperty(“Query”, query);

return message;
}
}

Give the address as shown below in the Odata Adapter:

CPI%20Odata%20API

CPI Odata API

In the CustomQuery set the query as shown in the image below, and also we put the query property we set in the script earlier.

OdataQuery

OdataQuery

After the Odata response is received,we do the message mapping and map the fields we need for displaying the html output

MessageMapping

MessageMapping

Next, we split the records into individual messages

SplitMessage

SplitMessage

Next, we fetch the values we need to display and store it in property

SetProperty

SetProperty

Next, we append the property in a html body as shown below:

setHtmlBody

setHtmlBody

<tr>
<td><IntegrationFlowName>${property.IntegrationFlowName}</IntegrationFlowName></td>
<td><ReferenceFields>${property.ReferenceFields}</ReferenceFields></td>
<td><Plant>${property.Plant}</Plant></td>
<td><IdocNumber>${property.IdocNumber}</IdocNumber></td>
<td><MessageTimeStamp>${property.MessageTimeStamp}</MessageTimeStamp></td>
<td><FailureReason>${property.IntegrationStatus}</FailureReason></td>
<td style=”color:red;”><FailureReason>${property.FailureReason}</FailureReason></td>
<td><LogEnd>${property.LogEnd}</LogEnd></td>
</tr>

Next, we gather the split messages as shown below:

Gather

Gather

Now, in the final step we build the final Html, code is given below:

HtmlHeadandBodyCode

HtmlHeadandBodyCode

<!doctype html>
<html lang=”en”>
<head>
<meta charset=”utf-8″>
<title>CPI Support</title>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link href=”https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css” rel=”stylesheet” integrity=”sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1″ crossorigin=”anonymous”>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js”></script>
<script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js”></script>
</head>
<body style=”background-color:powderblue;”>
<table class=”table table-bordered”>
<thead class=”thead-dark”>
<tr>
<th scope=”col”>IntegrationFlowName</th>
<th scope=”col”>ReferenceFields</th>
<th scope=”col”>Plant</th>
<th scope=”col”>IdocNumber</th>
<th scope=”col”>MessageTimeStamp</th>
<th scope=”col”>IntegrationStatus</th>
<th scope=”col”>FailureReason</th>
<th scope=”col”>LogTime</th>
</tr>
</thead>
<tbody>
${in.body} // put the gathered body we got from the previous step
</tbody>
</body>
</html>

Next, when we select ALL, it displays the list of interfaces that have failed or ran successfully, similarly we can also view status of individual interfaces.

Output

Output

Final Note

Try this in your interfaces as per your need. We can customize and use the CPI Odata API’s for various other purposes.

That is it for this blog. Try this in your scenarios! Reward points if you like it!

References

https://blogs.sap.com/2019/03/25/sap-cloud-platform-integration-odata-api-for-accessing-the-service-endpoints/

https://help.sap.com/viewer/368c481cd6954bdfa5d0435479fd4eaf/Cloud/en-US/a617d6f37ddc43db8eeb1279662ed5c2.html

 

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.