How to create Bussiness Rules
Go to All → System Definition → Business Rules
Create New in Application “Global”:
Select “Table”
Check “Advanced”
Select “When to run”
Go to “Advanced” and add the code to be executed (Turn on ECMAScript 2021 (ES12) mode)
(function executeRule(current, previous /*null when async*/) {
try{
let tmAction = new global.TMAction();
//uncomment if Quality Checks should be enabled
/*let qas = tmAction.checkQAS(current);
qas = typeof qas == "string" ? JSON.parse(qas) : qas;
if(qas.type == "E"){
tmAction.addComment(current, qas.message);
} else if(qas.length){
if(qas[0]["HasCollisions"] || qas[0]["HasCriticalObj"] || qas[0]["HasDependency"] || qas[0]["HasSpecialAspects"]){
qas.shift();
tmAction.addComment(current, JSON.stringify(qas));
}
}*/
let response = tmAction.startToC(current);
response = typeof response == "string" ? JSON.parse(response) : response;
if(response.type == "E"){
tmAction.addComment(current, response.message);
}
} catch(error){
gs.addErrorMessage(error); //add error message for user
current.setAbortAction(true); //abort state change action
}
})(current, previous);
Go to All → System Definition → Business Rules
Create New in Application “Global”:
Select “Table”
Check “Advanced”
Select “When to run”
Go to “Advanced” and add the code to be executed
(function executeRule(current, previous /*null when async*/) {
try{
let tmAction = new global.TMAction();
let response = tmAction.approveRequests(current, {"levelName": "Production", "approve": "A", "type": "C", "group": "CONS"});
/*{"levelName": TM LEVEL,
"approve": A to approve, R to revoke,
"type": C or T,
"group": TM Group
}*/
//BUG FIX for response parsing
response = typeof response == "string" && response.startsWith("{") ? JSON.parse(response) : response;
if(response != "SUCCESS" && response.type == "E"){
tmAction.addComment(current, response.message);
}
} catch(error){
gs.addErrorMessage(error.message); //add error message for user
current.setAbortAction(true); //abort state change action
}
})(current, previous);(function executeRule(current, previous /*null when async*/) {
let tmAction = new global.TMAction();
try{
const config = {
"levelName": "Quality",
"approve": "R",
"type": "C",
"group": "",
"includeTOCs": true,
"strictMode": false
};
let updated = tmAction.runMassApprovals(current, config);
if(updated){
let msg = `\u2705 SAP ${config.approve == 'A' ? "approval" : "revokal"} performed successfully. Level: ${config.levelName}, Type: ${config.type == 'C' ? "Content" : "Technical"}`;
tmAction.addComment(current, msg); //add comment inside the current task
}
} catch(error){
let msg = error.message;
gs.info(`ERROR Mass approval: ${msg}`);
tmAction.addComment(current, msg); //add comment inside the current task
gs.addErrorMessage(msg); //add error message for user
current.setAbortAction(true); //abort state change action
}
})(current, previous);
TMAction Script Include
Go to All → System Definition → Script Include
Create New in “Global” Application
var TMAction = Class.create();
TMAction.prototype = {
initialize: function() {
},
type: 'TMAction',
approveRequests: function(current, approvalConfig){
let taskId = "" + current.sys_id;
let taskNumber = "" + current.number;
let type = current.getRecordClassName();
//get controller
let tmUtils = new global.TMUtils();
let tmSettingsDetails = tmUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
let tmServerValue = tmSettingsDetails.tmServer;
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.userLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let tmFeatures = tmUtils.getTMFeaturesForType(type);
let itsmName = tmFeatures["creation"]["itsmName"] || "";
let entityCall = approvalConfig.approve == 'A' ? "massApproval" : "massRevokal";
approvalConfig.group = approvalConfig.group || "";
let requestNumbers = this.getSAPTrsForApproval(taskId, approvalConfig);
let requestBody = {
"requests": `(Trkorr='${requestNumbers}')`,
"body": {
"Approve": approvalConfig.approve,
"ApprovalType": approvalConfig.type,
"LevelName": approvalConfig.levelName
}
};
if (approvalConfig.group) {
requestBody.body['Usrgroup'] = approvalConfig.group;
}
let response = tmUtils.setSAPTRData(JSON.stringify(connectDetails), entityCall, JSON.stringify(requestBody));
return response;
}
},
checkQAS: function(current){
let taskId = current.sys_id;
let type = current.getRecordClassName();
//get controller
let tmUtils = new global.TMUtils();
let tmSettingsDetails = tmUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
var tmServerValue = tmSettingsDetails.tmServer;
//call get SAP QAS
let trId = this.getSAPTrs(taskId);
let query = "?$expand=NavToCritObj,NavToSpecialAspects&$filter=Trkorr" + encodeURIComponent(" eq'" + trId +"' and Toc eq 'X'");
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.userLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let request = {"entityCall": "QA_TM_TR_RELSet", "query": query, "connectDetails": connectDetails};
let response = tmUtils.getSAPTRData(JSON.stringify(request));
return response;
}
},
addComment: function(current, text){
let taskId = current.sys_id;
let type = current.getRecordClassName();
let task = new GlideRecordSecure(type);
if (task.get(taskId)) {
task.comments = text;
task.update();
}
},
startToC: function(current){
let taskId = "" + current.sys_id;
let taskNumber = "" + current.number;
let type = current.getRecordClassName();
//get controller
let tmUtils = new global.TMUtils();
let tmSettingsDetails = tmUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
let tmServerValue = tmSettingsDetails.tmServer;
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.userLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let tmFeatures = tmUtils.getTMFeaturesForType(type);
let itsmName = tmFeatures["creation"]["itsmName"] || "";
let bodyReq = {
"ItsmId": itsmName,
"TicketId": taskNumber,
"TicketGuid": taskId,
"Trkorr": ""
};
let response = tmUtils.setSAPTRData(JSON.stringify(connectDetails), "TOC_StartSet", JSON.stringify(bodyReq));
return response;
}
},
checkAllImported: function(current, level, state){
let sapId = "" + current.sys_id;
//check SAP transports import
let imported = false;
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.get(sapId);
let taskId = reqTable.item_sys_id;
let reqTable2 = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable2.addQuery('item_sys_id', taskId);
reqTable2.addQuery('type_technical', "T");
reqTable2.query();
while(reqTable2.next()){
let imports = reqTable2.getValue("imports") || [];
imports = typeof imports == "string" ? JSON.parse(imports) : imports;
for(let i = 0; i < imports.length; i++){
if(imports[i]["TM_LEVEL"] == level){
let rc = "" + imports[i]["RETURN_CODE"];
rc = rc.trim();
imported = rc.length > 0;
}
}
}
gs.info("IMPORTED end:" +imported);
//update incident
if(imported){
gs.info("UPDATED");
let gr = new GlideRecord('task');
gr.get(taskId);
gr.state = state;
gr.update();
}
},
getSAPTrs: function(sys_id){
let sapTrs = "";
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.addQuery('item_sys_id', sys_id);
reqTable.addQuery('type_technical', "!=", "T");
reqTable.query();
while(reqTable.next()){
if(sapTrs.length){
sapTrs = sapTrs + ",";
}
sapTrs = sapTrs + reqTable.getValue("transport_no");
}
return sapTrs;
},
getSAPTrsForApproval: function(sys_id, approvalConfig){
let sapTrs = "";
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.addQuery('item_sys_id', sys_id);
reqTable.addQuery('tm_level_technical', approvalConfig.levelName);
if(approvalConfig.approve == 'A'){
reqTable.addQuery('status_technical', "S");
} else {
reqTable.addQuery('status_technical', "!=", "Q");
}
reqTable.query();
let approvals = [];
while(reqTable.next()){
let sapTr = reqTable.getValue("transport_no");
let approvalsTech = [];
if(approvalConfig.type == "C"){
approvalsTech = reqTable.getValue("content_approvals_tech") ? JSON.parse(reqTable.getValue("content_approvals_tech")) : [];
} else if(approvalConfig.type == "T"){
approvalsTech = reqTable.getValue("technical_approvals_tech") ? JSON.parse(reqTable.getValue("technical_approvals_tech")) : [];
}
for(let i = 0; i < approvalsTech.length; i++){
if((approvalConfig.approve == 'A' && !approvalsTech[i]["SIGN_USER"]) ||
(approvalConfig.approve == 'R' && approvalsTech[i]["SIGN_USER"])){
approvalsTech[i]["GROUP"] = approvalsTech[i]["GROUP"] || "";
if(approvalConfig.group != approvalsTech[i]["GROUP"]){
continue;
}
if(sapTrs.length){
sapTrs = sapTrs + ",";
}
sapTrs = sapTrs + sapTr;
break;
}
}
}
return sapTrs;
}
};
var TMAction = Class.create();
TMAction.prototype = {
initialize: function() {
},
type: 'TMAction',
_current: {},
_config: {},
_requestNumbers: "",
approveRequests: function(){
let taskId = "" + this._current.sys_id;
let taskNumber = "" + this._current.number;
let type = this._current.getRecordClassName();
//get controller
let tmSettingsUtils = new x_real3_app1.TMSettingsUtils();
let tmSettingsDetails = tmSettingsUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
let tmServerValue = tmSettingsDetails.tmServer;
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.userLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let tmFeaturesUtils = new x_real3_app1.TMFeaturesUtils();
let tmFeatures = tmFeaturesUtils.getTMFeaturesForType(type);
let itsmName = tmFeatures["creation"]["itsmName"] || "";
let entityCall = this._config.approve == 'A' ? "massApproval" : "massRevokal";
this._config.group = this._config.group || "";
if(!this._requestNumbers){
this._requestNumbers = this.getSAPTrs(this._current.sys_id);
}
if(!this._requestNumbers){
return false;
}
let requestBody = {
"requests": `(Trkorr='${this._requestNumbers}')`,
"body": {
"Approve": this._config.approve,
"ApprovalType": this._config.type,
"LevelName": this._config.levelName
}
};
if (this._config.group) {
requestBody.body['Usrgroup'] = this._config.group;
}
let sapTransations = new x_real3_app1.SAPTransactions();
let response = sapTransations.setSAPTRData(JSON.stringify(connectDetails), entityCall, JSON.stringify(requestBody));
response = typeof response == "string" && response.startsWith("{") ? JSON.parse(response) : response;
if(response == "SUCCESS"){
return true;
}
return response;
}
},
runMassApprovals: function(current, config){
this._current = current;
this._config = config;
//if strict mode then check vor invalid transports
if(config.strictMode){
let invalidMsg = this.validateApprovals();
gs.info("runMassApprovals strictMode: " + invalidMsg);
if (invalidMsg) {
let msg = "\u274C SAP approval validation failed: " + invalidMsg;
throw new Error(msg);
}
if(!this._requestNumbers){
return false;
}
}
let response = this.approveRequests();
if(response?.type == "E"){
// SAP returned an error -> keep state, inform user, abort the change
let msg = "\u274C SAP approval failed (SAP response): " + response.message;
throw new Error(msg);
}
return response;
},
validateApprovals: function(){
gs.info("Validating SAP Transports for massApproval BS");
let invalidMessage = "";
let sapTrs = "";
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.addQuery('item_sys_id', this._current.sys_id);
if(!this._config?.includeTOCs){
reqTable.addQuery('type_technical', "!=", "T");
}
reqTable.query();
let approvals = [];
while(!invalidMessage && reqTable.next()){
let sapTr = reqTable.getValue("transport_no");
//if it is not released into TM
let currentTMLevel = reqTable.getValue("tm_level_technical");
if(!currentTMLevel){
invalidMessage = `SAP transport (${sapTr}) is not released into TM!`;
continue;
} else if (reqTable.getValue("tm_history")){
gs.info(`Ignoring History SAP Transport ${sapTr}.`);
continue;
}
//check for level, showld be at least the levelName
if(currentTMLevel != this._config.levelName){
let imports = reqTable.getValue('imports') ? JSON.parse(reqTable.getValue('imports')) : [];
let isHigherLevel = this._isHigherLevel(imports, currentTMLevel, this._config.levelName);
if(!isHigherLevel){
invalidMessage = `SAP transport (${sapTr}) is on lower level '${currentTMLevel}' but should be on '${this._config.levelName}'.`;
continue;
} else if(this._config.approve == 'R'){
invalidMessage = `SAP transport (${sapTr}) is on higher level '${currentTMLevel}' but should be on '${this._config.levelName}'.`;
continue;
} else {
gs.info(`Ignoring SAP Transport ${sapTr} on higher level '${currentTMLevel}'.`);
continue;
}
}
//if approval/revokal is already done, then skipt the checks
//check for approval type available
let approvalsTech = [];
if(this._config.type == "C"){
approvalsTech = reqTable.getValue("content_approvals_tech") ? JSON.parse(reqTable.getValue("content_approvals_tech")) : [];
} else if(this._config.type == "T"){
approvalsTech = reqTable.getValue("technical_approvals_tech") ? JSON.parse(reqTable.getValue("technical_approvals_tech")) : [];
}
let approvalFound = false;
let isDone = false;
let i = 0;
while(!approvalFound && i < approvalsTech.length){
let group = approvalsTech[i]["GROUP"] || "";
if(this._config.group == group){
if(this._config.approve == 'A'){
approvalFound = !approvalsTech[i]["SIGN_USER"];
isDone = !approvalFound;
} else if(this._config.approve == 'R'){
approvalFound = approvalsTech[i]["SIGN_USER"];
isDone = !approvalFound;
}
}
i++;
}
if(isDone){
gs.info(`Ignoring already done ${this._config.approve == "A" ? 'approval': 'revokal'} for SAP Transport ${sapTr}.`);
continue;
}
if(!approvalFound){
invalidMessage = `SAP transport (${sapTr}) has no available ${this._config.approve == "A" ? 'approval': 'revokal'}!`;
continue;
}
//check for status
//approval type: "C" => status in {"S": for 'A,R'}, "T" => status in ["S": for 'A,R', "C": for R']
let status = reqTable.getValue("status_technical");
if(status != "S" &&
(this._config.type != "T" || (this._config.approve == "R" && status != "C"))){
invalidMessage = `SAP transport (${sapTr}) is on wrong TM status '${status}'.`;
continue;
}
if(sapTrs.length){
sapTrs = sapTrs + ",";
}
sapTrs = sapTrs + sapTr;
}
this._requestNumbers = !invalidMessage ? sapTrs : "";
return invalidMessage;
},
checkQAS: function(current){
let taskId = current.sys_id;
let type = current.getRecordClassName();
//get controller
let tmUtils = new global.TMUtils();
let tmSettingsDetails = tmUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
var tmServerValue = tmSettingsDetails.tmServer;
//call get SAP QAS
let trId = this.getSAPTrs(taskId);
let query = "?$expand=NavToCritObj,NavToSpecialAspects&$filter=Trkorr" + encodeURIComponent(" eq'" + trId +"' and Toc eq 'X'");
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.sapUserLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let request = {"entityCall": "QA_TM_TR_RELSet", "query": query, "connectDetails": connectDetails};
let response = tmUtils.getSAPTRData(JSON.stringify(request));
return response;
}
},
addComment: function(current, text){
let taskId = current.sys_id;
let type = current.getRecordClassName();
let task = new GlideRecordSecure(type);
if (task.get(taskId)) {
task.comments = text;
task.update();
}
},
startToC: function(current){
let taskId = "" + current.sys_id;
let taskNumber = "" + current.number;
let type = current.getRecordClassName();
//get controller
let tmUtils = new global.TMUtils();
let tmSettingsDetails = tmUtils.getTMSettingsForType(type);
if(tmSettingsDetails.tmEnabled){
let tmServerValue = tmSettingsDetails.tmServer;
let connectDetails = {
"tmServer": tmServerValue,
"loginAlias": tmSettingsDetails.loginAlias || "/sap/bc/gui/sap/its/webgui",
"logoffAlias": tmSettingsDetails.logoffAlias || "/sap/public/bc/icf/logoff",
"sapUserLogin": tmSettingsDetails.sapUserLogin,
"rtcHeaders": tmSettingsDetails.rtcHeaders,
"path": tmSettingsDetails.rtcAlias || "/sap/opu/odata/RTC/TM_GW_SRV",
"sapId": type,
"ExternalUser": gs.getUserName()
};
if (tmSettingsDetails.systemAlias) {
connectDetails["path"] = connectDetails["path"] + ";o=" + tmSettingsDetails.systemAlias;
}
let tmFeatures = tmUtils.getTMFeaturesForType(type);
let itsmName = tmFeatures["creation"]["itsmName"] || "";
let bodyReq = {
"ItsmId": itsmName,
"TicketId": taskNumber,
"TicketGuid": taskId,
"Trkorr": ""
};
let response = tmUtils.setSAPTRData(JSON.stringify(connectDetails), "TOC_StartSet", JSON.stringify(bodyReq));
return response;
}
},
checkAllImported: function(current, level, state){
let sapId = "" + current.sys_id;
//check SAP transports import
let imported = false;
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.get(sapId);
let taskId = reqTable.item_sys_id;
let reqTable2 = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable2.addQuery('item_sys_id', taskId);
reqTable2.addQuery('type_technical', "T");
reqTable2.query();
while(reqTable2.next()){
let imports = reqTable2.getValue("imports") || [];
imports = typeof imports == "string" ? JSON.parse(imports) : imports;
for(let i = 0; i < imports.length; i++){
if(imports[i]["TM_LEVEL"] == level){
let rc = "" + imports[i]["RETURN_CODE"];
rc = rc.trim();
imported = rc.length > 0;
}
}
}
gs.info("IMPORTED end:" +imported);
//update incident
if(imported){
gs.info("UPDATED");
let gr = new GlideRecord('task');
gr.get(taskId);
gr.state = state;
gr.update();
}
},
getSAPTrs: function(sys_id){
let sapTrs = "";
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.addQuery('item_sys_id', sys_id);
if(!this._config?.includeTOCs){
reqTable.addQuery('type_technical', "!=", "T");
}
reqTable.addQuery('tm_history', "NOT IN", "D,E");
reqTable.query();
while(reqTable.next()){
if(sapTrs.length){
sapTrs = sapTrs + ",";
}
sapTrs = sapTrs + reqTable.getValue("transport_no");
}
return sapTrs;
},
getSAPTrsForApproval: function(sys_id, approvalConfig){
let sapTrs = "";
let reqTable = new GlideRecordSecure('x_real3_app1_sap_transports');
reqTable.addQuery('item_sys_id', sys_id);
reqTable.addQuery('tm_level_technical', approvalConfig.levelName);
if(approvalConfig.approve == 'A'){
reqTable.addQuery('status_technical', "S");
} else {
reqTable.addQuery('status_technical', "!=", "Q");
}
reqTable.query();
let approvals = [];
while(reqTable.next()){
let sapTr = reqTable.getValue("transport_no");
let approvalsTech = [];
if(approvalConfig.type == "C"){
approvalsTech = reqTable.getValue("content_approvals_tech") ? JSON.parse(reqTable.getValue("content_approvals_tech")) : [];
} else if(approvalConfig.type == "T"){
approvalsTech = reqTable.getValue("technical_approvals_tech") ? JSON.parse(reqTable.getValue("technical_approvals_tech")) : [];
}
for(let i = 0; i < approvalsTech.length; i++){
if((approvalConfig.approve == 'A' && !approvalsTech[i]["SIGN_USER"]) ||
(approvalConfig.approve == 'R' && approvalsTech[i]["SIGN_USER"])){
approvalsTech[i]["GROUP"] = approvalsTech[i]["GROUP"] || "";
if(approvalConfig.group != approvalsTech[i]["GROUP"]){
continue;
}
if(sapTrs.length){
sapTrs = sapTrs + ",";
}
sapTrs = sapTrs + sapTr;
break;
}
}
}
return sapTrs;
},
_isHigherLevel: function(imports, currentTMLevel, levelName){
const currentTMLevelLower = currentTMLevel.toLowerCase();
const levelNameLower = levelName.toLowerCase();
let currentLevelImp = -1;
let indexOfLevel = -1;
for (let i = 0; i < imports.length; i++) {
const tmLevel = imports[i].TM_LEVEL.toLowerCase();
if (currentLevelImp === -1 && tmLevel === currentTMLevelLower) {
currentLevelImp = i;
}
if (indexOfLevel === -1 && tmLevel === levelNameLower) {
indexOfLevel = i;
}
if (currentLevelImp !== -1 && indexOfLevel !== -1) {
break;
}
}
return currentLevelImp > indexOfLevel;
}
};TMUtil Script Include
Go to All → System Definition → Script Include
Create New in “Global” Application
var TMUtils = Class.create();
TMUtils.metaDataPatternsEntities = {
"TR_ReleaseSet": "TR_Release",
"Proj_DestiSet": "Proj_Desti",
"CTS_ProjectSet": "CTS_Project",
"QA_TM_TR_RELSet": "QA_TM_TR_REL",
"QA_TM_TRSet": "QA_TM_TR",
"Transport_RequestSet": "Transport_Request",
"TR_AttributeSet": "TR_Attribute",
"TR_TO_TMSet": "TR_TO_TM",
"TR_ReassignSet": "TR_Reassign",
"TR_AssignReleaseSet": "TR_AssignRelease",
"TOC_StartSet": "TOC_Start",
"TR_ApproveSet": "TR_Approve",
"QA_Collision": "QA_Collision"
};
TMUtils.prototype = {
initialize: function() {
},
type: 'TMUtils',
_getExternalUser(){
var gr = new GlideRecord('sys_user');
gr.addQuery('sys_id', gs.getUserID());
gr.query();
gr.next();
return "" + gr.user_name;
},
_getIfPropertyExists(propertyName, entityCall){
let propertyExists = false;
if (!this._metadata) {
return propertyExists;
}
let iter = this._metadata.getChildNodeIterator();
let entityName = TMUtils.metaDataPatternsEntities[entityCall];
let nodes = 0;
while(iter.hasNext() || nodes < 100) {
nodes++;
let n = iter.next();
if(n.getNodeName() == "EntityType" && n.getAttribute("Name") == entityName){
let iter2 = n.getChildNodeIterator();
let propertyNodes = 0;
while(iter2.hasNext() || propertyNodes < 50) {
propertyNodes++;
let n2 = iter2.next();
if(n2.getNodeName() == "Property" && n2.getAttribute("Name") == propertyName){
propertyExists = true;
break;
}
}
break;
}
}
return propertyExists;
},
_getIfNavigationPropertyExists(propertyName, entityCall){
let propertyExists = false;
if (!this._metadata) {
return propertyExists;
}
let iter = this._metadata.getChildNodeIterator();
let entityName = TMUtils.metaDataPatternsEntities[entityCall];
let nodes = 0;
while(iter.hasNext() || nodes < 100) {
nodes++;
let n = iter.next();
if(n.getNodeName() == "EntityType" && n.getAttribute("Name") == entityName){
let iter2 = n.getChildNodeIterator();
let propertyNodes = 0;
while(iter2.hasNext() || propertyNodes < 50) {
propertyNodes++;
let n2 = iter2.next();
if(n2.getNodeName() == "NavigationProperty" && n2.getAttribute("Name") == propertyName){
propertyExists = true;
break;
}
}
break;
}
}
return propertyExists;
},
_getTMMessage: function (response, url) {
let errorStatus = response ? response.getStatusCode() : 500;
let errorMessage = "Service '" + url + "' " + response.getErrorMessage();
let isCustomMessage = false;
let responseBody = {};
try{
responseBody = response.getBody();
responseBody = typeof responseBody == 'string' ? JSON.parse(responseBody) : responseBody;
}catch(error){
responseBody = {};
gs.info(error.message);
}
// Custom SAP BTP error handling
if (responseBody?.error && responseBody?.message) {
errorMessage = `${String(responseBody.error)}, ${String(responseBody.message)}`;
isCustomMessage = true;
} else if(errorStatus != 401){
// OData-style
errorMessage = responseBody && responseBody.error && responseBody.error.message ? responseBody.error.message.value : errorMessage;
}
return JSON.stringify({"type": "E", "status": errorStatus, "message": errorMessage || "An unknown error occurred!", "customMessage": isCustomMessage});
},
_getOdataInfo: function (entityCall, propertyName, url, authHeaders) {
let odataInfo = {"x-csrf-token": "", "set-cookie": "", "propertyExists": false};
authHeaders["X-CSRF-Token"] = "FETCH";
try{
let request = new sn_ws.RESTMessageV2();
request.setEndpoint(url + "/$metadata");
request.setHttpMethod('GET');
for(let key in authHeaders){
request.setRequestHeader(key, authHeaders[key]);
}
let metadataResp = request.execute();
let respHeaders = metadataResp.getHeaders("set-cookie");
for(let key2 in respHeaders){
if(key2 == "set-cookie"){
odataInfo["set-cookie"] = respHeaders[key2];
} else if(key2 == "x-csrf-token"){
odataInfo["x-csrf-token"] = respHeaders[key2];
}
}
if(metadataResp.getStatusCode() != 200){
return odataInfo;
}
let responseBody = {};
try{
responseBody = metadataResp.getBody();
let xmlDoc = new XMLDocument2();
xmlDoc.parseXML(responseBody);
this._metadata = xmlDoc.getFirstNode("edmx:Edmx/edmx:DataServices/Schema");
}catch(error){
responseBody = {};
gs.info(error.message);
}
odataInfo["propertyExists"] = this._getIfPropertyExists(propertyName, entityCall);
return odataInfo;
} catch(ex){
let message = ex.getMessage();
gs.info("error getting metadata" + message);
return odataInfo;
}
},
getTMSettingsForType: function(type){
var tmSettingsRecords = new GlideRecordSecure('x_real3_app1_settings');
tmSettingsRecords.addQuery('type', type);
tmSettingsRecords.query();
var updateReqTable = tmSettingsRecords.next();
var devSystems = tmSettingsRecords.getDisplayValue("dev_systems");
devSystems = devSystems ? devSystems.split(",") : [];
var tmSettings = {"tmServer": "", "secure": "", "server": "", "port": "", "systems": devSystems, "ctrlName": tmSettingsRecords.getDisplayValue("tm_controller_name"), "tmEnabled": "", "tmLoginBlank": false, "loginAlias": "", "logoffAlias": "", "rtcAlias": "", "systemAlias": "", "rtcHeaders": "{}", "userLogin": ""};
var tmCtrlRecords = new GlideRecordSecure('x_real3_app1_tm_controllers');
tmCtrlRecords.addQuery('tm_controller_id', tmSettings.ctrlName);
tmCtrlRecords.query();
if(tmCtrlRecords.next() && tmCtrlRecords.getDisplayValue("server")){
tmSettings.tmEnabled = tmCtrlRecords.getValue("enabled") == 1;
tmSettings.tmServer = tmCtrlRecords.getValue("secure") == 1 ? "https://" : "http://";
tmSettings.tmServer = tmSettings.tmServer + tmCtrlRecords.getDisplayValue("server");
tmSettings.secure = tmCtrlRecords.getValue("secure") == 1;
tmSettings.server = tmCtrlRecords.getDisplayValue("server");
tmSettings.port = tmCtrlRecords.getDisplayValue("port");
tmSettings.tmLoginBlank = tmCtrlRecords.getValue("tm_login_new_tab") == 1;
tmSettings.loginAlias = tmCtrlRecords.getDisplayValue("login_service_alias");
tmSettings.logoffAlias = tmCtrlRecords.getDisplayValue("logoff_service_alias");
tmSettings.rtcAlias = tmCtrlRecords.getDisplayValue("rtc_service_alias");
tmSettings.systemAlias = tmCtrlRecords.getDisplayValue("system_alias");
var rtcHeaders = tmCtrlRecords.getDisplayValue("rtc_headers") || "{}";
tmSettings.rtcHeaders = JSON.parse(rtcHeaders);
tmSettings.userLogin = tmCtrlRecords.getValue("user_login") == 1;
var port = tmCtrlRecords.getDisplayValue("port");
if(port){
tmSettings.tmServer = tmSettings.tmServer + ":" + port;
}
}
return tmSettings;
},
getTMFeaturesForType: function(type){
var tmFeaturesRecords = new GlideRecordSecure('x_real3_app1_features');
tmFeaturesRecords.addQuery('type', type);
tmFeaturesRecords.query();
var tmFeatures = {"uiConfig": {}, "creation": {}, "approval": {}, "reassign": {}, "release": {}, "prodRelease": {}, "toc": {}};
if(tmFeaturesRecords.next()){
var toc = tmFeaturesRecords.getDisplayValue("toc") || "";
var prod_release = tmFeaturesRecords.getDisplayValue("prod_release") || "";
var ui_config = tmFeaturesRecords.getDisplayValue("ui_config") || "";
tmFeatures.uiConfig = ui_config ? JSON.parse(ui_config) : {};
tmFeatures.creation = JSON.parse(tmFeaturesRecords.getDisplayValue("creation") || "{}");
tmFeatures.approval = JSON.parse(tmFeaturesRecords.getDisplayValue("approval") || "{}");
tmFeatures.reassign = JSON.parse(tmFeaturesRecords.getDisplayValue("reassign") || "{}");
tmFeatures.release = JSON.parse(tmFeaturesRecords.getDisplayValue("release") || "{}");
tmFeatures.prodRelease = prod_release ? JSON.parse(prod_release) : {},
tmFeatures.toc = toc ? JSON.parse(toc) : {};
}
return tmFeatures;
},
getSAPTRData: function(req){
req = req || this.getParameter("sysparm_req");
req = JSON.parse(req) || {};
let entityCall = req.entityCall;
let query = req.query;
let connectDetails = req.connectDetails || {};
let tmServer = connectDetails.tmServer;
let path = connectDetails.path;
let sapId = req.sapId;
let authHeaders = connectDetails.rtcHeaders || {};
if(connectDetails.sapUserLogin){
let session = gs.getSession();
let sapCookies = session.getClientData("SAPLogonCookies_"+ sapId);
if(sapCookies && sapCookies.length){
authHeaders["Cookie"] = sapCookies;
}
}
//FETCH odata info ["x-csrf-token", "set-cookie", "propertyExists"]
let odataInfo = this._getOdataInfo(entityCall, "ExternalUser", tmServer + path, authHeaders);
if(entityCall == "TR_AttributeSet"){
entityCall = "Transport_RequestSet('"+ query.trkorr +"')/TR_AttributeSet";
query = "";
} else if (entityCall == "Proj_DestiSet") {
let odataInfoTRShort = this._getIfPropertyExists("TrShortDescr", entityCall);
query = odataInfoTRShort ? query.path + ` and TrShortDescr eq '${query.currentShortText}'` : query.path;
}
if(query && entityCall != "QA_TM_TR_RELSet" && entityCall != "QA_TM_TRSet"){
query = "?$filter=" + encodeURIComponent(query);
}
if (odataInfo["propertyExists"]) {
let externalUser = this._getExternalUser();
query = query ? query + encodeURIComponent(" and ") : "?$filter=";
query = query + encodeURIComponent("ExternalUser eq '" + externalUser + "'");
}
if(entityCall == "QA_TM_TRSet" && query.includes('{navToCollObj}')){
let navToCollObj = this._getIfNavigationPropertyExists("NavToCollisionObjects", "QA_Collision");
if(navToCollObj){
query = query.replace("{navToCollObj}", "NavToCollisionObjects");
} else {
query = query.replace("/{navToCollObj}", "");
}
}
try{
let request = new sn_ws.RESTMessageV2();
request.setEndpoint(tmServer + path + "/" + entityCall + query);
request.setHttpMethod('GET');
request.setRequestHeader("Accept", "application/json");
for(let key in authHeaders){
request.setRequestHeader(key, authHeaders[key]);
}
let response = request.execute();
let responseBody = {};
try{
responseBody = response.getBody();
responseBody = typeof responseBody == 'string' ? JSON.parse(responseBody) : responseBody;
}catch(error){
responseBody = {};
gs.info(error.message);
}
if(response.getStatusCode() != 200){
return this._getTMMessage(response, tmServer + path + "/" + entityCall);
} else {
responseBody = typeof responseBody == "string" ? JSON.parse(responseBody) : (responseBody || {});
return JSON.stringify(responseBody.d ? responseBody.d.results : []);
}
} catch(ex){
let errorMessage = ex.getMessage();
gs.info(errorMessage);
return JSON.stringify({"type": "E", "status": 500, "message": errorMessage || "An unknown error occurred!"});
}
},
setSAPTRData: function(connect_details, entity_call, req_body){
let connectDetails = connect_details || this.getParameter("sysparm_connect_details");
connectDetails = JSON.parse(connectDetails);
let entityCall = entity_call || this.getParameter("sysparm_entity_call");
let requestBody = req_body || this.getParameter("sysparm_req_body");
requestBody = requestBody.startsWith("{") ? JSON.parse(requestBody) : requestBody;
let authHeaders = connectDetails.rtcHeaders || {};
let tmServer = connectDetails.tmServer;
let path = connectDetails.path;
let reqType = "POST";
let username = "SUCCESS";
let serviceQuery = "";
let externalUser = this._getExternalUser();
if(connectDetails.sapUserLogin){
let session = gs.getSession();
let sapCookies = session.getClientData("SAPLogonCookies_"+ connectDetails.sapId);
if(sapCookies && sapCookies.length){
authHeaders["Cookie"] = sapCookies;
username = session.getClientData("SAPLogonCookieUSER_"+ connectDetails.sapId);
}
}
//FETCH odata info ["x-csrf-token", "set-cookie", "propertyExists"]
if(entityCall == "massProdRelease"){
entityCall = "TR_AssignReleaseSet";
} else if(entityCall == "massApproval" || entityCall == "massRevokal"){
entityCall = "TR_ApproveSet";
serviceQuery = requestBody.requests;
requestBody = requestBody.body;
} else if(entityCall == "TR_ApproveSet"){
requestBody['UserName'] = "";
serviceQuery = "(Trkorr='" + requestBody.trkorr + "')";
} else if(entityCall == "Proj_DestiSet_Reassign"){
entityCall = "Proj_DestiSet";
serviceQuery = requestBody.serviceQuery;
requestBody = requestBody.body;
}
let odataInfo = this._getOdataInfo(entityCall, "ExternalUser", tmServer + path, authHeaders);
authHeaders["X-CSRF-Token"] = odataInfo["x-csrf-token"];
if(entityCall == "massRelease"){
authHeaders["Content-Type"] = "multipart/mixed;boundary=batch_test";
entityCall = "$batch";
//from url path it's mandatory
requestBody = requestBody.replaceAll(",ExternalUser='$externalUser'", ",ExternalUser='"+ externalUser +"'");
if(odataInfo["propertyExists"]){
requestBody = requestBody.replaceAll("$externalUser", externalUser);
} else {
requestBody = requestBody.replaceAll(',\n"ExternalUser":"$externalUser"', '');
}
} else {
//update request
if (["TR_TO_TMSet", "TR_ReassignSet", "TR_AssignReleaseSet", "TR_ApproveSet", "Proj_DestiSet"].includes(entityCall)) {
reqType = "PUT";
if(entityCall == "TR_TO_TMSet"){
if(odataInfo["propertyExists"]){
entityCall = "TR_TO_TMSet(TransportReq='" + requestBody.trkorr + "',ExternalUser='" + externalUser + "',CrudMode='C')";
} else {
entityCall = "TR_TO_TMSet(TransportReq='" + requestBody.trkorr + "',CrudMode='C')";
}
} else if(entityCall == "TR_AssignReleaseSet"){
entityCall = "TR_AssignReleaseSet(RelId='" + requestBody.prodReleaseID + "')";
delete requestBody["prodReleaseID"];
} else if(entityCall == "TR_ReassignSet"){
entityCall = "TR_ReassignSet('" + requestBody.trkorr + "')";
}
delete requestBody.trkorr;
}
if(odataInfo["propertyExists"]){
requestBody["ExternalUser"] = externalUser;
}
authHeaders["Accept"] = "application/json";
authHeaders["Content-Type"] = "application/json";
requestBody = JSON.stringify(requestBody);
}
if(odataInfo["set-cookie"] && !connectDetails.sapUserLogin){
let cookies = odataInfo["set-cookie"].split("; ");
let cookie = "";
for(let i = 0; i < cookies.length; i++){
if(cookies[i].includes("sap-usercontext=") || cookies[i].includes("SAP_SESSIONID")){
cookies[i] = cookies[i].replace("path=/", "");
cookies[i] = cookies[i].replace(",", "");
cookie = i ? cookie + "; " + cookies[i].trim() : cookies[i].trim();
}
}
authHeaders["Cookie"] = cookie;
}
try{
let request = new sn_ws.RESTMessageV2();
request.setEndpoint(tmServer + path + "/" + entityCall + serviceQuery);
request.setHttpMethod(reqType);
request.setRequestBody(requestBody);
for(let key in authHeaders){
request.setRequestHeader(key, authHeaders[key]);
}
let response = request.execute();
let statusCode = response.getStatusCode();
gs.info("Update status code" + statusCode);
if(statusCode >= 400 || response.getErrorMessage()){
return this._getTMMessage(response, tmServer + path + "/" + entityCall);
} else if(statusCode == 202){
return "Accepted";
} else if(reqType == "PUT"){
return username;
} else {
let responseBody = {};
try{
responseBody = response.getBody();
responseBody = typeof responseBody == 'string' ? JSON.parse(responseBody) : responseBody;
}catch(error){
responseBody = {};
gs.info(error.message);
}
return JSON.stringify(responseBody ? responseBody.d : {});
}
} catch(ex){
let errorMessage = ex.getMessage();
gs.info(errorMessage);
return JSON.stringify({"type": "E", "status": 500, "message": errorMessage || "An unknown error occurred!"});
}
}
};