Name

sn_int_studio.CMDBIntegrationStudioUtility

Description

Utility scripts for the CMDB Integration Studio

Script

var CMDBIntegrationStudioUtility = Class.create();
  	CMDBIntegrationStudioUtility.prototype = {

  		initialize: function() {

  			this.CMDB_INST_APPLICATION = "cmdb_inst_application";
  			this.CMDB_INST_APPLICATION_FEED = "cmdb_inst_application_feed";
  			this.SYS_DATA_SOURCE = "sys_data_source";
  			this.SYS_IMPORT_SET = "sys_import_set";
  			this.NUMBER = "number";
  
  			this.CMDB_INST_ENTITY = 'cmdb_inst_entity';
  			this.IMPORT_LOG = 'import_log';

  			this.SYS_DICTIONARY = "sys_dictionary";
  			this.ELEMENT = "element";
  			this.SYS_PREFIX = "sys_";

  			this.SYS_RTE_EB_DEFINITION = "sys_rte_eb_definition";
  			this.SYS_RTE_EB_ENTITY = "sys_rte_eb_entity";
  			this.CMDB_INST_ENTITY = "cmdb_inst_entity";
  			this.SYS_RTE_EB_FIELD = "sys_rte_eb_field";
  			this.SYS_RTE_EB_ENTITY_MAPPING = "sys_rte_eb_entity_mapping";
  			this.SYS_RTE_EB_FIELD_MAPPING = "sys_rte_eb_field_mapping";
  			this.SYS_RTE_EB_SCRIPT_OPERATION = "sys_rte_eb_script_operation";
  			this.SYS_RTE_EB_OPERATION_TYPE = 'sys_rte_eb_operation_type';
  			this.OPERATION_TYPE_PARAMETER_TABLE = 'sn_cmdb_int_util_rte_eb_operation_type_parameter';
  			this.SYS_RTE_EB_OPERATION = 'sys_rte_eb_operation';
  			this.SCHEDULED_IMPORT_SET = 'scheduled_import_set';
  			this.SET = 'SET';
  			this.INTEGRATION_STUDIO_API_PLUGIN = 'com.glide.integration_studio';

  			// COMMON FIELDS
  			this.ORDER = "order";
  			this.SYS_ID = "sys_id";
  			this.SYS_NAME = "sys_name";
  			this.SYS_CREATED_ON = 'sys_created_on';
  			this.SUCCESS = 'success';

  			// ENTITY FIELDS
  			this.PATH = "path";
  			this.TABLE = "table";

  			// CMDB ENTITY FIELDS
  			this.LOOKUP_FOR_ENTITY = "lookup_for_entity";
  			this.RELATED_FOR_ENTITY = "related_for_entity";
  			this.RELATIONSHIP_TYPE = "relationship_type";

  			// ENTITY MAPPING FIELDS
  			this.CONDITION_SCRIPT = "condition_script";
  			this.IS_CONDITIONAL = "is_conditional";
  			this.NAME = "name";
  			this.SOURCE_SYS_RTE_EB_ENTITY = "source_sys_rte_eb_entity";
  			this.TARGET_SYS_RTE_EB_ENTITY = "target_sys_rte_eb_entity";
  			this.ENTITY_MAPPING_GROUP = 'entity_mapping_group';
  			this.ENCODED_QUERY = 'encoded_query';
  			this.IGNORE = 'ignore';

  			// ENTITY FIELD FIELDS
  			this.FIELD = "field";

  			// FIELD MAPPING FIELDS
  			this.REFERENCED_SYS_RTE_EB_ENTITY = "referenced_sys_rte_eb_entity";
  			this.SOURCE_SYS_RTE_EB_FIELD = "source_sys_rte_eb_field";
  			this.TARGET_SYS_RTE_EB_FIELD = "target_sys_rte_eb_field";

  			// TEMPLATE STATE
  			this.CMDB_INST_TEMPLATE_STATE = "sn_int_studio_template_state";
  			this.APPLICATION_FEED_ID = "application_feed_id";
  			this.TEMPLATE_STATE = "template_state";
  			this.MAX_STATE = "500";
  			this.NESTED_PAYLOAD_SCHEMA = 'nested_payload_schema';
  			this.LOAD_COMPLETE_SCHEMA = 'load_complete_schema';
  			this.IS_COMPLETE_SCHEMA = 'is_complete_schema';
  			this.PREVIEW_SIZE_OVERRIDE = "preview_size_override";
  			this.IMPORT_SET_ID = 'import_set_id';

  			// IMPORT LOG FIELDS
  			this.LEVEL = 'level';
  			this.RUN_HISTORY = 'run_history';
  			this.ACTIVITY = 'activity';
  			this.MESSAGE = 'message';
  			this.SOURCE = 'source';

  			//REL
  			this.CMDB_REL_CI = 'cmdb_rel_ci';
  			this.PARENT = 'parent';
  			this.CHILD = 'child';
  			this.TYPE = 'type';

  			// OPERATION TYPE FIELDS
  			this.DESCRIPTION = 'description';
  			this.HAS_MULTIPLE_INPUT_FIELDS = 'has_multiple_input_fields';
  			this.HAS_MULTIPLE_OUTPUT_FIELDS = 'has_multiple_output_fields';
  			this.IS_TEMPLATE_SCRIPT = 'is_template_script';
  			this.INPUT_OUTPUT = 'input_output';
  			this.IS_OPTIONAL = 'is_optional';
  			this.IS_MULTIPLE = 'is_multiple';
  			this.IS_FIXED_VALUE = 'is_fixed_value';
  			this.FIXED_VALUE = 'fixed_value';
  			this.PARAMETER_TYPE = 'parameter_type';
  			this.OPERATION_COLUMN_NAME = 'operation_column_name';
  			this.CHOICE_LIST = 'choice_list';
  			this.HINT = 'hint';
  			this.SOURCE_SYS_RTE_EB_FIELDS = "source_sys_rte_eb_fields";
  			this.TARGET_SYS_RTE_EB_FIELDS = "target_sys_rte_eb_fields";
  			this.EXTRACT_NUMERIC_OPERATION = 'sys_rte_eb_extract_numeric_operation';

  			this.TEMP = "Temp";
  			this.IMPORT = "Import";
  			this.POST = "post";

  			this.INPUT_PREFIX = "input.";

  			this.DATA_SOURCE_EXCEPTION = "DataSourceException";
  			this.NO_INCLUSIVE_REL_EXCEPTION = "NoInclusiveRelException";
  			this.NESTED_PAYLOAD_PLACEHOLDER_FIELD = "placeholder#";
  			this.IRE_SETTINGS_PREFIX = "settings.";

  			this.STATUS_SUCCESS = 200;
  			this.STATUS_ERROR = 500;
  			
  			this.PREVIEW_SIZE_PROPERTY_NAME = 'sn_int_studio.preview.size';
  			this.MAX_PREVIEW_SIZE = 10000;
  			this.DEFAULT_PREVIEW_SIZE = 100;
  			this.DISABLE_ENTITY_MAPPING_REORDER_PROPERTY_NAME = 'sn_int_studio.nested_payload.mapping_reorder.disabled.list';
  			
  			this.UA_EVENT_TYPE_CUSTOM = 'custom_metric';
  			this.UA_INSTRUMENTATION_POINT = 'ih_etl_metrics';
  			this.UA_INSTRUMENTATION_EVENT = 'ih_etl_api_usage';
  			this.ACTION = 'action';
  			this.FEED_NAME = 'feed_name';
  			this.STEP_NUMBER = 'step_number';
  			this.RECORD_VALUE = 'record_value';
  			this.RECORD_ID = 'record_id';
  			this.CLASS_SAVED = 'class_saved';
  			this.CLASS_DELETED = 'class_deleted';
  			this.CLASS_UPDATED = 'class_updated';
  			this.FIELD_MAPPING_SAVED = 'field_mapping_saved';
  			this.FIELD_MAPPING_UPDATED = 'field_mapping_updated';
  			this.FIELD_MAPPING_DELETED = 'field_mapping_deleted';
  			this.TRANSFORM_OPERATION_SAVED = 'transform_operation_saved';
  			this.TRANSFORM_OPERATION_DELETED = 'transform_operation_deleted';
  			this.RELATIONSHIP_DELETED = 'relationship_deleted';
  			this.RELATIONSHIP_SAVED = 'relationship_saved';
  			this.RELATIONSHIP_UPDATED = 'relationship_updated';
  			this.ASSOCIATED_CLASS_UPDATED = 'associated_class_updated';
  			this.BASIC_DETAIL_UPDATED = 'basic_detail_updated';
  			this.TRANSFORM_MAP_CREATED = 'transform_map_created';
  			this.TRANSFORM_MAP_DUPLICATED = 'transform_map_duplicated';
  			this.DATA_ROLLED_BACK = 'data_rolled_back';
  			this.BASIC_DETAIL_STEP_NUMBER = 100;
  			this.PREVIEW_AND_PREPARE_STEP_NUMBER = 200;
  			this.CLASS_MAPPING_STEP_NUMBER = 300;
  			this.RELATIONSHIP_MAPPING_STEP_NUMBER = 350;
  			this.TEST_AND_ROLLBACK_STEP_NUMBER = 400;
  			this.SET_SCHEDULE_STEP_NUMBER = 450;
  							
  			// ROLLBACK
  			this.SYS_ROLLBACK_RUN = 'sys_rollback_run';
  			this.ROLLBACK_CONTEXT_FIELD = 'context';
  			this.ROLLBACK_STATE_FIELD = 'state';
  			this.ROLLBACK_FINISHED_STATE = 'finished';

  			this.LOOKDOWN_CLASS = 'lookdown_class';
  			this.NESTED_PAYLOAD_TOP_NODE_NAME = 'object';
  		},

  		buildErrorObject: function(message, detail, additionalDebugObject) {
  			var obj = {};
  			if (message)
  				obj.message = message;
  			if (detail)
  				obj.detail = detail;
  			if (additionalDebugObject)
  				obj.additionalDebug = additionalDebugObject;

  			return obj;
  		},

  		getInternalServerError: function(errorConfig) {
  			var internalError = new sn_ws_err.ServiceError();
  			internalError.setStatus(this.STATUS_ERROR);
  			if (errorConfig.message)
  				internalError.setMessage(errorConfig.message);
  			if (errorConfig.detail)
  				internalError.setDetail(errorConfig.detail);

  			gs.error(JSON.stringify(errorConfig));

  			return internalError;
  		},

  		getApplicationFeed: function(applicationFeedId) {
  			if (!applicationFeedId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No application feed passed to getApplicationFeed")));
  			var gr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  			if (gr.get(applicationFeedId))
  				return gr;
  			return null;
  		},

  		getApplication: function(applicationId) {
  			if (!applicationId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No application passed to getApplication")));
  			var gr = new GlideRecord(this.CMDB_INST_APPLICATION);
  			if (gr.get(applicationId))
  				return gr;
  			return null;
  		},

  		getTemplateState: function(templateStateId) {
  			if (!templateStateId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No template state passed to getTemplateState")));
  			var gr = new GlideRecord(this.CMDB_INST_TEMPLATE_STATE);
  			if (gr.get(templateStateId))
  				return gr;
  			return null;
  		},

  		getEntitiesForApplicationFeed: function(applicationFeedId, encodedQuery) {
  			var entities = [];
  			if (!applicationFeedId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No application feed passed to getEntitiesForApplicationFeed")));
  			var gr = new GlideRecord(this.CMDB_INST_ENTITY);
  			gr.addQuery(this.SYS_RTE_EB_DEFINITION, applicationFeedId);
  			if (encodedQuery)
  				gr.addEncodedQuery(encodedQuery);
  			gr.orderBy('sys_created_on');
  			gr.query();
  			while (gr.next()) {
  				entities.push({
  					name: gr.getValue(this.NAME),
  					table: gr.getValue(this.TABLE),
  					path: gr.getValue(this.PATH),
  					lookup_for_entity: gr.getValue(this.LOOKUP_FOR_ENTITY),
  					related_for_entity: gr.getValue(this.RELATED_FOR_ENTITY),
  					relationship_type: gr.getValue(this.RELATIONSHIP_TYPE),
  					sys_id: gr.getUniqueValue()
  				});
  			}
  			return entities;
  		},

  		getEntityFieldsForEntity: function(entityId) {
  			var fields = [];
  			if (!entityId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No entity passed to getEntityFieldsForEntity")));
  			var gr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  			gr.addQuery(this.SYS_RTE_EB_ENTITY, entityId);
  			gr.orderBy(this.FIELD);
  			gr.query();

  			while (gr.next()) {
  				fields.push({
  					field: gr.getValue(this.FIELD),
  					name: gr.getValue(this.NAME),
  					sys_id: gr.getUniqueValue()
  				});
  			}
  			return fields;
  		},

  		getEntityMappingsForApplicationFeed: function(applicationFeedId, encodedQuery) {
  			var entityMappings = [];
  			if (!applicationFeedId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No application feed passed to getEntityMappingsForApplicationFeed")));
  			var gr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			gr.addQuery(this.SYS_RTE_EB_DEFINITION, applicationFeedId);
  			if (encodedQuery)
  				gr.addEncodedQuery(encodedQuery);
  			gr.query();

  			while (gr.next()) {
  				entityMappings.push({
  					condition_script: gr.getValue(this.CONDITION_SCRIPT),
  					is_conditional: gr.getDisplayValue(this.IS_CONDITIONAL),
  					name: gr.getValue(this.NAME),
  					order: gr.getValue(this.ORDER),
  					encoded_query: gr.getValue(this.ENCODED_QUERY),
  					group_id: gr.getValue(this.ENTITY_MAPPING_GROUP),
  					source_sys_rte_eb_entity: gr.getValue(this.SOURCE_SYS_RTE_EB_ENTITY),
  					sys_id: gr.getUniqueValue(),
  					target_sys_rte_eb_entity: gr.getValue(this.TARGET_SYS_RTE_EB_ENTITY),
  					active: !this.parseBooleanString(gr.getDisplayValue(this.IGNORE))
  				});
  			}
  			return entityMappings;
  		},

  		getFieldMappingsForEntityMapping: function(entityMappingId, encodedQuery) {
  			var fieldMappings = [];
  			if (!entityMappingId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No entity mapping passed to getFieldMappingsForEntityMapping")));
  			var gr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			gr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  			if (encodedQuery)
  				gr.addEncodedQuery(encodedQuery);
  			gr.query();
  			while (gr.next()) {
  				var	targetFieldDisplayValue = gr.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD + '.field').split(".");
  				targetFieldDisplayValue = targetFieldDisplayValue[targetFieldDisplayValue.length - 1];
  				fieldMappings.push({
  					order: gr.getValue(this.ORDER),
  					referenced_sys_rte_eb_entity: gr.getValue(this.REFERENCED_SYS_RTE_EB_ENTITY),
  					referenced_sys_rte_eb_entity_display_value: gr.getDisplayValue(this.REFERENCED_SYS_RTE_EB_ENTITY + '.name'),
  					source_sys_rte_eb_field: gr.getValue(this.SOURCE_SYS_RTE_EB_FIELD),
  					source_sys_rte_eb_field_display_value: gr.getDisplayValue(this.SOURCE_SYS_RTE_EB_FIELD + '.field'),
  					sys_id: gr.getValue(this.SYS_ID),
  					target_sys_rte_eb_field: gr.getValue(this.TARGET_SYS_RTE_EB_FIELD),
  					target_sys_rte_eb_field_display_value: targetFieldDisplayValue,
  					target_sys_rte_eb_entity_name: gr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.name + '',
  					target_sys_rte_eb_entity_table_name: gr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table + ''
  				});
  			}
  			return fieldMappings;
  		},

  		createEntity: function(feedId, name, table) {
  			return this.createCmdbEntity(feedId, name, table, table, '', '');
  		},

  		createCmdbEntity: function(feedId, name, path, table, relType, lookupEntity) {
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.initialize();
  			entityGr.setValue(this.NAME, name);
  			entityGr.setValue(this.SYS_RTE_EB_DEFINITION, feedId);
  			if (table)
  				entityGr.setValue(this.TABLE, table);
  			if (path)
  				entityGr.setValue(this.PATH, path);
  			if (relType)
  				entityGr.setValue(this.RELATIONSHIP_TYPE, relType);
  			if (lookupEntity)
  				entityGr.setValue(this.LOOKUP_FOR_ENTITY, lookupEntity);
  			if (entityGr.insert())
  				return entityGr;

  			var errorObj = this.buildErrorObject(gs.getMessage("Failed to create entity"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		createRelCmdbEntity: function(feedId, name, path, relType) {
  			return this.createCmdbEntity(feedId, name, path, this.CMDB_REL_CI, relType);
  		},

  		findImpCmdbEntity: function(applicationFeedId) {
  			var errorObj = {};
  			if (applicationFeedId) {
  				var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, applicationFeedId);
  				entityGr.addNullQuery(this.PATH);
  				entityGr.addEncodedQuery('nameSTARTSWITHImp');
  				entityGr.query();
  				var count = 0;
  				if (!entityGr.hasNext()) {
  					errorObj = this.buildErrorObject(gs.getMessage("No imp entity was found"));
  					return this.getInternalServerError(errorObj);
  				}
  				while (entityGr.next()) {
  					if (count === 0)
  						count++;
  					else {
  						errorObj = this.buildErrorObject(gs.getMessage("More than one imp entity was found"));
  						throw this.getInternalServerError(errorObj);
  					}
  				}
  				return entityGr;
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("No application feed sent to findImpCmdbEntity"));
  			throw this.getInternalServerError(errorObj);
  		},

  		findTempCmdbEntity: function(applicationFeedId, impEntityId) {
  			var errorObj = {};
  			if (applicationFeedId && impEntityId) {
  				var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, impEntityId);
  				entityMappingGr.addNotNullQuery(this.TARGET_SYS_RTE_EB_ENTITY);
  				entityMappingGr.query();

  				if (!entityMappingGr.hasNext()) {
  					errorObj = this.buildErrorObject(gs.getMessage("There should be one entity mapping from imp to temp"), '', {args: arguments});
  					return this.getInternalServerError(errorObj);
  				}
  				var tempEntities = {};
  				while (entityMappingGr.next()) {
  					var tempEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  					var tempEntityGr = new GlideRecord(this.SYS_RTE_EB_ENTITY);
  					if (!tempEntityGr.get(tempEntityId)) {
  						errorObj = this.buildErrorObject(gs.getMessage("Temp entity record not found"), '', {args: arguments});
  						return this.getInternalServerError(errorObj);
  					}
  					var currentTempEntity = {
  						name: tempEntityGr.getValue(this.NAME),
  						table: tempEntityGr.getValue(this.TABLE),
  						path: tempEntityGr.getValue(this.PATH),
  						lookup_for_entity: tempEntityGr.getValue(this.LOOKUP_FOR_ENTITY),
  						related_for_entity: tempEntityGr.getValue(this.RELATED_FOR_ENTITY),
  						relationship_type: tempEntityGr.getValue(this.RELATIONSHIP_TYPE),
  						sys_id: tempEntityId
  					};
  					tempEntities[currentTempEntity.path] = currentTempEntity;
  				}
  				return tempEntities;
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("Invalid parameters sent to function findTempCmdbEntity"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		getRecordsFromTable: function(table, ids, fields) {
  			var result = {};
  			var gr = new GlideRecord(table);
  			gr.addQuery(this.SYS_ID, ids);
  			gr.query();
  			while (gr.next()) {
  				if (!gr.canRead()) {
  					continue;
  				}
  				var sys_id = gr.getValue(this.SYS_ID);
  				result[sys_id] = {};
  				for (var i = 0; i < fields.length; i++) {
  					fields[i] = fields[i].trim();
  					if (!fields[i] || !gr.isValidField(fields[i]))
  						continue;
  					result[sys_id][fields[i]] = gr.getDisplayValue(fields[i]);
  				}
  			}
  			return result;
  		},

  		createMappingBetweenEntities: function(applicationFeedId, name, sourceEntitySysId, targetEntitySysId, condition, order, groupId, encodedQuery, ignore) {
  			var errorObj = {};
  			if (name && sourceEntitySysId && targetEntitySysId) {
  				var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.initialize();
  				entityMappingGr.setValue(this.NAME, name);
  				entityMappingGr.setValue(this.SYS_RTE_EB_DEFINITION, applicationFeedId);
  				entityMappingGr.setValue(this.SOURCE_SYS_RTE_EB_ENTITY, sourceEntitySysId);
  				entityMappingGr.setValue(this.TARGET_SYS_RTE_EB_ENTITY, targetEntitySysId);
  				if (groupId)
  					entityMappingGr.setValue(this.ENTITY_MAPPING_GROUP, groupId);
  				if (encodedQuery)
  					entityMappingGr.setValue(this.ENCODED_QUERY, encodedQuery);
  				if (order)
  					entityMappingGr.setValue(this.ORDER, order);
  				else
  					entityMappingGr.setValue(this.ORDER, '100');
  				if (condition) {
  					entityMappingGr.setValue(this.IS_CONDITIONAL, true);
  					entityMappingGr.setValue(this.CONDITION_SCRIPT, condition);
  				}
  				if (ignore) {
  					entityMappingGr.setValue(this.IGNORE, true);
  				}
  				if (entityMappingGr.insert())
  					return entityMappingGr;
  				errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into mapping entity table"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("No entities sys_id provided to createMappingBetweenEntities"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		findMappingBetweenEntities: function(sourceEntitySysId, targetEntitySysId) {
  			var errorObj = {};
  			if (sourceEntitySysId && targetEntitySysId) {
  				var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, sourceEntitySysId);
  				entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, targetEntitySysId);
  				entityMappingGr.query();
  				var count = 0;
  				if (!entityMappingGr.hasNext()) {
  					errorObj = this.buildErrorObject(gs.getMessage("No entity mapping found"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}

  				while (entityMappingGr.next()) {
  					if (count === 0)
  						count++;
  					else {
  						errorObj = this.buildErrorObject(gs.getMessage("More than one mapping between 2 entities"), '', {args: arguments});
  						throw this.getInternalServerError(errorObj);
  					}
  				}
  				return entityMappingGr;
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("No entities sys_id provided to findMappingBetweenEntities"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		// function to remove spaces and limit max chars to a name
  		prettifyName: function(name, maxChars) {
  			if (name) {
  				name = name.toLowerCase().replace(/ /g, '_');
  				if (!isNaN(maxChars) && name.length > maxChars)
  					name = name.substring(0, maxChars);
  				return name;
  			}
  			return '';
  		},

  		getTableColumns: function(tableName) {
  			if (!tableName)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No table name sent to getTableColumns")));
  			var columns = [];
  			var gr = new GlideRecord(this.SYS_DICTIONARY);
  			gr.addQuery(this.NAME, tableName);
  			gr.addNotNullQuery(this.ELEMENT);
  			gr.orderBy(this.ELEMENT);
  			gr.query();
  			while (gr.next()) {
  				var name = gr.getValue(this.ELEMENT);
  				if (!name || name.indexOf(this.SYS_PREFIX) === 0)
  					continue;
  				columns.push(name);
  			}
  			return columns;
  		},

  		createEntityField: function(applicationFeedSysId, entityId, field, name) {
  			var errorObj = {};
  			if (applicationFeedSysId && entityId && field && name) {
  				var entityFieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  				entityFieldGr.initialize();
  				entityFieldGr.setValue(this.SYS_RTE_EB_DEFINITION, applicationFeedSysId);
  				entityFieldGr.setValue(this.SYS_RTE_EB_ENTITY, entityId);
  				entityFieldGr.setValue(this.FIELD, field);
  				entityFieldGr.setValue(this.NAME, name);
  				if (entityFieldGr.insert())
  					return entityFieldGr;

  				errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity field table"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("Invalid parameters sent to createEntityField"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		createMappingBetweenEntityFields: function(applicationFeedSysId, entityMappingId, sourceField, targetField, order) {
  			var errorObj = {};
  			if (applicationFeedSysId && entityMappingId && sourceField && targetField && order) {
  				var entityFieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  				entityFieldMappingGr.initialize();
  				entityFieldMappingGr.setValue(this.SYS_RTE_EB_DEFINITION, applicationFeedSysId);
  				entityFieldMappingGr.setValue(this.SOURCE_SYS_RTE_EB_FIELD, sourceField);
  				entityFieldMappingGr.setValue(this.TARGET_SYS_RTE_EB_FIELD, targetField);
  				entityFieldMappingGr.setValue(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  				entityFieldMappingGr.setValue(this.ORDER, order);

  				if (entityFieldMappingGr.insert())
  					return entityFieldMappingGr;

  				errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity field mapping table"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("Invalid parameters sent to createMappingBetweenEntityFields"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		deleteEntity: function(entitySysId) {
  			if (!entitySysId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No entity sys id sent to deleteEntity")));
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			if (entityGr.get(entitySysId))
  				return entityGr.deleteRecord();
  			else
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("Entity sys id sent to deleteEntity does not exist")));
  		},

  		loadAllMetadataForApplicationFeed: function(applicationFeedSysId, impEntityGr) {
  			var errorObj = {};
  			if (applicationFeedSysId && impEntityGr) {
  				var impEntityId = impEntityGr.getUniqueValue();
  				var result = {
  					entities: {},
  					fieldsPerEntity: {},
  					entitiesMapping: [],
  					fieldMappingPerEntityMapping: {},
  					operationGraph: {},
  					savedOperations: []
  				};

  				// import entity
  				result.entities.imp_entity = {
  					name: impEntityGr.getValue(this.NAME),
  					table: impEntityGr.getValue(this.TABLE),
  					path: impEntityGr.getValue(this.PATH),
  					lookup_for_entity: impEntityGr.getValue(this.LOOKUP_FOR_ENTITY),
  					related_for_entity: impEntityGr.getValue(this.RELATED_FOR_ENTITY),
  					relationship_type: impEntityGr.getValue(this.RELATIONSHIP_TYPE),
  					sys_id: impEntityId
  				};

  				// temp entity
  				var tempEntities = this.findTempCmdbEntity(applicationFeedSysId, impEntityId);
  				if (tempEntities) {
  					result.entities.temp_entity = tempEntities;

  					// the rest of the entities
  					var encodedQuery = '';
  					var tempEntityPaths = Object.keys(tempEntities);
  					for (var i = 0; i < tempEntityPaths.length; i++) {
  						var tempEntityId = tempEntities[tempEntityPaths[i]].sys_id;
  						if (i == 0) {
  							encodedQuery += 'sys_id!=' + tempEntityId;
  						} else {
  							encodedQuery += '^sys_id!=' + tempEntityId;
  						}
  						var tempFields = this.getEntityFieldsForEntity(tempEntityId);
  						result.fieldsPerEntity[tempEntityId] = tempFields;
  					}
  					encodedQuery += '^sys_id!=' + impEntityId;
  					result.entities.other_entities = this.getEntitiesForApplicationFeed(applicationFeedSysId, encodedQuery);

  					var impFields = this.getEntityFieldsForEntity(impEntityId);
  					result.fieldsPerEntity[impEntityId] = impFields;

  					for (var i = 0; i < result.entities.other_entities.length; i++) {
  						var entity = result.entities.other_entities[i];
  						if (entity && entity.sys_id)
  							result.fieldsPerEntity[entity.sys_id] = this.getEntityFieldsForEntity(entity.sys_id);
  					}

  					result.entitiesMapping = this.getEntityMappingsForApplicationFeed(applicationFeedSysId, '');

  					for (i = 0; i < result.entitiesMapping.length; i++) {
  						var entityMapping = result.entitiesMapping[i];
  						if (entityMapping && entityMapping.sys_id)
  							result.fieldMappingPerEntityMapping[entityMapping.sys_id] = this.getFieldMappingsForEntityMapping(entityMapping.sys_id, '');
  					}

  					// load all operations
  					result.savedOperations = this.getSavedOperationsForFeed(applicationFeedSysId);

  					// set the operation graph
  					var graph = {};
  					for (var i = 0; i < tempEntityPaths.length; i++) {
  						var currentPath = tempEntityPaths[i];
  						var currentTempEntity = result.entities.temp_entity[currentPath];
  						
  						graph[currentPath] = this.getOperationGraph(currentTempEntity.sys_id);
  						
  					}
  					result.operationGraph = graph;

  					return result;
  				}
  				errorObj = this.buildErrorObject(gs.getMessage("Failed to find temp entity record in loadAllMetadataForApplicationFeed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			errorObj = this.buildErrorObject(gs.getMessage("Invalid parameters sent to loadAllMetadataForApplicationFeed"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		createFieldMappingsWithEntityRef: function(appFeedSysId, entityMappingId, referencedEntityId, targetField, order) {
  			if (!appFeedSysId || !entityMappingId || !referencedEntityId || !targetField || !order)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("Invalid parameters sent to createFieldMappingsWithEntityRef"), '', {args: arguments}));

  			var isNestedPayload = this.isNestedPayload(appFeedSysId);
  			if (isNestedPayload) {
  				var currentEntityId = '';
  				var currentEntityTempPath = '';
  				var currentEntityTable = '';
  				var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.addQuery(this.SYS_ID, entityMappingId);
  				entityMappingGr.query();
  				if (entityMappingGr.next()) {
  					currentEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  					currentEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  					currentEntityTable = entityMappingGr.target_sys_rte_eb_entity.table + '';
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with id {0}', entityMappingId));
  					throw this.getInternalServerError(errorObj);
  				}

  				var referenceEntityTempPath = '';
  				var referenceEntityName = '';
  				entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, referencedEntityId);
  				entityMappingGr.query();
  				if (entityMappingGr.next()) {
  					referenceEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  					referenceEntityName = entityMappingGr[this.TARGET_SYS_RTE_EB_ENTITY].name + '';
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', referencedEntityId));
  					throw this.getInternalServerError(errorObj);
  				}

  				if (currentEntityTable != this.CMDB_REL_CI) {
  					this.checkIsDifferentDataBranch(currentEntityId, referencedEntityId, currentEntityTempPath, referenceEntityTempPath);
  				}
  			}

  			var entityFieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			entityFieldMappingGr.initialize();
  			entityFieldMappingGr.setValue(this.SYS_RTE_EB_DEFINITION, appFeedSysId);
  			entityFieldMappingGr.setValue(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  			entityFieldMappingGr.setValue(this.TARGET_SYS_RTE_EB_FIELD, targetField);
  			entityFieldMappingGr.setValue(this.REFERENCED_SYS_RTE_EB_ENTITY, referencedEntityId);
  			entityFieldMappingGr.setValue(this.ORDER, order);

  			if (entityFieldMappingGr.insert()) {
  				return entityFieldMappingGr;
  			}

  			var errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity field mapping table"), '', {args: arguments});
  			throw this.getInternalServerError(errorObj);
  		},

  		deleteFields: function(fieldIds) {
  			if (!fieldIds)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No fields sent to deleteFields")));

  			var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  			fieldGr.addQuery(this.SYS_ID, 'IN', fieldIds);
  			fieldGr.query();
  			fieldGr.deleteMultiple();
  		},

  		getParamAsString: function(paramName) {
  			if (request.queryParams.hasOwnProperty(paramName))
  				return request.queryParams[paramName] + '';

  			return '';
  		},
  		
  		transformQueryToFailingPathScript: function(query, sourceEntityPath, hasElseClause) {
  			var conditionBlocksArray = [];
  			for (var i = 0; i < query.length; i++) {
  				var conditionBlocks = this._processQueryString(query[i]);
  				if (conditionBlocks.length > 0) {
  					conditionBlocksArray.push(conditionBlocks);
  				}
  			}
  			
  			var resultArr = [];
  			if (conditionBlocksArray.length <= 0) {
  				return resultArr;
  			}
  			
  			var conditionArr = [];
  			for (var j = 0; j < conditionBlocksArray.length; j++) {
  				var condition = this._processConditionBlocksForFailingPathScript(conditionBlocksArray[j], '');
  				conditionArr.push(condition);
  			}
  			
  			var sourceEntityPathParts = sourceEntityPath.split('.');
  			var sourceEntityLoops = this._getEntityLoopsFromPathParts(sourceEntityPathParts);
  			
  			
  			for (var k = 0; k < conditionArr.length; k++) {
  				var currCondition = conditionArr[k];
  				var result = '(function(input, failingPaths) {\n';
  				var prevConditions = [];
  				var prevCollectionConditions = [];
  				if (k > 0) {
  					conditionArr.slice(0, k).forEach(function(elem) {
  						if (elem.nonCollection && elem.nonCollection.condition) {
  							prevConditions.push(elem.nonCollection.condition);
  						}
  						if (elem.collection && elem.collection.condition) {
  							prevCollectionConditions.push(elem.collection.condition);
  						}
  					});
  				}
  				// If there are non-collection conditions from previous blocks that pass, automatically return false
  				if (prevConditions && prevConditions.length) {
  					result += 'if (' + prevConditions.join(' || ') + '){\n';
  					result += '    return false;\n';
  					result += '}\n';
  				}
  				// If there are conditions outside the for loop that fail, automatically return false
  				if (currCondition.nonCollection && currCondition.nonCollection.notCondition) {
  					result += 'if (' + currCondition.nonCollection.notCondition + '){\n';
  					result += '    return false;\n';
  					result += '}\n';
  				}
  				
  				// Add the for loops based off the sourceEntity if there are conditions for it
  				if ((prevCollectionConditions && prevCollectionConditions.length) || (currCondition.collection && currCondition.collection.notCondition)) {
  					result += this._processForLoops(sourceEntityLoops);

  					// If there are collection conditions from previous blocks that pass, automatically return false
  					if (prevCollectionConditions && prevCollectionConditions.length) {
  						result += 'if (' + prevCollectionConditions.join(' || ') + '){\n';
  						result += '    return false;\n';
  						result += '}\n';
  					}

  					// Check the failing condition for the collection to add path to failingPaths array
  					if (currCondition.collection && currCondition.collection.notCondition) {
  						result += 'if (' + currCondition.collection.notCondition + ') {\n';
  						result += '    failingPaths.push(\'';
  						result += this._processFailingPath(sourceEntityPathParts);
  						result += '\');\n';
  						result += '}\n';
  					}

  					// Close out the for loops
  					for (var o = 0; o < sourceEntityLoops.length; o++) {
  						result += '}\n';
  					}
  				}
  				
  				result += 'return true;\n';
  				result += '})(input, failingPaths);';
  				
  				resultArr[k] = result;
  			}
  			
  			if (hasElseClause) {
  				var elsePrevConditions = [];
  				var elsePrevCollectionConditions = [];
  				conditionArr.forEach(function(elem) {
  					if (elem.nonCollection && elem.nonCollection.condition) {
  						elsePrevConditions.push(elem.nonCollection.condition);
  					}
  					if (elem.collection && elem.collection.condition) {
  						elsePrevCollectionConditions.push(elem.collection.condition);
  					}
  				});
  				var elseScript = '(function(input, failingPaths) {\n';
  				if (elsePrevConditions && elsePrevConditions.length) {
  					elseScript += 'if (' + elsePrevConditions.join(' || ') + '){\n';
  					elseScript += '    return false;\n';
  					elseScript += '}\n';
  				}
  				if (elsePrevCollectionConditions && elsePrevCollectionConditions.length) {
  					elseScript += this._processForLoops(sourceEntityLoops);
  					elseScript += '    if (' + elsePrevCollectionConditions.join(' || ') + ') {\n';
  					elseScript += '        failingPaths.push(\'';
  					elseScript += this._processFailingPath(sourceEntityPathParts);
  					elseScript += '\');\n';
  					elseScript += '    }\n';
  					// Close out the for loops
  					for (var p = 0; p < sourceEntityLoops.length; p++) {
  						elseScript += '}\n';
  					}
  				}
  				elseScript += '    return true;\n';
  				elseScript += '})(input, failingPaths);';
  				
  				resultArr.push(elseScript);
  			}
  			
  			return resultArr;
  		},

  		transformQueryToScript: function(query, tempEntityPath, hasElseClause) {
  			var conditionBlocksArray = [];
  			for (var i = 0; i < query.length; i++) {
  				var conditionBlocks = this._processQueryString(query[i]);
  				if (conditionBlocks.length > 0) {
  					conditionBlocksArray.push(conditionBlocks);
  				}
  			}

  			var resultArr = [];
  			if (conditionBlocksArray.length <= 0) {
  				return resultArr;
  			}

  			var conditionArr = [];
  			for (var j = 0; j < conditionBlocksArray.length; j++) {
  				var condition = this._processConditionBlocks(conditionBlocksArray[j], tempEntityPath);
  				conditionArr.push(condition);
  			}

  			for (var k = 0; k < conditionArr.length; k++) {
  				var result = '(function() {\n';
  				var currCondition = '(' + conditionArr[k] + ')';
  				for (var l = 0; l < k; l++) {
  					var notCondition = '!(' + conditionArr[l] + ')';
  					currCondition += ' && ' + notCondition;
  				}

  				result += '    if (' + currCondition + ') {\n';
  				result += '        return true;\n';
  				result += '    }\n';
  				result += '    return false;\n';
  				result += '})();';

  				resultArr[k] = result;
  			}

  			if (hasElseClause) {
  				var elseScript = '(function() {\n';
  				var conditionClause = '';
  				for (var m = 0; m < conditionArr.length; m++) {
  					var complementaryCondition = '!(' + conditionArr[m] + ')';
  					conditionClause += complementaryCondition;
  					if (m < conditionArr.length - 1)
  						conditionClause += ' && ';
  				}

  				elseScript += '    if (' + conditionClause + ') {\n';
  				elseScript += '        return true;\n';
  				elseScript += '    }\n';
  				elseScript += '    return false;\n';
  				elseScript += '})();';

  				resultArr.push(elseScript);
  			}

  			return resultArr;
  		},

  		_processQueryString: function(queryString) {
  			var conditionList = [];
  			var blocks = queryString.split('^');
  			for (var i = 0; i < blocks.length; i++) {
  				var section = [];
  				section.push(this._processBlock(blocks[i]));
  				for (var j = i + 1; j < blocks.length; j++) {
  					if (!blocks[j].startsWith('OR')) {
  						break;
  					}
  					section.push(this._processBlock(blocks[j].slice(2)));
  				}
  				i = j - 1;
  				conditionList.push(section);
  			}
  			return conditionList;
  		},

  		_processBlock: function(block) {
  			var parts = block.split(/(=|!=|ANYTHING|IN|STARTSWITH|ENDSWITH|LIKE|NOT LIKE|ISEMPTY|ISNOTEMPTY|ISACTIVATED|ISNOTACTIVATED)/);
  			var processedBlock = {};
  			processedBlock.field = parts[0];
  			processedBlock.operator = parts[1];
  			parts.splice(0, 2);
  			processedBlock.condition = parts.join(''); // Edge case: testSTARTSWITHANYTHINGLIKE
  			return processedBlock;
  		},

  		_processConditionBlocks: function(blocks, tempEntityPath) {
  			var outerConditions = [];
  			for (var i = 0; i < blocks.length; i++) {
  				var innerConditions = [];
  				for (var j = 0; j < blocks[i].length; j++) {
  					innerConditions.push(this._decodeOperator(blocks[i][j], tempEntityPath).operatorString);
  				}
  				outerConditions.push('(' + innerConditions.join(' || ') + ')');
  			}
  			return outerConditions.join(' && ');
  		},

  		// for failing path condition, need to create each condition block with !({condition]) and join them with ||
  		_processConditionBlocksForFailingPathScript: function(blocks, tempEntityPath) {
  			var conditions = {
  				nonCollection: {
  					condition: '',
  					notCondition: ''
  				},
  				collection: {
  					condition: '',
  					notCondition: ''
  				}
  			}
  			var nonCollectionOuterPassing = [];
  			var nonCollectionOuterFailing = [];
  			var collectionOuterPassing = [];
  			var collectionOuterFailing = [];
  			for (var i = 0; i < blocks.length; i++) {
  				var innerConditions = [];
  				var isCollectionCondition = false;
  				for (var j = 0; j < blocks[i].length; j++) {
  					var decodedOperator = this._decodeOperator(blocks[i][j], tempEntityPath);
  					// Used to track if any inner conditions are a collection condition
  					if (!isCollectionCondition && decodedOperator.isCollectionCondition)
  						isCollectionCondition = decodedOperator.isCollectionCondition;
  					innerConditions.push(decodedOperator.operatorString);
  				}
  				var joinedInnerConditions = innerConditions.join(' || ');
  				if (isCollectionCondition) {
  					collectionOuterPassing.push('(' + joinedInnerConditions + ')');
  					collectionOuterFailing.push('!(' + joinedInnerConditions + ')');
  				} else {
  					nonCollectionOuterPassing.push('(' + joinedInnerConditions + ')');
  					nonCollectionOuterFailing.push('!(' + joinedInnerConditions + ')');
  				}
  			}

  			conditions.nonCollection.condition = nonCollectionOuterPassing.join(' && ');
  			conditions.nonCollection.notCondition = nonCollectionOuterFailing.join(' || ');
  			conditions.collection.condition = collectionOuterPassing.join(' && ');
  			conditions.collection.notCondition = collectionOuterFailing.join(' || ');

  			return conditions;
  		},

  		_decodeOperator: function(section, tempEntityPath) {
  			var field = section.field;
  			var operator = section.operator;
  			var condition = GlideStringUtil.escapeTicks(section.condition);
  			var decodeResult = '';
  			var isCollectionCondition = false;
  			if (tempEntityPath) {
  				tempEntityPath = tempEntityPath + '.';
  			} else {
  				tempEntityPath = '';
  				field = field.replace(/\#/g, '.');
  				var fieldParts = field.split('.');
  				var collectionIndex = -1;
  				for (var i = 0; i < fieldParts.length; i++) {
  					var part = fieldParts[i];
  					if (part.endsWith('[*]')) {
  						collectionIndex++;
  						isCollectionCondition = true;
  						var collectionName = part.slice(0, part.indexOf('['));
  						fieldParts[i] = collectionName + '[' + collectionName + '_' + collectionIndex + ']';
  					}
  				}
  				field = fieldParts.join('.');
  			}
  			
  			switch (operator) {
  				case '=':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + ' === ' + '\'' + condition + '\'';
  					break;
  				case '!=':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + ' !== ' + '\'' + condition + '\'';
  					break;
  				case 'ANYTHING':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + ' || ' + this.INPUT_PREFIX + tempEntityPath + field + ' === null || ' + this.INPUT_PREFIX + tempEntityPath + field + ' === \'\'';
  					break;
  				case 'IN':
  					decodeResult += '\'' + condition + '\'' + '.split(\',\').indexOf(' + this.INPUT_PREFIX + tempEntityPath + field + ') > -1';
  					break;
  				case 'STARTSWITH':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + '.startsWith(' + '\'' + condition + '\'' + ')';
  					break;
  				case 'ENDSWITH':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + '.endsWith(' + '\'' + condition + '\'' + ')';
  					break;
  				case 'LIKE':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + '.indexOf(' + '\'' + condition + '\'' + ') > -1';
  					break;
  				case 'NOT LIKE':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field + '.indexOf(' + '\'' + condition + '\'' + ') < 0';
  					break;
  				case 'ISEMPTY':
  					decodeResult += '!' + this.INPUT_PREFIX + tempEntityPath + field;
  					break;
  				case 'ISNOTEMPTY':
  					decodeResult += this.INPUT_PREFIX + tempEntityPath + field;
  					break;
  				case 'ISACTIVATED':
  					decodeResult += 'GlidePluginManager.isActive(\'' + field + '\')';
  					break;
  				case 'ISNOTACTIVATED':
  					decodeResult += '!GlidePluginManager.isActive(\'' + field + '\')';
  					break;
  				default:
  					decodeResult += '';
  			}

  			return {
  				operatorString: decodeResult,
  				isCollectionCondition: isCollectionCondition
  			}
  		},
  		
  		_getEntityLoopsFromPathParts: function(pathPartsArray) {
  			var currentPath = [];
  			var loops = [];

  			for (var i = 0; i < pathPartsArray.length; i++) {
  				currentPath.push(pathPartsArray[i]);
  				if (pathPartsArray[i].endsWith('[*]')) {
  					loops.push(currentPath.join('.'));
  				}
  			}

  			return loops;
  		},
  		
  		_processForLoops: function(entityLoops) {
  			var resultString = '';
  			for (var i = 0; i < entityLoops.length; i++) {
  				var collectionName = '';
  				var collectionIndex = -1;
  				var finalLoopParts = [];
  				var splitPath = entityLoops[i].split('.');
  				for (var j = 0; j < splitPath.length; j++) {
  					var part = splitPath[j];
  					if (part.endsWith('[*]')) {
  						collectionIndex++;
  						collectionName = part.slice(0, part.indexOf('['));
  						var collectionString = collectionName;
  						// if not the last collection in the path, add index from previous loop
  						if (j < splitPath.length - 1) {
  							collectionString += '[' + collectionName + '_' + collectionIndex + ']';
  						}
  						finalLoopParts.push(collectionString);
  					} else {
  						finalLoopParts.push(part);
  					}
  				}
  				var entityVarName = collectionName + '_' + collectionIndex;
  				var comparisonVarName = this.INPUT_PREFIX + finalLoopParts.join('.');
  				resultString += 'for (var ' + entityVarName + ' = 0; ' + entityVarName + ' < ' + comparisonVarName + '.length; ' + entityVarName + '++) {\n';
  			}
  			return resultString;
  		},
  		
  		_processFailingPath: function(sourceEntityPathParts) {
  			var resultString = '';
  			var collectionIndex = -1;
  			var failingPathParts = [];
  			for (var i = 0; i < sourceEntityPathParts.length; i++) {
  				var part = sourceEntityPathParts[i];
  				if (part.endsWith('[*]')) {
  					collectionIndex++;
  					var collectionName = part.slice(0, part.indexOf('['));
  					failingPathParts.push(collectionName + '[\'+' + collectionName + '_' + collectionIndex + '+\']');
  				} else {
  					failingPathParts.push(part);
  				}
  			}
  			resultString += failingPathParts.join('.');
  			return resultString;
  		},

  		deleteOperations: function(operationIds) {
  			if (!operationIds)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No operation ids sent to deleteOperations")));
  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_ID, 'IN', operationIds);
  			operationGr.query();
  			operationGr.deleteMultiple();
  		},

  		getOperationType: function(typeId) {
  			if (!typeId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No type id sent to getOperationTypes")));
  			var typeGr = new GlideRecord(this.SYS_RTE_EB_OPERATION_TYPE);
  			typeGr.get(typeId);
  			return typeGr;
  		},

  		getOperationGraph: function(entityId) {
  			var errorObj = {};
  			if (!entityId)
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("No entity id sent to getOperationGraph")));
  			var graph = {};
  			var nodes = {};
  			var edges = {};

  			var fields = this.getEntityFieldsForEntity(entityId);
  			for (var i = 0; i < fields.length; i++) {
  				nodes[fields[i].sys_id] = {};
  				nodes[fields[i].sys_id].name = fields[i].name;
  				nodes[fields[i].sys_id].field = fields[i].field;
  			}

  			edges[this.SET] = {};

  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_ENTITY, entityId);
  			operationGr.query();
  			while (operationGr.next()) {
  				var isSetOperation = false;
  				var sys_id = operationGr.getUniqueValue();
  				var type = operationGr.getValue(this.TYPE);
  				isSetOperation = (type === this.getSetOperationTypeSysId());
  				var sourceIds = operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELD);
  				if (!sourceIds)
  					sourceIds = operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELDS);
  				var targetIds = operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELD);
  				if (!targetIds)
  					targetIds = operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELDS);
  				if ((!sourceIds && !isSetOperation) || !targetIds) {
  					gs.error("Error while reading operation " + sys_id + ", source or target ids are missing");
  					errorObj = this.buildErrorObject(gs.getMessage("Missing source or target fields in RTE Entity Operations table records invalidates the ETL Transform Map, which might have happened during data migration. Contact ServiceNow Support for assistance."), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  					
  				}
  				
  				if (isSetOperation)
  					sourceIds = '';

  				sourceIds = sourceIds.split(',');
  				targetIds = targetIds.split(',');

  				// handle special case for extract first numeric which creates a different value
  				if (operationGr.getDisplayValue("type.table") === this.EXTRACT_NUMERIC_OPERATION) {
  					var realTableOperationGr = new GlideRecord(this.EXTRACT_NUMERIC_OPERATION);
  					if (!realTableOperationGr.get(sys_id)) {
  						gs.error("Error while getting operation " + sys_id + ", operation does not exist in correct table");
  						continue;
  					}
  					var remainderField = realTableOperationGr.getValue("remainder_target_field");
  					if (remainderField)
  						targetIds.push(remainderField);
  				}
  				if (!isSetOperation) {
  					for (var j = 0; j < sourceIds.length; j++) {
  						for (var k = 0; k < targetIds.length; k++) {
  							edges[sourceIds[j]] = edges[sourceIds[j]] || {};
  							edges[sourceIds[j]][targetIds[k]] = {};
  							edges[sourceIds[j]][targetIds[k]].operationTypeId = type;
  							edges[sourceIds[j]][targetIds[k]].operationId = sys_id;
  						}
  					}
  				}
  				// handle SET scenario
  				else if (isSetOperation && targetIds.length === 1) {
  					edges[this.SET][targetIds[0]] = {};
  					edges[this.SET][targetIds[0]].operationTypeId = type;
  					edges[this.SET][targetIds[0]].operationId = sys_id;
  				}
  			}

  			graph.nodes = nodes;
  			graph.edges = edges;

  			return graph;
  		},

  		saveOperation: function(feedId, source, target, operation, option) {
  			var errorObj = {};
  			var isNewOperation = (option === this.POST);
  			var isSetOperation = operation.type === this.getSetOperationTypeSysId();
  			var result = {
  				fieldsCreated: [],
  				operationCreated: {},
  				status: ''
  			};

  			var operationTypeGr = new GlideRecord(this.SYS_RTE_EB_OPERATION_TYPE);
  			if (!operationTypeGr.get(operation.type)) {
  				errorObj = this.buildErrorObject(gs.getMessage("Failed to find operation type"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}

  			var targetFieldIds = [];
  			var multipleInputFields = this.parseBooleanString(operationTypeGr.getDisplayValue(this.HAS_MULTIPLE_INPUT_FIELDS));
  			var multipleOutputFields = this.parseBooleanString(operationTypeGr.getDisplayValue(this.HAS_MULTIPLE_OUTPUT_FIELDS));
  			if (isNewOperation) {
  				var targetNames = target.split(',');
  				for (var i = 0; i < targetNames.length; i++) {
  					var targetGr = this.createEntityField(feedId, source.entityId, targetNames[i], targetNames[i]);
  					if (!targetGr.getValue)
  						return targetGr;
  					result.fieldsCreated.push({
  						field: targetGr.getValue(this.FIELD),
  						name: targetGr.getValue(this.NAME),
  						sys_id: targetGr.getUniqueValue()
  					});
  					targetFieldIds.push(targetGr.getValue(this.SYS_ID));
  				}
  			}

  			var sourceFields = [];
  			if (!isSetOperation)
  				sourceFieldIds = source.fieldIds.split(',');

  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			operationGr.addNotNullQuery(this.ORDER);
  			operationGr.orderByDesc(this.ORDER);
  			operationGr.setLimit(1);
  			operationGr.query();

  			var highestOrder = 0;
  			if (operationGr.next())
  				highestOrder = operationGr.getValue('order');
  			highestOrder = parseInt(highestOrder);

  			// determine the order
  			var order = 100;
  			if (!isNaN(highestOrder))
  				order = Math.max(100, highestOrder + 100);
  			if (isSetOperation)
  				order = 1;

  			var table = operationTypeGr.getValue(this.TABLE);
  			operationGr = new GlideRecord(table);
  			if (isNewOperation)
  				operationGr.initialize();
  			else {
  				if (!operationGr.get(option)) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to find operation"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  			}

  			operationGr.setValue(this.SYS_RTE_EB_DEFINITION, feedId);
  			operationGr.setValue(this.SYS_RTE_EB_ENTITY, source.entityId);
  			operationGr.setValue(this.TYPE, operation.type);
  			operationGr.setValue(this.NAME, operation.transformDescription);
  			if (isNewOperation)
  				operationGr.setValue(this.ORDER, order);
  			if (!isSetOperation) {
  				if (multipleInputFields)
  					operationGr.setValue(this.SOURCE_SYS_RTE_EB_FIELDS, sourceFieldIds.join());
  				else
  					operationGr.setValue(this.SOURCE_SYS_RTE_EB_FIELD, sourceFieldIds.join());
  			}
  			if (isNewOperation) {
  				if (multipleOutputFields)
  					operationGr.setValue(this.TARGET_SYS_RTE_EB_FIELDS, targetFieldIds.join());
  				else
  					operationGr.setValue(this.TARGET_SYS_RTE_EB_FIELD, targetFieldIds.join());
  			}

  			for (var param in operation.optionalParams) {
  				// First check if this a valid param on the operation table
  				if (operationGr.isValidField(param)) {
  					// Get the type of the param and the actual value
  					var paramType = operation.optionalParams[param].type;
  					var paramValue = operation.optionalParams[param].value;
  					if (paramType === 'string' || paramType === 'choice') {
  						operationGr.setValue(param, paramValue);
  					}
  					else if (isNewOperation && paramType === 'field' && paramValue) {
  						// create a new field for this operation
  						var newFieldGr = this.createEntityField(feedId, source.entityId, paramValue, paramValue);
  						if (!newFieldGr.getValue)
  							return newFieldGr;
  						operationGr.setValue(param, newFieldGr.getUniqueValue());
  						result.fieldsCreated.push({
  							field: newFieldGr.getValue(this.FIELD),
  							name: newFieldGr.getValue(this.NAME),
  							sys_id: newFieldGr.getUniqueValue()
  						});
  					}
  				}
  			}
  			if (isNewOperation) {
  				if (!operationGr.insert()) {
  					// revert the created fields
  					var listOfFieldIds = result.fieldsCreated.map(function(field) {
  						return field.sys_id;
  					}).join();

  					if (listOfFieldIds)
  						this.deleteFields(listOfFieldIds);

  					errorObj = this.buildErrorObject(gs.getMessage("Failed to insert operation"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  			} else {
  				if (!operationGr.update()) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to update operation"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  			}

  			order = parseInt(operationGr.getValue(this.ORDER));
  				if (isNaN(order))
  					order = -1;

  			result.operationCreated = {
  				operationTypeSysId: operationGr.getValue(this.TYPE),
  				operationType: operationGr.type.name + '',
  				description: operationGr.getValue(this.NAME),
  				order: order,
  				sourceField: operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELD),
  				sourceFields: operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELDS),
  				targetField: operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELD),
  				targetFields: operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELDS),
  				operationSysId: operationGr.getUniqueValue(),
  				additionalParams: operation.optionalParams
  			};
  			for (var parameter in operation.optionalParams) {
  				if (operationGr.isValidField(parameter)) {
  					result.operationCreated.additionalParams[parameter] = {
  						value: operationGr.getValue(parameter),
  						displayValue: operationGr.getDisplayValue(parameter)
  					};
  				}
  			}

  			// update the created fields to add the operation
  			for (var j = 0; j < result.fieldsCreated.length; j++) {
  				var field = result.fieldsCreated[j];
  				field.operation = result.operationCreated;
  			}
  			result.status = this.SUCCESS;
  			return result;
  		},

  		deleteClass: function(feedId, entityId, isRecommendedClass, isRecursionLevel) {
  			var errorObj = {};
  			entityId = entityId.split(',');
  			var entityMappingId = [];
  			var entityIdToConceptualClassMap = {};
  			var entityIdToEntityMappingIdMap = {};
  			var entityPath = '';
  			var isNestedPayload = this.isNestedPayload(feedId);

  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			while (entityGr.next()) {
  				entityIdToConceptualClassMap[entityGr.getValue(this.SYS_ID)] = entityGr.getValue('table');
  				entityPath = entityGr.getValue(this.PATH);
  			}

  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();

  			// Get entity mapping ids
  			while (entityMappingGr.next()) {
  				var mappingId = entityMappingGr.getValue(this.SYS_ID);
  				var targetEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  				entityMappingId.push(mappingId);
  				entityIdToEntityMappingIdMap[targetEntityId] = mappingId;
  			}

  			// Delete fields
  			var entityFieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  			entityFieldGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityFieldGr.addQuery(this.SYS_RTE_EB_ENTITY, entityId);
  			entityFieldGr.query();
  			entityFieldGr.deleteMultiple();

  			// Delete field mappings for this entity mapping
  			var entityFieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			entityFieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityFieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  			entityFieldMappingGr.query();
  			entityFieldMappingGr.deleteMultiple();

  			// Get all entity mapping ids for relationship entities
  			var relEntityMappings = this.getCmdbRelationEntityMappings(feedId);
  			var relEntityMappingIds = Object.keys(relEntityMappings);

  			// Also delete all field mappings where this is referenced
  			entityFieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			entityFieldMappingGr.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, entityId);
  			entityFieldMappingGr.query();
  			if (relEntityMappingIds.length > 0) {
  				while (entityFieldMappingGr.next()) {
  					// Check if this is for rel
  					var id = entityFieldMappingGr.getValue(this.SYS_RTE_EB_ENTITY_MAPPING);
  					if (relEntityMappingIds.indexOf(id) > -1)
  						this.deleteClass(feedId, relEntityMappings[id], false, true);
  				}
  			}
  			// Delete field mapping records;
  			entityFieldMappingGr.deleteMultiple();

  			var lookupEntityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			lookupEntityGr.addQuery(this.LOOKUP_FOR_ENTITY, entityId);
  			lookupEntityGr.query();
  			while (lookupEntityGr.next()) {
  				var sysId = lookupEntityGr.getUniqueValue();
  				this.deleteClass(feedId, sysId, false, true);
  			}

  			if (isNestedPayload && entityPath) {
  				var entityPathParts = entityPath.split('.');
  				var selfPath = entityPathParts[entityPathParts.length - 1];
  				this.updateChildEntityPath(entityId, selfPath + '.', '');
  			}

  			var cmdbRecommendations = new CmdbRecommendations();
  			var isRecommendationDataCaptureEnabled = this.parseBooleanString(gs.getProperty('sn_int_studio.recommendation.data_capture'));

  			if(isRecommendationDataCaptureEnabled && !isRecursionLevel) {
  				var applicationFeed = this.getApplicationFeed(feedId);
  				if (!applicationFeed) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to get Application Feed"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  				var dataSource = applicationFeed.sys_data_source.name + '';
  				if (!dataSource) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to get Data Source from Application Feed"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}

  				var tempEntities = "";
  				var impEntityGr = this.findImpCmdbEntity(feedId);
  				if (impEntityGr.getUniqueValue) {
  					tempEntities = this.findTempCmdbEntity(feedId, impEntityGr.getUniqueValue());
  				}

  				var tempEntityPaths = Object.keys(tempEntities);
  				var sourceFields = [];
  				for (var i = 0; i < tempEntityPaths.length; i++) {
  					var tempEntityPath = tempEntityPaths[i];
  					var tempEntity = tempEntities[tempEntityPath];
  					var tempEntityId = tempEntity[this.SYS_ID];

  					var sourceFieldsList = this.getEntityFieldsForEntity(tempEntityId);
  					for (var j = 0; j < sourceFieldsList.length; j++) {
  						sourceFields.push(sourceFieldsList[j].field);
  					}
  				}

  				// Delete recommendation state record
  				if(isRecommendedClass) {
  					var delStateRe = cmdbRecommendations.deleteClassRecommendationState(feedId, entityIdToConceptualClassMap[entityId[0]]);
  					if (delStateRe.status == 'failure') {
  						errorObj = this.buildErrorObject(deleteClassRecState.error.message, '', {deleteState: deleteClassRecState});
  						throw this.getInternalServerError(errorObj);
  					}
  				}

  				for (var k = 0; k < entityId.length; k++) {
  					var currentEntityId = entityId[k];
  					var conceptualClass = entityIdToConceptualClassMap[currentEntityId];
  					var currentEntityMappingId = entityIdToEntityMappingIdMap[currentEntityId];
  					// Delete class recommendation record
  					var deleteClassRec = cmdbRecommendations.deleteClassRecommendation(currentEntityMappingId, dataSource, conceptualClass, sourceFields);
  					if (deleteClassRec.status == 'failure') {
  						errorObj = this.buildErrorObject(deleteClassRec.error.message, '', {deleteState: deleteClassRec});
  						throw this.getInternalServerError(errorObj);
  					}
  				}
  			}

  			// Delete entity mapping
  			entityMappingGr.deleteMultiple();

  			// Delete entity
  			entityGr.deleteMultiple();

  			if (isRecommendationDataCaptureEnabled) {
  				var delEmptyAttrState = cmdbRecommendations.deleteAttributeRecommendationStateWithEmptyEntityMapping();
  				if (delEmptyAttrState.status == 'failure') {
  					this.internalServerError.setMessage(delEmptyAttrState.error.message);
  					return this.internalServerError;
  				}
  			}

  			return this.SUCCESS;
  		},

  		saveClass: function(feedId, tempEntityPath, tempEntitySysId, name, path, table, conditionScript, order, groupId, condition, lookupEntity, isRecommendedClass, ignore) {
  			var errorObj = {};
  			var cmdbEntityIdRecord = this.createCmdbEntity(feedId, name, path, table, '', lookupEntity);
  			var cmdbEntityId = cmdbEntityIdRecord.getUniqueValue();
  			if (!cmdbEntityId) {
  				errorObj = this.buildErrorObject(gs.getMessage("Failed to save class"), gs.getMessage('Failed to insert CMDB entity record'), {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			var entityMappingGr = this.createMappingBetweenEntities(feedId, tempEntityPath + 'To' + name, tempEntitySysId, cmdbEntityId, conditionScript, order, groupId, condition, ignore);
  			if (!entityMappingGr || !entityMappingGr.getUniqueValue()) {
  				errorObj = this.buildErrorObject(gs.getMessage("Failed to save class"), gs.getMessage('Failed to insert entity mapping record'), {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			var isNestedPayload = this.isNestedPayload(feedId);
  			if (isNestedPayload) {
  				this.orderSecondStageMapping(feedId);
  			}

  			var isRecommendationDataCaptureEnabled = this.parseBooleanString(gs.getProperty('sn_int_studio.recommendation.data_capture'));

  			if(isRecommendationDataCaptureEnabled && isRecommendedClass) {
  				var cmdbRecomendations = new CmdbRecommendations();
  				var classRecomendationStateId = cmdbRecomendations.changeClassRecommendationState(feedId, table, 'applied');
  				if (classRecomendationStateId == null) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to change class recommendation state"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  			}

  			return {
  				entities: {
  					name: cmdbEntityIdRecord.getValue(this.NAME),
  					table: cmdbEntityIdRecord.getValue(this.TABLE),
  					path: cmdbEntityIdRecord.getValue(this.PATH),
  					lookup_for_entity: cmdbEntityIdRecord.getValue(this.LOOKUP_FOR_ENTITY),
  					related_for_entity: cmdbEntityIdRecord.getValue(this.RELATED_FOR_ENTITY),
  					relationship_type_string: cmdbEntityIdRecord.getValue(this.RELATIONSHIP_TYPE_STRING),
  					sys_id: cmdbEntityIdRecord.getUniqueValue()
  				},
  				entitiesMapping: {
  					encoded_query: entityMappingGr.getValue(this.ENCODED_QUERY),
  					condition_script: entityMappingGr.getValue(this.CONDITION_SCRIPT),
  					is_conditional: entityMappingGr.getDisplayValue(this.IS_CONDITIONAL),
  					name: entityMappingGr.getValue(this.NAME),
  					order: entityMappingGr.getValue(this.ORDER),
  					source_sys_rte_eb_entity: entityMappingGr.getValue(this.SOURCE_SYS_RTE_EB_ENTITY),
  					sys_id: entityMappingGr.getUniqueValue(),
  					target_sys_rte_eb_entity: entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY),
  					group_id: entityMappingGr.getValue(this.ENTITY_MAPPING_GROUP)
  				}
  			};
  		},

  		createFieldAndFieldMapping: function(feedId, entityId, source, target, refEntityId, entityMappingId, targetFieldPath) {
  			var isNestedPayload = this.isNestedPayload(feedId);
  			var isFirstFieldMapping = false;
  			var targetTempEntityId = '';
  			var targetTempPath = '';
  			if (!targetFieldPath) {
  				targetFieldPath = target;
  			}
  			if (!refEntityId && isNestedPayload) {
  				var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  				fieldGr.addQuery(this.SYS_ID, source);
  				fieldGr.query();
  				if (fieldGr.next()) {
  					targetTempPath = fieldGr.sys_rte_eb_entity.path + '';
  					targetTempEntityId = fieldGr.getValue(this.SYS_RTE_EB_ENTITY);
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot find field with id {0}', source));
  					throw this.getInternalServerError(errorObj);
  				}

  				var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  				fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  				fieldMappingGr.addNullQuery(this.REFERENCED_SYS_RTE_EB_ENTITY);
  				fieldMappingGr.query();
  				if (fieldMappingGr.next()) {
  					var existingTempPath = fieldMappingGr.sys_rte_eb_entity_mapping.source_sys_rte_eb_entity.path + '';
  					if (existingTempPath != targetTempPath) {
  						errorObj = this.buildErrorObject(gs.getMessage('Target field temp path {0} does not match existing temp path {1}', [targetTempPath, existingTempPath]));
  						throw this.getInternalServerError(errorObj);
  					}
  				} else {
  					isFirstFieldMapping = true;
  				}
  			}

  			var entityFields = this.getEntityFieldsForEntity(entityId);
  			var foundEntityField = -1;
  			for (var i = 0; i < entityFields.length; i++) {
  				if (entityFields[i].field === targetFieldPath) {
  					foundEntityField = i;
  					break;
  				}
  			}

  			if (foundEntityField > -1)
  				var cmdbFieldId = entityFields[foundEntityField].sys_id;
  			else {
  				var cmdbFieldGr = this.createEntityField(feedId, entityId, targetFieldPath, target);
  				if (!cmdbFieldGr.getValue) {
  					return cmdbFieldGr;
  				}
  				var cmdbFieldId = cmdbFieldGr.getValue(this.SYS_ID);
  			}

  			var fieldMappingGr;
  			var field_mapping_ids = [];

  			// Check if this is reference or direct attribute mapping
  			if (refEntityId) {
  				if (isNestedPayload) {
  					fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  					fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  					fieldMappingGr.addNullQuery(this.REFERENCED_SYS_RTE_EB_ENTITY);
  					fieldMappingGr.query();
  					if (!fieldMappingGr.next()) {
  						var targetClassName = '';
  						var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  						entityMappingGr.addQuery(this.SYS_ID, entityMappingId);
  						entityMappingGr.query();
  						if (entityMappingGr.next()) {
  							targetClassName = entityMappingGr[this.TARGET_SYS_RTE_EB_ENTITY].name + '';
  						}

  						errorObj = this.buildErrorObject(gs.getMessage('Please map at least one non-reference field in class \'{0}\'.', targetClassName));
  						throw this.getInternalServerError(errorObj);
  					}
  				}

  				var refEntityIdArr = refEntityId.split(',');
  				for (var i = 0; i < refEntityIdArr.length; i++) {
  					fieldMappingGr = this.createFieldMappingsWithEntityRef(feedId, entityMappingId, refEntityId, cmdbFieldId, '100');
  					field_mapping_ids.push(fieldMappingGr.getValue(this.SYS_ID));
  				}
  			} else {
  				if (isFirstFieldMapping && isNestedPayload) {
  					var targetEntityId = '';
  					var targetEntityPath = '';
  					var targetEntityLookupEntityId = '';
  					var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  					entityMappingGr.addQuery(this.SYS_ID, entityMappingId);
  					entityMappingGr.query();
  					if (entityMappingGr.next()) {
  						targetEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  						targetEntityPath = entityMappingGr.target_sys_rte_eb_entity.path + '';
  						targetEntityLookupEntityId = entityMappingGr.target_sys_rte_eb_entity.lookup_for_entity + '';
  						var targetEntityName = entityMappingGr.target_sys_rte_eb_entity.name + '';
  						var currentTempEntityId = entityMappingGr.getValue(this.SOURCE_SYS_RTE_EB_ENTITY);
  						var targetTempPathParts = targetTempPath.split('.');
  						var targetTempSelfPath = targetTempPathParts[targetTempPathParts.length - 1];
  						var targetEntityPathParts = targetEntityPath.split('.');
  						var targetEntitySelfPath = targetEntityPathParts[targetEntityPathParts.length - 1];

  						if (currentTempEntityId != targetTempEntityId) {
  							var prefixErrorMsg = gs.getMessage('Cannot change the branch of source data because class');
  							
  							var lookupEntityName = entityMappingGr.target_sys_rte_eb_entity.lookup_for_entity.name + '';
  							var associatedEntityName = entityMappingGr.target_sys_rte_eb_entity.related_for_entity.name + '';
  							if (lookupEntityName) {
  								errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' has lookup class \'{2}\'', [prefixErrorMsg, targetEntityName, lookupEntityName]));
  								throw this.getInternalServerError(errorObj);
  							}
  							if (associatedEntityName) {
  								errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' has associated class \'{2}\'', [prefixErrorMsg, targetEntityName, associatedEntityName]));
  								throw this.getInternalServerError(errorObj);
  							}

  							var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  							entityGr.addQuery(this.LOOKUP_FOR_ENTITY, targetEntityId);
  							entityGr.query();
  							if (entityGr.next()) {
  								var className = entityGr.getValue(this.NAME);
  								errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' is lookup class for class \'{2}\'', [prefixErrorMsg, targetEntityName, className]));
  								throw this.getInternalServerError(errorObj);
  							}

  							entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  							entityGr.addQuery(this.RELATED_FOR_ENTITY, targetEntityId);
  							entityGr.query();
  							if (entityGr.next()) {
  								var className = entityGr.getValue(this.NAME);
  								errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' is associated class for class \'{2}\'', [prefixErrorMsg, targetEntityName, className]));
  								throw this.getInternalServerError(errorObj);
  							}

  							var fieldMappingGr2 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  							fieldMappingGr2.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  							fieldMappingGr2.addNotNullQuery(this.REFERENCED_SYS_RTE_EB_ENTITY);
  							fieldMappingGr2.query();
  							if (fieldMappingGr2.next()) {
  								var childEntityName = fieldMappingGr2[this.REFERENCED_SYS_RTE_EB_ENTITY].name + '';
  								errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' references class \'{2}\'', [prefixErrorMsg, targetEntityName, childEntityName]));
  								throw this.getInternalServerError(errorObj);
  							}

  							fieldMappingGr2 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  							fieldMappingGr2.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, targetEntityId);
  							fieldMappingGr2.query();
  							if (fieldMappingGr2.next()) {
  								var childEntityTable = fieldMappingGr2.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table + '';
  								var childEntityName = fieldMappingGr2.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.name + '';
  								if (childEntityTable == this.CMDB_REL_CI) {
  									var relClassName = '';
  									var relEntityMappingId = fieldMappingGr2.sys_rte_eb_entity_mapping;
  									var fieldMappingGr3 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  									fieldMappingGr3.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, relEntityMappingId);
  									fieldMappingGr3.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, '!=', targetEntityId);
  									fieldMappingGr3.query();
  									if (fieldMappingGr3.next()) {
  										relClassName = fieldMappingGr3[this.REFERENCED_SYS_RTE_EB_ENTITY].name + '';
  									}
  									errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' has relationship with \'{2}\' class.', [prefixErrorMsg, targetEntityName, relClassName]));
  									throw this.getInternalServerError(errorObj);
  								} else {
  									errorObj = this.buildErrorObject(gs.getMessage('{0} \'{1}\' references class \'{2}\'', [prefixErrorMsg, childEntityName, targetEntityName]));
  									throw this.getInternalServerError(errorObj);
  								}
  							}

  							var currentEntityMappingLabel = entityMappingGr.getValue(this.NAME);
  							var currentEntityMappingLabelParts = currentEntityMappingLabel.split('To');
  							currentEntityMappingLabelParts[0] = targetTempPath;
  							var newEntityMappingLabel = currentEntityMappingLabelParts.join('To');
  							entityMappingGr.setValue(this.SOURCE_SYS_RTE_EB_ENTITY, targetTempEntityId);
  							entityMappingGr.setValue(this.NAME, newEntityMappingLabel);
  							entityMappingGr.update();

  							this.updateChildEntityPath(targetEntityId, targetEntitySelfPath + '.', '');
  						}
  						var updatePath = '';
  						var isSameLevelLookup = false;
  						if (targetEntityLookupEntityId) {
  							var targetEntityLookupEntityTempPath = '';
  							entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  							entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, targetEntityLookupEntityId);
  							entityMappingGr.query();
  							if (entityMappingGr.next()) {
  								targetEntityLookupEntityTempPath = entityMappingGr[this.SOURCE_SYS_RTE_EB_ENTITY].path + '';
  								if (targetEntityLookupEntityTempPath.split('.').length == targetTempPathParts.length) {
  									isSameLevelLookup = true;
  								}
  							}
  						}
  						if (isSameLevelLookup) {
  							updatePath = '';
  						} else if (targetTempSelfPath.indexOf('[*]') < 0 && targetTempPathParts.length > 1 && targetTempPath.indexOf('[*]') > -1 && targetEntityPathParts.length == 1 && targetEntitySelfPath.indexOf('[*]') < 0) {
  							updatePath = targetEntityPath + '[*]';
  						} else if (targetTempSelfPath.indexOf('[*]') > -1 && targetEntitySelfPath.indexOf('[*]') < 0) {
  							updatePath = targetEntityPath + '[*]';
  						} else if (targetTempSelfPath.indexOf('[*]') < 0 && targetEntitySelfPath.indexOf('[*]') > -1 && !(targetTempPathParts.length > 1 && targetTempPath.indexOf('[*]') > -1 && targetEntityPathParts.length == 1)) {
  							targetEntityPathParts[targetEntityPathParts.length - 1] = targetEntitySelfPath.substring(0, targetEntitySelfPath.length - 3);
  							updatePath = targetEntityPathParts.join('.');
  						}
  						if (updatePath) {
  							entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  							entityGr.addQuery(this.SYS_ID, targetEntityId);
  							entityGr.query();
  							if (entityGr.next()) {
  								entityGr.setValue(this.PATH, updatePath);
  								entityGr.update();
  							} else {
  								errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with id {0}', targetEntityId));
  								throw this.getInternalServerError(errorObj);
  							}
  						}
  					}
  				}

  				fieldMappingGr = this.createMappingBetweenEntityFields(feedId, entityMappingId, source, cmdbFieldId, '100');
  				field_mapping_ids.push(fieldMappingGr.getValue(this.SYS_ID));
  			}

  			return {
  				field_id: cmdbFieldId,
  				field_mapping_id: field_mapping_ids.length === 1 ? field_mapping_ids[0] : '',
  				field_mapping_ids: field_mapping_ids
  			};
  		},

  		createUniqueEntityNameAndPath: function(feedId, label, table) {
  			var existingNames = [];
  			var existingPaths = [];
  			var entities = this.getEntitiesForApplicationFeed(feedId, 'table=' + table);
  			for (var i = 0; i < entities.length; i++) {
  				existingNames.push(entities[i].name);
  				existingPaths.push(entities[i].path);
  			}
  			var count = 1;
  			while (true) {
  				var potentialName = label + ' ' + count;
  				var potentialPath = table + '_' + count;
  				if (existingNames.indexOf(potentialName) < 0 && existingPaths.indexOf(potentialPath) < 0) {
  					return {
  						name: potentialName,
  						path: potentialPath
  					};
  				}
  				count++;
  			}
  		},

  		getSavedOperationsForFeed: function(feedId) {
  			var errorObj = {};
  			var allFieldsPerOperationType = {};
  			var operationTypesGr = new GlideRecord(this.SYS_RTE_EB_OPERATION_TYPE);
  			operationTypesGr.query();
  			while (operationTypesGr.next()) {
  				var table = operationTypesGr.getValue(this.TABLE);
  				allFieldsPerOperationType[table] = [];
  			}
  			var allTables = Object.keys(allFieldsPerOperationType);
  			// get the columns from sys dictionary
  			for (var i = 0; i < allTables.length; i++)
  				allFieldsPerOperationType[allTables[i]] = this.getTableColumns(allTables[i]);

  			var allOperations = {};
  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			operationGr.query();
  			while (operationGr.next()) {
  				var operationTypeTable = operationGr.getDisplayValue('type.table');
  				var operationSysId = operationGr.getUniqueValue();
  				var order = parseInt(operationGr.getValue(this.ORDER));
  				if (isNaN(order))
  					order = -1;
  				allOperations[operationSysId] = {
  					operationTypeSysId: operationGr.getValue(this.TYPE),
  					description: operationGr.getValue(this.NAME),
  					order: order,
  					sourceField: operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELD),
  					sourceFields: operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELDS),
  					targetField: operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELD),
  					targetFields: operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELDS),
  					operationSysId: operationSysId,
  					additionalParams: {}
  				};
  				// add the additional parameters specific to the each operation
  				if (allFieldsPerOperationType[operationTypeTable].length > 0) {
  					var correctOperationTableGr = new GlideRecord(operationTypeTable);
  					if (!correctOperationTableGr.get(operationSysId)) {
  						errorObj = this.buildErrorObject(gs.getMessage("Operation not found on the right table"), '', {args: arguments});
  						throw this.getInternalServerError(errorObj);
  					}

  					for (var j = 0; j < allFieldsPerOperationType[operationTypeTable].length; j++) {
  						allOperations[operationSysId].additionalParams[allFieldsPerOperationType[operationTypeTable][j]] = {
  							value: correctOperationTableGr.getValue(allFieldsPerOperationType[operationTypeTable][j]),
  							displayValue: correctOperationTableGr.getDisplayValue(allFieldsPerOperationType[operationTypeTable][j])
  						};
  					}
  				}
  			}
  			return allOperations;
  		},

  		parseBooleanString: function(booleanString) {
  			if (booleanString)
  				return booleanString === 'true' ? true : false;
  			return false;
  		},

  		getCmdbRelationEntityMappings: function(feedId) {
  			var relEntities = this.getEntitiesForApplicationFeed(feedId, 'table=cmdb_rel_ci');
  			var sysIds = [];
  			for (var i = 0; i < relEntities.length; i++) {
  				sysIds.push(relEntities[i].sys_id);
  			}

  			// Now get mappings for these
  			var entityMappings = this.getEntityMappingsForApplicationFeed(feedId, 'target_sys_rte_eb_entityIN' + sysIds.join());
  			var entityMappingIds = {};
  			for (var j = 0; j < entityMappings.length; j++) {
  				entityMappingIds[entityMappings[j].sys_id] = entityMappings[j].target_sys_rte_eb_entity;
  			}

  			return entityMappingIds;
  		},

  		getSetOperationTypeSysId: function() {
  			var gr = new GlideRecord(this.SYS_RTE_EB_OPERATION_TYPE);
  			gr.addQuery(this.TABLE, 'sys_rte_eb_set_operation');
  			gr.setLimit(1);
  			gr.query();
  			if (gr.hasNext()) {
  				gr.next();
  				return gr.getUniqueValue();
  			}
  			return '';
  		},

  		getUpdatedSysIdsFromMap: function(currentString, sysIdMap) {
  			var existingSysIds = currentString.split(',');
  			return existingSysIds.map(function(sysId) {
  				return sysIdMap[sysId];
  			}).join();
  		},

  		canCopyFeed: function(sourceDataSourceGr, targetDataSourceGr) {
  			var errorObj = {};
  			// First, check that data sources given are unique
  			if (sourceDataSourceGr.getValue(this.SYS_ID) === targetDataSourceGr.getValue(this.SYS_ID))
  				return false;

  			if (!sourceDataSourceGr.import_set_table_name) {
  				errorObj = this.buildErrorObject(gs.getMessage("Invalid import set table name for selected data source '{0}'.", [sourceDataSourceGr.name]));
  				throw this.getInternalServerError(errorObj);
  			}

  			if (!targetDataSourceGr.import_set_table_name) {
  				errorObj = this.buildErrorObject(gs.getMessage("Invalid import set table name for data source '{0}' associated to this map.", [targetDataSourceGr.name]));
  				throw this.getInternalServerError(errorObj);
  			}

  			var sourceFields = this.getTableColumns(sourceDataSourceGr.import_set_table_name);
  			var destFields = this.getTableColumns(targetDataSourceGr.import_set_table_name);

  			if (sourceFields.length === 0) {
  				errorObj = this.buildErrorObject(gs.getMessage("Import set table columns are empty for selected data source '{0}'.", [sourceDataSourceGr.name]));
  				throw this.getInternalServerError(errorObj);
  			}

  			if (destFields.length === 0) {
  				errorObj = this.buildErrorObject(gs.getMessage("Import set table columns are empty for data source '{0}' associated to this map.", [targetDataSourceGr.name]));
  				throw this.getInternalServerError(errorObj);
  			}


  			// Check if field array lengths are not the same
  			if (sourceFields.length !== destFields.length)
  				return false;
  			else {
  				// If they have the same length, loop through and compare values from both arrays
  				for (var i = 0; i < sourceFields.length; i++) {
  					if (sourceFields[i] != destFields[i])
  						return false;
  				}
  			}

  			return true;
  		},
  		
  		updateFeedMetadataWithNewImportSet: function(applicationFeedId) {
  			var isEmptyImportSet = false;
  			var isNestedPayload = this.isNestedPayload(applicationFeedId);
  			var forceMetadataReload = false;
  			var templateStateGr = new GlideRecord(this.CMDB_INST_TEMPLATE_STATE);
  			templateStateGr.addQuery('application_feed_id', applicationFeedId);
  			templateStateGr.query();
  			if (templateStateGr.next()) {
  				var importSetId = templateStateGr.getValue('import_set_id');
  				var impEntityGr = this.findImpCmdbEntity(applicationFeedId);
  				var impEntityId = '';
  				if (impEntityGr.getUniqueValue) {
  					impEntityId = impEntityGr.sys_id + '';
  				}
  			
  				var importSetRunGr = new GlideRecord('sys_import_set_row');
  				importSetRunGr.addQuery('sys_import_state', '!=', 'test');
  				importSetRunGr.addQuery('sys_import_set', importSetId);
  				importSetRunGr.query();
  				if (!importSetRunGr.next()) {
  					isEmptyImportSet = true;
  				}

  				if (isNestedPayload && impEntityId && !isEmptyImportSet) {
  					var impTableColumns = [];
  					var returnedSchema = this.getNestedPayloadSchema(templateStateGr, false);
  					var rawSchemaStr = returnedSchema.rawSchemaStr;
  					var schema = returnedSchema.schemaJson;

  					var message = this.getNestedPayloadFields(schema, '', impTableColumns);
  					if (message) {
  						errorObj = this.buildErrorObject(message, '', '');
  						throw this.getInternalServerError(errorObj);
  					}

  					var existingImpFieldPaths = {};
  					var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  					fieldGr.addQuery(this.SYS_RTE_EB_ENTITY, impEntityId);
  					fieldGr.query();
  					while (fieldGr.next()) {
  						existingImpFieldPaths[fieldGr.getValue(this.FIELD)] = true;
  					}

  					var existingTempEntityPathToOrder = {};
  					var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  					entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, impEntityId);
  					entityMappingGr.query();
  					while (entityMappingGr.next()) {
  						var path = entityMappingGr[this.TARGET_SYS_RTE_EB_ENTITY].path + '';
  						existingTempEntityPathToOrder[path] = Number(entityMappingGr.getValue(this.ORDER));
  					}

  					var tempEntitiesPaths = {};
  					for (var i = 0; i < impTableColumns.length; i++) {
  						var fieldParts = impTableColumns[i].split('.');
  						if (fieldParts.length > 1 && fieldParts[fieldParts.length - 2].indexOf('[*]') < 0) {
  							continue;
  						}
  						var path = 'temp';
  						for (var j = 0; j < fieldParts.length - 1; j++) {
  							path += '.' + fieldParts[j];
  						}
  						tempEntitiesPaths[path] = true;
  					}
  					var tempEntityKeys = Object.keys(tempEntitiesPaths);
  					tempEntityKeys.sort(function(a, b) {
  						var aLevelCount = a.split('.').length;
  						var bLevelCount = b.split('.').length;
  						if (aLevelCount - bLevelCount != 0) {
  							return aLevelCount - bLevelCount;
  						}
  						if (a < b) {
  							return -1;
  						} else if (a > b) {
  							return 1;
  						} else {
  							return 0;
  						}
  					});

  					var tempEntities = {};
  					var tempEntityIdToMappingIdMap = {};
  					for (var i = 0; i < tempEntityKeys.length; i++) {
  						var path = tempEntityKeys[i];
  						var order = i * 10 + 100;

  						if (!existingTempEntityPathToOrder[path]) {
  							forceMetadataReload = true;
  							var tempEntityGr = this.createCmdbEntity(applicationFeedId, path, path, '', '');
  							if (!tempEntityGr.getUniqueValue()) {
  								errorObj = this.buildErrorObject(gs.getMessage("Failed to create temp entity"), '', {pathParams: pathParams});
  								throw this.getInternalServerError(errorObj);
  							}
  							var tempEntity = {
  								name: tempEntityGr.getValue(this.NAME),
  								table: tempEntityGr.getValue(this.TABLE),
  								path: tempEntityGr.getValue(this.PATH),
  								lookup_for_entity: impEntityGr.getValue(this.LOOKUP_FOR_ENTITY),
  								related_for_entity: impEntityGr.getValue(this.RELATED_FOR_ENTITY),
  								relationship_type_string: impEntityGr.getValue(this.RELATIONSHIP_TYPE_STRING),
  								sys_id: tempEntityGr.getUniqueValue()
  							};
  							tempEntities[path] = tempEntity;

  							var impToTempMappingGr = this.createMappingBetweenEntities(applicationFeedId, 'impTo' + tempEntity.name, impEntityId, tempEntity.sys_id, '', order, '', '');
  							if (!impToTempMappingGr.getUniqueValue()) {
  								errorObj = this.buildErrorObject(gs.getMessage("Failed to create imp to temp mapping"), '', {pathParams: pathParams});
  								throw this.getInternalServerError(errorObj);
  							}

  							tempEntityIdToMappingIdMap[tempEntity.sys_id] = impToTempMappingGr.getUniqueValue();
  						} else if (order != existingTempEntityPathToOrder[path] && !this.shouldDisableEntityMappingReorder(applicationFeedId)) {
  							forceMetadataReload = true;
  							var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  							entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, impEntityId);
  							entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY + '.path', path);
  							entityMappingGr.query();
  							if (entityMappingGr.next()) {
  								entityMappingGr.setValue(this.ORDER, order);
  								entityMappingGr.update();
  							}
  						}
  					}

  					var order = 100;
  					if (impTableColumns && impTableColumns.length > 0) {
  						for (var i = 0; i < impTableColumns.length; i++) {
  							var impColumn = impTableColumns[i];
  							if (existingImpFieldPaths[impColumn]) {
  								continue;
  							}
  							var fieldParts = impColumn.split('.');
  							var lastPart = fieldParts[fieldParts.length - 1];
  							if (lastPart == this.NESTED_PAYLOAD_PLACEHOLDER_FIELD) {
  								continue;
  							}
  							var tempColumnParts = [];
  							for (var j = fieldParts.length - 1; j >= 0; j--) {
  								if (fieldParts[j].indexOf('[*]') < 0) {
  									tempColumnParts.push(fieldParts[j]);
  								} else {
  									break;
  								}
  							}
  							tempColumnParts.reverse();
  							var tempColumnSelfName = tempColumnParts[tempColumnParts.length - 1];
  							var tempColumn = tempColumnParts.join('.');

  							var impTableFieldGr = this.createEntityField(applicationFeedId, impEntityId, impColumn, impColumn);

  							var tempPath = 'temp';
  							var lastCollectionPath = 'temp';
  							for (var j = 0; j < fieldParts.length - 1; j++) {
  								tempPath += '.' + fieldParts[j];
  								if (fieldParts[j].indexOf('[*]') > -1) {
  									lastCollectionPath = tempPath;
  								}
  							}
  							var tempEntityId = '';
  							if (tempEntities[lastCollectionPath]) {
  								tempEntityId = tempEntities[lastCollectionPath].sys_id;
  							} else {
  								var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  								entityGr.addQuery(this.PATH, lastCollectionPath);
  								entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, applicationFeedId);
  								entityGr.query();
  								if (entityGr.next()) {
  									tempEntityId = entityGr.getValue(this.SYS_ID);
  								} else {
  									errorObj = this.buildErrorObject(gs.getMessage('Cannot find temp entity with path \'{0}\'', [lastCollectionPath]));
  									throw this.getInternalServerError(errorObj);
  								}
  							}
  							var tempTableFieldGr = this.createEntityField(applicationFeedId, tempEntityId, tempColumn, tempColumnSelfName);

  							if (impTableFieldGr.getUniqueValue() && tempTableFieldGr.getUniqueValue()) {
  								var currentImpToTempMappingId = '';
  								if (tempEntityIdToMappingIdMap[tempEntityId]) {
  									currentImpToTempMappingId = tempEntityIdToMappingIdMap[tempEntityId];
  								} else {
  									var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  									entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, impEntityId);
  									entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, tempEntityId);
  									entityMappingGr.query();
  									if (entityMappingGr.next()) {
  										currentImpToTempMappingId = entityMappingGr.getValue(this.SYS_ID);
  									} else {
  										errorObj = this.buildErrorObject(gs.getMessage('Cannot find temp entity mapping with path \'{0}\'', [tempPath]));
  										throw this.getInternalServerError(errorObj);
  									}
  								}
  								var impToTempFieldMappingGr = this.createMappingBetweenEntityFields(applicationFeedId, currentImpToTempMappingId, impTableFieldGr.getUniqueValue(), tempTableFieldGr.getUniqueValue(), order);
  								if (!impToTempFieldMappingGr.getUniqueValue()) {
  									errorObj = this.buildErrorObject(gs.getMessage("Failed to create imp to temp field mapping"), '', {pathParams: pathParams});
  									throw this.getInternalServerError(errorObj);
  								}
  							}
  						}
  					}
  				} else {
  					if (impEntityGr.getUniqueValue) {
  						// We do have metadata setup, so check for diffs
  						var importSetTable = templateStateGr.getDisplayValue('import_set_id.table_name');
  						if (!importSetTable) {
  							errorObj = this.buildErrorObject(gs.getMessage("No import set table associated with this template state"), '', {pathParams: pathParams});
  							throw this.getInternalServerError(errorObj);
  						}

  						// Get all columns
  						var impTableColumns = this.getTableColumns(importSetTable);

  						// Get existing fields for imp entity
  						var impFields = this.getEntityFieldsForEntity(impEntityGr.getUniqueValue());

  						var encodedQuery = this.SOURCE_SYS_RTE_EB_ENTITY + '=' + impEntityGr.getUniqueValue();
  						var impToTempEntityMapping = this.getEntityMappingsForApplicationFeed(applicationFeedId, encodedQuery);

  						var order = 1000;
  						// Check for diff
  						for (var i = 0; i < impTableColumns.length; i++) {
  							// Check if record already exists
  							var impColumn = impTableColumns[i];
  							var isMapped = false;
  							for (var j = 0; j < impFields.length; j++) {
  								if (impFields[j].field === impColumn) {
  									isMapped = true;
  									break;
  								}
  							}

  							if (!isMapped) {
  								forceMetadataReload = true;

  								// Create imp field
  								var impTableFieldGr = this.createEntityField(applicationFeedId, impEntityGr.getUniqueValue(), impColumn, impColumn);

  								// Create temp field
  								var tempTableFieldGr = this.createEntityField(applicationFeedId, impToTempEntityMapping[0].target_sys_rte_eb_entity, impColumn, impColumn);

  								if (impTableFieldGr.getUniqueValue() && tempTableFieldGr.getUniqueValue()) {
  									this.createMappingBetweenEntityFields(applicationFeedId, impToTempEntityMapping[0].sys_id, impTableFieldGr.getUniqueValue(), tempTableFieldGr.getUniqueValue(), order);
  									order = order + 50;
  								}
  							}
  						}
  					}
  				}
  			}
  			return forceMetadataReload;
  		},

  		deepCopyFeed: function(sourceFeedId, targetFeedId) {
  			var errorObj = {};

  			var entities = {};
  			var entityMappings = {};
  			var fields = {};

  			var sourceFeedGr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  			if (!sourceFeedGr.get(sourceFeedId)) {
  				errorObj = this.buildErrorObject(gs.getMessage("Could not find record for given source Application Feed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			var sourceDataSourceGr = new GlideRecord(this.SYS_DATA_SOURCE);
  			if (!sourceDataSourceGr.get(sourceFeedGr.getValue(this.SYS_DATA_SOURCE))) {
  				errorObj = this.buildErrorObject(gs.getMessage("Could not find record for Data Source associated with source Application Feed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}

  			var targetFeedGr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  			if (!targetFeedGr.get(targetFeedId)) {
  				errorObj = this.buildErrorObject(gs.getMessage("Could not find record for given target Application Feed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			var targetDataSourceGr = new GlideRecord(this.SYS_DATA_SOURCE);
  			if (!targetDataSourceGr.get(targetFeedGr.getValue(this.SYS_DATA_SOURCE))) {
  				errorObj = this.buildErrorObject(gs.getMessage("Could not find record for Data Source associated with target Application Feed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}
  			var targetDataSourceTableName = targetDataSourceGr.getValue('import_set_table_name');

  			if (!this.canCopyFeed(sourceDataSourceGr, targetDataSourceGr)) {
  				errorObj = this.buildErrorObject(gs.getMessage("Cannot copy source feed to target feed"), '', {args: arguments});
  				throw this.getInternalServerError(errorObj);
  			}

  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', sourceFeedId);
  			entityGr.orderBy('sys_created_on');
  			entityGr.query();
  			while (entityGr.next()) {
  				entityGr.setValue(this.SYS_RTE_EB_DEFINITION, targetFeedId);
  				if (entityGr.getValue(this.NAME) === this.IMPORT)
  					entityGr.setValue(this.TABLE, targetDataSourceTableName);

  				var originalId = entityGr.getValue(this.SYS_ID);
  				var entityId = entityGr.insert();
  				if (!entityId) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to create entity while copying"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  				entities[originalId] = entityId;

  				var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  				fieldGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', sourceFeedId);
  				fieldGr.addQuery(this.SYS_RTE_EB_ENTITY, '=', originalId);
  				fieldGr.query();

  				while(fieldGr.next()) {
  					fieldGr.setValue(this.SYS_RTE_EB_DEFINITION, targetFeedId);
  					fieldGr.setValue(this.SYS_RTE_EB_ENTITY, entities[fieldGr.sys_rte_eb_entity]);

  					var originalFieldId = fieldGr.getValue(this.SYS_ID);
  					var fieldId = fieldGr.insert();
  					if (!fieldId) {
  						errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity field table while copying"), '', {args: arguments});
  						throw this.getInternalServerError(errorObj);
  					}
  					fields[originalFieldId] = fieldId;
  				}
  			}

  			// Overwrite the lookup and related for values in the newly created entities with correct sys_id's
  			// Has to be done in a separate loop because there is no guarantee in original creation order
  			var newEntityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			newEntityGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', targetFeedId);
  			newEntityGr.orderBy('sys_created_on');
  			newEntityGr.query();

  			while (newEntityGr.next()) {
  				if (newEntityGr.lookup_for_entity)
  					newEntityGr.setValue(this.LOOKUP_FOR_ENTITY, entities[newEntityGr.lookup_for_entity]);
  				if (newEntityGr.related_for_entity)
  					newEntityGr.setValue(this.RELATED_FOR_ENTITY, entities[newEntityGr.related_for_entity]);

  				newEntityGr.update();
  			}

  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', sourceFeedId);
  			entityMappingGr.query();

  			while(entityMappingGr.next()) {
  				entityMappingGr.setValue(this.SYS_RTE_EB_DEFINITION, targetFeedId);
  				entityMappingGr.setValue(this.SOURCE_SYS_RTE_EB_ENTITY, entities[entityMappingGr.source_sys_rte_eb_entity]);
  				entityMappingGr.setValue(this.TARGET_SYS_RTE_EB_ENTITY, entities[entityMappingGr.target_sys_rte_eb_entity]);

  				var originalMappingId = entityMappingGr.getValue(this.SYS_ID);
  				var entityMappingId = entityMappingGr.insert();
  				if (!entityMappingId) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity mapping table while copying"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  				entityMappings[originalMappingId] = entityMappingId;

  				var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  				fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', sourceFeedId);
  				fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, '=', originalMappingId);
  				fieldMappingGr.query();

  				while (fieldMappingGr.next()) {
  					fieldMappingGr.setValue(this.SYS_RTE_EB_DEFINITION, targetFeedId);
  					fieldMappingGr.setValue(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappings[fieldMappingGr.sys_rte_eb_entity_mapping]);

  					if (fieldMappingGr.referenced_sys_rte_eb_entity)
  						fieldMappingGr.setValue(this.REFERENCED_SYS_RTE_EB_ENTITY, entities[fieldMappingGr.referenced_sys_rte_eb_entity]);
  					if (fieldMappingGr.source_sys_rte_eb_field)
  						fieldMappingGr.setValue(this.SOURCE_SYS_RTE_EB_FIELD, fields[fieldMappingGr.source_sys_rte_eb_field]);
  					fieldMappingGr.setValue(this.TARGET_SYS_RTE_EB_FIELD, fields[fieldMappingGr.target_sys_rte_eb_field]);

  					if (!fieldMappingGr.insert()) {
  						errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into entity field mapping table while copying"), '', {args: arguments});
  						throw this.getInternalServerError(errorObj);
  					}
  				}
  			}

  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_DEFINITION, '=', sourceFeedId);
  			operationGr.query();

  			while (operationGr.next()) {
  				var extendedOperationGr = new GlideRecord(operationGr.sys_class_name);
  				if (!extendedOperationGr.get(operationGr.getValue(this.SYS_ID))) {
  					errorObj = this.buildErrorObject(gs.getMessage("Could not find Operation record in extended table while copying"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  				extendedOperationGr.setValue(this.SYS_RTE_EB_DEFINITION, targetFeedId);
  				extendedOperationGr.setValue(this.SYS_RTE_EB_ENTITY, entities[extendedOperationGr.sys_rte_eb_entity]);
  				if (extendedOperationGr.source_sys_rte_eb_field)
  					extendedOperationGr.setValue(this.SOURCE_SYS_RTE_EB_FIELD, fields[extendedOperationGr.source_sys_rte_eb_field]);
  				if (extendedOperationGr.target_sys_rte_eb_field)
  					extendedOperationGr.setValue(this.TARGET_SYS_RTE_EB_FIELD, fields[extendedOperationGr.target_sys_rte_eb_field]);
  				if (extendedOperationGr.source_sys_rte_eb_fields)
  					extendedOperationGr.setValue(this.SOURCE_SYS_RTE_EB_FIELDS, this.getUpdatedSysIdsFromMap(extendedOperationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELDS), fields));
  				if (extendedOperationGr.target_sys_rte_eb_fields)
  					extendedOperationGr.setValue(this.TARGET_SYS_RTE_EB_FIELDS, this.getUpdatedSysIdsFromMap(extendedOperationGr.getValue(this.TARGET_SYS_RTE_EB_FIELDS), fields));

  				if (!extendedOperationGr.insert()) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to insert into extended operation table while copying"), '', {args: arguments});
  					throw this.getInternalServerError(errorObj);
  				}
  			}

  			var impEntityGr = this.findImpCmdbEntity(targetFeedId);

  			var metadata = this.loadAllMetadataForApplicationFeed(targetFeedId, impEntityGr);
  			var templateStateGr = new GlideRecord(this.CMDB_INST_TEMPLATE_STATE);
  			templateStateGr.addQuery(this.APPLICATION_FEED_ID, targetFeedId);
  			templateStateGr.query();
  			if (templateStateGr.next()) {
  				var importSetId = templateStateGr.import_set_id + '';
  				var dataInSingleColumn = templateStateGr.import_set_id.data_source.data_in_single_column.toString();
  				dataInSingleColumn = dataInSingleColumn === 'true' ? true : false;
  				metadata.isNestedPayload = dataInSingleColumn;

  				if (dataInSingleColumn) {
  					var returnedSchema = this.getNestedPayloadSchema(templateStateGr, false);
  					var rawSchemaStr = returnedSchema.rawSchemaStr;
  					var rawSchemaJson = returnedSchema.schemaJson;
  					metadata.rawSchema = rawSchemaJson;
  				}
  			}

  			var previewSize = this.getPreviewSize(templateStateGr);
  			metadata.batchSize = previewSize ? previewSize : this.DEFAULT_PREVIEW_SIZE;
  			if (metadata.batchSize > this.MAX_PREVIEW_SIZE) {
  				metadata.batchSize = this.MAX_PREVIEW_SIZE;
  			}
  			return metadata;
  		},

  		getServiceErrorObj: function(status, message, detail) {
  			var error = new sn_ws_err.ServiceError();
  			error.setStatus(status);
  			error.setMessage(message);
  			error.setDetail(detail);
  			return error;
  		},

  		deleteEntityByGroupId: function(groupId) {
  			var entitiesToDelete = [];
  			var deletedEntities = [];
  			var gr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			gr.addQuery(this.ENTITY_MAPPING_GROUP, groupId);
  			gr.query();
  			while (gr.next()) {
  				var relType = gr[this.TARGET_SYS_RTE_EB_ENTITY][this.RELATIONSHIP_TYPE][this.NAME] + '';
  				var entityId = gr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  				entitiesToDelete.push(entityId);
  				deletedEntities.push({
  					'relType': relType,
  					'entityId': entityId
  				});
  			}
  			gr = new GlideRecord(this.SYS_RTE_EB_ENTITY);
  			gr.addQuery(this.SYS_ID, entitiesToDelete);
  			gr.query();
  			while (gr.next()) {
  				gr.deleteRecord();
  			}
  			return deletedEntities;
  		},

  		getNestedPayloadFields: function(schema, dotwalk, fields) {
  			var schemaKeys = Object.keys(schema);
  			var prefixErrorMsg = gs.getMessage('Unable to preview data because');
  			for (var i = 0; i < schemaKeys.length; i++) {
  				var currentField = schemaKeys[i];
  				if (!schema[currentField]) {
  					return gs.getMessage('{0} field \'{1}\' cannot be found.', [prefixErrorMsg, currentField]);
  				}
  				if (currentField == 'temp') {
  					return gs.getMessage('{0} field name cannot be \'temp\'.', prefixErrorMsg);
  				}
  				if (currentField == 'object') {
  					return gs.getMessage('{0} field name cannot be \'object\'.', prefixErrorMsg);
  				}
  				// Schema parser returns $ sign when encounter invalid field names
  				if (currentField == '$') {
  					return gs.getMessage('{0} one or more field names are invalid. Field names must start with a letter (between A-Z or a-z) or with \'_\', and must only contain letters (between A-Z or a-z), digits (0-9), or  \'_\'.', prefixErrorMsg);
  				}

  				var nextDotwalk = [];
  				if (dotwalk) {
  					nextDotwalk = dotwalk.split(',');
  				}

  				if (Array.isArray(schema[currentField]) && typeof schema[currentField][0] != 'object') {
  					nextDotwalk.push(currentField);
  					fields.push(nextDotwalk.join('.'));
  				} else if (typeof schema[currentField] != 'string') {
  					var nextSchema = schema[currentField];
  					if (Array.isArray(nextSchema)) {
  						nextSchema = nextSchema[0];
  						currentField = currentField + '[*]';
  					}
  					nextDotwalk.push(currentField);
  					var message = this.getNestedPayloadFields(nextSchema, nextDotwalk.join(','), fields);
  					if (message) {
  						return message;
  					}
  					nextDotwalk.push(this.NESTED_PAYLOAD_PLACEHOLDER_FIELD);
  					fields.push(nextDotwalk.join('.'));
  				} else {
  					nextDotwalk.push(currentField);
  					fields.push(nextDotwalk.join('.'));
  				}
  			}
  		},

  		updateEntityPathRelation: function(currentEntityId, targetEntityId) {
  			var currentEntityPath = '';
  			var currentEntityName = '';
  			var targetEntityPath = '';
  			var targetEntityName = '';
  			var feedId = '';
  			var errorObj = {};

  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, currentEntityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				currentEntityPath = entityGr.getValue(this.PATH);
  				currentEntityName = entityGr.getValue(this.NAME);
  				feedId = entityGr.getValue(this.SYS_RTE_EB_DEFINITION);
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with id {0}', currentEntityId));
  				throw this.getInternalServerError(errorObj);
  			}
  			entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, targetEntityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				targetEntityPath = entityGr.getValue(this.PATH);
  				targetEntityName = entityGr.getValue(this.NAME);
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with id {0}', targetEntityId));
  				throw this.getInternalServerError(errorObj);
  			}

  			var currentEntityTempPath = '';
  			var targetEntityTempPath = '';
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, targetEntityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				targetEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', targetEntityId));
  				throw this.getInternalServerError(errorObj);
  			}
  			entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, currentEntityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				currentEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', currentEntityId));
  				throw this.getInternalServerError(errorObj);
  			}
  					
  			var removedCurrentEntityPathPart = false;
  			var removedTargetEntityPathPart = false;
  			var currentEntityTempPathParts = currentEntityTempPath.split('.');
  			var targetEntityTempPathParts = targetEntityTempPath.split('.');
  			var currentEntityTempSelfPath = currentEntityTempPathParts[currentEntityTempPathParts.length - 1];
  			var targetEntityTempSelfPath = targetEntityTempPathParts[targetEntityTempPathParts.length - 1];
  			var currentEntityPathParts = currentEntityPath.split('.');
  			var currentEntitySelfPath = currentEntityPathParts[currentEntityPathParts.length - 1];
  			var targetEntityPathParts = targetEntityPath.split('.');
  			var targetEntitySelfPath = targetEntityPathParts[targetEntityPathParts.length - 1];
  			var oldCurrentEntityPath = currentEntityPath;
  			var oldTargetEntityPath = targetEntityPath;
  				
  			try {
  				var updatePath = '';
  				if (currentEntityTempSelfPath.indexOf('[*]') < 0 && currentEntitySelfPath.indexOf('[*]') > -1) {
  					currentEntityPathParts[currentEntityPathParts.length - 1] = currentEntitySelfPath.substring(0, currentEntitySelfPath.length - 3);
  					updatePath = currentEntityPathParts.join('.');
  					this.updateEntityPath(currentEntityId, updatePath);
  					removedCurrentEntityPathPart = true;
  					currentEntitySelfPath = currentEntityPathParts[currentEntityPathParts.length - 1];
  					currentEntityPath = updatePath;
  				}
  				if (targetEntityTempSelfPath.indexOf('[*]') < 0 && targetEntitySelfPath.indexOf('[*]') > -1) {
  					targetEntityPathParts[targetEntityPathParts.length - 1] = targetEntitySelfPath.substring(0, targetEntitySelfPath.length - 3);
  					updatePath = targetEntityPathParts.join('.');
  					this.updateEntityPath(targetEntityId, updatePath);
  					removedTargetEntityPathPart = true;
  					targetEntitySelfPath = targetEntityPathParts[targetEntityPathParts.length - 1];
  					targetEntityPath = updatePath;
  				}
  				
  				if (currentEntityPathParts.length == 1) {
  					entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  					entityGr.addQuery(this.SYS_ID, currentEntityId);
  					entityGr.query();
  					if (entityGr.next()) {
  						var newPath = targetEntityPath + '.' + currentEntityPath;
  						entityGr.setValue(this.PATH, newPath);
  						entityGr.update();
  					}
  					this.orderSecondStageMapping(feedId);
  					return;
  				}

  				var currentEntityPrefixPath = [];
  				for (var i = 0; i < currentEntityPathParts.length - 1; i++) {
  					currentEntityPrefixPath.push(currentEntityPathParts[i]);
  				}
  				currentEntityPrefixPath = currentEntityPrefixPath.join('.');

  				if (currentEntityPrefixPath == targetEntityPath) {
  					this.orderSecondStageMapping(feedId);
  					return;
  				} else if (currentEntityPrefixPath.indexOf(targetEntityPath) > -1) {
  					this.orderSecondStageMapping(feedId);
  					return;
  				} else if (targetEntityPath.indexOf(currentEntityPrefixPath) > -1) {
  					entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  					entityGr.addQuery(this.SYS_ID, currentEntityId);
  					entityGr.query();
  					if (entityGr.next()) {
  						var newPath = targetEntityPath + '.' + currentEntitySelfPath;
  						entityGr.setValue(this.PATH, newPath);
  						entityGr.update();
  					}
  					this.orderSecondStageMapping(feedId);
  					return;
  				}

  				var prefixEntityId = '';
  				var prefixEntityName = '';
  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  				entityGr.addQuery(this.PATH, currentEntityPrefixPath);
  				entityGr.query();
  				if (entityGr.next()) {
  					prefixEntityId = entityGr.getValue(this.SYS_ID);
  					prefixEntityName = entityGr.getValue(this.NAME);
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with path {0}', currentEntityPrefixPath));
  					throw this.getInternalServerError(errorObj);
  				}

  				var prefixEntityTempPath = '';
  				entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  				entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, prefixEntityId);
  				entityMappingGr.query();
  				if (entityMappingGr.next()) {
  					prefixEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', prefixEntityId));
  					throw this.getInternalServerError(errorObj);
  				}

  				var childEntityId = '';
  				var childEntityPath = '';
  				var oldPath = '';
  				var prefixEntityTempPathParts = prefixEntityTempPath.split('.');
  				
  				if (prefixEntityTempPath == targetEntityTempPath) {
  					errorObj = this.buildErrorObject(gs.getMessage('Class \'{0}\' is already associated with class \'{1}\'. Please duplicate or delete class \'{2}\', or delete its all downstream references to reset class.', [currentEntityName, prefixEntityName, currentEntityName]));
  					throw this.getInternalServerError(errorObj);
  				} else if (prefixEntityTempPathParts.length == targetEntityTempPathParts.length) {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot select this class \'{0}\' because mapped source columns are at a sibling level in nested data.', [targetEntityName]), this.NO_INCLUSIVE_REL_EXCEPTION);
  					throw this.getInternalServerError(errorObj);
  				} else if (targetEntityTempPath.indexOf(prefixEntityTempPath) > -1) {
  					childEntityId = targetEntityId;
  					childEntityPath = currentEntityPrefixPath + '.' + targetEntityPath;
  				} else if (prefixEntityTempPath.indexOf(targetEntityTempPath) > -1) {
  					childEntityId = prefixEntityId;
  					childEntityPath = targetEntityPath + '.' + currentEntityPrefixPath;
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot select this class \'{0}\' because mapped source columns are at a sibling level in nested data.', [targetEntityName]), this.NO_INCLUSIVE_REL_EXCEPTION);
  					throw this.getInternalServerError(errorObj);
  				}

  				var nextPrefixPath = currentEntityPrefixPath.substring(0, currentEntityPrefixPath.lastIndexOf('.'));
  				this.validatePrefixEntityTempPath(nextPrefixPath, targetEntityTempPath, targetEntityName, currentEntityName, feedId);
  				nextPrefixPath = targetEntityPath.substring(0, targetEntityPath.lastIndexOf('.'));
  				this.validatePrefixEntityTempPath(nextPrefixPath, prefixEntityTempPath, prefixEntityName, targetEntityName, feedId);

  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_ID, childEntityId);
  				entityGr.query();
  				if (entityGr.next()) {
  					oldPath = entityGr.getValue(this.PATH);
  					entityGr.setValue(this.PATH, childEntityPath);
  					entityGr.update();
  				}
  				this.updateChildEntityPath(childEntityId, oldPath, childEntityPath);

  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_ID, currentEntityId);
  				entityGr.query();
  				if (entityGr.next()) {
  					var newPath = childEntityPath + '.' + currentEntitySelfPath;
  					entityGr.setValue(this.PATH, newPath);
  					entityGr.update();
  				}
  			} catch (error) {
  				if (removedCurrentEntityPathPart) {
  					this.updateEntityPath(currentEntityId, oldCurrentEntityPath);
  				}
  				if (removedTargetEntityPathPart) {
  					this.updateEntityPath(targetEntityId, oldTargetEntityPath);
  				}
  				throw error;
  			}
  		},
  		
  		updateEntityPath: function(entityId, path) {
  			var errorObj = {};
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				entityGr.setValue(this.PATH, path);
  				if (!entityGr.update()) {
  					errorObj = this.buildErrorObject(gs.getMessage('Fail to update entity with entity id {0}', entityId));
  					throw this.getInternalServerError(errorObj);
  				}
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with entity id {0}', entityId));
  				throw this.getInternalServerError(errorObj);
  			}
  		},

  		validatePrefixEntityTempPath: function(prefixPath, targetEntityTempPath, targetEntityName, currentEntityName, feedId) {
  			if (!prefixPath) {
  				return;
  			}
  			var prefixEntityId = '';
  			var prefixEntityName = '';
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityGr.addQuery(this.PATH, prefixPath);
  			entityGr.query();
  			if (entityGr.next()) {
  				prefixEntityId = entityGr.getValue(this.SYS_ID);
  				prefixEntityName = entityGr.getValue(this.NAME);
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with path {0}', prefixPath));
  				throw this.getInternalServerError(errorObj);
  			}

  			var prefixEntityTempPath = '';
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, prefixEntityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				prefixEntityTempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', prefixEntityId));
  				throw this.getInternalServerError(errorObj);
  			}

  			var prefixEntityTempPathParts = prefixEntityTempPath.split('.');
  			var targetEntityTempPathParts = targetEntityTempPath.split('.');
  			if (prefixEntityTempPath == targetEntityTempPath) {
  				errorObj = this.buildErrorObject(gs.getMessage('Class \'{0}\' is already associated with class \'{1}\'. Please duplicate or delete class \'{2}\', or delete its all downstream references to reset class.', [currentEntityName, prefixEntityName, currentEntityName]));
  				throw this.getInternalServerError(errorObj);
  			} else if (prefixEntityTempPathParts.length == targetEntityTempPathParts.length) {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot select this class \'{0}\' because mapped source columns are at a sibling level in nested data.', [targetEntityName]), this.NO_INCLUSIVE_REL_EXCEPTION);
  				throw this.getInternalServerError(errorObj);
  			}

  			var nextPrefixPath = prefixPath.substring(0, prefixPath.lastIndexOf('.'));
  			this.validatePrefixEntityTempPath(nextPrefixPath, targetEntityTempPath, targetEntityName, currentEntityName, feedId);
  		},

  		updateChildEntityPath: function(parentEntityId, oldPath, newPath) {
  			var feedId = '';
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, parentEntityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				feedId = entityGr.getValue(this.SYS_RTE_EB_DEFINITION);
  			}

  			var targetEntityIds = {};
  			var impEntityId = this.findImpCmdbEntity(feedId).sys_id + '';
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, '!=', impEntityId);
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var targetEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  				targetEntityIds[targetEntityId] = true;
  			}
  			targetEntityIds = Object.keys(targetEntityIds);

  			entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, targetEntityIds);
  			entityGr.query();
  			while (entityGr.next()) {
  				var currentPath = entityGr.getValue(this.PATH);
  				var currentEntityId = entityGr.getValue(this.SYS_ID);
  				if (currentEntityId != parentEntityId && currentPath.indexOf(oldPath) > -1) {
  					var updatePath = currentPath.replace(oldPath, newPath);
  					if (updatePath != currentPath) {
  						entityGr.setValue(this.PATH, updatePath);
  						entityGr.update();
  						var updatePathParts = updatePath.split('.');
  						if (updatePathParts.length == 1 && updatePath.indexOf('[*]') < 0) {
  							var entityMappingGr2 = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  							entityMappingGr2.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, currentEntityId);
  							entityMappingGr2.query();
  							if (entityMappingGr2.next()) {
  								var currentEntityTempPath = entityMappingGr2[this.SOURCE_SYS_RTE_EB_ENTITY][this.PATH] + '';
  								var currentEntityTempPathParts = currentEntityTempPath.split('.');
  								var currentEntityTempSelfPath = currentEntityTempPathParts[currentEntityTempPathParts.length - 1];
  								if (currentEntityTempSelfPath.indexOf('[*]') < 0) {
  									this.updateEntityPath(currentEntityId, updatePath + '[*]');
  								}
  							}
  						}
  					}
  				}
  			}

  			this.orderSecondStageMapping(feedId);
  		},

  		orderSecondStageMapping: function(feedId) {
  			if (!feedId) {
  				return;
  			}

  			var errorObj = {};
  			var tempMappingMaxOrder = '';
  			var impEntityId = this.findImpCmdbEntity(feedId).sys_id + '';
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, impEntityId);
  			entityMappingGr.orderByDesc(this.ORDER);
  			entityMappingGr.setLimit(1);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				tempMappingMaxOrder = Number(entityMappingGr.getValue(this.ORDER));
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with import entity id {0}', impEntityId));
  				throw this.getInternalServerError(errorObj);
  			}
  			var currentOrder = tempMappingMaxOrder + 10;

  			var pathToOrderMap = {};
  			entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, '!=', impEntityId);
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var targetEntityPath = entityMappingGr.target_sys_rte_eb_entity.path + '';
  				pathToOrderMap[targetEntityPath] = true;
  			}

  			var paths = Object.keys(pathToOrderMap);
  			paths.sort(function(a, b) {
  				var lengthDiff = a.split('.').length - b.split('.').length;
  				if (lengthDiff != 0) {
  					return lengthDiff;
  				}
  				return (a > b) ? 1 : -1;;
  			});
  			for (var i = 0; i < paths.length; i++) {
  				var path = paths[i];
  				pathToOrderMap[path] = currentOrder;
  				currentOrder = currentOrder + 10;
  			}

  			entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_ENTITY, '!=', impEntityId);
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var targetEntityPath = entityMappingGr.target_sys_rte_eb_entity.path + '';
  				var targetEntityOrder = Number(entityMappingGr.getValue(this.ORDER));
  				var updateOrder = pathToOrderMap[targetEntityPath];
  				if (updateOrder != targetEntityOrder) {
  					entityMappingGr.setValue(this.ORDER, updateOrder);
  					entityMappingGr.update();
  				}
  			}
  		},

  		isNestedPayload: function(feedId) {
  			var isNestedPayload = false;
  			var feedGr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  			if (feedGr.get(feedId)) {
  				var dataInSingleColumn = feedGr.sys_data_source.data_in_single_column.toString();
  				dataInSingleColumn = dataInSingleColumn === 'true' ? true : false;
  				isNestedPayload = dataInSingleColumn;
  			}
  			return isNestedPayload;
  		},

  		checkIsDifferentDataBranch: function(firstEntityId, secondEntityId, firstEntityTempPath, secondEntityTempPath) {
  			var isDifferentBranch = false;
  			if (firstEntityTempPath != secondEntityTempPath) {
  				if (firstEntityTempPath.split('.').length == secondEntityTempPath.split('.').length) {
  					isDifferentBranch = true;
  				} else if (firstEntityTempPath.indexOf(secondEntityTempPath) < 0 && secondEntityTempPath.indexOf(firstEntityTempPath) < 0) {
  					isDifferentBranch = true;
  				}
  				if (isDifferentBranch) {
  					if (secondEntityId == this.LOOKDOWN_CLASS) {
  						var allowedTempPath = '';
  						var allowedTempPathParts = firstEntityTempPath.split('.');
  						allowedTempPathParts[0] = this.NESTED_PAYLOAD_TOP_NODE_NAME;
  						allowedTempPath = allowedTempPathParts.join('.');
  						allowedTempPath = allowedTempPath.replace(/\[\*\]/g, '');
  						errorObj = this.buildErrorObject(
  							gs.getMessage(
  								'Columns mapped to lookup must be within the same data branch as the class currently being mapped, \'{0}\'. Selected column is at a sibling level in nested data.', 
  								[allowedTempPath]
  							)
  						);
  						throw this.getInternalServerError(errorObj);
  					}
  					var firstEntityName = '';
  					var secondEntityName = '';
  					var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  					entityGr.addQuery(this.SYS_ID, firstEntityId);
  					entityGr.query();
  					if (entityGr.next()) {
  						firstEntityName = entityGr.getValue(this.NAME);
  					}
  					entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  					entityGr.addQuery(this.SYS_ID, secondEntityId);
  					entityGr.query();
  					if (entityGr.next()) {
  						secondEntityName = entityGr.getValue(this.NAME);
  					}
  					if (!firstEntityName) {
  						firstEntityName = firstEntityId;
  					}
  					if (!secondEntityName) {
  						secondEntityName = secondEntityId;
  					}
  					errorObj = this.buildErrorObject(gs.getMessage('Cannot select this class \'{0}\' because mapped source columns are at a sibling level in nested data.', [secondEntityName]), this.NO_INCLUSIVE_REL_EXCEPTION);
  					throw this.getInternalServerError(errorObj);
  				}
  			}
  		},

  		determineAndUpdateEntityPathRelation: function(currentEntityId, targetEntityId, currentEntityTempPath, targetEntityTempPath) {
  			if (currentEntityTempPath != targetEntityTempPath) {
  				var parentEntityId = '';
  				var childEntityId = '';
  				if (currentEntityTempPath.indexOf(targetEntityTempPath) > -1) {
  					childEntityId = currentEntityId;
  					parentEntityId = targetEntityId;
  				} else if (targetEntityTempPath.indexOf(currentEntityTempPath) > -1) {
  					childEntityId = targetEntityId;
  					parentEntityId = currentEntityId;
  				}
  				this.updateEntityPathRelation(childEntityId, parentEntityId);
  			} else {
  				var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_ID, currentEntityId);
  				entityGr.query();
  				if (entityGr.next()) {
  					var tableName = entityGr.getValue(this.TABLE);
  					var relatedGr = new GlideRecord('cmdb_related_entry');
  					relatedGr.addQuery(this.TABLE, tableName);
  					relatedGr.query();
  					if (!relatedGr.next()) {
  						return;
  					}
  				}

  				this.updateEntityPathRelation(currentEntityId, targetEntityId);

  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_ID, currentEntityId);
  				entityGr.query();
  				if (entityGr.next()) {
  					var currentEntityPath = entityGr.getValue(this.PATH);
  					var currentEntityPathParts = currentEntityPath.split('.');
  					if (currentEntityPathParts.length >= 2) {
  						var selfPath = currentEntityPathParts[currentEntityPathParts.length - 1];
  						selfPath = selfPath.replace('[*]', '');
  						currentEntityPathParts[currentEntityPathParts.length - 1] = selfPath;
  						var newPath = currentEntityPathParts.join('.');
  						entityGr.setValue(this.PATH, newPath);
  						entityGr.update();
  					}
  				}
  			}
  		},

  		isReferencedByOtherEntity: function(entityId, throwError, errorMessagePrefix) {
  			var errorObj = {};
  			var additionalMessagePrefix = '';
  			if (errorMessagePrefix) {
  				additionalMessagePrefix = errorMessagePrefix + ' ';
  			}

  			var entityName = '';
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				entityName = entityGr.getValue(this.NAME);
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with id {0}', entityId));
  				throw this.getInternalServerError(errorObj);
  			}

  			entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.LOOKUP_FOR_ENTITY, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				if (throwError) {
  					var lookdownName = entityGr.getValue(this.NAME);
  					errorObj = this.buildErrorObject(gs.getMessage('{0}Class \'{2}\' class is a lookup class for the \'{1}\' class .', [additionalMessagePrefix, entityName, lookdownName]));
  					throw this.getInternalServerError(errorObj);
  				}
  				return true;
  			}

  			entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.RELATED_FOR_ENTITY, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				if (throwError) {
  					var associateName = entityGr.getValue(this.NAME);
  					errorObj = this.buildErrorObject(gs.getMessage('{0}Class \'{1}\' is associated with class \'{2}\'.', [additionalMessagePrefix, entityName, associateName]));
  					throw this.getInternalServerError(errorObj);
  				}
  				return true;
  			}

  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, entityId);
  			fieldMappingGr.query();
  			if (fieldMappingGr.next()) {
  				var table = fieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table + '';
  				if (table == this.CMDB_REL_CI) {
  					if (throwError) {
  						var relClassName = '';
  						var relEntityMappingId = fieldMappingGr.sys_rte_eb_entity_mapping;
  						var fieldMappingGr2 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  						fieldMappingGr2.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, relEntityMappingId);
  						fieldMappingGr2.query();
  						while (fieldMappingGr2.next()) {
  							var refId = fieldMappingGr2.getValue(this.REFERENCED_SYS_RTE_EB_ENTITY);
  							if (refId != entityId) {
  								relClassName = fieldMappingGr2[this.REFERENCED_SYS_RTE_EB_ENTITY].name + '';
  								break;
  							}
  						}
  						
  						errorObj = this.buildErrorObject(gs.getMessage('Cannot add class \'{1}\' as Reference Source to the \'{0}\' class because \'{0}\' class has a relationship with the \'{1}\' class.', [entityName, relClassName]));
  						throw this.getInternalServerError(errorObj);
  					}
  				} else {
  					if (throwError) {
  						var targetEntityName = fieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.name + '';
  						errorObj = this.buildErrorObject(gs.getMessage('{0}Class \'{1}\' is referenced by class \'{2}\'.', [additionalMessagePrefix, entityName, targetEntityName]));
  						throw this.getInternalServerError(errorObj);
  					}
  				}
  				return true;
  			}

  			return false;
  		},

  		hasReferenceEntity: function(entityId, throwError, excludeFieldMappingId, excludeAssociatedEntityId) {
  			var entityName = '';
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				entityName = entityGr.getValue(this.NAME);
  				var lookupEntityName = entityGr[this.LOOKUP_FOR_ENTITY].name + '';
  				if (lookupEntityName) {
  					if (throwError) {
  						errorObj = this.buildErrorObject(gs.getMessage('Class \'{0}\' has lookup class \'{1}\'.', [entityName, lookupEntityName]));
  						throw this.getInternalServerError(errorObj);
  					}
  					return true;
  				}
  				var associateEntityName = entityGr[this.RELATED_FOR_ENTITY].name + '';
  				var associateEntityId = entityGr[this.RELATED_FOR_ENTITY] + '';
  				if (associateEntityName && associateEntityId && associateEntityId != excludeAssociatedEntityId) {
  					if (throwError) {
  						errorObj = this.buildErrorObject(gs.getMessage('Class \'{0}\' is associated with class \'{1}\'.', [entityName, associateEntityName]));
  						throw this.getInternalServerError(errorObj);
  					}
  					return true;
  				}
  			}

  			var entityMappingId = '';
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				entityMappingId = entityMappingGr.getValue(this.SYS_ID);
  			}

  			fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, entityMappingId);
  			fieldMappingGr.addNotNullQuery(this.REFERENCED_SYS_RTE_EB_ENTITY);
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				var fieldMappingId = fieldMappingGr.getValue(this.SYS_ID);
  				if (fieldMappingId == excludeFieldMappingId) {
  					continue;
  				}
  				var referenceEntityName = fieldMappingGr[this.REFERENCED_SYS_RTE_EB_ENTITY].name + '';
  				if (throwError) {
  					errorObj = this.buildErrorObject(gs.getMessage('Class \'{0}\' references class \'{1}\'.', [entityName, referenceEntityName]));
  					throw this.getInternalServerError(errorObj);
  				}
  				return true;
  			}

  			return false;
  		},

  		resetEntityPath: function(entityId) {
  			var errorObj = {};
  			var isTempCollection = false;
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				var tempPath = entityMappingGr.source_sys_rte_eb_entity.path + '';
  				var tempPathParts = tempPath.split('.');
  				var tempSelfPath = tempPathParts[tempPathParts.length - 1];
  				if (tempSelfPath.indexOf('[*]') >= 0) {
  					isTempCollection = true;
  				}
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with entity id {0}', entityId));
  				throw this.getInternalServerError(errorObj);
  			}

  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				var entityPath = entityGr.getValue(this.PATH);
  				var entityPathParts = entityPath.split('.');
  				var entitySelfPath = entityPathParts[entityPathParts.length - 1];
  				entitySelfPath = entitySelfPath.replace('[*]', '');
  				if (isTempCollection || (!isTempCollection && tempPathParts.length > 1)) {
  					entitySelfPath = entitySelfPath + '[*]';
  				}
  				entityGr.setValue(this.PATH, entitySelfPath);
  				entityGr.update();
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity id {0}', entityId));
  				throw this.getInternalServerError(errorObj);
  			}
  		},
  		
  		getPreviewSize: function(templateStateGr) {
  			var maxRows = this.DEFAULT_PREVIEW_SIZE;
  			var previewSize;
  			if(templateStateGr){
  				var appPreviewSize = Number(templateStateGr.getValue(this.PREVIEW_SIZE_OVERRIDE));
  				if(appPreviewSize)
  					maxRows = appPreviewSize;
  				else{
  					previewSize = Number(gs.getProperty(this.PREVIEW_SIZE_PROPERTY_NAME));
  					if (previewSize) {
                      maxRows = previewSize;
  					}
  				}
  			}else{
  				previewSize = Number(gs.getProperty(this.PREVIEW_SIZE_PROPERTY_NAME));	
  				if (previewSize) {
  					maxRows = previewSize;
  				}
  			}
  			var hasPreviewSizeWarning = false;
  			if (maxRows > this.MAX_PREVIEW_SIZE) {
  				maxRows = this.MAX_PREVIEW_SIZE;
  			}
  			return maxRows;
  		},
  		
  		hasPreviewSizeWarning: function(importSetId, previewSize) {
  			if (previewSize >= this.MAX_PREVIEW_SIZE) {
  				var importSetRowCount = this.getImportSetRowCount(importSetId);
  				if (importSetRowCount >= this.MAX_PREVIEW_SIZE) {
  					return true;
  				}
  			}
  			return false;
  		},
  		
  		getImportSetRowCount: function(importSetId) {
  			var ga = new GlideAggregate('sys_import_set_row');
  			ga.addQuery('sys_import_set', importSetId);
  			ga.addAggregate('COUNT');
  			ga.query();
  			if (ga.next()) {
  				return Number(ga.getAggregate('COUNT'));
  			}
  			return null;
  		},

  		buildUACustomPayload: function(action, feedId, stepNumber, recordValue, recordId) {
  			var feedName = '';
  			if (feedId) {
  				var feedGr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  				feedGr.addQuery(this.SYS_ID, feedId);
  				feedGr.query();
  				if (feedGr.next()) {
  					feedName = feedGr.getValue(this.NAME);
  				} else {
  					errorObj = this.buildErrorObject(gs.getMessage('UA Error - Cannot find feed with ID \'{0}\'', [feedId]));
  					throw this.getInternalServerError(errorObj);
  				}
  			}
  			
  			var uaPayload = new GCFSampleMap();
  			uaPayload.put(this.ACTION, action);
  			uaPayload.put(this.FEED_NAME, feedName);
  			uaPayload.put(this.STEP_NUMBER, stepNumber);
  			uaPayload.put(this.RECORD_VALUE, recordValue);
  			uaPayload.put(this.RECORD_ID, recordId);
  			return uaPayload;
  		},

  		setIgnoreForGivenEntityMapping: function(entityMappingSysId, activate) {
  			var errorObj = {};
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_ID, entityMappingSysId);
  			entityMappingGr.query();

  			if (entityMappingGr.next()) {
  				entityMappingGr.setValue(this.IGNORE, !activate);
  				if (!entityMappingGr.update()) {
  					errorObj = this.buildErrorObject(gs.getMessage("Failed to update entity mapping record '{0}'", [entityMappingSysId]));
  					throw this.getInternalServerError(errorObj);
  				}
  			}
  		},
  		
  		findRelEntityMappingsAndSetIgnore: function(targetEntitySysId, toActivate) {
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, targetEntitySysId);
  			fieldMappingGr.query();

  			while (fieldMappingGr.next()) {
  				var table = fieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table + '';

  				if (table == this.CMDB_REL_CI) {
  					var relEntityMappingId = fieldMappingGr.sys_rte_eb_entity_mapping;
  					var fieldMappingGr1 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  					fieldMappingGr1.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, relEntityMappingId);
  					fieldMappingGr1.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, '!=', targetEntitySysId);
  					fieldMappingGr1.query();
  					
  					if (fieldMappingGr1.next()) {
  						var otherRefEntityId = fieldMappingGr1.referenced_sys_rte_eb_entity;
  						var otherRefEntityMapping = this.getEntityMappingByEntityId(otherRefEntityId);
  						
  						var otherEntityIgnored = otherRefEntityMapping.ignore;
  						if (otherEntityIgnored && toActivate)
  							continue;
  						
  						var relEntityMappingSysId = fieldMappingGr.sys_rte_eb_entity_mapping;
  						this.setIgnoreForGivenEntityMapping(relEntityMappingSysId, toActivate);
  					}
  				}
  			}
  		},

  		hasPreviewSizeWarning: function(importSetId, previewSize) {
  			if (previewSize >= this.MAX_PREVIEW_SIZE) {
  				var importSetRowCount = this.getImportSetRowCount(importSetId);
  				if (importSetRowCount >= this.MAX_PREVIEW_SIZE) {
  					return true;
  				}
  			}
  			return false;
  		},

  		getImportSetRowCount: function(importSetId) {
  			var ga = new GlideAggregate('sys_import_set_row');
  			ga.addQuery('sys_import_set', importSetId);
  			ga.addAggregate('COUNT');
  			ga.query();
  			if (ga.next()) {
  				return Number(ga.getAggregate('COUNT'));
  			}
  			return null;
  		},

  		getEntitiesAffectedByToggle: function(feedId, toggleEntities, dependentRelMap, tableDependencyMap, ignoreCurrentActiveState) {
  			var affectedEntities = {};
  			var parentToChildrenMap = {};
  			var childToParentsMap = {};

  			var relEntityIdToTypeMap = {};
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityGr.addQuery(this.TABLE, this.CMDB_REL_CI);
  			entityGr.query();
  			while (entityGr.next()) {
  				relEntityIdToTypeMap[entityGr.getValue(this.SYS_ID)] = entityGr.getValue(this.RELATIONSHIP_TYPE);
  			}

  			var relEntityIds = Object.keys(relEntityIdToTypeMap);
  			var relEntityMappingIdToType = [];
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, relEntityIds);
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var relEntityId = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  				var relEntityType = relEntityIdToTypeMap[relEntityId];
  				relEntityMappingIdToType[entityMappingGr.getValue(this.SYS_ID)] = relEntityType;
  			}

  			var relEntityMappingIds = Object.keys(relEntityMappingIdToType);
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, relEntityMappingIds);
  			fieldMappingGr.orderBy(this.SYS_RTE_EB_ENTITY_MAPPING);
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				var currentRel = {};
  				var role = fieldMappingGr.target_sys_rte_eb_field.field + '';
  				var roleEntityId = fieldMappingGr.getValue(this.REFERENCED_SYS_RTE_EB_ENTITY);
  				var roleEntityTable = fieldMappingGr[this.REFERENCED_SYS_RTE_EB_ENTITY].table + '';
  				currentRel[role] = {
  					'id': roleEntityId,
  					'table': roleEntityTable
  				};
  				// Relationship field mappings always come with a pair. One for parent reference and one for child reference.
  				if (fieldMappingGr.next()) {
  					role = fieldMappingGr.target_sys_rte_eb_field.field + '';
  					roleEntityId = fieldMappingGr.getValue(this.REFERENCED_SYS_RTE_EB_ENTITY);
  					roleEntityTable = fieldMappingGr[this.REFERENCED_SYS_RTE_EB_ENTITY].table + '';
  					currentRel[role] = {
  						'id': roleEntityId,
  						'table': roleEntityTable
  					};
  				} else {
  					throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("Cannot find relationship reference")));
  				}

  				var relEntityMappingId = fieldMappingGr.getValue(this.SYS_RTE_EB_ENTITY_MAPPING);
  				var relTypeId = relEntityMappingIdToType[relEntityMappingId];
  				var parentEntityId = currentRel[this.PARENT].id;
  				var parentEntityTable = currentRel[this.PARENT].table;
  				var childEntityId = currentRel[this.CHILD].id;
  				var childEntityTable = currentRel[this.CHILD].table;
  				var relDependencyHash = parentEntityTable + childEntityTable + relTypeId;
  				var isDependentRel = dependentRelMap[relDependencyHash];
  				if (!isDependentRel) {
  					continue;
  				}

  				var tableHierarchyMap = {};
  				var actualParentEntityId = parentEntityId;
  				var actualChildEntityId = childEntityId;
  				if (!tableDependencyMap[childEntityTable] || tableDependencyMap[childEntityTable].length == 0) {
  					actualParentEntityId = childEntityId;
  					actualChildEntityId = parentEntityId;
  				} else {
  					var dependencies = tableDependencyMap[childEntityTable];
  					var foundDependentParent = false;
  					for (var i = 0; i < dependencies.length; i++) {
  						var dependentTable = dependencies[i];
  						var dependentTableHierarchy = tableHierarchyMap[dependentTable];
  						if (!dependentTableHierarchy) {
  							dependentTableHierarchy = new GlideTableHierarchy(dependentTable).getHierarchy();
  							tableHierarchyMap[dependentTable] = dependentTableHierarchy;
  						}
  						if (dependentTableHierarchy.indexOf(parentEntityTable) >= 0) {
  							foundDependentParent = true;
  							break;
  						}
  					}
  					if (!foundDependentParent) {
  						actualParentEntityId = childEntityId;
  						actualChildEntityId = parentEntityId;
  					}
  				}

  				if (!parentToChildrenMap[actualParentEntityId]) {
  					parentToChildrenMap[actualParentEntityId] = {};
  				}
  				if (!childToParentsMap[actualChildEntityId]) {
  					childToParentsMap[actualChildEntityId] = {};
  				}
  				parentToChildrenMap[actualParentEntityId][actualChildEntityId] = true;
  				childToParentsMap[actualChildEntityId][actualParentEntityId] = true;
  			}

  			var deactivateEntityState = {};
  			for (var i = 0; i < toggleEntities.length; i++) {
  				deactivateEntityState[toggleEntities[i].id] = toggleEntities[i].toActivate ? false : true;
  			}
  			
  			toggleEntities.sort(function(entityA, entityB) {
  				if (entityA.toActivate && !entityB.toActivate) {
  					return -1;
  				} else if (!entityA.toActivate && entityB.toActivate) {
  					return 1;
  				} else {
  					return 0;
  				}
  			});

  			var entityIgnoreMap = {};
  			for (var i = 0; i < toggleEntities.length; i++) {
  				var toCheckEntityId = toggleEntities[i].id;
  				var toActivate = toggleEntities[i].toActivate;
  				this.checkEntityAffectedByToggle(toCheckEntityId, deactivateEntityState, affectedEntities, parentToChildrenMap, childToParentsMap, toActivate, entityIgnoreMap, ignoreCurrentActiveState);

  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.RELATED_FOR_ENTITY, toCheckEntityId);
  				entityGr.query();
  				while (entityGr.next()) {
  					var associatedEntityId = entityGr.getValue(this.SYS_ID);
  					if (!ignoreCurrentActiveState && toActivate !== this.isClassIgnored(associatedEntityId)) {
  						continue;
  					}
  					
  					var associatedEntityName = entityGr.getValue(this.NAME);
  					var associatedEntityMappingGr = this.getEntityMappingByEntityId(associatedEntityId);
  					var associatedEntityMappingId = associatedEntityMappingGr.getValue(this.SYS_ID);

  					affectedEntities[associatedEntityId] = {
  						'entityName': associatedEntityName,
  						'entityMappingId': associatedEntityMappingId,
  						'isLookdownEntity': false,
  						'toActivate': toActivate
  					};
  				}
  			}

  			return affectedEntities;
  		},

  		checkEntityAffectedByToggle: function(toggleEntityId, deactivateEntityState, affectedEntities, parentToChildrenMap, childToParentsMap, toActivate, entityIgnoreMap, ignoreCurrentActiveState) {
  			if (affectedEntities[toggleEntityId]) {
  				return;
  			}

  			var dependentChildrenEntityIds = [];
  			if (parentToChildrenMap[toggleEntityId]) {
  				dependentChildrenEntityIds = Object.keys(parentToChildrenMap[toggleEntityId]);
  			}
  			for (var i = 0; i < dependentChildrenEntityIds.length; i++) {
  				var dependentChildEntityId = dependentChildrenEntityIds[i];
  				if (!ignoreCurrentActiveState && toActivate !== this.isClassIgnored(dependentChildEntityId)) {
  					deactivateEntityState[dependentChildEntityId] = toActivate ? false : true;
  					this.checkEntityAffectedByToggle(dependentChildEntityId, deactivateEntityState, affectedEntities, parentToChildrenMap, childToParentsMap, toActivate, entityIgnoreMap, ignoreCurrentActiveState);
  					continue;
  				}
  				
  				var parentEntityIds = [];
  				if (childToParentsMap[dependentChildEntityId]) {
  					parentEntityIds = Object.keys(childToParentsMap[dependentChildEntityId]);
  				}

  				var hasBackupDependentRel = false;
  				if (!toActivate) {
  					for (var j = 0; j < parentEntityIds.length; j++) {
  						var parentEntityId = parentEntityIds[j];
  						var isClassIgnored = entityIgnoreMap[parentEntityId];
  						if (typeof isClassIgnored != 'boolean') {
  							isClassIgnored = this.isClassIgnored(parentEntityId);
  							entityIgnoreMap[parentEntityId] = isClassIgnored;
  						}
  						if (deactivateEntityState[parentEntityId] === false || (!deactivateEntityState[parentEntityId] && !isClassIgnored)) {
  							hasBackupDependentRel = true;
  							break;
  						}
  					}
  				}

  				var isLookdownEntity = false;
  				var hasValidLookupEntity = false;
  				var dependentChildEntityName = '';
  				entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  				entityGr.addQuery(this.SYS_ID, dependentChildEntityId);
  				entityGr.query();
  				if (entityGr.next()) {
  					dependentChildEntityName = entityGr.getValue(this.NAME);
  					var lookupEntityId = entityGr.getValue(this.LOOKUP_FOR_ENTITY);
  					if (lookupEntityId) {
  						isLookdownEntity = true;
  						if (!toActivate && !deactivateEntityState[lookupEntityId]) {
  							var isClassIgnored = entityIgnoreMap[lookupEntityId];
  							if (typeof isClassIgnored != 'boolean') {
  								isClassIgnored = this.isClassIgnored(lookupEntityId);
  								entityIgnoreMap[lookupEntityId] = isClassIgnored;
  							}
  							if (deactivateEntityState[lookupEntityId] === false || (!deactivateEntityState[lookupEntityId] && !isClassIgnored)) {
  								hasValidLookupEntity = true;
  							}
  						}
  					}
  				}

  				if (toActivate || (!hasBackupDependentRel && !hasValidLookupEntity)) {
  					deactivateEntityState[dependentChildEntityId] = toActivate ? false : true;
  					this.checkEntityAffectedByToggle(dependentChildEntityId, deactivateEntityState, affectedEntities, parentToChildrenMap, childToParentsMap, toActivate, entityIgnoreMap, ignoreCurrentActiveState);

  					var dependentChildEntityMappingGr = this.getEntityMappingByEntityId(dependentChildEntityId);
  					var dependentChildEntityMappingId = dependentChildEntityMappingGr.getValue(this.SYS_ID);

  					affectedEntities[dependentChildEntityId] = {
  						'entityName': dependentChildEntityName,
  						'entityMappingId': dependentChildEntityMappingId,
  						'isLookdownEntity': isLookdownEntity,
  						'toActivate': toActivate
  					};
  				}
  			}
  		},

  		isClassIgnored: function(entityId) {
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				var ignore = entityMappingGr.getDisplayValue(this.IGNORE);
  				return this.parseBooleanString(ignore);
  			} else {
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("Cannot find entity mapping with entity ID '{0}'", entityId)));
  			}
  		},

  		getEntityMappingByEntityId: function(entityId) {
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();
  			if (entityMappingGr.next()) {
  				return entityMappingGr;
  			} else {
  				throw this.getInternalServerError(this.buildErrorObject(gs.getMessage("Cannot find entity mapping with entity id '{0}'", entityId)));
  			}
  		},
  					
  		validateIntStudioPluginIsActive: function() {
  			var gpm = new GlidePluginManager();
  			var isActive = gpm.isActive(this.INTEGRATION_STUDIO_API_PLUGIN);
  			if (!isActive) {
  				var errMessage = gs.getMessage('Integration Studio API plugin is not activated. Activate the plugin or contact your CMDB Admin for help.');
  				var errDetail = gs.getMessage('Integration Studio API plugin is required for IntegrationHub ETL.');
  				throw this.getInternalServerError(this.buildErrorObject(errMessage, errDetail));
  			}
  			return true;
  		},
  		
  		validateIfFileAttachedToDatasource: function(dataSourceId) {
  			var gr = new GlideRecord(this.SYS_DATA_SOURCE);
  			gr.addQuery('sys_id', dataSourceId);
  			gr.query();
  			
  			if (gr.next()) {
  				var dsType = gr.getValue('type');
  				var fileRetrievalMethod = gr.getValue('file_retrieval_method');
  				if (
  					dsType && dsType.toLowerCase() === 'file'
  					&& fileRetrievalMethod && fileRetrievalMethod.toLowerCase() === 'attachment'
  				) {
  					var attachmentGA = new GlideAggregate('sys_attachment');
  					attachmentGA.addQuery('table_sys_id', dataSourceId);
  					attachmentGA.addAggregate('COUNT');
  					attachmentGA.query();
  					if (attachmentGA.next()) {
  						var count = Number(attachmentGA.getAggregate('COUNT'));
  						if (count === 0) {
  							var errMessage = gs.getMessage('Data Source does not have any file attached. Verify Data Source configuration is correct or contact your CMDB Admin for help.');
  							var errDetail = gs.getMessage('Data Source of type File and file retrieval method of attachment must have at least one file attached to it.');
  							throw this.getInternalServerError(this.buildErrorObject(errMessage, errDetail));
  						}
  					}
  				}
  			} else {
  				errMessage = gs.getMessage('Invalid Data Source. Verify Data Source configuration is correct or contact your CMDB Admin for help.');
  				errDetail = gs.getMessage('Invalid Data Source. Verify Data Source configuration is correct or contact your CMDB Admin for help.');
  				throw this.getInternalServerError(this.buildErrorObject(errMessage, errDetail));
  			}
  			return true;
  		},

  		handleSchemaLoadError: function(err) {
  			var errorObj;
  			if (err.toString().indexOf('IllegalArgumentException') > -1) {
  				var errorMessage = err.getMessage();
  				errorObj = this.buildErrorObject(gs.getMessage('{0}. Verify Data Source configuration is correct or contact your CMDB Admin for help.', errorMessage), err.toString());
  				throw this.getInternalServerError(errorObj);
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Unable to retrieve schema from import set possibly due to invalid field names. Field names must start with a letter (between A-Z or a-z) or with \'_\', and must only contain letters (between A-Z or a-z), digits (0-9), or  \'_\'. Verify Data Source configuration is correct or contact your CMDB Admin for help.'), err.toString());
  				throw this.getInternalServerError(errorObj);
  			}
  		},
  		
  		getEntityMappingWithEmptySourceOrTargetEntity: function(feedId, result, doDelete) {
  			// Invalid records
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addEncodedQuery('source_sys_rte_eb_entityISEMPTY^ORtarget_sys_rte_eb_entityISEMPTY');
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var id = entityMappingGr.getValue(this.SYS_ID);
  				var mappingName = entityMappingGr.getValue(this.NAME);
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Entity mapping \'{0}\' can be deleted because the source or the target entity is missing.', mappingName),
  						recordTable: this.SYS_RTE_EB_ENTITY_MAPPING,
  						recordId: id
  					});
  				} else if (entityMappingGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Entity mapping \'{0}\' has been internally deleted because the source or the target entity is missing.', mappingName)
  					});
  				}
  			}
  		},
  		
  		getFieldMappingWithEmptyTargetField: function(feedId, result, doDelete) {
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldMappingGr.addEncodedQuery('target_sys_rte_eb_fieldISEMPTY');
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				var id = fieldMappingGr.getValue(this.SYS_ID);
  				var sourceFieldName = fieldMappingGr.source_sys_rte_eb_field.name + '';
  				var entityMappingName = fieldMappingGr.sys_rte_eb_entity_mapping.name + '';
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' in the class mapping of \'{1}\' can be deleted because the target field is missing.', [sourceFieldName, entityMappingName]),
  						recordTable: this.SYS_RTE_EB_FIELD_MAPPING,
  						recordId: id
  					});
  				} else if (fieldMappingGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' in the class mapping of \'{1}\' has been deleted because the target field is missing.', [sourceFieldName, entityMappingName])
  					});
  				}
  			}
  		},
  		
  		getFieldMappingWithEmptyEntityMapping: function(feedId, result, doDelete) {
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldMappingGr.addEncodedQuery('sys_rte_eb_entity_mappingISEMPTY');
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				id = fieldMappingGr.getValue(this.SYS_ID);
  				sourceFieldName = fieldMappingGr.source_sys_rte_eb_field.name + '';
  				var targetFieldName = fieldMappingGr.target_sys_rte_eb_field.name + '';
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' to target field \'{1}\' can be deleted because the class mapping record is missing.', [sourceFieldName, targetFieldName]),
  						recordTable: this.SYS_RTE_EB_FIELD_MAPPING,
  						recordId: id
  					});
  				} else if (fieldMappingGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' to target field \'{1}\' has been deleted because the class mapping record is missing.', [sourceFieldName, targetFieldName])
  					});
  				}
  			}
  		},
  		
  		getLookupEntityAndRelationWithoutIRERule: function(feedId, rules, result, doDelete) {
  			var lookupTableMap = {};
  			var cmdbTables = Object.keys(rules);
  			for (var i = 0; i < cmdbTables.length; i++) {
  				var cmdbTable = cmdbTables[i];
  				if (!lookupTableMap[cmdbTable]) {
  					lookupTableMap[cmdbTable] = {};
  				}
  				var classRules = rules[cmdbTable].rules;
  				for (var j = 0; j < classRules.length; j++) {
  					var rule = classRules[j];
  					if (rule.lookupTable) {
  						lookupTableMap[cmdbTable][rule.lookupTable] = true;
  					}
  				}
  			}

  			var lookupEntityToDelete = {};
  			var relEntity = [];
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityGr.query();
  			while (entityGr.next()) {
  				var entityId = entityGr.getValue(this.SYS_ID);
  				var entityName = entityGr.getValue(this.NAME);
  				var table = entityGr.getValue(this.TABLE);
  				if (table == this.CMDB_REL_CI) {
  					relEntity.push(entityId);
  					continue;
  				}
  				var lookupTable = entityGr.lookup_for_entity.table + '';
  				if (lookupTable && lookupTableMap[lookupTable] && !lookupTableMap[lookupTable][table]) {
  					if (!doDelete) {
  						lookupEntityToDelete[entityId] = gs.getMessage('Class \'{0}\' can be deleted because it is a lookup item and there is no corresponding IRE lookup rule.', entityName);
  					} else {
  						lookupEntityToDelete[entityId] = gs.getMessage('Class \'{0}\' has been deleted because it is a lookup item and there is no corresponding IRE lookup rule.', entityName);
  					}
  				} else if (lookupTable) { // Lookup entities without field mappings
  					var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  					fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  					fieldMappingGr.addQuery('sys_rte_eb_entity_mapping.target_sys_rte_eb_entity', entityId);
  					fieldMappingGr.query();
  					if (!fieldMappingGr.next() && !(entityId in lookupEntityToDelete)) {
  						if (!doDelete) {
  							lookupEntityToDelete[entityId] = gs.getMessage('Class \'{0}\' can be deleted because it is a lookup item which has no corresponding field mappings.', entityName);
  						} else {
  							lookupEntityToDelete[entityId] = gs.getMessage('Class \'{0}\' has been deleted because it is a lookup item which has no corresponding field mappings.', entityName);
  						}
  					}
  				}
  			}

  			var relMappingToEntity = {};
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, relEntity);
  			entityMappingGr.query();
  			while (entityMappingGr.next()) {
  				var mappingId = entityMappingGr.getValue(this.SYS_ID);
  				relMappingToEntity[mappingId] = entityMappingGr.getValue(this.TARGET_SYS_RTE_EB_ENTITY);
  			}

  			var relEntityToDelete = {};
  			fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, Object.keys(relMappingToEntity));
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				var referenceEntityId = fieldMappingGr.getValue(this.REFERENCED_SYS_RTE_EB_ENTITY);
  				var referenceEntityName = fieldMappingGr.referenced_sys_rte_eb_entity.name + '';
  				if (lookupEntityToDelete[referenceEntityId]) {
  					var mappingId = fieldMappingGr.getValue(this.SYS_RTE_EB_ENTITY_MAPPING);
  					var relEntityId = relMappingToEntity[mappingId];

  					var innerGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  					innerGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  					innerGr.addQuery(this.SYS_RTE_EB_ENTITY_MAPPING, mappingId);
  					innerGr.query();
  					var referenceNames = {};
  					while (innerGr.next()) {
  						var fieldName = innerGr.target_sys_rte_eb_field.field + '';
  						referenceNames[fieldName] = innerGr.referenced_sys_rte_eb_entity.name + '';
  					}
  					if (!doDelete) {
  						relEntityToDelete[relEntityId] = gs.getMessage('Relationship mapping between the \'{0}\' and the \'{1}\' classes can be deleted because \'{2}\' is a lookup item and there is no corresponding IRE lookup rule.', [referenceNames['parent'], referenceNames['child'], referenceEntityName]);
  					} else {
  						relEntityToDelete[relEntityId] = gs.getMessage('Relationship mapping between the \'{0}\' and the \'{1}\' classes has been deleted because \'{2}\' is a lookup item and there is no corresponding IRE lookup rule.', [referenceNames['parent'], referenceNames['child'], referenceEntityName]);
  					}
  				}
  			}

  			var entityToDelete = Object.keys(relEntityToDelete);
  			for (var i = 0; i < entityToDelete.length; i++) {
  				var id = entityToDelete[i];
  				var message = relEntityToDelete[id];
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: message,
  						recordTable: this.CMDB_INST_ENTITY,
  						recordId: id
  						
  					})
  				} else if (this.deleteEntity(id)) {
  					result.deletedRecords.push({
  						message: message
  					});
  				}
  			}
  			entityToDelete = Object.keys(lookupEntityToDelete);
  			for (var i = 0; i < entityToDelete.length; i++) {
  				var id = entityToDelete[i];
  				var message = lookupEntityToDelete[id];
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: message,
  						recordTable: this.CMDB_INST_ENTITY,
  						recordId: id
  					});
  				} else if (this.deleteEntity(id)) {
  					result.deletedRecords.push({
  						message: message
  					});
  				}
  			}
  		},
  		
  		hasMetadata: function(cmdbInstApplicationFeedSysId) {	
  			var impEntityGr = this.findImpCmdbEntity(cmdbInstApplicationFeedSysId);
  			if (impEntityGr.getUniqueValue) {
  				var tempEntities = this.findTempCmdbEntity(cmdbInstApplicationFeedSysId, impEntityGr.getUniqueValue());
  				if (tempEntities && Object.keys(tempEntities).indexOf('temp') >= 0)
  					return true;
  				return false;
  			}
  		},
  		
  		getNestedPayloadSchema: function(templateStateGr, forceLoad, forceCompleteSchema) {
  			var rowLimit = this.getPreviewSize(templateStateGr);
  			var importSetSysId = templateStateGr.import_set_id + '';
  			var loadCompleteSchema = this.parseBooleanString(templateStateGr.getDisplayValue(this.LOAD_COMPLETE_SCHEMA));
  			if (loadCompleteSchema || forceCompleteSchema) {
  				rowLimit = '';
  			}
  			try {
  				var rawSchemaStr = forceLoad ? '' : templateStateGr.getValue(this.NESTED_PAYLOAD_SCHEMA);
  				if (!rawSchemaStr) {
  					rawSchemaStr = sn_integration_studio.IntegrationStudioScriptableApi.schemaFromSampleData(importSetSysId, '', rowLimit);
  					gs.debug('IH-ETL schema reloaded for ' + importSetSysId);
  					var isCompleteSchema = loadCompleteSchema || forceCompleteSchema;
  					templateStateGr.setValue(this.IS_COMPLETE_SCHEMA, isCompleteSchema);
  					templateStateGr.setValue(this.NESTED_PAYLOAD_SCHEMA, rawSchemaStr);
  					templateStateGr.update();
  				}

  				// If there is no schema to validate against, just log and return
  				if (!rawSchemaStr) {
  					throw 'Empty schema string returned from ' + importSetSysId;
  				}
  				var schema = JSON.parse(rawSchemaStr);
  				return {
  					rawSchemaStr: rawSchemaStr,
  					schemaJson: schema
  				};
  			} catch (e) {
  				templateStateGr.setValue(this.IS_COMPLETE_SCHEMA, false);
  				templateStateGr.setValue(this.NESTED_PAYLOAD_SCHEMA, '');
  				templateStateGr.update();
  				this.handleSchemaLoadError(e);
  			}
  		},
  		
  		getAllMappingRecordsOfMissingImportTableColumns: function(feedId, result, doDelete) {
  			var isNestedPayload = this.isNestedPayload(feedId);
  			var fieldsToDelete = [];
  			var feedGr = new GlideRecord(this.CMDB_INST_APPLICATION_FEED);
  			feedGr.addQuery(this.SYS_ID, feedId);
  			feedGr.query();
  			if (feedGr.next()) {
  				var importTable = feedGr.sys_data_source.import_set_table_name + '';
  				var currentImpColumns = [];
  				if (!isNestedPayload) {
  					currentImpColumns = this.getTableColumns(importTable);
  				} else {
  					var templateStateGr = new GlideRecord(this.CMDB_INST_TEMPLATE_STATE);
  					templateStateGr.addQuery(this.APPLICATION_FEED_ID, feedId);
  					templateStateGr.query();
  					if (templateStateGr.next()) {
  						var importSetSysId = templateStateGr.import_set_id + '';
  						var returnedSchema = this.getNestedPayloadSchema(templateStateGr, false);
  						var rawSchemaStr = returnedSchema.rawSchemaStr;
  						var schema = returnedSchema.schemaJson;

  						var message = this.getNestedPayloadFields(schema, '', currentImpColumns);
  						if (message) {
  							errorObj = this.buildErrorObject(message, '', '');
  							throw this.getInternalServerError(errorObj);
  						}
  					}
  				}
  				
  				if (currentImpColumns.length > 0) {
  					var currentImpColumnMap = {};
  					for (var i = 0; i < currentImpColumns.length; i++) {
  						currentImpColumnMap[currentImpColumns[i]] = true;
  					}

  					var importEntityId = '';
  					var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  					entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  					entityGr.addQuery(this.NAME, this.IMPORT);
  					entityGr.query();
  					if (entityGr.next()) {
  						importEntityId = entityGr.getValue(this.SYS_ID);
  					} else {
  						result.error = gs.getMessage('Import entity record missing for this ETL transform map');
  						return result;
  					}

  					var entityIdToGraphMap = {};
  					var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  					fieldGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  					fieldGr.addQuery(this.SYS_RTE_EB_ENTITY, importEntityId);
  					fieldGr.query();
  					while (fieldGr.next()) {
  						var impFieldName = fieldGr.getValue(this.FIELD);
  						if (!currentImpColumnMap[impFieldName]) {
  							var impFieldId = fieldGr.getValue(this.SYS_ID);
  							var tempFieldId = '';
  							fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  							fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  							fieldMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_FIELD, impFieldId);
  							fieldMappingGr.query();
  							if (fieldMappingGr.next()) {
  								tempFieldId = fieldMappingGr.getValue(this.TARGET_SYS_RTE_EB_FIELD);
  								if (!tempFieldId) {
  									result.error = gs.getMessage('Import to temp field mapping is missing for import field \'{0}\'', impFieldName);
  									return result;
  								}
  								var tempEntityId = fieldMappingGr.target_sys_rte_eb_field.sys_rte_eb_entity + '';
  								var graph = entityIdToGraphMap[tempEntityId];
  								if (!graph) {
  									graph = this.getOperationGraph(tempEntityId);
  									entityIdToGraphMap[tempEntityId] = graph;
  								}
  								this.getOperationRecursive(tempFieldId, graph, feedId, result, doDelete);
  							}
  							if (!doDelete) {
  								result.invalidRecords.push({
  									message: gs.getMessage('Import field \'{0}\' can be deleted because the corresponding import table column is missing.', [impFieldName]),
  									recordTable: this.SYS_RTE_EB_FIELD,
  									recordId: impFieldId
  								});
  							} else if (fieldGr.deleteRecord()) {
  								result.deletedRecords.push({
  									message: gs.getMessage('Import field \'{0}\' has been deleted because the corresponding import table column is missing.', [impFieldName])
  								});
  							}
  						}
  					}
  				}
  			}
  		},
  		
  		getOperationRecursive: function(fieldId, graph, feedId, result, doDelete) {
  			var deleteOperations = [];
  			var deleteFields = [];
  			if (!fieldId) {
  				return;
  			}

  			var queue = [];
  			var visited = [];
  			var parents = Object.keys(graph.edges);
  			for (var i = 0; i < parents.length; i++) {
  				if (Object.keys(graph.edges[parents[i]]).indexOf(fieldId) >= 0)
  					deleteOperations.push(graph.edges[parents[i]][fieldId].operationId);
  			}
  			queue.push(fieldId);
  			visited.push(fieldId);
  			deleteFields.push(fieldId);
  			while (queue.length != 0) {
  				var node = queue[0];
  				queue.splice(0, 1);
  				if (!graph.edges[node])
  					continue;
  				else {
  					var children = Object.keys(graph.edges[node]);
  					for (var j = 0; j < children.length; j++) {
  						if (visited.indexOf(children[j]) >= 0)
  							continue;
  						else {
  							deleteOperations.push(graph.edges[node][children[j]].operationId);
  							deleteFields.push(children[j]);
  							queue.push(children[j]);
  							visited.push(children[j]);
  						}
  					}
  				}
  			}

  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			operationGr.addQuery(this.SYS_ID, 'IN', deleteOperations);
  			operationGr.query();
  			while (operationGr.next()) {
  				var operationName = operationGr.getValue(this.NAME);
  				var operationId = operationGr.getValue(this.SYS_ID);
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Operation \'{0}\' can be deleted because its Import field is missing.', [operationName]),
  						recordTable: this.SYS_RTE_EB_OPERATION,
  						recordId: operationId
  					});
  				} else if (operationGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Operation \'{0}\' has been deleted because its Import field is missing.', [operationName])
  					});
  				}
  			}

  			var fieldsToDeleteMapping = deleteFields.join();
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldMappingGr.addQuery(this.SOURCE_SYS_RTE_EB_FIELD, 'IN', fieldsToDeleteMapping);
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				// add the target fields to the fields to be deleted
  				deleteFields.push(fieldMappingGr.getValue(this.TARGET_SYS_RTE_EB_FIELD));
  				var id = fieldMappingGr.getValue(this.SYS_ID);
  				var sourceFieldName = fieldMappingGr.source_sys_rte_eb_field.name + '';
  				var targetFieldName = fieldMappingGr.target_sys_rte_eb_field.name + '';
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' to target field \'{1}\' can be deleted because the corresponding import table column is missing.', [sourceFieldName, targetFieldName]),
  						recordTable: this.SYS_RTE_EB_FIELD_MAPPING,
  						recordId: id
  					});
  				} else if (fieldMappingGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Field mapping with the source field \'{0}\' to target field \'{1}\' has been deleted because the corresponding import table column is missing.', [sourceFieldName, targetFieldName])
  					});
  				}
  			}

  			var fieldGr = new GlideRecord(this.SYS_RTE_EB_FIELD);
  			fieldGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldGr.addQuery(this.SYS_ID, deleteFields);
  			fieldGr.query();
  			while (fieldGr.next()) {
  				var id = fieldGr.getValue(this.SYS_ID);
  				var fieldName = fieldGr.getValue(this.NAME);
  				if (!doDelete) {
  					result.invalidRecords.push({
  						message: gs.getMessage('Field \'{0}\' can be deleted because the corresponding import table column is missing.', [fieldName]),
  						recordTable: this.SYS_RTE_EB_FIELD,
  						recordId: id
  					})
  				} else if (fieldGr.deleteRecord()) {
  					result.deletedRecords.push({
  						message: gs.getMessage('Field \'{0}\' has been internally deleted because the corresponding import table column is missing.', [fieldName])
  					});
  				}
  			}
  		},
  		
  		getNonReferenceFieldMappingWithEmptySourceField: function(feedId, result, doDelete) {
  			var fieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			fieldMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			fieldMappingGr.addEncodedQuery('source_sys_rte_eb_fieldISEMPTY^referenced_sys_rte_eb_entityISEMPTY');
  			fieldMappingGr.query();
  			while (fieldMappingGr.next()) {
  				var id = fieldMappingGr.getValue(this.SYS_ID);
  				var targetFieldName = fieldMappingGr.target_sys_rte_eb_field.name + '';
  				var entityMappingName = fieldMappingGr.sys_rte_eb_entity_mapping.name + '';
  				var resultMessage = '';
  				if (!doDelete) {
  					if (targetFieldName) {
  						resultMessage = gs.getMessage('Field mapping with the target field \'{0}\' in the class mapping of \'{1}\' can be deleted because the source field is missing.', [targetFieldName, entityMappingName]);
  					} else {
  						resultMessage = gs.getMessage('Field mapping in the class mapping of \'{0}\' can be deleted because the source and target fields are missing.', [entityMappingName]);
  					}
  					result.invalidRecords.push({
  						message: resultMessage,
  						recordTable: this.SYS_RTE_EB_FIELD_MAPPING,
  						recordId: id
  					});
  				} else if (fieldMappingGr.deleteRecord()) {
  					if (targetFieldName) {
  						resultMessage = gs.getMessage('Field mapping with the target field \'{0}\' in the class mapping of \'{1}\' has been deleted because the source field is missing.', [targetFieldName, entityMappingName]);
  					} else {
  						resultMessage = gs.getMessage('Field mapping in the class mapping of \'{0}\' has been deleted because the source and target fields are missing.', [entityMappingName]);
  					}
  					result.deletedRecords.push({
  						message: resultMessage
  					});
  				}
  			}
  		},
  		
  		getOperationsWithoutSourceOrTargetField: function(feedId, result, doDelete) {
  			var operationGr = new GlideRecord(this.SYS_RTE_EB_OPERATION);
  			operationGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			operationGr.query();
  			while (operationGr.next()) {
  				var isSetOperation = false;
  				var sys_id = operationGr.getUniqueValue();
  				var type = operationGr.getValue(this.TYPE);
  				isSetOperation = (type === this.getSetOperationTypeSysId());
  				var sourceIds = operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELD);
  				if (!sourceIds)
  					sourceIds = operationGr.getValue(this.SOURCE_SYS_RTE_EB_FIELDS);
  				var targetIds = operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELD);
  				if (!targetIds)
  					targetIds = operationGr.getValue(this.TARGET_SYS_RTE_EB_FIELDS);
  				if ((!sourceIds && !isSetOperation) || !targetIds) {
  					var operationName = operationGr.getValue(this.NAME);
  					var operationId = operationGr.getValue(this.SYS_ID);
  					if (!doDelete) {
  						result.invalidRecords.push({
  							message: gs.getMessage('Operation \'{0}\' can be deleted because its source or target field is missing.', [operationName]),
  							recordTable: this.SYS_RTE_EB_OPERATION,
  							recordId: operationId
  						});
  					} else if (operationGr.deleteRecord()) {
  						result.deletedRecords.push({
  							message: gs.getMessage('Operation \'{0}\' has been deleted because its source or target field is missing.', [operationName])
  						});
  					}
  				}
  			}
  		},
  		
  		getRollbackRunRecord: function(rollbackContextId) {
  			var rollbackRunGr = new GlideRecord(this.SYS_ROLLBACK_RUN);
  			rollbackRunGr.addQuery(this.ROLLBACK_CONTEXT_FIELD, rollbackContextId);
  			rollbackRunGr.orderByDesc(this.SYS_CREATED_ON);
  			rollbackRunGr.query();
  			if (rollbackRunGr.next()) {
  				return rollbackRunGr;
  			}
  			return;
  		},

  		validateIfRollbackCompleted: function(rollbackContextId) {
  			var rollbackRunGr, rollbackRunSysId, rollbackState;
              rollbackRunGr = this.getRollbackRunRecord(rollbackContextId);
              if (rollbackRunGr) {
                  rollbackRunSysId = rollbackRunGr.getValue(this.SYS_ID);
                  rollbackState = rollbackRunGr.getValue(this.ROLLBACK_STATE_FIELD);
  				if (rollbackState !== this.ROLLBACK_FINISHED_STATE) {
  					gs.error("Rollback error. Rollback context id: " + rollbackContextId + " Rollback run id: " + rollbackRunSysId + " Rollback state: " + rollbackState);
  					throw new Error(gs.getMessage("Rollback did not complete successfully. Rollback run record '{0}' has '{1}' status.", [rollbackRunSysId, rollbackState]));
  				}
              } else {
                  gs.error("Cannot find rollback run history with context id '{0}'" + rollbackContextId);
                  throw new Error(gs.getMessage("Cannot find rollback run history with context id '{0}'", rollbackContextId));
              }

  			return true;
  		},
  		
  		checkIfApplicationScopeIsSame: function(dataSourceRec) {
              // Need to verify couple of things
              // Check if currently in the correct scope
              var currentApplicationId = gs.getCurrentApplicationId();
              var dataSourceApplicationId = dataSourceRec.sys_scope;
              if (currentApplicationId != dataSourceApplicationId) {
                  var dataSourceName = dataSourceRec.getValue(this.NAME);
                  var dataSourceApplication = dataSourceRec.getDisplayValue('sys_scope');
  				
                  var currentApplicationGr = new GlideRecord('sys_scope');
  				if(currentApplicationGr.get(currentApplicationId)) {
  					var errorMessage = gs.getMessage("The data source, '{0}', is in the {1} application, but {2} is the current application. An ETL Map needs to be created from the same application scope as its data source.",
  						[dataSourceName, dataSourceApplication, currentApplicationGr.name]);
  					var errorObj = this.buildErrorObject(errorMessage);
  					throw this.getInternalServerError(errorObj);
  				}
              }
          },
          
          checkIfDSAssociatedWithETLMap: function(dataSourceRec, payload) {
              // Make sure data source is not already associated with another ETL map
              var robustTransformer = new GlideRecord('sys_robust_import_set_transformer');
              robustTransformer.addQuery('source_table', dataSourceRec.import_set_table_name);
              robustTransformer.addActiveQuery();
              robustTransformer.query();
              if (robustTransformer.next()) {
                  var robustTransformerName = robustTransformer.getValue(this.NAME);
                  var errorMessage = gs.getMessage("Data source table already associated with '{0}' ETL Map", [robustTransformerName]);
                  var errorObj = this.buildErrorObject(errorMessage, '', {requestPayload: payload});
                  throw this.getInternalServerError(errorObj);
              }
          },
          
          checkIfDSAssociatedWithTableTransformMap: function(dataSourceRec, payload) {
              var transformMap = new GlideRecord('sys_transform_map');
              transformMap.addQuery('source_table', dataSourceRec.import_set_table_name);
              transformMap.addActiveQuery();
              transformMap.query();
              if (transformMap.next()) {
                  var transformMapName = transformMap.getValue(this.NAME);
                  var errorMessage = gs.getMessage("Data source table already associated with '{0}' Table Transform Map", transformMapName);
                  var errorObj = this.buildErrorObject(errorMessage, '', {requestPayload: payload});
                  throw this.getInternalServerError(errorObj); 
              }
          },
  		
  		shouldDisableEntityMappingReorder: function(feedId) {
  			var disabledFeedIds = gs.getProperty(this.DISABLE_ENTITY_MAPPING_REORDER_PROPERTY_NAME);
  			var feedIdList = disabledFeedIds.split(',');
  			return feedIdList.indexOf(feedId) > -1;
  		},
  		
  		updateEntity: function(feedId, entityId, group) {
  			var errorObj = {};
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityGr.addQuery(this.SYS_ID, entityId);
  			entityGr.query();
  			if (entityGr.next()) {
  				entityGr.setValue(this.NAME, group.name);
  				entityGr.setValue(this.PATH, group.path);
  				entityGr.setValue(this.TABLE, group.table);
  				entityGr.update();
  			} else {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity with id {0}', originalId));
  				throw this.getInternalServerError(errorObj);
  			}
  			return this.SUCCESS;
  		},

  		updateEntityMapping: function(feedId, entityId, entityName, order, conditionScript, condition) {
  			var errorObj = {};
  			var entityMappingGr = new GlideRecord(this.SYS_RTE_EB_ENTITY_MAPPING);
  			entityMappingGr.addQuery(this.SYS_RTE_EB_DEFINITION, feedId);
  			entityMappingGr.addQuery(this.TARGET_SYS_RTE_EB_ENTITY, entityId);
  			entityMappingGr.query();

  			if (!entityMappingGr.hasNext()) {
  				errorObj = this.buildErrorObject(gs.getMessage('Cannot find entity mapping with target entity id {0}', entityMappingGroupId));
  				throw this.getInternalServerError(errorObj);
  			} else {
  				entityMappingGr.next();
  				entityMappingGr.setValue(this.NAME, 'TempTo' + entityName);
  				if (order) {
  					entityMappingGr.setValue(this.ORDER, order);
  				}
  				if (conditionScript) {
  					entityMappingGr.setValue(this.CONDITION_SCRIPT, conditionScript);
  				}
  				if (condition) {
  					entityMappingGr.setValue(this.ENCODED_QUERY, condition);
  				}
  				entityMappingGr.update();
  			}
  			return this.SUCCESS;
  		},
  		
  		getEntityMappingsAffectedByClassUpdate: function(feedId, oldEntityId, newClassFieldsList) {
  			var affectedMappingsList = {};
  			
  			var encodedQuery = this.TARGET_SYS_RTE_EB_ENTITY + '=' + oldEntityId;
  			var entityMappingIds = this.getEntityMappingsForApplicationFeed(feedId, encodedQuery);
  			var fieldMappings = this.getFieldMappingsForEntityMapping(entityMappingIds[0].sys_id);
  			
  			affectedMappingsList["fieldMappings"] = [];
  			
  			for (var i = 0; i < fieldMappings.length; i++) {
  				if (newClassFieldsList.attributes.indexOf(fieldMappings[i].target_sys_rte_eb_field_display_value) === -1) {
  					
  					affectedMappingsList["fieldMappings"].push({
  						'targetField': fieldMappings[i].target_sys_rte_eb_field_display_value,
  						'targetEntityTableName': fieldMappings[i].target_sys_rte_eb_entity_table_name,
  						'fieldMappingSysId': fieldMappings[i].sys_id,
  						'is_lookup': "false"
  					});
  				}
  			}
  			
  			var lookupEntityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			lookupEntityGr.addQuery(this.LOOKUP_FOR_ENTITY, oldEntityId);
  			lookupEntityGr.query();
  			while (lookupEntityGr.next()) {
  				var sysId = lookupEntityGr.getUniqueValue();
  				
  				encodedQuery = this.TARGET_SYS_RTE_EB_ENTITY + '=' + sysId;
  				entityMappingIds = this.getEntityMappingsForApplicationFeed(feedId, encodedQuery);
  				
  				if (entityMappingIds.length > 0) {
  					fieldMappings = this.getFieldMappingsForEntityMapping(entityMappingIds[0].sys_id);

  					for (var j = 0; j < fieldMappings.length; j++) {
  						if (newClassFieldsList.lookUpRules && Object.keys(newClassFieldsList.lookUpRules).length > 0) {
  							var targetTableName = fieldMappings[j].target_sys_rte_eb_entity_table_name;
  							if (newClassFieldsList.lookUpRules[targetTableName] && newClassFieldsList.lookUpRules[targetTableName].indexOf(fieldMappings[j].target_sys_rte_eb_field_display_value) === -1) {
  								affectedMappingsList["fieldMappings"].push({
  									'targetField': fieldMappings[j].target_sys_rte_eb_field_display_value,
  									'targetEntityTableName': fieldMappings[j].target_sys_rte_eb_entity_table_name,
  									'targetEntityName': fieldMappings[j].target_sys_rte_eb_entity_name,
  									'fieldMappingSysId': fieldMappings[j].sys_id,
  									'is_lookup': "true"
  								});
  							}
  						} else {
  							affectedMappingsList["fieldMappings"].push({
  								'targetField': fieldMappings[j].target_sys_rte_eb_field_display_value,
  								'targetEntityTableName': fieldMappings[j].target_sys_rte_eb_entity_table_name,
  								'targetEntityName': fieldMappings[j].target_sys_rte_eb_entity_name,
  								'fieldMappingSysId': fieldMappings[j].sys_id,
  								'is_lookup': "true"
  							});
  						}
  					}
  				}
  			}
  			
  			affectedMappingsList["relMappings"] = [];
  			affectedMappingsList["referenceMappings"] = [];
  			
  			var entityFieldMappingGr = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  			entityFieldMappingGr.addQuery(this.REFERENCED_SYS_RTE_EB_ENTITY, oldEntityId);
  			entityFieldMappingGr.query();
  			while (entityFieldMappingGr.next()) {
  				
  				var entityTable = entityFieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table + '';
  				if (entityTable === this.CMDB_REL_CI) {
  					
  					var parentEntityName = "";
  					var childEntityName = "";
  					var lookupEntityName = entityFieldMappingGr.referenced_sys_rte_eb_entity.lookup_for_entity.name;
  					
  					if (entityFieldMappingGr.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD + '.field') === this.PARENT) {
  						parentEntityName = lookupEntityName ? lookupEntityName + ' - ' + entityFieldMappingGr.referenced_sys_rte_eb_entity.name : entityFieldMappingGr.referenced_sys_rte_eb_entity.name;
  					} else if (entityFieldMappingGr.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD + '.field') === this.CHILD) {
  						childEntityName = lookupEntityName ? lookupEntityName + ' - ' + entityFieldMappingGr.referenced_sys_rte_eb_entity.name : entityFieldMappingGr.referenced_sys_rte_eb_entity.name;
  					}
  					
  					encodedQuery = '';
  					encodedQuery += 'referenced_sys_rte_eb_entity!=' + oldEntityId;
  					encodedQuery += '^sys_rte_eb_entity_mapping=' + entityFieldMappingGr.sys_rte_eb_entity_mapping;
  					lookupEntityName = '';
  					
  					var entityFieldMappingGr2 = new GlideRecord(this.SYS_RTE_EB_FIELD_MAPPING);
  					entityFieldMappingGr2.addEncodedQuery(encodedQuery);
  					entityFieldMappingGr2.query();
  					if(entityFieldMappingGr2.next()) {
  						lookupEntityName = entityFieldMappingGr2.referenced_sys_rte_eb_entity.lookup_for_entity.name;
  						if (entityFieldMappingGr2.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD + '.field') === this.PARENT) {
  							parentEntityName = lookupEntityName ? lookupEntityName + ' - ' + entityFieldMappingGr2.referenced_sys_rte_eb_entity.name : entityFieldMappingGr2.referenced_sys_rte_eb_entity.name;
  						} else if (entityFieldMappingGr2.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD + '.field') === this.CHILD) {
  							childEntityName = lookupEntityName ? lookupEntityName + ' - ' + entityFieldMappingGr2.referenced_sys_rte_eb_entity.name : entityFieldMappingGr2.referenced_sys_rte_eb_entity.name;
  						}
  						affectedMappingsList["relMappings"].push({
  							'relMapping': gs.getMessage('\'{0}\' (Parent) :: \'{1}\' (Child)', [parentEntityName, childEntityName]),
  							'relType': entityFieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.relationship_type.name
  						});
  					}
  				} else {
  					affectedMappingsList["referenceMappings"].push({
  						'targetField': entityFieldMappingGr.getDisplayValue(this.TARGET_SYS_RTE_EB_FIELD),
  						'entityTableName': entityFieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.table,
  						'entityName': entityFieldMappingGr.sys_rte_eb_entity_mapping.target_sys_rte_eb_entity.name
  					});
  				}
  			}
  			
  			affectedMappingsList["associatedMappings"] = [];
  			
  			var entityGr = new GlideRecord(this.CMDB_INST_ENTITY);
  			entityGr.addQuery(this.RELATED_FOR_ENTITY, oldEntityId);
  			entityGr.query();
  			while (entityGr.next()) {
  				var associateName = entityGr.getValue(this.NAME);
  				affectedMappingsList["associatedMappings"].push({
  					'associatedEntityName': associateName
  				});
  			}
  			
  			return affectedMappingsList;
  		},
  		
  		calculateFurthestStep: function(appFeedGr) {
  			// Check if a scheduled exists for the data source, if so all steps are complete
  			var scheduleGr = new GlideRecord(this.SCHEDULED_IMPORT_SET);
  			scheduleGr.addQuery('data_source', appFeedGr.getValue(this.SYS_DATA_SOURCE));
  			scheduleGr.query();
  			if (scheduleGr.hasNext()) {
  				return this.MAX_STATE;
  			}
  			// If metadata exists, open up all of the mapping steps
  			if (this.hasMetadata(appFeedGr.getUniqueValue())) {
  				return this.RELATIONSHIP_MAPPING_STEP_NUMBER;
  			}
  			
  			return "0";
  		},

  		type: 'CMDBIntegrationStudioUtility'
  	};

Sys ID

dc2ef8337324001008f8dca09ff6a7b1

Offical Documentation

Official Docs: