Name

global.PatternPrePostHook

Description

This script provides the option to run scripts from sa_pattern_prepost_script as part of Discovery in 2 modes 1. Pre sensor payload processing 2. Post sensor payload processing 3. On pattern failure processing

Script

gs.include("PrototypeServer");

var PatternPrePostHook = Class.create();

var preSensorIntroductionComment = '/*\n * Pre sensor: You can change payload before it will be proccesed by Identification Engine.\n * Use IEJsonUtility in order to add relevant information to the payload\n * Input parameters in Pre sensor mode: payload, patternId\n */\n';

var postSensorIntroductionComment = '/*\n * Post sensor: You can update/add missing info to the DB based on result (Json) from\n * Identification Engine\n * Output parameters in Post sensor mode: payload\n */\n';

var prePostTemplateCommonPortion = '\n\nvar rtrn = {};\n\n// parsing the json string to a json object\nvar payloadObj = JSON.parse(payload);\n\n// Clearing payload string to save memory\npayload = null;\n\n';

var returnObjectCommonPortion = '// You can return a message and a status, on top of the input variables that you MUST return.\n// Returning the payload as a Json String is mandatory in case of a pre sensor script, and optional in\ case of post sensor script.\n// If you want to terminate the payload processing due to your business logic - you can set isSuccess to false.\nrtrn = {\n\t\'status\': {\n\t\t\'message\': \'Enter your message here\',\n\t\t\'isSuccess\' :true\n\t},\n\t\'patternId\': patternId';

var returnObjectPayloadPortion = ',\n\t\'payload\': JSON.stringify(payloadObj)';
var returnObjectClosingPortion = '\n};';

var businessLogicComment = '// Put your business logic here\n// For node logger, please use: prePostNodeLogger.info\\warn\\error\\debug(prePostLogPrefix + \'<YOUR_LOG_STATEMENT>\')\n\n';

PatternPrePostHook.preSensorTemplate = preSensorIntroductionComment + prePostTemplateCommonPortion + businessLogicComment + returnObjectCommonPortion + returnObjectPayloadPortion + returnObjectClosingPortion;

PatternPrePostHook.postSensorTemplate = postSensorIntroductionComment + prePostTemplateCommonPortion + businessLogicComment + returnObjectCommonPortion + returnObjectClosingPortion;

PatternPrePostHook.preExecTemplate = '/* \n * Pre execution: This script will run before the execution of the assigned pattern/s\n * It allows the user to add data that can be accessed by the running the pattern\n * This is done by adding variables to the scriptable PrePatternExecutionData object\n * Below is an example of the possible variables that can be added.\n */\n\nvar data = new SNC.PrePatternExecutionData();\n\n// Add a string with variable name \'stringA\'\ndata.addString(\'stringA\',\'string_value\');\n\n// Add a list with variable name \'listA\', value given must be a list\nvar lst = [\'list_first_value\',\'list_second_value\'];\ndata.addList(\'listA\',lst);\n\n// This will create a new table variable \'tableA\'\n// The value given must be a map, whose keys will be the tables fields and values the first row\ndata.addTableEntry(\'tableA\',{\n\t\'first_field\':\'row 1 arg1\',\n\t\'second_field\':\'row 1 arg2\'\n});\n\n// This will add a second row to the previous table \'tableA\'\n// Note that the keys in map must match the previous tables fields\ndata.addTableEntry(\'tableA\',{\n\t\'first_field\':\'row 2 arg3\',\n\t\'second_field\':\'row 2 arg4\'\n});\n\n// Use this method if you want the pattern not to be executed\n // data.executePattern(false);\n\n// Must return the data at end\nrtrn = data;';

PatternPrePostHook.onFailureTemplate = '/*\n * On Failure: You can do operations in case a pattern failed.\n */\nvar rtrn = {};\n\n' + businessLogicComment + returnObjectCommonPortion + returnObjectClosingPortion;

PatternPrePostHook.executionPhase = {
  preSensor: 1,
  postSensor: 2,
  preExecution: 3,
  onFailure: 4
};

PatternPrePostHook.LOGGER_NAME = 'PatternPrePostExecutor';
PatternPrePostHook.LOGGER_NAME_LOG_PREFIX = '[' + PatternPrePostHook.LOGGER_NAME + ']';

PatternPrePostHook.prototype = {
  HD_DISCOVERY_TYPE: 1,
  SM_DISCOVERY_TYPE: 2,
  PATTERN_PREPOST_TABLE_NAME: "sa_pattern_prepost_script",
  LOG_PREFIX: PatternPrePostHook.LOGGER_NAME_LOG_PREFIX + '[sys_script_include_a5aff88193321200248635bb357ffb4d]',

  initialize: function(inputEccQueueSysId, outputEccQueueSysId) {
  	this.nodeLogger = new sn_automation.AutomationAPI().getNodeLogger(PatternPrePostHook.LOGGER_NAME);
  	
  	// Pattern debugger simulator is initializing this "class" with no parameters, hence setting prefixes to default
  	this.preSensorLogPrefix = this.LOG_PREFIX;
  	this.postSensorLogPrefix = this.LOG_PREFIX;
  	this.onFailureLogPrefix = this.LOG_PREFIX;
  	
  	if (inputEccQueueSysId)
  		this.preSensorLogPrefix += '[ecc_queue_' + inputEccQueueSysId + '] ';
  	
  	if (outputEccQueueSysId) {
  		this.postSensorLogPrefix += '[ecc_queue_' + outputEccQueueSysId + '] ';
  		this.onFailureLogPrefix += '[ecc_queue_' + outputEccQueueSysId + '] ';
  	}
  },

  /**
  * This function running script from table sa_pattern_prepost_script 
  * in order to perform pre sensor logic.
  * For example: we can add additional info to the db after the payload was processed
  * @method runPreProcess
  * @param {Json} params with the following mandatory structure { 'patternId':'someid',payload: {some object} }
  * @return {Json} params with the same structure as received
  */
  runPreProcess: function(params) {
  	this.nodeLogger.info(this.preSensorLogPrefix + 'About to run pre-sensor scripts for pattern "' + params.patternId + '"');
  	this.nodeLogger.debug(this.preSensorLogPrefix + 'Post-sensors scripts parameters [resultHandler parameter is not a JS object, hence will always be empty]:\n' + JSON.stringify(params));

  	var patternSysId = this.getPatternSysIdByName(params.patternId);
  	var resultHandler = params.resultHandler;
  	var nodeName = params.nodeName;

  	if(gs.nil(patternSysId)) {
  		var errPatSysId = 'Abort runPreProcess operation due to null pattern sys_id';
  		this.nodeLogger.error(this.preSensorLogPrefix + errPatSysId);
  		resultHandler.addNodeToLogWithPages(errPatSysId, 'FAILURE', nodeName, '');
  		throw errPatSysId;
  	}
  	
  	if (params.payload ===  null || gs.nil(params.payload) || params.payload == 'null') {
  		var errPayload = 'Abort runPreProcess operation due to empty payload. check the log for more information';
  		this.nodeLogger.error(this.preSensorLogPrefix + errPayload);
  		resultHandler.addNodeToLogWithPages(errPayload, 'FAILURE', nodeName, '');
  		throw errPayload;
  	}
  	
      var gr = this.getPrePostGlideRecords(patternSysId, PatternPrePostHook.executionPhase.preSensor);
  	
  	// Define the success/failure function passed into processing.
  	var successFunc = function (params, gr, successMessage) {
  		var resultHandler = params.resultHandler;
  		var nodeName = params.nodeName;
  		var scriptName = gr.getValue('name');
  		resultHandler.addNodeToLogWithPages(successMessage, 'SUCCESS', nodeName, scriptName);
  	};
  	var failureFunc = function (params, gr, failureMessage) {
  		var resultHandler = params.resultHandler;
  		var nodeName = params.nodeName;
  		var scriptName = gr.getValue('name');
  		resultHandler.addNodeToLogWithPages(failureMessage, 'FAILURE', nodeName, scriptName);
  		throw 'name of failed script: ' + scriptName;
  	};

      return this.runSelectedPreProcess(params, gr, successFunc, failureFunc);
  },

  /**
  * This function runs the selected scripts from table sa_pattern_prepost_script 
  * in order to perform pre sensor logic.
  * For example: we can add additional info to the db after the payload was processed
  * @method runSelectedPreProcess
  * @param {Json} params with the following mandatory structure { 'patternId':'someid',payload: {some object} }
  * @gr GlideRecord which has queried for the desired pre-sensors.
  * @successFunc function variable which is used to provide an option for logging successful processing.
  * @failureFunc function variable which is used to provide an option for logging failed processing.
  * @return {Json} params with the same structure as received
  */
  
  runSelectedPreProcess: function(params, gr, successFunc, failureFunc) {
  	var updatedParams = params;
  	var totalExecutionTime = 0;

  	while(gr.next()) {
          var result;
  		var scriptName = gr.getValue('name');
  		this.nodeLogger.info(this.preSensorLogPrefix + 'About to run pre-sensor: "' + scriptName + '"');
  		try{
  			var stop_watch_for_pre_sensor = new GlideStopWatch();
  			result = this.executeJsScript(gr, updatedParams, PatternPrePostHook.executionPhase.preSensor);
  			stop_watch_for_pre_sensor.stop();
  			totalExecutionTime = totalExecutionTime + stop_watch_for_pre_sensor.getTime();
  			this.nodeLogger.info(this.preSensorLogPrefix + 'Pre-sensor finished with execution time of '+stop_watch_for_pre_sensor.getTime()+' milliseconds.');
  			this.nodeLogger.debug(this.preSensorLogPrefix + 'Result returned:\n' + JSON.stringify(result));
  		} catch(err){
  			var failureMessage = 'Pre-sensor script "' + scriptName + '" failed due to: ' + err;
  			this.nodeLogger.error(this.preSensorLogPrefix + failureMessage);
  			failureFunc(params, gr, failureMessage);
  		}
  		
  		if (!gs.nil(result.status) && result.status.isSuccess == true) {
              updatedParams.payload = result.payload;
  			if(totalExecutionTime >= 0)
  				updatedParams.totalExecutionTime = totalExecutionTime;
  			else
  				updatedParams.totalExecutionTime = null;
              var successMessage = 'Success: ' + result.status.message + ".";
  			this.nodeLogger.debug(this.preSensorLogPrefix + successMessage);
  			successFunc(params, gr, successMessage);
          } else {
  			var failureMessage;
  			if(!gs.nil(result.status)){
  				failureMessage = 'Failure: ' + result.status.message + ".";
  			} else {
  				failureMessage = 'Failure: no result status.';
  			}
  			this.nodeLogger.error(this.preSensorLogPrefix + failureMessage);
  			failureFunc(params, gr, failureMessage);
          }
      }
  	return updatedParams;
  },
  
  
  needsPreProcessing: function(patternId) {
  	var logPrefix = this.preSensorLogPrefix || '';
  	var patternSysId = this.getPatternSysIdByName(patternId);
  	if(gs.nil(patternSysId)) {
  		var failureMessage = 'Abort runPreProcess operation due to null pattern sys_id';
  		this.nodeLogger.error(logPrefix + failureMessage);
  		throw failureMessage;
  	}
      var gr = this.getPrePostGlideRecords(patternSysId, PatternPrePostHook.executionPhase.preSensor);
  	this.nodeLogger.debug(logPrefix + 'Found ' + gr.getRowCount() + ' pre-sensors for pattern');
      return gr.hasNext();
  },
  
  needsPostProcessing: function(patternId) {
  	var logPrefix = this.postSensorLogPrefix || '';
  	var patternSysId = this.getPatternSysIdByName(patternId);
  	if(gs.nil(patternSysId)){
  		var failureMessage = 'Abort runPreProcess operation due to null pattern sys_id';
  		this.nodeLogger.error(logPrefix + failureMessage);
  		throw failureMessage;
  	}
  	var gr = this.getPrePostGlideRecords(patternSysId, PatternPrePostHook.executionPhase.postSensor);
  	this.nodeLogger.debug(logPrefix + 'Found ' + gr.getRowCount() + ' post-sensors for pattern');
  	return gr.hasNext();
  },

  /**
  * This function running script from table sa_pattern_prepost_script 
  * in order to perform post sensor logic.
  * For example: we can verify that some info was added to the DB.
  * @method runPostProcess
  * @param {Json} params with the following mandatory structure { 'patternId':'someid', payload: {some object} }
  * @return {Json} params with the same structe as arrived
  */
  runPostProcess: function(params) {
  	this.nodeLogger.info(this.postSensorLogPrefix + 'About to run post-sensor scripts for pattern "' + params.patternId + '"');
  	this.nodeLogger.debug(this.postSensorLogPrefix + 'Post-sensors scripts parameters [resultHandler parameter is not a JS object, hence will always be empty]:\n' + JSON.stringify(params));

  	var patternSysId = this.getPatternSysIdByName(params.patternId);
  	
  	var resultHandler = params.resultHandler;
  	//variable for the pattern log
  	var nodeName = params.nodeName;

  	if (gs.nil(patternSysId)) {
  		var errPatSysId = 'Abort runPostProcess operation due to null pattern sys_id';
  		this.nodeLogger.error(this.postSensorLogPrefix + errPatSysId);
  		resultHandler.addNodeToLogWithPages(errPatSysId, 'FAILURE', nodeName, '');
  		throw errPatSysId;
  	}
  	var totalExecutionTime = 0;
  	var gr = this.getPrePostGlideRecords(patternSysId, PatternPrePostHook.executionPhase.postSensor);
  	
  	while(gr.next()) {
  		var result;
  		var scriptName = gr.getValue('name');
  		this.nodeLogger.info(this.postSensorLogPrefix + 'About to run post-sensor: "' + scriptName + '"');

  		if (gr.getValue('use_split_payload') + '' != '1') {
  			this.nodeLogger.debug(this.postSensorLogPrefix + 'Using IRE output based post sensor. "use_split_payload" is set to "false" on the post-sensor');
  			params['payloadRecords'] = '';
  		}
  		else {
  			this.nodeLogger.debug(this.postSensorLogPrefix + 'Using GlideRecord based post sensor. "use_split_payload" is set to "true" on the post-sensor');
  			params['payloadRecords'] = params.resultHandler.getSaPagedPayloadQueryRecord(g_probe);
  		}
  		
  		
  		try{
  			var stop_watch_for_post_sensor = new GlideStopWatch();
  			result = this.executeJsScript(gr, params, PatternPrePostHook.executionPhase.postSensor);
  			stop_watch_for_post_sensor.stop();
  			totalExecutionTime = totalExecutionTime + stop_watch_for_post_sensor.getTime();
  			this.nodeLogger.info(this.postSensorLogPrefix + 'Post-sensor finished with execution time of '+stop_watch_for_post_sensor.getTime()+' milliseconds');
  			this.nodeLogger.debug(this.postSensorLogPrefix + 'Result returned:\n' + JSON.stringify(result));
  		} catch(err){
  			var failureMessage = 'Post-sensor script "' + scriptName + '" failed due to: ' + err;
  			this.nodeLogger.error(this.postSensorLogPrefix + failureMessage);
  			resultHandler.addNodeToLogWithNoPages(failureMessage, 'FAILURE', nodeName, scriptName);
  			throw 'name of failed script: ' + scriptName;
  		}			
          if (!gs.nil(result.status) && result.status.isSuccess == true) {
              var successMessage = 'Success: ' + result.status.message + ".";
  			this.nodeLogger.debug(this.postSensorLogPrefix + successMessage);
  			gs.info('Result is success');
  			resultHandler.addNodeToLogWithNoPages(successMessage, 'SUCCESS', nodeName, scriptName);
          } else {
  			var failureMessage;
  			if(!gs.nil(result.status)){
  				failureMessage = 'Failure: ' + result.status.message + ".";
  			} else {
  				failureMessage = 'Failure: no result status.';
  			}
  			this.nodeLogger.error(this.postSensorLogPrefix + failureMessage);
  			resultHandler.addNodeToLogWithNoPages(failureMessage, 'FAILURE', nodeName, scriptName);
              throw 'Abort runPostProcess operation due to: ' + result.status.message + ' GlideRecord = ' + gr + ".";	
          }
      }
  	if(totalExecutionTime >= 0)
  		params.totalExecutionTime = totalExecutionTime;
  	else
  		params.totalExecutionTime = null;
  	return params;
  },
  
  /**
  * This function running script from table sa_pattern_prepost_script 
  * in order to perform on-failure logic.
  * @method runOnFailure
  * @param {Json} params with the following mandatory structure { 'patternId':'someid' }
  * @return {Json} params with the same structe as arrived
  */
  runOnFailure: function(params) {
  	this.nodeLogger.info(this.onFailureLogPrefix + 'About to run on-failure scripts for pattern "' + params.patternId + '"');
  	this.nodeLogger.debug(this.onFailureLogPrefix + 'On-failure scripts parameters [resultHandler parameter is not a JS object, hence will always be empty]:\n' + JSON.stringify(params));
  	var patternSysId = this.getPatternSysIdByName(params.patternId);
  	
  	var resultHandler = params.resultHandler;
  	//variable for the pattern log
  	var nodeName = params.nodeName;

  	if (gs.nil(patternSysId)) {
  		var errPatSysId = 'Abort runOnFailure operation due to null pattern sys_id';
  		this.nodeLogger.error(this.onFailureLogPrefix + errPatSysId);
  		resultHandler.addNodeToLogWithPages(errPatSysId, 'FAILURE', nodeName, '');
  		throw errPatSysId;
  	}
  	var gr = this.getPrePostGlideRecords(patternSysId, PatternPrePostHook.executionPhase.onFailure);

  	while(gr.next()) {
  		var result;
  		var scriptName = gr.getValue('name');
  		
  		try{
  			result = this.executeJsScript(gr, params, PatternPrePostHook.executionPhase.onFailure);
  		} catch(err){
  			var failureMessage = 'Running on failure script- ' + scriptName + ' failed due to: ' + err + ".";
  			resultHandler.addNodeToLogWithNoPages(failureMessage, 'FAILURE', nodeName, scriptName);
  			throw 'name of failed script: ' + scriptName;
  		}			
          if (!gs.nil(result.status) && result.status.isSuccess == true) {
              var successMessage = 'Success: ' + result.status.message + ".";
  			this.nodeLogger.debug(this.onFailureLogPrefix + successMessage);
  			gs.debug('runOnFailure: result is success');
  			resultHandler.addNodeToLogWithNoPages(successMessage, 'SUCCESS', nodeName, scriptName);
          } else {
  			var failureMessage;
  			if(!gs.nil(result.status)){
  				failureMessage = 'Failure: ' + result.status.message + ".";
  			} else {
  				failureMessage = 'Failure: no result status.';
  			}
  			resultHandler.addNodeToLogWithNoPages(failureMessage, 'FAILURE', nodeName, scriptName);
  			
  			var detailedFailureMessage = 'Abort runOnFailure operation due to: ' + failureMessage + ' GlideRecord = ' + gr;
  			this.nodeLogger.error(this.onFailureLogPrefix + detailedFailureMessage);
              throw detailedFailureMessage;	
          }
      }
  },
  
  getPatternSysIdByName: function(patternName){
  	var gr = new GlideRecord('sa_pattern');
  	gr.addQuery("name", patternName);
      gr.query();
  	
  	if(gr.next())
  		return gr.sys_id;
  	return null;
  },
  
  getPrePostGlideRecords: function(patternId, whenToExec) {
      var gr = new GlideRecord(this.PATTERN_PREPOST_TABLE_NAME);
  	gr.addQuery("active", true);
      gr.addQuery("pattern", 'CONTAINS', patternId);
      gr.addQuery("execution_type", this.HD_DISCOVERY_TYPE);
      gr.addQuery("when_to_exec", whenToExec);
      gr.orderBy("order");
      gr.orderBy("name");
      gr.query();
      
      return gr;
  },

  /**
  * This function executing script from GlideRecord 
  * @method executeJsScript
  * @param {Json} params with the following structure { 'param1':'value1', ... }
  * @return {Json} result {}
  */
  executeJsScript: function(gr, params, execType) {
  	var logPrefix = this.getLogPrefixByExecutionPhase(execType);
  	params.prePostNodeLogger = this.nodeLogger;
  	params.prePostLogPrefix = logPrefix;

      var evaluator = new GlideScopedEvaluator();
      var result = evaluator.evaluateScript(gr, "script", params);
  	
  	try {
  		this.nodeLogger.debug(logPrefix + 'The evaluation result is: ' + JSON.stringify(result));
  	} catch (e){
  		// nothing to do here...
  	}

      if (this.isValidResultFormat(result, execType) == true){
            return result;
      } else {
  		var errorMessage = 'Abort executeJsScript due to invalid result format ';
  		this.nodeLogger.error(this.LOG_PREFIX + errorMessage);
          throw errorMessage;
      }        
  },
  
  getLogPrefixByExecutionPhase: function(executionPhase) {
  	var logPrefix = this.LOG_PREFIX;

  	switch (executionPhase) {
  		case PatternPrePostHook.executionPhase.preSensor:
  			logPrefix = this.preSensorLogPrefix;
  			break;
  		case PatternPrePostHook.executionPhase.postSensor:
  			logPrefix = this.postSensorLogPrefix;
  			break;
  		case PatternPrePostHook.executionPhase.onFailure:
  			logPrefix = this.onFailureLogPrefix;
  			break;
  	}

  	return logPrefix;
  },

  /*
  * This function is checking if the result format is valid 
  * Example of the result format:
  * { "status":{"message":"test",
  *             "isSuccess":true},
  *   "patternId":"70a27f8593301200f81a35bb357ffb82",
  *   "payload":{JSON PAYLOAD FORMAT}
  */
  
  isValidResultFormat: function(result, execType){
      if(execType == PatternPrePostHook.executionPhase.preSensor && !result.hasOwnProperty('payload'))
          return false;
      
      if(result.hasOwnProperty('status') && 
         result.status.hasOwnProperty('isSuccess') && 
         result.status.hasOwnProperty('message')){
          
          return true;
      }
      return false;
  },

  type: "PatternPrePostHook"
};

Sys ID

a5aff88193321200248635bb357ffb4d

Offical Documentation

Official Docs: