Name
sn_nb_action.RecommendedActionEvaluatorImpl
Description
This class is responsible for evaluating Recommended Action Records.
Script
var RecommendedActionEvaluatorImpl = Class.create();
RecommendedActionEvaluatorImpl.prototype = {
initialize: function() {
this._inputGenerator = new sn_nb_action.InputGenerator();
this._actionInputPillParser = new sn_nb_action.NBAActionInputPillParser();
this._log = new global.GSLog(sn_nb_action.Constants.PROP_LOG_LEVEL, this.type);
},
// prepares query for Recommended Action Records based on current rule
prepQuery: function(ruleRecord, recommendationType) {
var recommendedActionsGr = new GlideRecord(sn_nb_action.Constants.TBL_RECOMMENDED_ACTIONS);
recommendedActionsGr.addQuery(sn_nb_action.Constants.COL_RULE, ruleRecord.getUniqueValue());
recommendedActionsGr.addActiveQuery();
if (recommendationType == sn_nb_action.Constants.VAL_FIELD_RECOMMENDATIONS)
recommendedActionsGr.addQuery(sn_nb_action.Constants.COL_ACTION_TYPE, sn_nb_action.Constants.VAL_ACTION_TYPE_FIELD_RECOMMENDATION);
else
recommendedActionsGr.addQuery(sn_nb_action.Constants.COL_ACTION_TYPE, '!=', sn_nb_action.Constants.VAL_ACTION_TYPE_FIELD_RECOMMENDATION);
recommendedActionsGr.orderBy(sn_nb_action.Constants.COL_ORDER);
return recommendedActionsGr;
},
evaluateRecommendedAction: function(recommendedActionRecord, currentRecord) {
var currentRecommendedActions = [];
var action, inputObjArray, recommendedAction, hint, groups;
action = recommendedActionRecord[sn_nb_action.Constants.COL_ACTION].getRefRecord();
hint = recommendedActionRecord.getDisplayValue(sn_nb_action.Constants.COL_RECOMMENDATION_HINT);
groups = recommendedActionRecord.getValue(sn_nb_action.Constants.COL_CONTEXT_GROUPS);
var actionInputGeneratorRecord = recommendedActionRecord[sn_nb_action.Constants.COL_ACTION_INPUT_GENERATOR].getRefRecord();
var resourceGeneratorRecord = recommendedActionRecord[Constants.COL_RESOURCE_GENERATOR].getRefRecord();
inputObjArray = this._getInputObjectArray(recommendedActionRecord, actionInputGeneratorRecord, currentRecord, resourceGeneratorRecord);
if (!inputObjArray) {
this._log.warn("Failed to evaluate recommendedActionRecord " + recommendedActionRecord.getUniqueValue() + " for current context record " + currentRecord.getUniqueValue());
return false;
}
for (var i = 0; i < inputObjArray.length; i++) {
recommendedAction = sn_nb_action.RecommendedAction.createFromRule(action, inputObjArray[i], hint, groups);
currentRecommendedActions.push(recommendedAction);
}
return currentRecommendedActions;
},
// gets an attribute which has value of type array/list of records else returns false
_getArrayAttribute: function(object) {
for (var key in object) {
if (Array.isArray(object[key]) || ((object[key] instanceof GlideRecord) && !object[key].isValidRecord()))
return key;
}
return false;
},
// object attributes storing value of array type must have equal and non zero array length. If not returns false
_isArrayOutputLengthConsistent: function(object) {
var arrayLength = 0;
var flag = 1;
for (var key in object) {
var len = this._getLengthOfList(key, object);
if (len == -1)
continue;
if (arrayLength != len && arrayLength == 0)
arrayLength = len;
else if (arrayLength != len) {
flag = 0;
break;
}
}
if (arrayLength == 0) {
this._log.warn("Flow output returns atleast one empty array/list of records. Flow input generator outputs are mandatory inputs for a recommended action. Hence Ignoring evaluation of recommended action record");
return false;
} else if (flag == 0) {
this._log.warn("Flow output returns multiple outputs of array/list of records type of unequal length. Ignoring evaluation of recommended action record");
return false;
}
return true;
},
//get length of output attribute of array/list of records type. returns -1 if no attribute of this type found.
_getLengthOfList: function(key, object) {
if (Array.isArray(object[key]))
return object[key].length;
else if ((object[key] instanceof GlideRecord) && !object[key].isValidRecord())
return object[key].getRowCount();
else return -1;
},
_consolidateInputObject: function(inputObj) {
// if multiple arrays are present they should have equal length
var inputObjArray = [];
var arrayTypeAttribute = this._getArrayAttribute(inputObj);
var length = 1;
if (arrayTypeAttribute)
length = inputObj[arrayTypeAttribute].length;
for (var i = 0; i < length; i++) {
var actionObject = new Object();
var accessibleCheck = true;
for (var key in inputObj) {
if (inputObj[key] && Array.isArray(inputObj[key]))
actionObject[key] = inputObj[key][i];
else
actionObject[key] = inputObj[key];
try {
//adding accessible check for reference type records
if (sn_nb_action.NextBestActionUtil.isValidRecord(actionObject[key])) {
if (!actionObject[key].canRead()) {
accessibleCheck = false;
break;
}
}
} catch (err) {
this._log.error("Error while checking canRead Access on inputs " + actionObject[key].sys_id);
}
}
if (accessibleCheck)
inputObjArray.push(actionObject);
}
return inputObjArray;
},
_getInputObjectArray: function(recommendedActionRecord, actionInputGeneratorRecord, currentRecord, resourceGeneratorRecord) {
var inputObjArray, inputGeneratorOutput = {};
var generator = new GeneratorService(resourceGeneratorRecord);
if (generator.hasValidRecord()) {
inputObjArray = [];
var generatorOutput = generator.getGeneratorOutput(currentRecord);
var outputs = generatorOutput && generatorOutput.outputs || [];
var actionInputGlideVar = recommendedActionRecord[Constants.COL_ACTION_INPUT];
for (var idx = 0; idx < outputs.length; idx++) {
var inputObj = this._actionInputPillParser.parseActionInputsPill(actionInputGlideVar, outputs[idx], currentRecord);
inputObjArray.push(inputObj);
}
return inputObjArray;
} else if ((recommendedActionRecord.getValue(sn_nb_action.Constants.COL_SET_ACTION_INPUTS_DYNAMICALLY) == 1) && sn_nb_action.NextBestActionUtil.isValidRecord(actionInputGeneratorRecord)) {
inputGeneratorOutput = this._inputGenerator.runInputGenerator(actionInputGeneratorRecord, currentRecord);
if (!this._checkValidInputGeneratorOutput(inputGeneratorOutput, currentRecord))
return false;
if (!this._checkOuputArrayLengthConsistent(inputGeneratorOutput, currentRecord))
return false;
}
inputObjArray = this._createInputObjectArray(inputGeneratorOutput, currentRecord, recommendedActionRecord);
return inputObjArray;
},
_createInputObjectArray: function(inputGeneratorOutput, currentRecord, recommendedActionRecord) {
var inputObjArray, inputObj;
var actionInputGlideVar = recommendedActionRecord[sn_nb_action.Constants.COL_ACTION_INPUT];
try {
inputObj = this._actionInputPillParser.parseActionInputsPill(actionInputGlideVar, inputGeneratorOutput, currentRecord);
} catch (ex) {
this._log.error("Error parsing action inputs. Ignoring evaluation of recommended action record");
return false;
}
inputObjArray = this._consolidateInputObject(inputObj);
return inputObjArray;
},
_checkValidInputGeneratorOutput: function(inputGeneratorOutput, currentRecord) {
var messg = "Check subflow is active and flow ouputs are accessible to user,not empty, null or undefined. Ignoring evaluation of recommended action record";
if (!inputGeneratorOutput) {
this._log.warn(messg);
return false;
}
for (var outputVariable in inputGeneratorOutput) {
if (inputGeneratorOutput[outputVariable] == null || !this._checkInputGeneratorOutputAccessible(inputGeneratorOutput[outputVariable])) {
this._log.warn(messg);
return false;
}
}
return true;
},
_checkInputGeneratorOutputAccessible: function(inputGeneratorOutputVar) {
if (sn_nb_action.NextBestActionUtil.isValidRecord(inputGeneratorOutputVar)) {
if (!inputGeneratorOutputVar.canRead()) {
return false;
}
}
return true;
},
_checkOuputArrayLengthConsistent: function(inputGeneratorOutput, currentRecord) {
if (this._getArrayAttribute(inputGeneratorOutput) && !this._isArrayOutputLengthConsistent(inputGeneratorOutput))
return false;
return true;
},
type: 'RecommendedActionEvaluatorImpl'
};
Sys ID
cc943508eb3220106fd0b6302a522886