Name
sn_irm_shared_cmn.IRMTableSchemaUtils
Description
Utility class to register, and process IRM tables schema updates.
Script
var IRMTableSchemaUtils = Class.create();
IRMTableSchemaUtils.CONSTANTS = {};
IRMTableSchemaUtils.CONSTANTS.OPERATIONS = {
OPERATION_UPDATE: 'UPDATE',
};
/**
* Utility class to register, and process IRM tables schema updates.
*/
IRMTableSchemaUtils.prototype = {
initialize: function(scope) {
this.scope = scope;
this.OPERATION_UPDATE = IRMTableSchemaUtils.CONSTANTS.OPERATIONS.OPERATION_UPDATE;
this.QUERYS = {
AND: '^',
EMPTY_QUERY: 'name=NULL^element=NULL',
NEW_QUERY: '^NQ',
};
this.TABLES = {
SYS_CHOICE: 'sys_choice',
SYS_DB_OBJECT: 'sys_db_object',
SYS_DICTIONARY: 'sys_dictionary',
};
this.currentElement = null;
this.logger = {
info: gs.info,
debug: gs.debug,
warn: gs.warn,
error: gs.error,
};
this.glideUtils = new sn_irm_shared_cmn.IRMGlideUtils({
logger: this.logger
});
this.operations = {};
},
/**
* A utility class to process table operations using a builder pattern.
* @private
* @param {string} tableName - table name to process operations for.
* @param {string} operation - operation name.
* @returns {IRMTableOperationProcessor} processor - mini-class to process table operations.
*/
IRMTableOperationProcessor: function(tableName, operation) {
var self = this;
return {
registerElementUpdate: self._registerElementUpdate(tableName, operation),
registerChoiceUpdate: self._registerChoiceUpdate(tableName, operation),
};
},
/**
* Get update operation configurations for a table.
* @private
* @param {string} tableName - table name
* @returns {Object}
*/
_getTableUpdateConfigurations: function(tableName) {
this.operations[tableName] = this.operations[tableName] || {};
this.operations[tableName][this.OPERATION_UPDATE] = this.operations[tableName][this.OPERATION_UPDATE] || {};
this.operations[tableName][this.OPERATION_UPDATE].elements =
this.operations[tableName][this.OPERATION_UPDATE].elements || {};
this.operations[tableName][this.OPERATION_UPDATE].overrides =
this.operations[tableName][this.OPERATION_UPDATE].overrides || {};
return this.operations[tableName][this.OPERATION_UPDATE];
},
/**
* Get all table elements registered for update operation.
* @private
* @param {string} tableName - table name
* @returns {Object}
*/
_getTableUpdateElements: function(tableName) {
return this._getTableUpdateConfigurations(tableName).elements;
},
/**
* Get configurations for a table element registered for update operation.
* @private
* @param {string} tableName - table name
* @param {string} element - element name
* @returns {Object}
*/
_getTableUpdateElementConfigurations: function(tableName, element) {
var currentElements = this._getTableUpdateElements(tableName);
currentElements[element] = currentElements[element] || {};
currentElements[element].choices = currentElements[element].choices || {};
currentElements[element].overrides = currentElements[element].overrides || {};
return currentElements[element];
},
/**
* Get all configurations for element's choices registered for an update operation.
* @private
* @param {string} tableName - table name.
* @param {string} element - element name.
* @returns {Object}
*/
_getTableUpdateElementChoicesConfigurations: function(tableName, element) {
var currentElement = this._getTableUpdateElementConfigurations(tableName, element);
return currentElement.choices;
},
/**
* Get element's choice configurations.
* @private
* @param {string} tableName - table name.
* @param {string} element - element name.
* @param {string} choice - choice name.
* @returns {Object}
*/
_getTableUpdateElementChoiceConfigurations: function(tableName, element, choice) {
var currentChoices = this._getTableUpdateElementChoicesConfigurations(tableName, element);
currentChoices[choice] = currentChoices[choice] || {};
currentChoices[choice].overrides = currentChoices[choice].overrides || {};
return currentChoices[choice];
},
/**
* Get all update operation overrides registered for an element.
* @private
* @param {string} tableName - table name.
* @param {string} element - element name.
* @returns {Object}
*/
_getTableUpdateElementOverrides: function(tableName, element) {
var currentElement = this._getTableUpdateElementConfigurations(tableName, element);
return currentElement.overrides;
},
/**
* Get all update operation overrides registered for an element's choice.
* @private
* @param {string} tableName - table name.
* @param {string} element - element name.
* @param {string} choice - choice name.
* @returns {Object}
*/
_getTableUpdateElementChoiceOverrides: function(tableName, element, choice) {
var currentChoice = this._getTableUpdateElementChoiceConfigurations(tableName, element, choice);
return currentChoice.overrides;
},
/**
* Get all update operation overrides registered for a table.
* @private
* @param {string} tableName - table name.
* @returns {Object}
*/
_getTableUpdateOverrides: function(tableName) {
return this._getTableUpdateConfigurations(tableName).overrides;
},
/**
* Set table update overrides.
* @private
* @param {string} tableName - table name
* @param {TableUpdateOverrides} overrides - Update overrides to apply.
*/
_setTableUpdateOverrides: function(tableName, overrides) {
var currentOverrides = this._getTableUpdateOverrides(tableName);
currentOverrides = IRMCoreUtils.objectAssign(currentOverrides, overrides);
},
/**
* Set element update overrides.
* @private
* @param {string} tableName - table name
* @param {string} element - element name
* @param {ElementUpdateOverrides} overrides - Update overrides to apply.
*/
_setTableUpdateElementOverrides: function(tableName, element, overrides) {
this.currentElement = element;
var currentOverrides = this._getTableUpdateElementOverrides(tableName, element);
IRMCoreUtils.objectAssign(currentOverrides, overrides);
},
/**
* Set element's choice update overrides.
* @private
* @param {string} tableName - table name
* @param {string} element - element name
* @param {string} choice - choice name
* @param {ChoiceUpdateOverrides} overrides - Update overrides to apply.
*/
_setTableUpdateElementChoiceOverrides: function(tableName, element, choice, overrides) {
var currentOverrides = this._getTableUpdateElementChoiceOverrides(tableName, element, choice);
IRMCoreUtils.objectAssign(currentOverrides, overrides);
},
/**
* Get all element's choices registered for update operation.
* @param {string} table - table name.
* @param {string} element - element name.
* @returns {string[]} choices
*/
getRegisteredChoices: function(tableName, element) {
var choices = this._getTableUpdateElementChoicesConfigurations(tableName, element);
return Object.keys(choices);
},
/**
* Get all tables registered for schema changes.
* @returns {string[]} tables
*/
getRegisteredTables: function() {
return Object.keys(this.operations);
},
/**
* Get all elements registered for schema changes.
* @param {string} table - table name to get elements for.
* @returns {string[]} Fields in table
*/
getRegisteredElements: function(table) {
var elements = this._getTableUpdateElements(table);
return Object.keys(elements);
},
/**
* A method that gets all SysChoice records for choices registered for schema changes.
* @returns {GlideRecord} choiceRecords
*/
getChoiceRecords: function() {
var self = this;
var AND = self.QUERYS.AND;
var EMPTY_QUERY = self.QUERYS.EMPTY_QUERY;
var NEW_QUERY = self.QUERYS.NEW_QUERY;
var filteredTables = self.getRegisteredTables().filter(function(table) {
return Object.keys(self._getTableUpdateConfigurations(table).elements || {}).length;
});
var query = filteredTables.reduce(function(acc, cur, index) {
var fields = self.getRegisteredElements(cur);
var filteredFields = fields.filter(function(element) {
return Object.keys(self._getTableUpdateElementConfigurations(cur, element).choices || {}).length;
});
if (index > 0 && filteredFields.length > 0) {
acc += NEW_QUERY;
}
acc += filteredFields.reduce(function(filter, element, _index) {
var values = self.getRegisteredChoices(cur, element);
if (_index > 0) {
filter += NEW_QUERY;
}
filter += 'name=' + cur + AND + 'element=' + element + AND + 'valueIN' +
values + AND + 'language=en';
return filter;
}, '');
return acc;
}, '') || EMPTY_QUERY;
var choiceRecords = new GlideRecord(self.TABLES.SYS_CHOICE);
choiceRecords.addEncodedQuery(query);
choiceRecords.query();
return choiceRecords;
},
/**
* A method that gets all SysDBObject records for tables registered for schema changes.
* @returns {GlideRecord} tableRecords
*/
getTableRecords: function() {
var self = this;
var tables = self.getRegisteredTables();
var filteredTables = tables.filter(function(table) {
var overrides = self._getTableUpdateOverrides(table);
return overrides.hasOwnProperty('label');
});
var tableRecords = new GlideRecord(self.TABLES.SYS_DB_OBJECT);
tableRecords.addQuery('sys_scope.scope', this.scope);
tableRecords.addQuery('name', 'IN', filteredTables.join(','));
tableRecords.query();
return tableRecords;
},
/**
* A method that gets all SysDictionary records for elements registered for schema changes.
* @returns {GlideRecord} elementRecords
*/
getElementRecords: function() {
var self = this;
var AND = self.QUERYS.AND;
var EMPTY_QUERY = self.QUERYS.EMPTY_QUERY;
var NEW_QUERY = self.QUERYS.NEW_QUERY;
var filteredTables = this.getRegisteredTables().filter(function(table) {
return Object.keys(self._getTableUpdateConfigurations(table).elements || {}).length;
});
var query = filteredTables.reduce(function(acc, cur, index) {
var fields = self.getRegisteredElements(cur);
var filteredFields = fields.filter(function(element) {
return Object.keys(self._getTableUpdateElementConfigurations(cur, element).overrides || {}).length;
});
if (index > 0) {
acc += NEW_QUERY;
}
acc += 'name=' + cur + AND + 'elementIN' + filteredFields;
return acc;
}, '') || EMPTY_QUERY;
var elementRecords = new GlideRecord(self.TABLES.SYS_DICTIONARY);
elementRecords.addQuery('sys_scope.scope', this.scope);
elementRecords.addEncodedQuery(query);
elementRecords.query();
return elementRecords;
},
/**
* Get table (SysDBObject) schema updates.
* @param {GlideRecord} record - SysDBObject record to get schema changes for.
* @returns {Object} changes - change for record
*/
getTableUpdateSchemaChanges: function(record) {
var self = this;
var tableName = record.getValue('name');
var overrides = self._getTableUpdateOverrides(tableName);
var safeChanges = {};
Object.keys(overrides).forEach(function(override) {
if (override === 'label') {
safeChanges[override] = overrides[override];
} else {
// eslint-disable-next-line sonarjs/no-duplicate-string, max-len
self.logger.warn('Failed to register "' + override + '" change for "' + tableName + '". "' + override + ': change is not allowed.');
}
});
return safeChanges;
},
/**
* Get element (SysDictionary) schema updates.
* @param {GlideRecord} record - record for get elements schema changes.
* @returns {Object} change for elements
*/
getTableUpdateElementSchemaChanges: function(record) {
var self = this;
var tableName = record.getValue('name');
var element = record.getValue('element');
var overrides = this._getTableUpdateElementOverrides(tableName, element);
var safeChanges = {};
Object.keys(overrides).forEach(function(override) {
if (override !== 'name') {
safeChanges[override] = overrides[override];
} else {
// eslint-disable-next-line sonarjs/no-duplicate-string, max-len
self.logger.warn('Failed to register "' + override + '" change for "' + tableName + '/' + element + '". "' + override + '" change is not allowed.');
}
});
return safeChanges;
},
/**
* Get choice (SysChoice) schema updates.
* @param {GlideRecord} record - record for get elements schema changes.
* @returns {Object} change for elements
*/
getTableUpdateElementChoiceSchemaChanges: function(record) {
var self = this;
var tableName = record.getValue('name');
var element = record.getValue('element');
var choice = record.getValue('value');
var safeChanges = {};
var overrides = this._getTableUpdateElementChoiceOverrides(tableName, element, choice);
Object.keys(overrides).forEach(function(override) {
if (override === 'label') {
safeChanges[override] = overrides[override];
} else {
// eslint-disable-next-line sonarjs/no-duplicate-string, max-len
self.logger.warn('Failed to register "' + override + '" change for "' + tableName + '/' + element + '/' + choice + '". "' + override + '" change is not allowed.');
}
});
return safeChanges;
},
/**
* A method to process the registered table schema operations.
* @param {string} operation - Operation to process.
* @param {Function} processor - method to perform updates on SysDBObject GlideRecord
*/
processTableOperation: function(operation, processor) {
var self = this;
var tableRecord = this.getTableRecords();
while (tableRecord.next()) {
var errors = this.validateSchemaUpdate(tableRecord);
if (errors.length > 0) {
for (var i = 0; i < errors.length; i++) {
self.logger.error(errors[i]);
}
continue;
}
var changes = this.getTableUpdateSchemaChanges(tableRecord);
var updateResult = processor(tableRecord, changes);
if (updateResult.updated) {
self.logger.info('Table update result is : ' + updateResult.updated);
} else {
self.logger.warn('Table update result is : ' + updateResult.updated);
}
}
},
/**
* A method to process the registered elements schema updates
* @param {Function} processor method to perform updates on SysDBObject GlideRecord
* @param {string} operation - Operation to process.
*/
processElementOperation: function(operation, processor) {
var self = this;
var currentElementRecord = this.getElementRecords();
while (currentElementRecord.next()) {
var errors = this.validateSchemaUpdate(currentElementRecord);
if (errors.length > 0) {
for (var i = 0; i < errors.length; i++) {
self.logger.error(errors[i]);
}
continue;
}
var changes = this.getTableUpdateElementSchemaChanges(currentElementRecord);
var updateResult = processor(currentElementRecord, changes);
self.logger.info('element update result is : ' + updateResult.updated);
}
},
/**
* A method to process the registered elements schema updates
* @param {Function} processor method to perform updates on SysDBObject GlideRecord
* @param {string} operation - Operation to process.
*/
processChoiceOperation: function(operation, processor) {
var self = this;
var choiceRecords = this.getChoiceRecords();
while (choiceRecords.next()) {
var errors = this.validateSchemaUpdate(choiceRecords);
if (errors.length > 0) {
for (var i = 0; i < errors.length; i++) {
self.logger.error(errors[i]);
}
continue;
}
var changes = this.getTableUpdateElementChoiceSchemaChanges(choiceRecords);
// if (!changes) continue
var updateResult = processor(choiceRecords, changes);
self.logger.info('element update result is : ' + updateResult.updated);
}
},
/**
* Wrapper function for registering choice updates.
* @param {string} tableName table name.
* @param {string} operation operation name.
* @returns {Function}
*/
_registerChoiceUpdate: function(tableName, operation) {
var self = this;
/**
* Function to register choice update operations.
* @param {string} choice - choice name.
* @param {Object} overrides - choice record overrides.
* @returns {IRMTableOperationProcessor} processor
*/
return function(choice, overrides) {
if (operation !== self.OPERATION_UPDATE) {
// eslint-disable-next-line max-len
self.logger.error('Failed to register "' + operation + ' " operation on Choice "' + tableName + '/' + self.currentElement + '/' + choice + '". Choice updates can only be registered for "' + self.OPERATION_UPDATE + '" table operation.');
return self.IRMTableOperationProcessor(tableName, operation);
}
if (!self.currentElement) {
// eslint-disable-next-line max-len
self.logger.error('Failed to register "' + operation + ' " operation on Choice "' + tableName + '/' + self.currentElement + '/' + choice + '". Choice updates can only registered after an element update operation.');
return self.IRMTableOperationProcessor(tableName, operation);
}
self._setTableUpdateElementChoiceOverrides(tableName, self.currentElement, choice, overrides);
return self.IRMTableOperationProcessor(tableName, operation);
};
},
/**
* Wrapper function for registering element updates.
* @param {string} tableName table name.
* @param {string} operation operation name.
* @returns {Function}
*/
_registerElementUpdate: function(tableName, operation) {
var self = this;
/**
* Function to register element update operations.
* @param {string} element - element name.
* @param {Object} overrides - element record overrides.
* @returns {IRMTableOperationProcessor} processor
*/
return function(element, overrides) {
if (operation !== self.OPERATION_UPDATE) {
// eslint-disable-next-line max-len
self.logger.error('Failed to register "' + operation + ' " operation on Element "' + tableName + '/' + element + '". Element updates can only be registered for "' + self.OPERATION_UPDATE + '" table operation.');
return self.IRMTableOperationProcessor(tableName, operation);
}
self._setTableUpdateElementOverrides(tableName, element, overrides);
return self.IRMTableOperationProcessor(tableName, operation);
};
},
/**
* Register schema changes operations on a table.
* @param {string} tableName - table name.
* @param {string} operation - operation name.
* @param {Object} overrides - table record overrides.
* @returns {IRMTableOperationProcessor} processor
*/
registerTableOperation: function(tableName, operation, overrides) {
var self = this;
self.currentElement = null;
if (operation === self.OPERATION_UPDATE) {
self._setTableUpdateOverrides(tableName, overrides);
return self.IRMTableOperationProcessor(tableName, operation);
}
// eslint-disable-next-line max-len
self.logger.error('Failed to register "' + operation + ' " operation on table "' + tableName + '". "' + operation + '" is an invalid operation.');
return self.IRMTableOperationProcessor(tableName, operation);
},
reportChanges: function() {},
/**
* Get an Object with Schema changes validation rules.
* @returns {ValidationRules}
*/
_getValidationRules: function() {
var self = this;
return {
validateCustomization: function(record) {
var updateName;
if (record.getTableName() === 'sys_choice') {
var tableName = record.getValue('name');
var element = record.getValue('element');
updateName = ['sys_choice', tableName, element].join('_');
} else {
updateName = record.getValue('sys_update_name');
}
var isRecordCustomized = self.glideUtils.isRecordCustomized(updateName);
if (isRecordCustomized) {
// eslint-disable-next-line max-len
return 'Unable to update "' + record.getDisplayValue() + ' (' + updateName + ')". This file has been customized.';
}
return null;
},
};
},
/**
* Check if the registered schema updates are valid for a record.
* @param {GlideRecord} record - record to use for validations.
* @returns {string[]} errors - Array of validation error messages.
*/
validateSchemaUpdate: function(record) {
var errors = [];
var validationRules = this._getValidationRules();
var rulesKeys = Object.keys(validationRules);
for (var i = 0; i < rulesKeys.length; i++) {
var key = rulesKeys[i];
var rule = validationRules[key];
var errorMessage = rule(record);
if (errorMessage) {
errors.push(errorMessage);
break;
}
}
return errors;
},
type: 'IRMTableSchemaUtils',
};
Sys ID
91a863d9535221108288ddeeff7b1205