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

Offical Documentation

Official Docs: