Name
sn_em_connector.PushConnectorCustomDomainUtil
Description
This script includes utility methods to populate custom domain information for PUSH connector events for instance type push connectors.
Script
var PushConnectorCustomDomainUtil = Class.create();
PushConnectorCustomDomainUtil.prototype = {
initialize: function() {},
/**
*
* @param {*} connectorSysId - Push Connector sys_id.
* @param {*} request - HttpRequest for incoming event
* @param {*} reqBody - Parsed Request body
* @param {*} event - Event object where domain information is to be populated
*
* This method populates sys_domain and sys_domain_path fields in event object using domain metadata properties.
*/
populateDomain: function(connectorSysId, request, reqBody, event) {
var customDomainSepEnabled = gs.getProperty('evt_mgmt.connector_enable_custom_domain_separation');
if (customDomainSepEnabled != null && customDomainSepEnabled == 'true') {
// Declare metadata variables here to access in catch block
var domainInfoTableName, domainInfoColumnName, domainRecordFieldValueToMatch, domainIdColumnName, domainPathColumnName;
try {
this._checkCustomDomainHelperAvailability();
var domainMetadata = this._readDomainMetadata(connectorSysId, request, reqBody);
domainInfoTableName = domainMetadata.domainInfoTableName;
domainInfoColumnName = domainMetadata.domainInfoColumnName;
domainIdColumnName = domainMetadata.domainIdColumnName;
domainPathColumnName = domainMetadata.domainPathColumnName;
var payloadDomainInfoFieldName = domainMetadata.payloadDomainInfoFieldName;
var payloadDomainInfoFieldValue = domainMetadata.payloadDomainInfoFieldValue;
// Use static value to match record OR read field value from flattened event payload.
// Dot-Walking of flattened payload is not yet supported.
// The payload field is stored as 'field' or 'flattened.field' in the additional_info
var addInfoObj = JSON.parse(event.additional_info);
// payloadDomainInfoFieldName takes preference over payloadDomainInfoFieldValue
if (payloadDomainInfoFieldName && payloadDomainInfoFieldName !== '') {
var flattenedPayloadDomainInfoFieldName = 'flattened.' + payloadDomainInfoFieldName;
domainRecordFieldValueToMatch = addInfoObj[payloadDomainInfoFieldName] || addInfoObj[flattenedPayloadDomainInfoFieldName] || '';
} else if (payloadDomainInfoFieldValue && payloadDomainInfoFieldValue !== '') {
domainRecordFieldValueToMatch = payloadDomainInfoFieldValue;
}
/*
gs.info("CONNECTOR_CUSTOM_DOMAIN:: Domain Info Variables: " + JSON.stringify({
"domainInfoTableName": domainInfoTableName,
"domainInfoColumnName": domainInfoColumnName,
"payloadDomainInfoFieldName": payloadDomainInfoFieldName,
"payloadDomainInfoFieldValue": payloadDomainInfoFieldValue,
"domainIdColumnName": domainIdColumnName,
"domainPathColumnName": domainPathColumnName,
"domainRecordFieldValueToMatch": domainRecordFieldValueToMatch
}));
*/
var domainId = '';
var domainPath = '';
if (domainInfoTableName && domainInfoTableName.trim() !== '' && domainRecordFieldValueToMatch && domainRecordFieldValueToMatch.trim() !== '') {
/**
* Read value from backend cache - example return value - {"domainPath":"!!!/!!!/","domainID":"09ff3d105f231000b12e3572f2b4775d"}
* The helper method will also raise self health events wherever necessary.
*/
var customDomainSepModelString = new global.EvtMgmtEventCustomDomainHelper().getCustomDomainSepModel(domainInfoTableName, domainInfoColumnName, domainRecordFieldValueToMatch, domainIdColumnName, domainPathColumnName);
if (customDomainSepModelString && customDomainSepModelString !== '') {
var customDomainJson = JSON.parse(customDomainSepModelString);
if (customDomainJson)
{
domainId = customDomainJson.domainID;
domainPath = customDomainJson.domainPath;
}
}
} else {
throw new Error('domainInfoTableName OR/AND domainRecordFieldValueToMatch could not be resolved');
}
// Check if user has access to resolved domain
if (domainId && domainId !== '' && domainPath && domainPath !== '' && this._checkReadAccessOnDomain(domainId)) {
event.setValue('sys_domain', domainId);
event.setValue('sys_domain_path', domainPath);
}
//gs.debug("CONNECTOR_CUSTOM_DOMAIN :: Resolved Domain : " + domainId + " : " + domainPath);
} catch (err) {
gs.debug('CONNECTOR_CUSTOM_DOMAIN :: Error: Unable to set custom domain for event - ' + err);
/**
* We will raise health events as part of processing the event in backend. But, we do not want to
* miss out on any exceptions we get in the parsing and reading stages of domain metadata parsing.
*/
var msgKey = 'SCRIPT_ERR_' + event.source;
var cause = gs.getMessage('The push connector script with sys ID {0} had the following error while resolving custom domain information - \n"{1}"', [connectorSysId, err.message]);
var additionalInfoFields = {};
// If we have exception after resolving data, add this data to the health event.
if (domainInfoTableName && domainInfoColumnName && domainRecordFieldValueToMatch) {
msgKey += '__' + domainInfoTableName + "__" + domainIdColumnName + "__" + domainRecordFieldValueToMatch;
cause += gs.getMessage('\nfor table \'{0}\' with column name \'{1}\' and value \'{2}\'', [domainInfoTableName, domainInfoColumnName, domainRecordFieldValueToMatch]);
additionalInfoFields["custom_domain_connectorDomainInfoTableName"] = domainInfoTableName;
additionalInfoFields["custom_domain_connectorDomainInfoColumnName"] = domainInfoColumnName;
additionalInfoFields["custom_domain_payloadDomainInfoFieldValue"] = domainRecordFieldValueToMatch;
}
var remediation = 'Check if domain metadata is valid and that user has access to the specified domain record.';
this._sendSelfHealthEvent(msgKey, cause, remediation, additionalInfoFields, event.source);
}
}
},
/**
*
*
* @returns domainMetadata {domainInfoTableName, domainInfoColumnName, payloadDomainInfoFieldName, payloadDomainInfoFieldValue, domainIdColumnName, domainPathColumnName }
*
* This method reads the domain metadata properties form sys_properties table and overrides any properties if passed from the payload request / headers / query parameters.
* The preference of applying domain metadata properties in DECREASING order is as follows:
*
* Push Connector Parameters > URL query Parameters > Request Headers > Request Body > sys_properties defaults
*
*/
_readDomainMetadata: function (sysid, request, reqBody) {
var queryParams = request.queryParams;
var headers = request.headers;
// gs.info("CONNECTOR_CUSTOM_DOMAIN:: QUERY: " + JSON.stringify(queryParams));
// gs.info("CONNECTOR_CUSTOM_DOMAIN:: HEADERS: " + JSON.stringify(headers));
var defaultDomainMetadata = {
domainInfoTableName: gs.getProperty('evt_mgmt.connector_domain_info_table_name') || '',
domainInfoColumnName: gs.getProperty('evt_mgmt.connector_domain_info_column_name') || '',
payloadDomainInfoFieldName: '',
payloadDomainInfoFieldValue: '',
domainIdColumnName: gs.getProperty('evt_mgmt.connector_domain_id_column_name') || '',
domainPathColumnName: gs.getProperty('evt_mgmt.connector_domain_path_column_name') || ''
};
gs.debug("CONNECTOR_CUSTOM_DOMAIN :: Default Domain Metadata: " + JSON.stringify(defaultDomainMetadata));
var domainMetadata = {};
/*
* props.name - Name of metadata property as it appears in request payload / headers / body
* props.prop - Variable name to refer in domain metadata object
*/
var props = [
{name: 'connectorDomainInfoTableName', prop: 'domainInfoTableName'},
{name: 'connectorDomainInfoColumnName', prop: 'domainInfoColumnName'},
{name: 'payloadDomainInfoFieldName', prop: 'payloadDomainInfoFieldName'},
{name: 'payloadDomainInfoFieldValue', prop: 'payloadDomainInfoFieldValue'},
{name: 'connectorDomainIdColumnName', prop: 'domainIdColumnName'},
{name: 'connectorDomainPathColumnName', prop: 'domainPathColumnName'}
];
// Read options from query parameters, headers, and request body
props.forEach(function(prop) {
var value = queryParams[prop.name] ? queryParams[prop.name][0] :
(headers[prop.name.toLowerCase()] && headers[prop.name.toLowerCase()] !== '' ? headers[prop.name.toLowerCase()] :
(reqBody[prop.name] && reqBody[prop.name] !== '' ? reqBody[prop.name] :
defaultDomainMetadata[prop.prop]));
domainMetadata[prop.prop] = value;
});
this._readFromConnectorParam(sysid, domainMetadata);
return domainMetadata;
},
_readFromConnectorParam: function(sysid, domainMetadata) {
var configurations = new sn_em_connector.PushConnectorConfigurations();
var connectorParam_payloadDomainInfoFieldName = configurations.getConfigurationValue(sysid, connector_sys_id, 'payloadDomainInfoFieldName');
var connectorParam_payloadDomainInfoFieldValue = configurations.getConfigurationValue(sysid, connector_sys_id, 'payloadDomainInfoFieldValue');
var connectorParam_connectorDomainInfoTableName = configurations.getConfigurationValue(sysid, connector_sys_id, 'connectorDomainInfoTableName');
var connectorParam_connectorDomainInfoColumnName = configurations.getConfigurationValue(sysid, connector_sys_id, 'connectorDomainInfoColumnName');
var connectorParam_connectorDomainIdColumnName = configurations.getConfigurationValue(sysid, connector_sys_id, 'connectorDomainIdColumnName');
var connectorParam_connectorDomainPathColumnName = configurations.getConfigurationValue(sysid, connector_sys_id, 'connectorDomainPathColumnName');
if (connectorParam_payloadDomainInfoFieldName && connectorParam_payloadDomainInfoFieldName !== '') {
domainMetadata.payloadDomainInfoFieldName = connectorParam_payloadDomainInfoFieldName;
}
if (connectorParam_payloadDomainInfoFieldValue && connectorParam_payloadDomainInfoFieldValue !== '') {
domainMetadata.payloadDomainInfoFieldValue = connectorParam_payloadDomainInfoFieldValue;
}
if (connectorParam_connectorDomainInfoTableName && connectorParam_connectorDomainInfoTableName !== '') {
domainMetadata.domainInfoTableName = connectorParam_connectorDomainInfoTableName;
}
if (connectorParam_connectorDomainInfoColumnName && connectorParam_connectorDomainInfoColumnName !== '') {
domainMetadata.domainInfoColumnName = connectorParam_connectorDomainInfoColumnName;
}
if (connectorParam_connectorDomainIdColumnName && connectorParam_connectorDomainIdColumnName !== '') {
domainMetadata.domainIdColumnName = connectorParam_connectorDomainIdColumnName;
}
if (connectorParam_connectorDomainPathColumnName && connectorParam_connectorDomainPathColumnName !== '') {
domainMetadata.domainPathColumnName = connectorParam_connectorDomainPathColumnName;
}
},
_checkReadAccessOnDomain: function(domainId) {
this._checkCustomDomainHelperAvailability();
var hasAccess = new global.EvtMgmtEventCustomDomainHelper().userHasDomainAccess(domainId);
return hasAccess === true;
},
_sendSelfHealthEvent: function(msgKey, cause, remediation, additionalInfo, scriptSource) {
var HEALTH_EVENT_SOURCE = 'push_connector_script_' + scriptSource;
try {
this._checkCustomDomainHelperAvailability();
var additionalInfoJsonStr;
try {
additionalInfoJsonStr = JSON.stringify(additionalInfo);
} catch (err) {
gs.debug('CONNECTOR_CUSTOM_DOMAIN :: Error: Unable to process additional info JSON for health event - ' + err);
}
// Send health event
new global.EvtMgmtEventCustomDomainHelper().sendSelfMonitoringEvent(msgKey, cause, remediation, HEALTH_EVENT_SOURCE, additionalInfoJsonStr);
} catch (e) {
/**
* EvtMgmtEventCustomDomainHelper will not be available to raise self health event if we are running an unsupported ServiceNow instance version.
* We still want to notify with a health event using EvtMgmtHealthMonitorManager instead.
*/
if (typeof global.EvtMgmtHealthMonitorManager === 'function') {
var emHMmanager = new global.EvtMgmtHealthMonitorManager();
var healthEventSeverity = '4'; // Warning
var remed = gs.getMessage('Verify if custom domain separation pre-requisites such as ServiceNow instance version requirement is fulfilled.');
var description = gs.getMessage("Custom domain information could not be set for some of the events.\nUser's domain will be set for these events.\n\nCause: \n{0}\n\nRemediation: \n{1}", [cause, remed]);
var monitorName = 'Connector Event Custom Domain Processing';
var bindCiName = 'Event Processing';
var expirationPeriodInSec = 0;
var updateTimeInSec = 0;
emHMmanager.sendEvent(msgKey, healthEventSeverity, description, additionalInfo, monitorName, HEALTH_EVENT_SOURCE, bindCiName, 'true', expirationPeriodInSec, updateTimeInSec);
} else {
// No other way to notify the user. Add a system log.
gs.error(e);
}
}
},
_checkCustomDomainHelperAvailability: function() {
if (typeof global.EvtMgmtEventCustomDomainHelper !== 'function') {
throw new Error(gs.getMessage('Custom domain separation for push connectors is not supported with this version of ServiceNow instance.'));
}
},
type: 'PushConnectorCustomDomainUtil'
};
Sys ID
3d5bfbd543c2a11044a83912bfb8f2ff