Name
global.LifeCycleUtil
Description
No description available
Script
var LifeCycleUtil = Class.create();
LifeCycleUtil.prototype = {
initialize: function() {
},
MAPPING_TABLE: 'life_cycle_mapping',
TBD_VAL: {'life_cycle_stage' : 'To Be Determined', 'life_cycle_stage_status' : 'To Be Determined'}, //default life_cycle* values
LIFE_CYCLE_STAGE: 'life_cycle_stage',
LIFE_CYCLE_STAGE_STATUS: 'life_cycle_stage_status',
LIFE_CYCLE_CTRL_TABLE: 'life_cycle_control',
LIFE_CYCLE_CTRL_FIELD: 'life_cycle_control',
CSDM_LIFE_CYCLE_MIGRATION_ACTIVATED: 'csdm.lifecycle.migration.activated',
//This has built-in access to the current object as it's called from
//a business rule condition field
//Optional parameter gr is sent to this method when this method is not called from business rule.
shouldUpdateLegacy: function(gr) {
if ((typeof current === 'undefined' || !current) && !gr) {
throw 'One of current object or gr is mandatory';
}
gr = gr || current;
var util = GlideScriptRecordUtil.get(gr);
changes = util.getChangedFieldNames();
//Convert to JavaScript Array
gs.include('j2js');
changes = j2js(changes);
var canRunUpdate = false;
for(var i = 0; i < changes.length; i++){
var fieldName = changes[i];
if(gr.getElement(fieldName).getED().isChoiceTable()){
canRunUpdate = true;
break;
}
}
return canRunUpdate;
},
filterLifeCycleStage: function() {
var filter = 'sys_idIN';
var ids = [];
var currentTable = current.getTableName();
var tables = this.getParents(currentTable);
tables.push(currentTable);
var gr = new GlideRecord('life_cycle_control');
gr.addEncodedQuery('table.nameIN' + tables.join());
gr.addActiveQuery();
gr.query();
while (gr.next()) {
if (ids.indexOf(gr.life_cycle_stage.sys_id) < 0 &&
gr.life_cycle_stage != this.TBD_VAL.life_cycle_stage) {
ids.push(gr.life_cycle_stage.sys_id);
}
}
filter += ids.join();
return filter;
},
filterLifeCycleStageStatus: function() {
var filter = 'sys_idIN';
var ids = [];
var currentTable = current.getTableName();
var tables = this.getParents(currentTable);
tables.push(currentTable);
var stage = current.getValue('life_cycle_stage');
var gr = new GlideRecord('life_cycle_control');
gr.addEncodedQuery('table.nameIN' + tables.join() + '^life_cycle_stage.sys_id=' + stage+'^ORlife_cycle_stage='+stage);
gr.addActiveQuery();
gr.query();
while (gr.next()) {
if (ids.indexOf(gr.life_cycle_stage_status.sys_id) < 0) {
ids.push(gr.life_cycle_stage_status.sys_id);
}
}
filter += ids.join();
return filter;
},
findMatchingLifeCycleControl: function(tableName, currentRef, useReverseMapping){
//collect unique list of tables with mappings, then only
//query on those in the source table's hierarchy that actually
//exist in the mapping table, to prevent unnecessary lookups in
//the case of deeply nested child tables
var mappedTables = JSON.parse(SNC.CSDMLifeCycleMappingCacheManager.getUniqueTables());
var tables = this.getParents(tableName, true);
tables.unshift(tableName);
var legacyValueSet = []; //for reverse mapping, to update fallback fields
for (var i = 0; i < tables.length; i++) {
var table = tables[i];
if (mappedTables.indexOf(table) > -1) {
var ordered = JSON.parse(SNC.CSDMLifeCycleMappingCacheManager.getMappingForTable(table));
for (var rec = 0; rec < ordered.length; rec++) {
if(useReverseMapping){
var lifeCycleStage = ordered[rec].life_cycle_stage;
var lifeCycleStageStatus = ordered[rec].life_cycle_stage_status;
var reverseChoice = ordered[rec].reverse_sync_choice;
if(currentRef.getValue(this.LIFE_CYCLE_STAGE) == lifeCycleStage &&
currentRef.getValue(this.LIFE_CYCLE_STAGE_STATUS) == lifeCycleStageStatus &&
reverseChoice == 'true'){
var legacyValues = {};
legacyValues.primaryCol = ordered[rec].legacy_field_name;
legacyValues.primaryValue = ordered[rec].legacy_field_value;
legacyValues.subCol = ordered[rec].legacy_subfield_name;
legacyValues.subValue = ordered[rec].legacy_subfield_value;
legacyValues.mappedTable = table;
legacyValueSet.push(legacyValues);
}
} else {
var primaryCol = ordered[rec].legacy_field_name;
var primaryValue = ordered[rec].legacy_field_value;
var subCol = ordered[rec].legacy_subfield_name;
var subValue = ordered[rec].legacy_subfield_value;
var lifeCycleControl = {};
lifeCycleControl.life_cycle_stage = ordered[rec].life_cycle_stage;
lifeCycleControl.life_cycle_stage_status = ordered[rec].life_cycle_stage_status;
if (currentRef.getValue(primaryCol) == primaryValue) {
if (gs.nil(subCol) || gs.nil(subValue))
return lifeCycleControl;
else {
//compare against subCol as well
if(currentRef.getValue(subCol) == subValue)
return lifeCycleControl;
}
}
}
}
if(useReverseMapping)
return legacyValueSet;
//if no matching rules found for the most specific class that's mapped, return TBD
//do not check for further parent tables
return this.TBD_VAL;
}
}
//in the case of reverse mapping, if no matches are found then return an empty array for a no-op in the calling method
if(useReverseMapping)
return legacyValueSet;
return this.TBD_VAL;
},
_syncCSDMFieldsFromLegacy: function(tableName, currentRef) {
var lifeCycleValues = this.findMatchingLifeCycleControl(tableName, currentRef);
currentRef.setValue(this.LIFE_CYCLE_STAGE, lifeCycleValues.life_cycle_stage);
currentRef.setValue(this.LIFE_CYCLE_STAGE_STATUS, lifeCycleValues.life_cycle_stage_status);
},
updateFromLegacy: function(tableName, currentRef, isAsync) {
this._syncCSDMFieldsFromLegacy(tableName, currentRef);
if (isAsync === true)
currentRef.update();
},
updateFromCSDM: function(tableName, currentRef, isAsync) {
if( currentRef.getValue(this.LIFE_CYCLE_STAGE) &&
currentRef.getValue(this.LIFE_CYCLE_STAGE_STATUS) &&
currentRef.getValue(this.LIFE_CYCLE_STAGE) !== 'To Be Determined' ){
var legacyValueSet = this.findMatchingLifeCycleControl(tableName, currentRef, true);
var dependencyMap = this._getDependencyMap(legacyValueSet);
for(var i = 0; i < legacyValueSet.length; i++){
var legacyValues = legacyValueSet[i];
currentRef.setValue(legacyValues.primaryCol, legacyValues.primaryValue);
if(legacyValues.subCol){
currentRef.setValue(legacyValues.subCol, legacyValues.subValue);
} else if(dependencyMap[legacyValues.primaryCol]){
//If mapping has a dependent subfield with an empty value, set it back to empty
currentRef.setValue(dependencyMap[legacyValues.primaryCol], '');
}
}
//TODO: this is bad, async version will loop
if (isAsync === true)
currentRef.update();
}
},
_getDependencyMap: function(legacyValueSet) {
if(legacyValueSet.length < 1)
return {};
var primaryFieldsArr = [];
for(var i = 0; i < legacyValueSet.length; i++){
var primaryField = legacyValueSet[i].primaryCol;
if(primaryField)
primaryFieldsArr.push(primaryField);
}
var primaryFieldString = primaryFieldsArr.toString();
var gr = new GlideRecord('sys_dictionary');
gr.addQuery('dependent_on_field', 'IN', primaryFieldString);
gr.addQuery('name', legacyValueSet[0].mappedTable);
gr.query();
var dependencyMap = {};
while(gr.next()){
var dependentValue = gr.getValue('dependent_on_field');
if(!dependencyMap[dependentValue])
dependencyMap[dependentValue] = gr.getValue('element');
}
return dependencyMap;
},
validate: function(previousRef, currentRef) {
if (!currentRef) {
return;
}
// Insert
if (!previousRef || (!previousRef.life_cycle_stage && !previousRef.life_cycle_stage_status)) {
if (currentRef.life_cycle_stage_status && !currentRef.life_cycle_stage) { // Only status
gs.log('Abort insert! Life cycle stage is required but only stage status is provided');
currentRef.setAbortAction(true);
return;
} else if (currentRef.life_cycle_stage && !currentRef.life_cycle_stage_status) { // Only stage
currentRef.setValue('life_cycle_stage_status', 'NULL');
return;
}
// Both provided
if (!this._validateCombination(currentRef)) {
gs.log('Abort insert! Life cycle stage and stage status value combination is invalid');
currentRef.setAbortAction(true);
}
return;
}
// Update
if (previousRef.life_cycle_stage == currentRef.life_cycle_stage) { // Stage did not change
if (!this._validateCombination(currentRef)) {
gs.log('Abort update! Life cycle stage value did not change. Stage status value changed. Invalid stage and stage status value combination.');
currentRef.setAbortAction(true);
}
} else { // Stage changed
if (previousRef.life_cycle_stage_status == currentRef.life_cycle_stage_status) { // Status same
currentRef.setValue('life_cycle_stage_status', 'NULL');
} else { // Status changed
if (!this._validateCombination(currentRef)) {
gs.log('Abort update! Life cycle stage and stage status both changed. The value combination is invalid');
currentRef.setAbortAction(true);
}
}
}
},
validateUI: function(previousRef, currentRef) {
if (!currentRef) {
return;
}
if (!currentRef.life_cycle_stage && !currentRef.life_cycle_stage_status) {
return;
}
if (!this._validateCombination(currentRef)) {
var errorMessage = gs.getMessage('A valid lifecycle description requires both stage and stage status to be defined');
gs.addErrorMessage(errorMessage);
currentRef.setAbortAction(true);
}
},
getParamAsString: function(paramName) {
if (request.queryParams.hasOwnProperty(paramName))
return request.queryParams[paramName] + '';
return '';
},
bulkPopulate: function() {
var mutexName = '<<<-- Lifecycle Migration Mutex -->>>';
var mutex = new SelfCleaningMutex(mutexName);
// limit our attempt to get a mutex to 120 seconds...
mutex.setSpinWait(500);
mutex.setMaxSpins(240);
mutex.setMutexExpires(120000); //120 seconds
if (mutex.get()) {
try {
// Get list of tables
var mappedTables = JSON.parse(SNC.CSDMLifeCycleMappingCacheManager.getUniqueTables());
var map = {};
var countMap = {};
// Get parents for each
for (var i = 0; i < mappedTables.length; i++) {
var parents = this.getParents(mappedTables[i], true);
map[mappedTables[i]] = parents;
var len = parents.length;
if (!countMap[len]) {
countMap[len] = [];
}
countMap[len].push(mappedTables[i]);
}
// Now sort count map
var ordered = [];
Object.keys(countMap).sort().reverse().forEach(function(key) {
ordered.push(countMap[key]);
});
// Now loop
for (var j = 0; j < ordered.length; j++) {
var tableNames = ordered[j];
for (var k = 0; k < tableNames.length; k++) {
this._populateForTable(tableNames[k]);
}
}
} finally {
mutex.release();
}
} else {
//lock failed
gs.log("Unable to lock on to " + mutexName);
}
},
isLifeCycleMigrationActivated: function() {
return gs.getProperty(this.CSDM_LIFE_CYCLE_MIGRATION_ACTIVATED) === 'true';
},
isMappingTableReady: function() {
var mapGr = new GlideRecord(this.MAPPING_TABLE);
mapGr.addEncodedQuery('life_cycle_controlISEMPTY^ORactive=false');
mapGr.query();
return mapGr.hasNext() ? false : true;
},
_populateForTable: function(tableName) {
// First grab the mapping records
var ordered = JSON.parse(SNC.CSDMLifeCycleMappingCacheManager.getMappingForTable(tableName));
for (var rec = 0; rec < ordered.length; rec++) {
var legacyField = ordered[rec].legacy_field_name;
var legacyFieldVal = ordered[rec].legacy_field_value;
var legacySubfield = ordered[rec].legacy_subfield_name;
var legacySubfieldVal = ordered[rec].legacy_subfield_value;
var lifeCycleStage = ordered[rec].life_cycle_stage;
var lifeCycleStageStatus = ordered[rec].life_cycle_stage_status;
// Grab table records
var tableGr = new GlideRecord(tableName);
tableGr.addQuery(legacyField, legacyFieldVal);
if (legacySubfield && legacySubfieldVal) {
tableGr.addQuery('' + legacySubfield, '' + legacySubfieldVal);
}
//bulk populate for entire cmdb
tableGr.addNullQuery('life_cycle_stage');
tableGr.addNullQuery('life_cycle_stage_status');
tableGr.query();
if (tableGr.hasNext()) {
// Set lifecycle values
tableGr.setValue('life_cycle_stage', lifeCycleStage);
tableGr.setValue('life_cycle_stage_status', lifeCycleStageStatus);
tableGr.setWorkflow(false);
tableGr.updateMultiple();
}
}
// Fallback to TBD
tableGr = new GlideRecord(tableName);
tableGr.addNullQuery('life_cycle_stage');
tableGr.addNullQuery('life_cycle_stage_status');
tableGr.query();
if (tableGr.hasNext()) {
// Set lifecycle values
tableGr.setValue('life_cycle_stage', this.TBD_VAL.life_cycle_stage);
tableGr.setValue('life_cycle_stage_status', this.TBD_VAL.life_cycle_stage_status);
tableGr.setWorkflow(false);
tableGr.updateMultiple();
}
},
getParents: function(tableName, stringify) {
var manager = GlideDBObjectManager.get();
var parents = [];
var parent = manager.getBase(tableName);
var lastParent = '';
while (parent !== lastParent) {
parents.push(stringify ? parent + '' : parent);
lastParent = parent;
parent = manager.getBase(lastParent);
}
return parents;
},
_validateCombination: function(currentRef) {
if (!currentRef.life_cycle_stage || !currentRef.life_cycle_stage_status) {
return false;
}
var stage = currentRef.life_cycle_stage.sys_id;
var currentTable = currentRef.getTableName();
var tables = this.getParents(currentTable);
tables.push(currentTable);
var isValid = false;
var gr = new GlideRecord('life_cycle_control');
gr.addEncodedQuery('table.nameIN' + tables.join() + '^life_cycle_stage.sys_id=' + stage);
gr.query();
while (gr.next()) {
if (currentRef.life_cycle_stage_status.sys_id == gr.life_cycle_stage_status.sys_id) {
isValid = true;
break;
}
}
return isValid;
},
syncLegacyAndCSDMFields: function(current, previous) {
// if default value set for lifecycle fields ignore sync from CSDM
var isStageDefaultValueSet = String(current[this.LIFE_CYCLE_STAGE].getED().getDefault()) === String(current.life_cycle_stage);
var isStageStatusDefaultValueSet = String(current[this.LIFE_CYCLE_STAGE_STATUS].getED().getDefault()) === String(current.life_cycle_stage_status);
var isInsert = (current.operation() === 'insert');
var isCSDMDefaultValuesSet = isInsert && isStageDefaultValueSet && isStageStatusDefaultValueSet;
var callingTable = current.sys_class_name + '';
if (!isCSDMDefaultValuesSet && (current.life_cycle_stage.changes() || current.life_cycle_stage_status.changes())) {
this.updateFromCSDM(callingTable, current);
} else {
this.updateFromLegacy(callingTable, current);
}
},
type: 'LifeCycleUtil'
};
Sys ID
e133b911b7220010ee0d3177ee11a93b