Name

sn_nb_action.FlowGeneratorTypeHandler

Description

Handler script for flow generator type

Script

var FlowGeneratorTypeHandler = Class.create();
FlowGeneratorTypeHandler.prototype = Object.extendsObject(sn_nb_action.GeneratorTypeHandlerBase, {
  initialize: function() {
      this._log = new global.GSLog(Constants.PROP_LOG_LEVEL, this.type);
  },

  getId: function() {
      return Constants.GENERATOR_TYPE_SYS_ID.FLOW;
  },

  isValid: function(param) {
      var resourceGenerator = param.resourceGeneratorRecord;
      var flow = resourceGenerator.generator;
      if (!(flow && flow.type == Constants.FLOW_TYPE_SUBFLOW)) {
          var msg = gs.getMessage('Select a subflow for the generator field in order to use the flow generator type.');
          return {
              'isValid': false,
              'errorMessage': msg
          };
      }
      return {
          'isValid': true
      };
  },

  getOutputSchema: function(param) {
      var flowRecord = param.generatorRecord;
      if (!flowRecord || !flowRecord.isValidRecord()) {
          var errorDetails = new RAExceptionManager('INVALID_FLOW');
          this._log.error(errorDetails.getMessage());
          return {
              'status': Constants.STATUS_ERROR,
              'errorCode': errorDetails.getCode(),
              'errorMessage': errorDetails.getMessage()
          };
      }

      var schemaObjectList = [];
      var gr = new GlideRecord(Constants.TBL_FLOW_OUTPUT);
      gr.addQuery(Constants.COL_NAME, Constants.VAR__M_SYS_HUB_FLOW_OUTPUT_PREFIX + flowRecord.getUniqueValue());
      gr.query();
      while (gr.next()) {
          var name = gr.getValue(Constants.COL_ELEMENT);
          var label = gr.getValue(Constants.COL_LABEL);
          var type = gr.getValue(Constants.COL_INTERNAL_TYPE);
          if (type == Constants.TYPE_REFERENCE) {
              NextBestActionUtil.addToSchema(schemaObjectList, name, label, type, gr.getValue(Constants.COL_REFERENCE));
          } else {
              NextBestActionUtil.addToSchema(schemaObjectList, name, label, type);
          }
      }

      return {
          'status': Constants.STATUS_SUCCESS,
          'schema': schemaObjectList,
          'metaData': {}
      };
  },

  getOutputs: function(param) {
      var flowRecord = param.generatorRecord;
      var flowInputs = param.inputs;
      if (!flowRecord || !flowRecord.isValidRecord() || flowRecord.getValue(Constants.COL_ACTIVE) == 0) {
          var errorDetails = new RAExceptionManager('INVALID_OR_INACIVE_FLOW');
          this._log.error(errorDetails.getMessage());
          return {
              'status': Constants.STATUS_ERROR,
              'errorCode': errorDetails.getCode(),
              'errorMessage': errorDetails.getMessage()
          };
      }

      var flowOutputs;
      try {
          var flowName = flowRecord.getValue(Constants.COL_INTERNAL_NAME);
          var scope = flowRecord[Constants.COL_SYS_SCOPE][Constants.COL_SCOPE] + '';

          if (gs.getProperty(Constants.PROP_DEBUG_FLOW) == 'true') {
              flowOutputs = sn_fd.FlowAPI.executeSubflow(scope + '.' + flowName, flowInputs);
          } else {
              flowOutputs = sn_fd.FlowAPI.executeSubflowQuick(scope + '.' + flowName, flowInputs);
          }
      } catch (ex) {
          this._log.error('Failed to evaluate flow ' + flowRecord.getUniqueValue() + '. Error: ' + ex);
          errorDetails = new RAExceptionManager('UNKNOWN_ERROR_OCCURED');
          return {
              'status': Constants.STATUS_ERROR,
              'errorCode': errorDetails.getCode(),
              'errorMessage': errorDetails.getMessage()
          };
      }

      if (this._checkValidResourceGeneratorOutput(flowOutputs) && this._checkOutputArrayLengthConsistent(flowOutputs)) {
          flowOutputs = this._convertGlideRecordOutputsToArray(flowOutputs);
          var generatorOutputs = this._consolidateFlowOutputObject(flowOutputs);
          return {
              'status': Constants.STATUS_SUCCESS,
              'outputs': generatorOutputs
          };
      }

      errorDetails = new RAExceptionManager('INVALID_FLOW_OUTPUT');
      return {
          'status': Constants.STATUS_ERROR,
          'errorCode': errorDetails.getCode(),
          'errorMessage': errorDetails.getMessage()
      };

  },

  _checkValidResourceGeneratorOutput: function(flowOutputs) {
      var message = 'Flow output is empty, null or undefined';
      if (!flowOutputs) {
          this._log.warn(message);
          return false;
      }
      for (var outputVariable in flowOutputs) {
          if (flowOutputs[outputVariable] == null) {
              this._log.warn(message);
              return false;
          }
      }
      return true;
  },

  _checkOutputArrayLengthConsistent: function(flowOutputs) {
      if (this._getArrayAttribute(flowOutputs) && !this._isArrayOutputLengthConsistent(flowOutputs)) {
          return false;
      }
      return true;
  },

  //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 generator outputs are mandatory inputs for a recommended action.');
          return false;
      } else if (flag == 0) {
          this._log.warn('Flow output returns multiple outputs of array/list of records type of unequal length.');
          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) {
      var length = -1;
      if (Array.isArray(object[key])) {
          length = object[key].length;
      } else if ((object[key] instanceof GlideRecord) && !object[key].isValidRecord()) {
          length = object[key].getRowCount();
      }
      return length;
  },

  _convertGlideRecordOutputsToArray: function(flowOutputs) {
      var updatedFlowOutputs = {};
      for (var flowOutputVariable in flowOutputs) {
          var flowOutputValue = flowOutputs[flowOutputVariable];
          if ((flowOutputValue instanceof GlideRecord) && flowOutputValue.hasNext()) {
              var list = [];
              while (flowOutputValue.next()) {
                  var gr = new GlideRecord(flowOutputValue.getTableName());
                  gr.get(flowOutputValue.sys_id);
                  list.push(gr);
              }
              flowOutputValue = list;
          }
          updatedFlowOutputs[flowOutputVariable] = flowOutputValue;
      }
      return updatedFlowOutputs;
  },

  _consolidateFlowOutputObject: function(outputObj) {
      var outputObjArray = [];
      var arrayTypeAttribute = this._getArrayAttribute(outputObj);
      var length = 1;
      if (arrayTypeAttribute) {
          length = outputObj[arrayTypeAttribute].length;
      }
      for (var i = 0; i < length; i++) {
          var actionObject = {};
          var accessibleCheck = true;
          for (var key in outputObj) {
              if (outputObj[key] && Array.isArray(outputObj[key])) {
                  actionObject[key] = outputObj[key][i];
              } else {
                  actionObject[key] = outputObj[key];
              }
              //adding accessible check for reference type records
              if (sn_nb_action.NextBestActionUtil.isValidRecord(actionObject[key])) {
                  if (!actionObject[key].canRead()) {
                      accessibleCheck = false;
                      break;
                  }
              }
          }
          if (accessibleCheck)
              outputObjArray.push(actionObject);
      }
      return outputObjArray;
  },

  type: 'FlowGeneratorTypeHandler'
});

Sys ID

8129309553130110e530ddeeff7b1236

Offical Documentation

Official Docs: