Name
sn_grc.IssueAdvancedUtilsBase
Description
No description available
Script
var IssueAdvancedUtilsBase = Class.create();
IssueAdvancedUtilsBase.prototype = {
initialize: function() {
this.table = null;
this.issueColumn = "sn_grc_issue";
this.relationshipColumn = null;
this.baseTable = "sn_grc_m2m_issue_base";
this.ISSUE_LIFECYCLE_EXTENSION = "sn_grc.IssueLifecycleHandler";
},
/*
This method returns issues associated to an object (control/risk/engagement/entity etc).
Input: OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
recordId: sys id of the record (control/risk/engagement/entity etc)
*/
getIssuesLinkedToObject: function(OPTIONS, recordId){
var arr = [];
this._initialiseLocalVars(OPTIONS);
var record = new GlideAggregate(this.table);
record.addQuery(this.relationshipColumn, "IN", recordId);
record.groupBy(this.issueColumn);
record.query();
while(record.next()){
arr.push(record.getValue(this.issueColumn));
}
return arr;
},
/*
This method returns the count of issues associated to an object (control/risk/engagement/entity etc).
Input: OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
recordId: sys id of the record (control/risk/engagement/entity etc)
*/
getIssuesCountLinkedToObject: function(OPTIONS, recordId){
this._initialiseLocalVars(OPTIONS);
var record = new GlideAggregate(this.table);
record.addQuery(this.relationshipColumn, recordId);
record.addAggregate('COUNT');
record.query();
if(record.next()){
return record.getAggregate('COUNT');
}
return 0;
},
/*
This method returns the array of objects of a specific type associated to an issue. The type of object is derived by the relationship column passed as part of OPTIONS object
Input: OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
issueId: sys id of the issue
*/
getObjectsLinkedToIssue: function(OPTIONS, issueId){
var arr = [];
this._initialiseLocalVars(OPTIONS);
var record = new GlideRecord(this.table);
record.addQuery(this.issueColumn, issueId);
record.query();
while(record.next()){
arr.push(record.getValue(this.relationshipColumn));
}
return arr;
},
/*
This method returns the array of objects of a specific type associated to an issue. The type of object is derived by the relationship column passed as part of OPTIONS object
Input: OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
issueId: sys id of the issue
*/
getObjectsCountLinkedToIssue: function(OPTIONS, issueId){
var arr = [];
var recordCount = 0;
this._initialiseLocalVars(OPTIONS);
var record = new GlideAggregate(this.table);
record.addQuery(this.issueColumn, issueId);
record.addAggregate('COUNT');
record.query();
recordCount = (record.next()) ? record.getAggregate('COUNT') : 0;
return {
"count": recordCount,
"encodedQuery": record.getEncodedQuery()
};
},
/*
This method returns the array of objects of all types associated to an issue.
Input: sysId of the issue.
*/
isissueLinkedToAnyObject: function(issueId){
var m2ms = new GlideRecord(this.baseTable);
m2ms.addQuery(this.issueColumn, issueId);
m2ms.query();
return m2ms.hasNext();
},
/*
This method is called after an issue is deleted. Any life cycle operations to be performed on the associated objects have to be implemented in sn_grc.IssueLifecycleHandler extension
Input: GlideRecord object of the issue.
issueId: sys id of the issue
*/
deleteIssue: function(issueRecord){
var extensions = this._getExtensions(this.ISSUE_LIFECYCLE_EXTENSION);
for (var i = 0; i < extensions.length; i++) {
try {
var ext = extensions[i];
ext.handleIssueDeletion(issueRecord);
} catch (e) {
gs.error("Unable to run handleIssueDeletion method on the extension point " + this.ISSUE_LIFECYCLE_EXTENSION);
}
}
},
/*
This method can be called to associate multiple objects of a type to Issue object
issueId: sysId of the issue to which object should be associated
objects: Array of object sysIds to be linked
OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
*/
linkObjectsToIssue: function(OPTIONS, issueId, objects){
this._initialiseLocalVars(OPTIONS);
var objectType = this._getObjectType();
var m2m = new GlideRecord(this.table);
var successCount = 0, invalidCount = 0, errorCount = 0;
for(var i = 0; i < objects.length; i++){
if(this._isValid(objectType, objects[i]) && this._isValid('sn_grc_issue',issueId)){
m2m.initialize();
m2m.setValue(this.issueColumn, issueId);
m2m.setValue(this.relationshipColumn, objects[i]);
if(m2m.insert())
successCount++;
else
errorCount++;
}
else{
invalidCount++;
}
}
return {
'successCount': successCount,
'errorCount': errorCount,
'invalidCount': invalidCount
};
},
/*
This method can be called to associate multiple issues of a type to an object
objectId: sysId of the object to which issues should be associated
issueIds: Array of issues that should be associated
OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
*/
linkIssuesToObject: function(OPTIONS, objectId, issueIds){
this._initialiseLocalVars(OPTIONS);
var objectType = this._getObjectType();
var m2m = new GlideRecord(this.table);
var successCount = 0, invalidCount = 0, errorCount = 0;
for(var i = 0; i < issueIds.length; i++){
if(this._isValid('sn_grc_issue',issueIds[i]) && this._isValid(objectType, objectId)){
m2m.initialize();
m2m.setValue(this.issueColumn, issueIds[i]);
m2m.setValue(this.relationshipColumn, objectId);
if(m2m.insert())
successCount++;
else
errorCount++;
}
else{
invalidCount++;
}
}
return {
'successCount': successCount,
'errorCount': errorCount,
'invalidCount': invalidCount
};
},
/*
Call this method to create/delete an m2m record when there is an update on the reference field(Entity/Risk/Engagament) on Issue form
OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
currentIssueRecord: current Issue GlideRecord object
previousIssueRecord: previous Issue GlideRecord object
fieldName: Field on issue form for which the m2m needs to be created
*/
updateM2mOnIssueUpdate: function(OPTIONS, currentIssueRecord, previousIssueRecord, fieldName){
this._initialiseLocalVars(OPTIONS);
var m2m = new GlideRecord(this.table);
//Delete m2m record with previous value
if(!gs.nil(previousIssueRecord.getValue(fieldName))){
m2m.addQuery(this.issueColumn, currentIssueRecord.getUniqueValue());
m2m.addQuery(this.relationshipColumn, previousIssueRecord.getValue(fieldName));
m2m.query();
if(m2m.next()){
m2m.deleteRecord();
}
}
//Insert record with New value if it doesnot exist
var existingM2m = this._getExistingAssoc(currentIssueRecord.getUniqueValue(), currentIssueRecord.getValue(fieldName), this.table, this.relationshipColumn);
if(!gs.nil(currentIssueRecord.getValue(fieldName)) && gs.nil(existingM2m)){
m2m.setValue(this.issueColumn, currentIssueRecord.getUniqueValue());
m2m.setValue(this.relationshipColumn, currentIssueRecord.getValue(fieldName));
m2m.insert();
}
},
/*
Call this method to update an issue form reference field(Entity/Risk/Engagament) when there is insertion/deletion in M2m between Issue and object
OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
m2mRecord: GlideRecord object of the m2m record
fieldName: Field on issue form which should be updated
*/
updateIssueOnM2mUpdate: function(OPTIONS, m2mRecord, fieldName){
this._initialiseLocalVars(OPTIONS);
var issueId = m2mRecord.getValue(this.issueColumn);
var issueRecord = new GlideRecord("sn_grc_issue");
if(m2mRecord.operation() == "insert"){
if(issueRecord.get(issueId)){
if(gs.nil(issueRecord.getValue(fieldName))){
issueRecord.setValue(fieldName,m2mRecord.getValue(this.relationshipColumn));
issueRecord.update();
}
}
}
else if(m2mRecord.operation() == "delete"){
if(issueRecord.get(issueId)){
if(!gs.nil(issueRecord.getValue(fieldName)) && issueRecord.getValue(fieldName) != m2mRecord.getValue(this.relationshipColumn))
return;
//Populate reference field on issue with the next avaiable object linked to Issue if user has not manually updated the reference field on issue. User provided value is the preference
var nextObject = this._getFirstObjectLinkedToIssue(this.table, this.relationshipColumn, issueId);
if(gs.nil(nextObject))
issueRecord.setValue(fieldName,'');
else
issueRecord.setValue(fieldName,nextObject);
issueRecord.update();
}
}
},
/*
This will be called in a before update business rule, before issue state is updated to closed complete or closed incomplete
Input: GlideRecord of the issue.
*/
canCloseIssue: function(issueRecord){
return this._canCloseIssue(issueRecord);
},
/*
This will be called in a before an issue is marked as inactive to check if issue can be marked as inactive
Input: GlideRecord of the issue.
*/
canMarkIssueInactive: function(issueRecord){
return this._canMarkIssueInactive(issueRecord);
},
/*
Call this method to migrate issue data to the new m2m tables
OPTIONS = {
"tableName": "sn_grc_m2m_issue_to_entity",
"relationshipColumn":"entity"
};
tableName: m2m table name
relationshipColumn: Column name of the object linked to the issue in m2m table.
fieldName: Field on issue form which corresponds to the object this issue is linked to. Example profile field on Issue form
*/
migrateIssueDataToM2m: function(OPTIONS, fieldName){
return this._migrateIssueDataToM2m(OPTIONS, fieldName);
},
getIssueLifeCycleExtensions: function(){
return this._getExtensions(this.ISSUE_LIFECYCLE_EXTENSION);
},
getUpdatedSource: function(m2mRecord, operation){
this._getUpdatedSource(m2mRecord, operation);
},
associateIssueToDependentObj: function(sourceM2mRec){
this._associateIssueToDependentObj(sourceM2mRec);
},
removeIssueToDependentObjAssoc: function(sourceM2mRec){
this._removeIssueToDependentObjAssoc(sourceM2mRec);
},
getItemsLinkedToIssue: function(issueId, className){
var items = [];
var count = 0;
var itemGr = new GlideAggregate('sn_grc_m2m_issue_item');
itemGr.addQuery('sn_grc_issue',issueId);
itemGr.addQuery('sn_grc_item.sys_class_name', className);
itemGr.addAggregate('COUNT');
itemGr.query();
count = (itemGr.next()) ? itemGr.getAggregate('COUNT') : 0;
return {
"count": count,
"encodedQuery": itemGr.getEncodedQuery()
};
},
getDocumentsLinkedToIssue: function(issueId, className){
var docs = [];
var count = 0;
var docGr = new GlideAggregate('sn_grc_m2m_issue_document');
docGr.addQuery('sn_grc_issue',issueId);
docGr.addQuery('sn_grc_document.sys_class_name', className);
docGr.addAggregate('COUNT');
docGr.query();
count = (docGr.next()) ? docGr.getAggregate('COUNT') : 0;
return {
"count": count,
"encodedQuery": docGr.getEncodedQuery()
};
},
_migrateIssueDataToM2m: function(OPTIONS, relfieldNameOnIssue){
var totalCount = 0, successCount = 0, failureCount = 0;
if(gs.tableExists(OPTIONS.tableName) && this._isDataNotMigrated(OPTIONS, relfieldNameOnIssue)){
var issueGr = new GlideRecord("sn_grc_issue");
issueGr.addNotNullQuery(relfieldNameOnIssue);
issueGr.query();
while(issueGr.next()){
if (!gs.nil(issueGr.getValue(relfieldNameOnIssue))){
totalCount++;
try{
if(gs.nil(this._getExistingAssoc(issueGr.getUniqueValue(), issueGr.getValue(relfieldNameOnIssue), OPTIONS.tableName, OPTIONS.relationshipColumn))){
var m2m = new GlideRecord(OPTIONS.tableName);
m2m.setValue("sn_grc_issue", issueGr.getUniqueValue());
m2m.setValue(OPTIONS.relationshipColumn, issueGr.getValue(relfieldNameOnIssue));
if(m2m.insert())
successCount++;
else
failureCount++;
}
}
catch(error){
failureCount++;
gs.error(error + ': Error occurred while creating m2m record between issue ' + issueGr.getUniqueValue() + ' and ' + issueGr.getValue(relfieldNameOnIssue));
}
}
}
gs.info("{0} issues out of {1} migrated successfully. {2} issues failed to migrate.", [successCount.toString(), totalCount.toString(), failureCount.toString()]);
}
},
_isDataNotMigrated: function(OPTIONS, relfieldNameOnIssue){
var m2mCount = 0;
var issueCount = 0;
var issueGr = new GlideAggregate("sn_grc_issue");
issueGr.addNotNullQuery(relfieldNameOnIssue);
issueGr.addAggregate('COUNT');
issueGr.query();
if(issueGr.next()){
issueCount = issueGr.getAggregate('COUNT');
}
var m2m = new GlideAggregate(OPTIONS.tableName);
m2m.addAggregate('COUNT');
m2m.query();
if(m2m.next()){
m2mCount = m2m.getAggregate('COUNT');
}
if(m2mCount < issueCount){
return true;
}
return false;
},
_getFirstObjectLinkedToIssue: function(tableName, relationshipColumn, issueId){
var m2mTable = new GlideRecord(tableName);
m2mTable.addQuery("sn_grc_issue", issueId);
m2mTable.orderBy("sys_created_on");
m2mTable.setLimit(1);
m2mTable.query();
if(m2mTable.next()){
return m2mTable.getValue(relationshipColumn);
}
return null;
},
_associateIssueToDependentObj: function(sourceM2mRec){
var recordType = sourceM2mRec.getValue("sys_class_name");
var issueId = sourceM2mRec.getValue("sn_grc_issue");
var config = new GlideRecord("sn_grc_issue_objects_dependency_configuration");
config.addQuery("source_table",recordType);
config.addQuery("active", "true");
config.query();
while(config.next()){
var destinationTable = config.getValue("destination_table");
var source_field = config.getValue("source_field");
var destination_field = config.getValue("destination_field");
var destinationId = sourceM2mRec.getElement(source_field);
if(!gs.nil(destinationId)){
if(gs.nil(this._getExistingAssoc(issueId, destinationId, destinationTable, destination_field))){
var destRecord = new GlideRecord(destinationTable);
destRecord.setValue("sn_grc_issue", sourceM2mRec.getValue("sn_grc_issue"));
destRecord.setValue(destination_field, sourceM2mRec.getElement(source_field));
destRecord.setValue("is_derived", "true");
destRecord.setValue("source", this._getUpdatedSource("",sourceM2mRec.getUniqueValue(),"add"));
if(!destRecord.insert()){
gs.error("Error in destination record creation for {0}", sourceM2mRec.getUniqueValue());
}
}
else{
var existingM2m = this._getExistingAssoc(issueId, destinationId, destinationTable, destination_field);
var source = this._getUpdatedSource(existingM2m.getValue("source"),sourceM2mRec.getUniqueValue(),"add");
existingM2m.setValue("source", source);
existingM2m.update();
}
}
}
},
_removeIssueToDependentObjAssoc: function(sourceM2mRec){
var recordType = sourceM2mRec.getValue("sys_class_name");
var issueId = sourceM2mRec.getValue("sn_grc_issue");
var config = new GlideRecord("sn_grc_issue_objects_dependency_configuration");
config.addQuery("source_table",recordType);
config.query();
while(config.next()){
var destinationTable = config.getValue("destination_table");
var source_field = config.getValue("source_field");
var destination_field = config.getValue("destination_field");
var destinationId = sourceM2mRec.getElement(source_field);
if(!gs.nil(destinationId)){
var destinationM2m = this._getExistingAssoc(issueId, destinationId, destinationTable, destination_field);
if(!gs.nil(destinationM2m) && !gs.nil(destinationM2m.getValue("source"))){
var updatedSource = this._getUpdatedSource(destinationM2m.getValue("source"),sourceM2mRec.getUniqueValue(),"remove");
if(updatedSource == "" && config.active){
destinationM2m.deleteRecord();
}
destinationM2m.setValue("source", updatedSource);
destinationM2m.update();
}
}
}
},
_getExistingAssoc: function(issueId, destinationId, destinationTable, destinationField){
var destRecord = new GlideRecord(destinationTable);
destRecord.addQuery("sn_grc_issue", issueId);
destRecord.addQuery(destinationField, destinationId);
destRecord.query();
if(destRecord.next()){
return destRecord;
}
return null;
},
_getUpdatedSource: function(currentSource, sourceSysIdToUpdate, operation){
var updatedSource = "";
if(operation == "add"){
updatedSource = (gs.nil(currentSource)) ? (sourceSysIdToUpdate) : ( currentSource + "," + sourceSysIdToUpdate);
}
else if(operation == "remove"){
var stringToReplace = "";
if(currentSource.indexOf("," + sourceSysIdToUpdate) != -1){
stringToReplace = "," + sourceSysIdToUpdate;
}
else if(currentSource.indexOf(sourceSysIdToUpdate) != -1){
stringToReplace = sourceSysIdToUpdate;
}
updatedSource = currentSource.replace(stringToReplace,"");
}
return updatedSource;
},
_canMarkIssueInactive: function(issueRecord){
var extensions = this._getExtensions(this.ISSUE_LIFECYCLE_EXTENSION);
for (var i = 0; i < extensions.length; i++) {
try {
var ext = extensions[i];
if(!ext.canMarkIssueInactive(issueRecord))
return false;
} catch (e) {
gs.error("Unable to run canMarkIssueInactive method on the extension point " + this.ISSUE_LIFECYCLE_EXTENSION);
return false;
}
}
return true;
},
_initialiseLocalVars: function(OPTIONS){
if(!OPTIONS.tableName || !OPTIONS.relationshipColumn){
gs.error("Missing options");
return;
}
this.table = OPTIONS.tableName;
this.relationshipColumn = OPTIONS.relationshipColumn;
},
_isValid: function(table, sysId){
var record = new GlideRecord(table);
return record.get(sysId);
},
_getObjectType: function(){
var dictionaryGr = new GlideRecord("sys_dictionary");
dictionaryGr.addQuery("name",this.table);
dictionaryGr.addQuery("element", this.relationshipColumn);
dictionaryGr.query();
if(dictionaryGr.next()){
return dictionaryGr.getValue("reference");
}
return null;
},
_canCloseIssue: function(issueRecord){
var extensions = this._getExtensions(this.ISSUE_LIFECYCLE_EXTENSION);
for (var i = 0; i < extensions.length; i++) {
try {
var ext = extensions[i];
if(!ext.canCloseIssue(issueRecord))
return false;
} catch (e) {
gs.error("Unable to run canCloseIssue method on the extension point " + this.ISSUE_LIFECYCLE_EXTENSION);
return false;
}
}
return true;
},
_getExtensions: function(extensionPointName){
if (extensionPointName != null) {
var extensions = new GlideScriptedExtensionPoint().getExtensions(extensionPointName);
return extensions;
}
},
type: 'IssueAdvancedUtilsBase'
};
Sys ID
abab5860538411109b88ddeeff7b125f