Name
sn_cmdb_int_util.ApplicationValidationUtil
Description
Utility class to verify RTE entity based configurations follow conventions enforced by the Integration Hub ETL UI
Script
/*
Utility to validate that a new CMDB Application is valid and loadable by the
Integration Studio ETL UI. Tests for a variety of correctness and
convention based rules.
validateAllApplications : validates all applications found under cmdb_inst_application
validateApplicationFeed(cmdb_inst_application_feed.sys_id) : validates a single application feed given a sys_id
*/
var ApplicationValidationUtil = Class.create();
ApplicationValidationUtil.prototype = {
initialize: function() {},
validateAllApplications: function() {
var result = {};
result.errors = [];
var applicationsGr = new GlideRecord('cmdb_inst_application');
applicationsGr.query();
while (applicationsGr.next()) {
result.errors = result.errors.concat(this._validateApplication(applicationsGr));
}
if (!result.errors)
result.status = "Valid";
else
result.status = "Error";
return result;
},
validateApplicationFeed: function(cmdbInstApplicationFeedSysId) {
var result = {};
result.errors = [];
var applicationsGr = new GlideRecord('cmdb_inst_application_feed');
applicationsGr.addQuery('sys_id', cmdbInstApplicationFeedSysId);
applicationsGr.query();
applicationsGr.next();
result.errors = result.errors.concat(this._validateApplicationFeed(applicationsGr));
if (result.errors.length == 0)
result.status = "Valid";
else
result.status = "Error";
return result;
},
_validateApplication: function(applicationGr) {
var errors = [];
var applicationName = applicationGr.getValue('name');
var applicationSysId = applicationGr.getUniqueValue();
var applicationDisplay = this._buildDisplay('cmdb_inst_application', applicationSysId, applicationName);
if (!applicationGr.getValue('discovery_source'))
errors.push(applicationDisplay + "No discovery_source found. Please select one.");
var applicationFeedGr = new GlideRecord('cmdb_inst_application_feed');
applicationFeedGr.addQuery('cmdb_inst_application', applicationGr.getUniqueValue());
applicationFeedGr.query();
while (applicationFeedGr.next()) {
var feedSysId = applicationFeedGr.getUniqueValue();
var feedName = applicationFeedGr.getValue('name');
var feedErrors = this._validateApplicationFeed(applicationFeedGr);
for (var i = 0; i < feedErrors.length; i++)
feedErrors[i] = 'cmdb_inst_application_feed:(' + feedSysId + "," + feedName + ") : " + feedErrors[i];
errors = errors.concat(feedErrors);
}
return errors;
},
_validateApplicationFeed: function(applicationFeedGr) {
var errors = [];
var applicationFeedName = applicationFeedGr.getValue('name');
var applicationFeedSysId = applicationFeedGr.getUniqueValue();
var applicationFeedDisplay = this._buildDisplay('cmdb_inst_application_feed', applicationFeedSysId, applicationFeedName);
var importTableName = '';
//check for data source
var dataSourceSysId = applicationFeedGr.getValue('sys_data_source');
if (!dataSourceSysId)
errors.push(applicationFeedDisplay + "No data source. UI will require a data source.");
else {
var dataSourceGr = new GlideRecord('sys_data_source');
dataSourceGr.addQuery('sys_id', dataSourceSysId);
dataSourceGr.query();
if (!dataSourceGr.next())
errors.push(applicationFeedDisplay + "Data source reference is somehow invalid. The referenced data source does not exist or cannot be found.");
else
importTableName = dataSourceGr.getValue('import_set_table_name');
}
var tempEntitySysId;
var importEntitySysId;
// check entities
var entityGr = new GlideRecord('cmdb_inst_entity');
entityGr.addQuery('sys_rte_eb_definition', applicationFeedSysId);
entityGr.query();
entitySysIds = [];
while (entityGr.next()) {
var entityName = entityGr.getValue('name');
var entitySysId = entityGr.getUniqueValue();
var entityDisplay = this._buildDisplay('cmdb_inst_entity', entitySysId, entityName);
var entityTargetTable = entityGr.getValue('table');
var entityPath = entityGr.getValue('path');
var lookupEntity = entityGr.getValue('lookup_for_entity');
var relatedForEntity = entityGr.getValue('related_for_entity');
var relationshipType = entityGr.getValue('relationship_type');
entitySysIds.push(entitySysId);
if (!entityName) {
errors.push(entityDisplay + "Has no name. Name should be added.");
continue;
}
errors = errors.concat(this._checkForExistence('cmdb_inst_entity', lookupEntity, entityDisplay));
errors = errors.concat(this._checkForExistence('cmdb_inst_entity', relatedForEntity, entityDisplay));
errors = errors.concat(this._checkForExistence('cmdb_rel_type', relationshipType, entityDisplay));
// import entity
if (entityName.toLowerCase().contains('import') && (entityTargetTable && importTableName === entityTargetTable)) {
if ('Import' != entityName)
errors.push(entityDisplay + "Should be named 'Import' by convention");
if (entityPath)
errors.push(entityDisplay + "Path must be empty. The import set processor will not append any path to the processing complex object.");
if (importEntitySysId)
errors.push(applicationFeedDisplay + "Seems to have multiple 'Import' entities. There should only be a single import entity to represent the import table. Remove the other.");
if (lookupEntity)
errors.push(entityDisplay + "Import entity should not be a lookup rule. Remove the Lookup For Entity value.");
if (relatedForEntity)
errors.push(entityDisplay + "Import entity should not be a related rule. Remove the Related For Entity value.");
if (relationshipType)
errors.push(entityDisplay + "Import entity should not be a relationship. Remove the Relationship type value.");
importEntitySysId = entitySysId;
}
// temp entity
else if (entityName.toLowerCase().contains('temp')) {
if ('Temp' != entityName)
errors.push(entityDisplay + "Should be named 'Temp' by convention");
if (entityPath != 'temp')
errors.push(entityDisplay + "Path should be 'temp' by convention.");
if (tempEntitySysId)
errors.push(applicationFeedDisplay + "Seems to have multiple 'Temp' entities. There should only be a single temp entity to represent the working grid in the UI. Remove the other.");
if (lookupEntity)
errors.push(entityDisplay + "Temp entity should not be a lookup rule. Remove the Lookup For Entity value.");
if (relatedForEntity)
errors.push(entityDisplay + "Temp entity should not be a related rule. Remove the Related For Entity value.");
if (relationshipType)
errors.push(entityDisplay + "Temp entity should not be a relationship. Remove the Relationship type value.");
tempEntitySysId = entitySysId;
}
//== cmdb entity
else {
errors = errors.concat(this._validateCmdbEntity(entityDisplay, entityName, entityTargetTable, entityPath));
// cmdb_rel_ci tables should have a relationship type set
if ('cmdb_rel_ci' == entityTargetTable && !relationshipType)
errors.push(entityDisplay + "Cmdb Relationship tables should have a Relationship Type selected. Please add one.");
// all entities not cmdb_rel_ci should have source native key set
else if ('cmdb_rel_ci' != entityTargetTable && !lookupEntity) {
var sourceNativeKeyGr = new GlideRecord('sys_rte_eb_field');
sourceNativeKeyGr.addQuery('field', 'source_native_key');
sourceNativeKeyGr.addQuery('sys_rte_eb_entity', entitySysId);
sourceNativeKeyGr.query();
if (!sourceNativeKeyGr.next())
errors.push(entityDisplay + "All entities (except relationships, lookups, temp, and import) should have a field called 'source_native_key'. Please add and map.");
}
}
errors = errors.concat(this._validateFields(entitySysId, applicationFeedSysId, entityTargetTable, importEntitySysId === entitySysId));
}
errors = errors.concat(this._validateEntityMappings(applicationFeedSysId, importEntitySysId, tempEntitySysId));
errors = errors.concat(this._validateOperations(applicationFeedSysId, tempEntitySysId));
return errors;
},
_validateOperations: function(applicationFeedSysId, tempEntitySysId) {
var errors = [];
if (!tempEntitySysId)
return errors;
var gr = new GlideRecord('sys_rte_eb_operation');
gr.addQuery('sys_rte_eb_definition', applicationFeedSysId);
gr.query();
while (gr.next()) {
var operationSysId = gr.getUniqueValue();
var operationName = gr.getValue('name');
var operationDisplay = this._buildDisplay('sys_rte_eb_operation', operationSysId, operationName);
var operationEntity = gr.getValue('sys_rte_eb_entity');
var targetField = gr.getValue('target_sys_rte_eb_field');
var targetFields = gr.getValue('target_sys_rte_eb_fields');
var sourceField = gr.getValue('source_sys_rte_eb_field');
var sourceFields = gr.getValue('source_sys_rte_eb_fields');
var operationType = gr.getValue('type');
if (operationEntity != tempEntitySysId)
errors.push(operationDisplay + "By convention only the Temp entity should have operations assigned to it. Please move operation to the Temp entity.");
// can't do the same check for sources because there are operations with no source fields
if (!targetField && !targetFields)
errors.push(operationDisplay + "Operation does not have any targets set.");
if (!operationType)
errors.push(operationDisplay + "Missing operation type. Please add operation type.");
if (sourceField) {
errors = errors.concat(this._checkForExistence('sys_rte_eb_field', sourceField, operationDisplay));
if (gr.source_sys_rte_eb_field.sys_rte_eb_entity != tempEntitySysId)
errors.push(operationDisplay + "By convention only the Temp entity should have operations assigned to it. Source field:" + sourceField + " is not a Temp entity field.");
}
if (sourceFields) {
var sourceFieldsSplit = sourceFields.split(',');
for (var i = 0; i < sourceFieldsSplit.length; i++) {
var sourceFieldGr = new GlideRecord('sys_rte_eb_field');
sourceFieldGr.addQuery('sys_id', sourceFieldsSplit[i]);
sourceFieldGr.query();
if (!sourceFieldGr.next())
errors.push(operationDisplay + "Source field:" + sourceFieldsSplit[i] + " does not exist or is invalid.");
else if (sourceFieldGr.getValue('sys_rte_eb_entity') != tempEntitySysId)
errors.push(operationDisplay + "By convention only the Temp entity should have operations assigned to it. Source field:" + sourceFieldsSplit[i] + " is not a Temp entity field.");
}
}
if (targetField) {
errors = errors.concat(this._checkForExistence('sys_rte_eb_field', targetField, operationDisplay));
if (gr.target_sys_rte_eb_field.sys_rte_eb_entity != tempEntitySysId) {
errors.push(operationDisplay + "By convention only the Temp entity should have operations assigned to it. Target field:" + targetField + " is not a Temp entity field.");
}
}
if (targetFields) {
var targetFieldsSplit = targetFields.split(',');
for (var j = 0; j < targetFieldsSplit.length; j++) {
var targetFieldGr = new GlideRecord('sys_rte_eb_field');
targetFieldGr.addQuery('sys_id', targetFieldsSplit[j]);
targetFieldGr.query();
if (!targetFieldGr.next())
errors.push(operationDisplay + "Target field:" + targetFieldsSplit[j] + " does not exist or is invalid.");
else if (targetFieldGr.getValue('sys_rte_eb_entity') != tempEntitySysId)
errors.push(operationDisplay + "By convention only the Temp entity should have operations assigned to it. Target field:" + targetFieldsSplit[j] + " is not a Temp entity field.");
}
}
}
return errors;
},
_validateEntityMappings: function(applicationFeedSysId, importEntitySysId, tempEntitySysId) {
var errors = [];
if (!tempEntitySysId || !importEntitySysId)
return errors;
var gr = new GlideRecord('sys_rte_eb_entity_mapping');
gr.addQuery('sys_rte_eb_definition', applicationFeedSysId);
gr.query();
while (gr.next()) {
var mappingSysId = gr.getUniqueValue();
var mappingName = gr.getValue('name');
var mappingDisplay = this._buildDisplay('sys_rte_eb_entity_mapping', mappingSysId, mappingName);
var mappingSourceEntity = gr.getValue('source_sys_rte_eb_entity');
var mappingTargetEntity = gr.getValue('target_sys_rte_eb_entity');
errors = errors.concat(this._checkForExistence('cmdb_inst_entity', mappingSourceEntity, mappingDisplay));
errors = errors.concat(this._checkForExistence('cmdb_inst_entity', mappingTargetEntity, mappingDisplay));
if ((mappingSourceEntity == importEntitySysId && mappingTargetEntity != tempEntitySysId) ||
(mappingSourceEntity != importEntitySysId && mappingTargetEntity == tempEntitySysId))
errors.push(mappingDisplay + "By convention the Import entity should only map to the Temp entity. Please change mapping.");
if (mappingSourceEntity == mappingTargetEntity)
errors.push(mappingDisplay + "Source and target entity cannot match. Remove this mapping.");
if (mappingTargetEntity == importEntitySysId)
errors.push(mappingDisplay + "By convention nothing should map to the Import entity. Remove this mapping.");
if (mappingSourceEntity != tempEntitySysId && (mappingTargetEntity != importEntitySysId && mappingTargetEntity != tempEntitySysId))
errors.push(mappingDisplay + "By convention only the Temp entity should map to any other non-Import entity. Remove this mapping.");
errors = errors.concat(this._validateEntityFieldMappings(applicationFeedSysId, mappingSysId, mappingSourceEntity, mappingTargetEntity, importEntitySysId, tempEntitySysId));
}
return errors;
},
_validateEntityFieldMappings: function(applicationFeedSysId, mappingSysId, mappingSourceEntity, mappingTargetEntity, importEntitySysId, tempEntitySysId) {
var errors = [];
var gr = new GlideRecord('sys_rte_eb_field_mapping');
gr.addQuery('sys_rte_eb_entity_mapping', mappingSysId);
gr.query();
while (gr.next()) {
var fieldMappingSysId = gr.getUniqueValue();
var fieldMappingDisplay = this._buildDisplay('sys_rte_eb_field_mapping', fieldMappingSysId, null);
var fieldMappingTargetField = gr.target_sys_rte_eb_field;
var fieldMappingTargetEntity = gr.target_sys_rte_eb_field.sys_rte_eb_entity;
var fieldMappingSourceEntity = gr.source_sys_rte_eb_field.sys_rte_eb_entity;
var fieldMappingReferencedEntity = gr.referenced_sys_rte_eb_entity;
var fieldMappingTargetFieldField = gr.target_sys_rte_eb_field.field;
var fieldMappingSourceFieldField = gr.source_sys_rte_eb_field.field;
if (!fieldMappingTargetEntity)
errors.push(fieldMappingDisplay + "Field mappings must have a target.");
if (fieldMappingTargetEntity != mappingTargetEntity)
errors.push(fieldMappingDisplay + "Field mapping target not the same as entity mapping target. Select a new field that is part of the target entity.");
if (!fieldMappingSourceEntity && !fieldMappingReferencedEntity)
errors.push(fieldMappingDisplay + "Must have either a source field or referenced entity.");
if (fieldMappingSourceEntity && fieldMappingReferencedEntity)
errors.push(fieldMappingDisplay + "Cannot have both a source field and referenced entity. Remove one.");
if (fieldMappingSourceEntity && (fieldMappingSourceEntity != mappingSourceEntity))
errors.push(fieldMappingDisplay + "Field mapping source not the same as entity mapping source. Select a new field that is part of the source entity.");
if (fieldMappingReferencedEntity && (fieldMappingReferencedEntity == importEntitySysId || fieldMappingReferencedEntity == tempEntitySysId))
errors.push(fieldMappingDisplay + "Referenced entity cannot be the Temp or Import entity. Remove field mapping or change referenced entity.");
if (fieldMappingSourceFieldField && mappingSourceEntity == importEntitySysId && mappingTargetEntity == tempEntitySysId && fieldMappingTargetFieldField != fieldMappingSourceFieldField)
errors.push(fieldMappingDisplay + "When an Import entity field is mapped to a temp entity the field values must match. Update entity field "+fieldMappingTargetField + " to change the 'field' value from:"+fieldMappingTargetFieldField+" to:" + fieldMappingSourceFieldField);
}
return errors;
},
_validateFields: function(entitySysId, applicationFeedSysId, entityTargetTable, isImportEntity) {
var errors = [];
var gr = new GlideRecord('sys_rte_eb_field');
gr.addQuery('sys_rte_eb_entity', entitySysId);
gr.query();
while (gr.next()) {
var fieldSysId = gr.getUniqueValue();
var fieldName = gr.getValue('name');
var fieldField = gr.getValue('field');
var fieldDisplay = this._buildDisplay('sys_rte_eb_field', fieldSysId, fieldName);
if (!fieldName)
errors.push(fieldDisplay + "Name value is missing. Please add.");
if (!fieldField)
errors.push(fieldDisplay + "Field value is missing. Please add.");
//if (fieldName && fieldField && fieldName != fieldField)
// errors.push(fieldDisplay + "Name and field value should match. Change name:"+fieldName + " to:"+fieldField);
if (gr.getValue('sys_rte_eb_definition') != applicationFeedSysId)
errors.push(fieldDisplay + "Is not in the correct definition. Set definition to be : " + applicationFeedSysId);
// if this entity has a table, all of the fields should exist on that table
if (entityTargetTable) {
if (fieldField == 'source_native_key' || fieldField == 'source_recency_timestamp') {
continue;
}
var tableGr = new GlideRecord('sys_dictionary');
tableGr.addQuery('name', entityTargetTable);
tableGr.addQuery('element', fieldField);
tableGr.query();
if (!tableGr.next()) {
if (isImportEntity) {
var importSetRowTableGr = new GlideRecord('sys_dictionary');
importSetRowTableGr.addQuery('name', 'sys_import_set_row');
importSetRowTableGr.addQuery('element', fieldField);
importSetRowTableGr.query();
if (importSetRowTableGr.next())
continue;
}
errors.push(fieldDisplay + "Does not exist as a column in the target table:" + entityTargetTable + ". All field values must be the a column name on the target table (except for source_native_key and source_recency_timestamp).");
}
}
}
return errors;
},
_validateCmdbEntity: function(entityDisplay, entityName, entityTargetTable, entityPath) {
var errors = [];
if (!entityPath)
errors.push(entityDisplay + "Must have path set.");
return errors;
},
_buildDisplay: function(table, sysId, name) {
if (name)
return table + "(" + name + "," + sysId + ") : ";
return table + "(" + sysId + ") : ";
},
_checkForExistence: function(table, sysId, sourceName) {
// needs to be a list so we don't add nulls to the collection
var errors = [];
if (!sysId)
return errors;
var gr = new GlideRecord(table);
gr.addQuery('sys_id', sysId);
gr.query();
if (!gr.next())
errors.push(sourceName + "Could not find sys_id:" + sysId + " in table " + table);
return errors;
},
type: 'ApplicationValidationUtil'
};
Sys ID
1ecd2ca0732100102b6265a751ab9e44