SAP IdM – accessing a REST API / WebService via JavaScript
Using the incredibly useful post by Jannis Rondorf as a starting point, I wanted to write this blog post to show how IdM could access a REST API / WebService (HTTP & HTTPS) only using JAVA & JavaScript (without any custom JAVA classes or VDS); the following script should be pretty self-explanatory for the most part, but comments should hopefully make things a little clearer; please feel free to comment if you have any further doubts / questions and I will do my best to answer them:
// Main function:pushDataToAPI
function pushDataToAPI(Par) {
importClass(Packages.java.net.HttpURLConnection);
importClass(Packages.java.net.URL);
importClass(Packages.java.io.DataOutputStream);
importClass(Packages.java.io.ByteArrayOutputStream);
var targetAPI = Par.get("TARGET_SYSTEM");
var APIintent = "";
var systemA_URL = "http://systemAHostNAME:1234/api/user";
var systemD_CreateURL = "http://systemDHostName:1234/system_API/systemapi.svc/rest/createUser";
var systemD_UpdateURL = "http://systemDHostName:1234/system_API/systemapi.svc/rest/updateUser";
var systemD_Username = "systemDAPIUser";
var systemD_Password = "systemD@APIUser1234";
var systemD_Encoding = uToBase64(systemD_Username + ":" + systemD_Password); //encode the system D user credentials into Base64
var systemA_Username = "systemARestAPIUser";
var systemA_Password = "systemA@RestAPIUser1234";
var systemA_Encoding = uToBase64(systemA_Username + ":" + systemA_Password); //encode the system A user credentials into Base64
var logFileText = "";
var urlString = "";
//determine target URL based on passed in TARGET_SYSTEM parameter
//with System D, there are two separate URLs for User CREATION and User UPDATING
if (targetAPI == "systemA")
urlString = systemA_URL;
else if (targetAPI == "systemD") {
APIintent = Par.get("API_intent");
if (APIintent == "Create") {
uWarning("In System D section now, going to hit CREATE API: ");
urlString = systemD_CreateURL;
} else if (APIintent == "Update") {
uWarning("In System D section now, going to hit UPDATE API: ");
urlString = systemD_UpdateURL;
}
}
uWarning("FOR THIS API system: " + targetAPI);
uWarning("determined URL: ");
uWarning(urlString);
//get the necessary attributes from the entry being processed currently
var currentEntry = uGetEntryID();
var allAttrs = uIS_GetEntry(currentEntry);
var entFName = allAttrs.get("MX_FIRSTNAME");
var entLName = allAttrs.get("MX_LASTNAME");
var entUsername = allAttrs.get("MSKEYVALUE");
var entServiceType = allAttrs.get("MX_FS_PERSONNEL_AREA");
var entPayrollNo = allAttrs.get("MX_FS_PERSONNEL_NUMBER");
var entEmail = allAttrs.get("MX_MAIL_PRIMARY");
var entPhone = allAttrs.get("MX_PHONE_PRIMARY");
var entMobile = allAttrs.get("MX_MOBILE_PRIMARY");
var entDepartment = allAttrs.get("MX_DEPARTMENT");
var entPrivCode = allAttrs.get("MX_FS_JOB_ID");
var entEmpStatus = allAttrs.get("MX_FS_EMPLOYMENT_STATUS_ID");
var entAdocode = allAttrs.get("MX_FS_ORGANIZATIONAL_UNIT_ID");
var entOrgID = allAttrs.get("MX_DEPARTMENT");
var entAccountingCode = allAttrs.get("MX_ACCOUNTING_NUMBER");
//set up the message data based on the specific system; system A in this case accepts XML data via its API, while system D accepts JSON data.
var systemA_ObjData = '<UserDataFeed xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="SystemA.API.Integration">\n<FirstName>' + entFName + '</FirstName>\n<LastName>' + entLName + '</LastName>\n<EmploymentTypeID>' + entServiceType + '</EmploymentTypeID>\n<StaffNumber>' + entPayrollNo + '</StaffNumber>\n<Adocode>' + entAdocode + '</Adocode>\n<UnitID>' + entOrgID + '</UnitID>\n<Workemail>' + entEmail + '</Workemail>\n<MilitaryTelephone>' + entPhone + '</MilitaryTelephone>\n<MobileTelephone>' + entMobile + '</MobileTelephone>\n<Username>' + entEmail + '</Username>\n<UserStatusID>' + entEmpStatus + '</UserStatusID>\n<UserType>' + entPrivCode + '</UserType>\n<Position>' + entDepartment + '</Position>\n<Employer>IDMEmployer</Employer>\n<BudgetCode>' + entAccountingCode + '</BudgetCode>\n</UserDataFeed>';
var systemD_ObjData = '{"emailAddress" : "' + entEmail + '","firstName" : "' + entFName + '","lastName" : "' + entLName + '","customerIdentifier" : "' + entAdocode + '","roleIdentifier" : "' + entPrivCode + '"}';
var objWithVars = '';
//determine data to be sent to target system
if (targetAPI == "systemA")
objWithVars = systemA_ObjData;
else if (targetAPI == "systemD")
objWithVars = systemD_ObjData;
uWarning("determined obj data: ");
uWarning(objWithVars);
//convert data to be sent into ByteArray
var bytesObj = new ByteArrayOutputStream();
bytesObj.reset();
for (var i = 0; i < objWithVars.length; ++i) {
bytesObj.write(objWithVars.charCodeAt(i));
}
var url = new URL(urlString); //create new URL based on selected target system
var writeConn;
writeConn = url.openConnection(); //open an HTTP connection using the selected URL
writeConn.setDoOutput(true); //set output flag for HTTP connection to be true
writeConn.setDoInput(true); //set input flag for HTTP connection to be true
var verb = "POST";
//based on selected target system, select the HTTP VERB; in this case it is either PUT if System D has to be updated, or POST for any other scenario
if (targetAPI == "systemD" && APIintent == "Update") {
verb = "PUT";
writeConn.setRequestMethod(verb);
} else if (targetAPI == "systemA" || (targetAPI == "systemD" && APIintent == "Create")) {
verb = "POST";
writeConn.setRequestMethod(verb);
}
//set up the authorization header for based on the selected target system; this section can be skipped if using an REST / WebService endpoint,
//that does not require authentication
if (targetAPI == "systemD") {
writeConn.setRequestProperty("Authorization", "Basic " + systemD_Encoding);
} else if (targetAPI == "systemA") {
writeConn.setRequestProperty("Authorization", "Basic " + systemA_Encoding);
}
//set up the content-type header based on the selected target system
if (targetAPI == "systemD") {
writeConn.setRequestProperty("Content-Type", "application/json");
} else {
writeConn.setRequestProperty("Content-Type", "application/xml");
}
uWarning("set request method and content type! Going to push data to API now!");
//initiate a connection to the selected target system and then write the composed data to that system
//response messages are then logged once the
try {
writeConn.connect();
var dataOS = new DataOutputStream(writeConn.getOutputStream());
dataOS.write(bytesObj.toByteArray(), 0, bytesObj.size());
dataOS.flush();
dataOS.close();
} catch (idmerr) {
uWarning("Encountered error while trying to push data to API: ");
uWarning(idmerr);
uWarning(idmerr.message);
uSkip(1);
}
var responseCode = writeConn.getResponseCode();
var responseMsg = writeConn.getResponseMessage();
uWarning("Response MSG for the " + verb + ": " + responseMsg);
uWarning("Response CODE for the " + verb + ": " + responseCode);
uWarning("Header FIELD 0 for the " + verb + ": " + writeConn.getHeaderField(0));
//if a response code other than 200 or 201 is encountered, that means an error has occurred and this will then be written to an external log file
//if logging to a file is not required this section can be removed.
if (writeConn.getResponseCode() !== 200 && writeConn.getResponseCode() !== 201) {
logFileText += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r\n";
logFileText += "For SYSTEM " + targetAPI + "\r\n";
logFileText += "hitting API URL: " + urlString + "\r\n";
logFileText += "with this payload:\n " + objWithVars + "\r\n";
logFileText += "*************************************\r\n";
logFileText += "THIS IS THE Header FIELD 0:\n " + writeConn.getHeaderField(0) + "\r\n";
logFileText += "THIS IS THE RESULT CODE:\n " + writeConn.getResponseCode() + "\r\n";
logFileText += "THIS IS THE RESULT MESSAGE:\n " + writeConn.getResponseMessage() + "\r\n";
logFileText += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r\n";
uToFile("C:\\IDM_ErrorFile.txt", logFileText, true);
}
//finally disconnect the HTTP connection after the data has been pushed to the REST API / WebService
writeConn.disconnect();
}
NOTE: HTTP & HTTPS connections can both be accessed using the HTTPUrlConnection JAVA Class; in the case of an HTTPS API, you will need to import the SSL certificate of the secured target API web server, into the JAVA Keystore (following the IDM documentation:Exporting and Installing the SSL Certificate – SAP Identity Management Installation and Update Guide – SAP Library); if the SSL certificate is not imported you will encounter an SSL handshake error, when trying to access the secured HTTPS REST API / WebService.
Using similar logic / concepts you can retrieve data from a REST API / WebService instead of pushing data to it (as demonstrated in the above example).
I hope this helps give you a better understanding of how you can access a REST API directly from SAP IDM, simply using JAVA / JavaScript, as well as the different use cases this can be applied to.
Excellent blog Sandeep!
Thank you so much Kautilya! 😀