Name
sn_em_ai.EvtMgmtJsonToEventsProcessor
Description
No description available
Script
var EvtMgmtJsonToEventsProcessor = Class.create();
EvtMgmtJsonToEventsProcessor.prototype = {
initialize: function() {
this.alertUtilsSnc = new global.EvtMgmtAlertUtilsSNC();
this.logAnalyticsJsonProcessor = new EvtMgmtProcessLogAnalyticsJson();
this.incidentSysId = "";
this.DEFAULT_CLASSIFICATION = "IT";
this.SOURCES = {
NONE: '0',
LOG_ANALYTICS: '1',
};
this.ALERT_EXTRA_DATA = {
TABLE: 'em_alert_extra_data',
FIELDS: {
RAW_JSON: 'raw_json',
SOURCE: 'source',
},
};
},
type: 'EvtMgmtJsonToEventsProcessor',
/**
* Extracts and creates new alerts from a given JSON.
* @param source - string or number that identifies the source of the JSON file
* @param json - the json to parse and extract events from
* @param sysId - the sys_id value of the extracted json
* @returns {string} - empty string on success, or a string with failure message on failure
*/
process: function(source, json, sysId) {
this.processTimestamp = new GlideDateTime().getNumericValue(); // Consistent timestamp to capture process start time
this.jsonSource = '';
this.msgKeyPrefix = "";
this.source = source;
this.jsonSysId = sysId; // sys_id of the json record itself, where it's stored in em_extra_data_json
this.jsonId = json.incident && json.incident.sysId ? json.incident.sysId : this.jsonSysId; // sysId as it is defined for the incident in the json object
this.incidentSysId = json.incident.sysId;
var processedJson = {};
source = this.getValidSourceValue(source);
if (sysId && this.isNotEmptyObject(json)) {
switch (source) {
case this.SOURCES.LOG_ANALYTICS:
this.jsonSource = 'Log Analytics';
this.msgKeyPrefix = "LOG_ANALYTICS";
this.activeProcessor = this.logAnalyticsJsonProcessor;
break;
default:
return gs.getMessage('Invalid or unsupported JSON source');
}
processedJson = this.activeProcessor.process(json);
}
// Check for valid processJson
if (this.isEmptyObject(processedJson)) {
return gs.getMessage('EvtMgmtJsonToEventsProcessor - Failure occurred while processing JSON with sys_id: {0}', [sysId]);
}
// Test the given against the expected "processedJson" structure -
// should have the 'tree' key, with values of non-empty object
if (!this.isValidProcessedJson(processedJson)) {
return gs.getMessage('EvtMgmtJsonToEventsProcessor - Processed JSON with sys_id: {0} is missing expected tree key or its content', [sysId]);
}
// Build tree and tree nodes based on given tree
var tree = new Tree(source, sysId);
tree.build(processedJson.tree);
this.rootId = tree.getRoot().getId();
// Modify, alter and change the built tree before the creation of events from it
if (this.activeProcessor && (typeof this.activeProcessor.modifyTreeBeforeCreatingEvents === 'function')) {
this.activeProcessor.modifyTreeBeforeCreatingEvents(tree);
}
// Create events from the built tree
this.createEvents(tree);
return '';
},
/**
* Creates events for every given event-object in the given treeNodesArray.
* @param tree: An array of TreeNodes objects. Each object describes holds data about an event.
*/
createEvents: function(tree) {
var treeNode, event, EmEvent;
var treeNodesArray = tree.toArray();
for (var i = 0; i < treeNodesArray.length; i++) {
treeNode = treeNodesArray[i];
if (treeNode.shouldBeCreatedAsEvent()) {
event = this.getCustomizedEvent(treeNode);
EmEvent = new EvtMgmtEmEvent(event);
EmEvent.createEvent();
}
}
},
/**
* Test the given against the expected "processedJson" structure -
* @param processedJson: should have the 'tree' key, with value of non-empty object
*/
isValidProcessedJson: function(processedJson) {
return this.isNotEmptyObject(processedJson) && processedJson.hasOwnProperty('tree') && this.isNotEmptyObject(processedJson.tree);
},
/**
* Returns an object with the structure of event for a given treeNode
* @param treeNode
* @returns An object describing a new event in em_event
*/
getCustomizedEvent: function(treeNode) {
var event = treeNode.getEventData();
// Event fields values
event.source = this.jsonSource;
event.event_class = this.jsonSource;
event.message_key = String(treeNode.getMessageKey(this.msgKeyPrefix, this.processTimestamp, this.rootId));
event.time_of_event = String(treeNode.getManipulatedTimeOfEvent(this.activeProcessor));
event.bucket = this.alertUtilsSnc.calculateBucket(this.jsonId, this.DEFAULT_CLASSIFICATION);
// Values that will go into additional_info
event.sn_root_id = String(this.rootId);
event.sn_hla_group_id = this.incidentSysId;
event.sn_json_id = String(this.jsonSysId);
event.sn_source = String(this.jsonSource);
event.sn_source_original_id = String(treeNode.getId());
event.sn_original_time = String(treeNode.getOriginalTime());
event.sn_source_parent_id = String(treeNode.getParentId());
event.sn_parent_message_key = String(treeNode.getParentMessageKey(this.msgKeyPrefix, this.processTimestamp, this.rootId));
event.sn_level = String(treeNode.getLevel());
event.sn_partofGOGgroup = String(treeNode.isGog());
event.sn_my_group_type = String(treeNode.getMyGroupType(this.activeProcessor));
event.sn_parent_group_type = String(treeNode.getParentGroupType(this.activeProcessor));
// Call hook, for adding more data to the event
if (this.activeProcessor.addToEvent && (typeof this.activeProcessor.addToEvent === 'function')) {
this.activeProcessor.addToEvent(event, treeNode);
}
// insert extra data into em_alert_extra_data and get its sys_id
event.sn_extra_data = this.insertExtraData(treeNode.getExtraData(this.activeProcessor));
return event;
},
/**
* Inserts the given extra data values into em_alert_extra_data
* Returns the sys_id of the inserted record
* @param extraDta
* @returns string
*/
insertExtraData: function(extraData) {
var gr = new GlideRecord(this.ALERT_EXTRA_DATA.TABLE);
gr.initialize();
gr.setValue(this.ALERT_EXTRA_DATA.FIELDS.RAW_JSON, this.jsonSysId);
gr.setValue(this.ALERT_EXTRA_DATA.FIELDS.SOURCE, this.source);
for (var field in extraData) {
if (extraData.hasOwnProperty(field) && (typeof extraData[field] !== 'undefined')) {
fieldValue = extraData[field];
gr.setValue(field, fieldValue);
}
}
return gr.insert();
},
/**
* Returns source value as stored in the database
* @param source
* @returns number
*/
getValidSourceValue: function(source) {
var sources = this.SOURCES;
if (sources.hasOwnProperty(source.toUpperCase())) {
return sources[source.toUpperCase()];
} else {
for (var name in sources) {
if (sources.hasOwnProperty(name) && (parseInt(sources[name], 10) === parseInt(source, 10))) {
return String(parseInt(source, 10));
}
}
}
return 0;
},
/**
* Returns true if a given object does not exist, is not an object or has no keys
* @param object
* @returns {boolean}
*/
isEmptyObject: function(object) {
return !object || (object.constructor !== Object) || (Object.keys(object).length === 0);
},
/**
* Returns true if a given object has keys (1 or more)
* @param object
* @returns {boolean}
*/
isNotEmptyObject: function(object) {
return !this.isEmptyObject(object);
},
};
/**
* Tree "Class" - Returns a Tree instance
* @returns TreeNode instance
*/
function Tree() {
/**
* PROPERTIES
*/
this.tree = {};
this.TOP_LEVEL = 1;
/**
* METHODS
*/
// Init the tree and build it
this.build = function(treeRoot) {
var treeRootNode = new TreeNode();
treeRootNode.build(treeRoot);
this.buildTree(treeRootNode, 1);
};
this.buildTree = function(treeNode, level) {
treeNode = this.flattenNodes(treeNode);
this.addTreeNodeToLevel(treeNode, level);
if (treeNode.hasChildren()) {
var children = treeNode.getChildTreeNodes();
for (var i = 0; i < children.length; i++) {
children[i].setParent(treeNode);
this.buildTree(children[i], level + 1);
}
} else if (!treeNode.getParent()) {
treeNode.setGog(false);
}
};
this.addTreeNodeToLevel = function(treeNode, level) {
if (this.tree.hasOwnProperty(level) === false) {
this.tree[level] = [];
}
treeNode.setLevel(level);
this.tree[level].push(treeNode);
};
/**
* Returns the treeNode that's at the top of the tree
* @returns {*}
*/
this.getRoot = function() {
if (this.tree.hasOwnProperty(this.TOP_LEVEL) && (this.tree[this.TOP_LEVEL] instanceof Array) && this.tree[this.TOP_LEVEL].length) {
return this.tree[this.TOP_LEVEL][0];
}
};
/**
* Flattens tree nodes when there's a node with only one child (merges the parent and the child)
* @param treeNode
*/
this.flattenNodes = function(treeNode) {
while (treeNode.getChildCount() === 1) {
var treeNodeChild = treeNode.getChildAt(0);
// Link parent field if exists, before replacement
treeNodeChild.setParent(treeNode.getParent());
treeNodeChild.flatten();
treeNode = treeNodeChild;
}
// If flattened and remained without parent or children then it's not gog
if (!treeNode.hasParent() && !treeNode.hasChildren()) {
treeNode.setGog(false);
}
return treeNode;
};
// Create and return an array of event object that are ordered by their levels in the tree
this.toArray = function() {
var treeNodesArray = [],
eventsInLevel;
var levels = Object.keys(this.tree);
if (levels.length) {
levels.sort();
for (var i = 0; i < levels.length; i++) {
eventsInLevel = this.tree[levels[i]];
treeNodesArray = treeNodesArray.concat(eventsInLevel);
}
}
return treeNodesArray;
};
}
/**
* TreeNode "Class" - Returns a TreeNode instance
* @returns TreeNode instance
*/
function TreeNode() {
/**
* PROPERTIES
*/
this.node = undefined;
this.meta_data = {
parent: undefined,
children: [],
level: 0,
flattenCount: 0,
inGroupOfGroups: true,
originalTime: undefined,
createAsEvent: true,
};
/**
* METHODS
*/
// Init the tree-node and build it
this.build = function(node) {
this.node = node;
this.meta_data.originalTime = node.time_of_event;
this.setChildTreeNodes();
this.setExtraData();
};
this.setChildTreeNodes = function() {
var children = [],
childTreeNode, childNode;
if (this.node.children && this.node.children instanceof Array && this.node.children.length) {
for (var i = 0; i < this.node.children.length; i++) {
childNode = this.node.children[i];
childTreeNode = new TreeNode();
childTreeNode.build(childNode);
children.push(childTreeNode);
}
}
delete this.node.children;
this.meta_data.children = children;
};
/**
* Returns the "node" field of the event that is saved on the treeNode (i.e., on "this.node")
*/
this.getEventNode = function() {
return this.node.node;
};
this.getId = function() {
return this.node.id ? String(this.node.id) : '';
};
this.getParentId = function() {
return this.hasParent() ? this.getParent().getId() : '';
};
this.getMessageKey = function(prefix, suffix, rootId) {
var treeNodeId = this.getId();
// Add parent id from node id if it isn't contained in it
if (this.getParentId() && (treeNodeId.includes(this.getParentId()) === false)) {
treeNodeId = this.getParentId() + '_' + treeNodeId;
}
if (treeNodeId.includes(rootId) === false) {
treeNodeId = rootId + '_' + treeNodeId;
}
return prefix + '_' + this.getLevel() + '_' + treeNodeId + '_' + suffix;
};
this.getParentMessageKey = function(prefix, suffix, rootId) {
return this.hasParent() ? this.getParent().getMessageKey(prefix, suffix, rootId) : '';
};
this.getChildTreeNodes = function() {
return this.meta_data.children;
};
this.getChildAt = function(index) {
if (this.hasChildren() && this.meta_data.children[index]) {
return this.meta_data.children[index];
}
};
this.setLevel = function(level) {
this.meta_data.level = level;
};
// Returns original treenode level (before flattening, if occured)
this.getLevel = function() {
return parseInt(this.meta_data.level, 10);
};
this.setGog = function(isGog) {
this.meta_data.inGroupOfGroups = isGog;
};
this.isGog = function() {
return this.meta_data.inGroupOfGroups;
};
this.getParent = function() {
return this.meta_data.parent;
};
this.setParent = function(treeNode) {
this.meta_data.parent = treeNode;
};
this.hasParent = function() {
return typeof this.meta_data.parent !== 'undefined';
};
this.getOriginalTime = function() {
var originalTime = parseInt(Number(this.meta_data.originalTime), 10);
var defaultOnError = new GlideDateTime();
return originalTime || defaultOnError.getNumericValue();
};
this.setExtraData = function() {
this.meta_data.extraData = this.node.extraData;
delete this.node.extraData;
};
this.getExtraData = function(activeProcessor) {
// Add more key-values to extra data based on TreeNode data
if (activeProcessor.manipulateExtraData && (typeof activeProcessor.manipulateExtraData === 'function')) {
activeProcessor.manipulateExtraData(this);
}
return this.meta_data.extraData;
};
this.getValueFromExtraData = function(field) {
return this.isFieldInExtraData(field) ? this.meta_data.extraData[field] : undefined;
};
this.isFieldInExtraData = function(field) {
return this.meta_data && this.meta_data.extraData && this.meta_data.extraData.hasOwnProperty(field);
};
this.addToExtraData = function(key, value) {
if (key && ((value != undefined) || value != null)) {
this.meta_data.extraData[key] = value;
}
};
this.removeFromExtraData = function(fields) {
if (Array.isArray(fields) && fields.length) {
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
delete this.meta_data.extraData[field];
}
}
};
// This function is mandatory for the correct creation of GOG alerts
this.getManipulatedTimeOfEvent = function(activeProcessor) {
if (activeProcessor.getManipulatedTimeOfEvent && (typeof activeProcessor.getManipulatedTimeOfEvent === 'function')) {
return activeProcessor.getManipulatedTimeOfEvent(this);
} else {
gs.error('EvtMgmtJsonToEventsProcessor - expecting "getManipulatedTimeOfEvent" function in active processor');
return '';
}
};
// This function is mandatory for the correct creation of GOG alerts
this.getMyGroupType = function(activeProcessor) {
if (activeProcessor.getGroupType && (typeof activeProcessor.getGroupType === 'function')) {
return activeProcessor.getGroupType(this);
} else {
gs.error('EvtMgmtJsonToEventsProcessor - expecting "getGroupType" function in active processor');
return '';
}
};
this.getParentGroupType = function(activeProcessor) {
if (this.hasParent()) {
return this.getParent().getMyGroupType(activeProcessor);
}
return '';
};
/**
* Whether this treeNode should be created as event or not.
* Defaults to true, i.e. every treeNode will be created as an event, unless the
* active processor has a function named "shouldBeCreatedAsEvent" that determines
* whether the treeNode needs to become an event (by returning true), or not (by returning false).
*/
this.shouldBeCreatedAsEvent = function() {
return this.meta_data.createAsEvent;
};
/**
* Sets CreateAsEvent to the given argument.
* When the argument is false, this treeNode will not be created as an event.
*
* @param isCreateAsEvent - boolean
*/
this.setCreateAsEvent = function(isCreateAsEvent) {
this.meta_data.createAsEvent = String(isCreateAsEvent) === 'true';
};
// Returns FINAL treenode level (AFTER flattening, if occurred)
this.getActualTreeNodeLevel = function() {
if (!this.isGog()) {
return '';
} else if (this.hasParent()) {
return this.getParent().getActualTreeNodeLevel() + 1;
}
var treeNodeLevel = this.getLevel();
if (this.isFlattened()) {
treeNodeLevel += this.getFlattenCount();
}
return treeNodeLevel;
};
/**
* Increases the flatten count of a node (the times it was overridden/merged with its parents)
*/
this.flatten = function() {
this.meta_data.flattenCount++;
};
this.getFlattenCount = function() {
return this.meta_data.flattenCount;
};
/**
* Returns true if the treeNode was flattened (got overridden/merged with its parent)
* @returns {boolean}
*/
this.isFlattened = function() {
return this.getFlattenCount() > 0;
};
this.hasChildren = function() {
return this.getChildCount() > 0;
};
this.getChildCount = function() {
return this.getChildTreeNodes().length;
};
/**
* Returns raw node data that has the event fields
* @returns Object that includes event fields, e.g. { description: '...', metric_name: '...' }
*/
this.getEventData = function() {
return JSON.parse(JSON.stringify(this.node));
};
}
Sys ID
f2af19d0b74310107c038229ce11a9a3