Name

sn_em_ai.EvtMgmtAttributesApi

Description

No description available

Script

var EvtMgmtAttributesApi = Class.create();
EvtMgmtAttributesApi.prototype = {
  initialize: function() {
      this.evtMgmtCommons = new global.EvtMgmtCommons();

      this.WARNING_STATUS = 204;
      this.ERROR_STATUS = 400;
      this.MAX_RETURNED_RECORDS_DEFAULT = 100000;
      this.MAX_RETURNED_RECORDS = parseInt(gs.getProperty(
          'sn_sow_exp_app.evt_mgmt.facet.max_returned_records', this.MAX_RETURNED_RECORDS_DEFAULT), 10) || 100000;

      //result keys
      this.NAME = "name";
      this.COUNT = "count";
      this.SYS_ID = "sys_id";
      this.EMPTY_VALUE = "(empty)";
      this.EMPTY_STRING = "empty";

      //constants
      this.CMDB_CI = 'cmdb_ci';
      this.SYS_CLASS_NAME = 'sys_class_name';
      this.ALERT = 'alert';
      this.BUSINESS_SERVICE = 'business_service';
      this.EM_ALERT_TABLE = "em_alert";

      //sn_em_ai_alert_cis
      this.ALERT_CIS_TABLE = 'sn_em_ai_alert_cis';
      this.CI_NAME = 'cmdb_ci_name';
      this.CI_CLASS = 'cmdb_ci_sys_class_name';

      //sn_em_ai_alert_services
      this.ALERT_SERVICES_TABLE = 'sn_em_ai_alert_services';
      this.BS_NAME = 'business_service_name';
      this.BS_CLASS = 'business_service_sys_class_name';

      //sn_em_ai_alert_tags
      this.ALERT_TAGS_TABLE = 'sn_em_ai_alert_tags';
      this.TAG_NAME = 'tag_name';
      this.TAG_VALUE = 'tag_value';
      this.ALERT_TAG_PREFIX = gs.getProperty('evt_mgmt.alert_tags_prefix', 't_');

      //prefixes when using database views
      this.ALERT_CIS_PREFIX = 'alertcis_';
      this.ALERT_SERVICES_PREFIX = 'alertservices_';
      this.ALERT_TAGS_PREFIX = 'alerttags_';

      // Prefix map for dot walking on reference fields or using table fields on database view
      this.prefixMap = {
          [this.ALERT_SERVICES_TABLE]: {
              [this.EM_ALERT_TABLE]: this.ALERT + "."
          },
          [this.ALERT_CIS_TABLE]: {
              [this.EM_ALERT_TABLE]: this.ALERT + "."
          },
          [this.ALERT_TAGS_TABLE]: {
              [this.EM_ALERT_TABLE]: this.ALERT + "."
          },
          sn_em_ai_alert_cis_services: {
              [this.EM_ALERT_TABLE]: this.ALERT + "_",
              [this.ALERT_SERVICES_TABLE]: this.ALERT_SERVICES_PREFIX,
              [this.ALERT_CIS_TABLE]: this.ALERT_CIS_PREFIX
          },
          sn_em_ai_alert_cis_tags: {
              [this.EM_ALERT_TABLE]: this.ALERT + "_",
              [this.ALERT_TAGS_TABLE]: this.ALERT_TAGS_PREFIX,
              [this.ALERT_CIS_TABLE]: this.ALERT_CIS_PREFIX
          },
          sn_em_ai_alert_services_tags: {
              [this.EM_ALERT_TABLE]: this.ALERT + "_",
              [this.ALERT_SERVICES_TABLE]: this.ALERT_SERVICES_PREFIX,
              [this.ALERT_TAGS_TABLE]: this.ALERT_TAGS_PREFIX
          },
          sn_em_ai_alert_cis_services_tags: {
              [this.EM_ALERT_TABLE]: this.ALERT + "_",
              [this.ALERT_SERVICES_TABLE]: this.ALERT_SERVICES_PREFIX,
              [this.ALERT_CIS_TABLE]: this.ALERT_CIS_PREFIX,
              [this.ALERT_TAGS_TABLE]: this.ALERT_TAGS_PREFIX
          }
      };

      //Use the indexed columns in sn_em_ai_alert_services instead of dot walking on class or name
      this.alertServicesColumnMap = {
          [this.BUSINESS_SERVICE]: this.BS_NAME,
          [this.BUSINESS_SERVICE + "." + this.SYS_CLASS_NAME]: this.BS_CLASS
      };

      //Use the indexed columns in sn_em_ai_alert_cis instead of dot walking on class or name
      this.alertCisColumnMap = {
          [this.CMDB_CI]: this.CI_NAME,
          [this.CMDB_CI + "." + this.SYS_CLASS_NAME]: this.CI_CLASS
      };
  },

  processFacet: function(tableName, columnName, queryStr, searchStr, searchColumn) {
      var startTimeInMsForPerformance = new Date().getTime();
      try {
          var errorObj = this.validateParameters(tableName, columnName, queryStr, searchStr);
          if (errorObj?.status != 0) {
              return errorObj;
          }

          queryStr += this.getEncodedQueryForSearch(searchColumn, searchStr, queryStr);
          // continue only if the queryStr is valid
          if (queryStr && !this.isValidQuery(queryStr, tableName)) {
              var err = gs.getMessage("Invalid query {0} on the table {1}​​​​​​​​​​.", [queryStr, tableName]);
              return {
                  status: this.ERROR_STATUS,
                  errorMessages: err
              };
          }

          var resMap = this.getFacetValues(tableName, columnName, queryStr);
          resMap = this.moveEmptyValueToBeFirst(resMap);
      } catch (e) {
          var err = this.evtMgmtCommons.getExceptionMessage(e, true);
          gs.error(err);
          return {
              status: this.ERROR_STATUS,
              errorMessages: err
          };
      }

      var duration = (new Date().getTime()) - startTimeInMsForPerformance;
      this.evtMgmtCommons.addDebugLogNoPrefix(this.type + " - processFacet, facet = " + columnName + ", table = " + tableName + ", duration = " + duration);

      return resMap;
  },

  getFacetValues: function(tableName, columnName, query) {
      var resultMap = {};

      var alertSysIdField = (this.prefixMap?.[tableName]?.[this.EM_ALERT_TABLE] ?? "") + this.SYS_ID;
      var isReferenceField = this.isReferenceField(tableName, columnName);

      var gr = new GlideAggregate(tableName);
      gr.groupBy(columnName);
      if (query && query.length != 0) {
          gr.addEncodedQuery(query);
      }
      gr.addAggregate("COUNT(DISTINCT", alertSysIdField);
      gr.orderByAggregate("COUNT(DISTINCT", alertSysIdField);
      gr.query();

      while (gr.next()) {
          var facetCount = parseInt(gr.getAggregate("COUNT(DISTINCT", alertSysIdField), 10) || 0;
          if (facetCount != 0) {
              var facetValue = gr.getValue(columnName);
              if (facetValue == '') {
                  facetValue = this.EMPTY_VALUE;
              }
              // for reference fields we'll return the display value in addition to the sys_id of the reference record
              if (isReferenceField) {
                  var facetDisplayValue = (facetValue == this.EMPTY_VALUE) ? this.EMPTY_VALUE : gr.getDisplayValue(columnName);
                  var facetObject = {};
                  facetObject[this.NAME] = facetDisplayValue;
                  facetObject[this.COUNT] = facetCount;
                  resultMap[facetValue] = facetObject;
              } else {
                  resultMap[facetValue] = facetCount;
              }
          }
      }
      return resultMap;
  },

  processListOfValuesFacet: function(tableName, columnName, queryStr) {
      columnName = (this.prefixMap?.[tableName]?.[this.EM_ALERT_TABLE] ?? "") + columnName;
      return this.processFacet(tableName, columnName, queryStr, "", "");
  },

  processSearchFacet: function(tableName, columnName, queryStr, searchStr) {
      columnName = (this.prefixMap?.[tableName]?.[this.EM_ALERT_TABLE] ?? "") + columnName;
      return this.processFacet(tableName, columnName, queryStr, searchStr, columnName);
  },

  processCisFacet: function(tableName, columnName, queryStr, searchStr) {
      columnName = columnName || this.CMDB_CI;
      columnName = (this.prefixMap?.[tableName]?.[this.ALERT_CIS_TABLE] ?? "") + columnName;
      //Use the indexed columns in sn_em_ai_alert_cis instead of dot walking on class or name
      var searchColumn = this.alertCisColumnMap?.[columnName] ?? columnName;

      return this.processFacet(tableName, columnName, queryStr, searchStr, searchColumn);
  },

  processImpactedServiceFacet: function(tableName, columnName, queryStr, searchStr) {
      columnName = columnName || this.BUSINESS_SERVICE;
      columnName = (this.prefixMap?.[tableName]?.[this.ALERT_SERVICES_TABLE] ?? "") + columnName;
      //Use the indexed columns in sn_em_ai_alert_servucess instead of dot walking on class or name
      var searchColumn = this.alertServicesColumnMap?.[columnName] ?? columnName;

      return this.processFacet(tableName, columnName, queryStr, searchStr, searchColumn);
  },

  processAlertTagsFacet: function(tableName, tagName, queryStr, searchStr) {
      if (!tagName) {
          var err = gs.getMessage("The mandatory tagName parameter is missing\n");
          return {
              status: this.ERROR_STATUS,
              errorMessages: err
          };
      }
      // add the "t_" prefix to the tag name, if doesn't exist
      if (!tagName.startsWith(this.ALERT_TAG_PREFIX)) {
          tagName = this.ALERT_TAG_PREFIX + tagName;
      }

      // build alert tag condition and concat to query
      var tagNameColumn = (this.prefixMap?.[tableName]?.[this.ALERT_TAGS_TABLE] ?? "") + this.TAG_NAME;
      queryStr += "^" + tagNameColumn + "=" + tagName;

      var tagValueColumn = (this.prefixMap?.[tableName]?.[this.ALERT_TAGS_TABLE] ?? "") + this.TAG_VALUE;
      return this.processFacet(tableName, tagValueColumn, queryStr, searchStr, tagValueColumn);
  },

  getDistinctAlertCount: function(tableName, queryStr) {
      var alertSysIdField = (this.prefixMap?.[tableName]?.[this.EM_ALERT_TABLE] ?? "") + this.SYS_ID;
      var errorObj = this.validateParameters(tableName, alertSysIdField, queryStr, "");
      if (errorObj?.status != 0) {
          return errorObj;
      }

      var ga = new GlideAggregate(tableName);
      ga.groupBy(alertSysIdField);
      if (queryStr && queryStr.length != 0) {
          ga.addEncodedQuery(queryStr);
      }
      ga.query();

      var rowCount = ga.getRowCount() || 0;
      return rowCount;

  },

  isValidColumnName: function(tableName, columnName) {
      if (!tableName || !columnName) return false;

      var gr = new GlideRecordSecure(tableName);
      var element = gr.getElement(columnName);
      return (element != undefined);
  },

  isValidQuery: function(query, tableName) {
      try {
          if (query && query.length != 0) {
              var gr = new GlideRecordSecure(tableName);
              return gr.isValidEncodedQuery(query);
          }
          return true;
      } catch (e) {
          return false;
      }
  },

  getRecordsCount: function(tableName, columnName, query, searchPattern) {
      var res = 0;

      if (tableName) {
          var gr = new GlideAggregate(tableName);
          gr.addAggregate('COUNT');
          if (query) {
              gr.addEncodedQuery(query);
          }
          if (columnName && searchPattern) {
              gr.addEncodedQuery(columnName + 'LIKE' + searchPattern);
          }

          gr.query();
          if (gr.next()) {
              res = gr.getAggregate('COUNT');
          }
      }
      return res;
  },

  validateParameters: function(tableName, columnName, queryStr, searchStr) {
      var err = "";
      var result = {
          status: 0,
          errorMessages: ''
      };

      if (!columnName) {
          result.errorMessages += gs.getMessage("The mandatory columnName parameter is missing\n");
      }

      if (!tableName) {
          result.errorMessages += gs.getMessage("The mandatory tableName parameter is missing\n");
      }

      if (!result.errorMessages) {
          if (!gs.tableExists(tableName)) {
              err = gs.getMessage('Invalid table name - {0}. Please provide a valid table name as a tableName parameter\n', tableName);
              result.errorMessages += err;
          } else {
              if (!this.isValidColumnName(tableName, columnName)) {
                  err = gs.getMessage("Invalid columnName parameter {0} for the table {1}​​​​​​​​​​. Please provide a valid column name as a columnName parameter\n", [columnName, tableName]);
                  result.errorMessages += err;
              }

              if (queryStr && !this.isValidQuery(queryStr, tableName)) {
                  err = gs.getMessage("Invalid query {0} on the table {1}​​​​​​​​​​. Please provide a valid encoded query as a query parameter\n", [queryStr, tableName]);
                  result.errorMessages += err;
              }
          }
          if (result.errorMessages) {
              result.status = this.ERROR_STATUS;
          } else {
              var count = this.getRecordsCount(tableName, columnName, queryStr, searchStr);
              if (count > this.MAX_RETURNED_RECORDS) {
                  result.status = this.WARNING_STATUS;
              }
          }
      } else {
          result.status = this.ERROR_STATUS;
      }
      return result;
  },

  getEncodedQueryForSearch: function(columnName, searchStr, currentQuery) {
      var queryStr = "";
      if (searchStr) {
          if (currentQuery) {
              queryStr += '^';
          }
          //if the user searchs for the string that is a beging of the word "empty" or the word "empty" itself
          //we should search for this string as well as for an empty string
          if (this.EMPTY_STRING.startsWith(searchStr.toLowerCase())) {
              queryStr += columnName + 'LIKE' + searchStr + '^OR' + columnName + 'ISEMPTY';
          } else {
              queryStr += columnName + 'LIKE' + searchStr;
          }
      }
      return queryStr;
  },

  moveEmptyValueToBeFirst: function(inputMap) {
      if (inputMap?.[this.EMPTY_VALUE]) {
          var resMap = {};
          resMap[this.EMPTY_VALUE] = inputMap[this.EMPTY_VALUE];
          delete inputMap[this.EMPTY_VALUE];
          for (key in inputMap) {
              resMap[key] = inputMap[key];
          }
          return resMap;
      }
      return inputMap;
  },

  isReferenceField: function(tableName, columnName) {
      var gr = new GlideRecord(tableName);
      return (gr.getElement(columnName).getRefRecord() != undefined);
  },

  type: 'EvtMgmtAttributesApi'
};

Sys ID

dfb44f1107572110b34ce06b0fd300fc

Offical Documentation

Official Docs: