Name

sn_cmdb_ws.CMDBWorkspaceUtil

Description

No description available

Script

var CMDBWorkspaceUtil = Class.create();
CMDBWorkspaceUtil.prototype = Object.extendsObject(CMDBWsConstants, {
  initialize: function() {
      this.log = new global.GSLog("com.snc.sn_cmdb_ws.log.level", this.type);
  },

  /**
   * Helper function that prepends reference field alias to all fields specified in a query
   *
   * @param {String} query         query supplied by a condition builder
   * @param {String} tableAlias    tableAlias that's prepended to each field in a query
   *
   * @example
   * this.appendTableName("operational_status=1^nameSTARTSWITHPS", "ci_item")
   * // returns ci_item.operational_status=1^ci_item.nameSTARTSWITHPS
   *
   * @return {String} Returns a query with field names that are prepended with tableAlias specified
   */
  appendRefAlias: function(query, tableAlias) {
      if (!query)
          return "";

      //first replace any ^OR or ^NQ at the end of the string with ##OR or ##NQ
      query = query.replace(/\^OR$/g, "##OR");
      query = query.replace(/\^NQ$/g, "##NQ");
      query = query.replace(/\^EQ$/g, "##EQ");

      //first replace ^ORs with ##OR - intentionally padding them with ##
      query = query.replace(/\^OR/g, "##OR" + tableAlias + ".");

      //next replace ^NQs
      query = query.replace(/\^NQ/g, "##NQ" + tableAlias + ".");

      //next replace ^EQs
      query = query.replace(/\^EQ/g, "##EQ" + tableAlias + ".");

      //next replace just ^s with the tableAlias dot
      query = query.replace(/\^/g, "^" + tableAlias + ".");

      //lastly replace ##ORs with ^OR and replace ##NQs with ^NQ
      query = query.replace(/##OR/g, '^OR');
      query = query.replace(/##NQ/g, '^NQ');
      query = query.replace(/##EQ/g, '^EQ');
      var encodedQuery = gs.nil(query) ? "" : tableAlias + "." + query;

      return encodedQuery;
  },


  /*
   * Iterates thru all the encoded queries within each CMDB Group
   * and gets counts for each which are written to sn_cmdb_ws_group_query_metadata.
   * Also adds up all the count for the group as a whole.
   *   Note: the group count is only for encoded queries
   *   and doesn't include manually selected or QueryBuilder queries.
   * The counts are the number of matching CIs for the encoded queries and the group as a whole.
   */

  populateNoOfMatchingCIsForEncQrys: function() {
      var logMsgs = false;
      var gr = new GlideRecord(this.TABLES.CMDB_GROUP);
      gr.addQuery(this.COLS.GROUP_TYPE, this.WORKSPACE_PARAM);
      gr.query();

      if (logMsgs) {
          this.log.info('Total Group Count: ' + gr.getRowCount());
      }

      while (gr.next()) {
          if (logMsgs) {
              this.log.info('Current group name: ' + gr.group_name);
          }
          var countForGroup = 0;
          countForGroup += this.getNoOfMatchingCIsOnEncQrys(gr.sys_id);
          this.insUpdCntGrpMetadata(gr.sys_id, countForGroup);
      }

      //remove orphaned group metadata records not picked up by cascade delete
      this.remOrphanedGrpRecords();
      //remove orphaned group query metadata records not picked up by cascade delete
      this.remOrphanedGrpQryRecords();
  },

  /**
   * Helper function that counts the number of matching CIs on Encoded Queries and
   * inserts/update count into the sn_cmdb_ws_group_query_metadata table
   * @param {String} groupSysId
   *
   * @return {Number} Returns the count of Matching CIs on encoded queries for a group
   */
  getNoOfMatchingCIsOnEncQrys: function(groupSysId) {

      var tableDisplayNames = {};
      var shouldUseSimpleCondition = this.isSimpleConditionEnabled();
      var gr = new GlideRecord(this.TABLES.CMDB_GROUP_CONTAINS_ENCODED_QUERY);
      gr.addQuery(this.COLS.GROUP, groupSysId);
      gr.query();
      var countForGroup = 0;

      while (gr.next()) {
          var className = gr.getValue(this.COLS.CLASS);
          var condition = gr.getValue(this.COLS.CONDITION);
          if (shouldUseSimpleCondition) {
              condition = gr.getValue(this.COLS.SIMPLE_CONDITION);
          }

          if (gs.tableExists(className)) {
              var ga = new GlideAggregate(className);
              var numCIs = 0;
              ga.addAggregate(this.COUNT_AGGREGATE);

              if (condition)
                  ga.addEncodedQuery(condition);
              ga.query();

              if (ga.next())
                  numCIs = parseInt(ga.getAggregate(this.COUNT_AGGREGATE));

              countForGroup += numCIs;

              if (!tableDisplayNames[className]) {
                  tableDisplayNames[className] = new GlideRecord(className).getClassDisplayValue();
              }
              this.insUpdCntGrpQryMetadata(gr.sys_id, numCIs, tableDisplayNames[className]);
          }

      }
      return countForGroup;
  },

  isSimpleConditionEnabled: function() {
      return this.getSystemProperty(this.SYS_PROPERTIES_NAME.SHOULD_USE_SIMPLE_CONDITION, this.DATATYPE.BOOLEAN);
  },

  /**
   * Helper function to update/insert count of CIs in sn_cmdb_ws_group_query_metadata table
   * @params {String} sysId
   * @params {Number} countCIs
   * @params {String} tableDisplayName
   */
  insUpdCntGrpQryMetadata: function(sysId, countCIs, tableDisplayName) {

      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_GROUP_QUERY_METADATA);
      var foundQuery = gr.get(this.COLS.ENCODED_QUERY, sysId);

      if (foundQuery) {
          gr.setValue(this.COLS.CI_COUNT, countCIs);
          gr.setValue(this.COLS.NAME, tableDisplayName);
          gr.update();
      } else {
          gr.initialize();
          gr.setValue(this.COLS.ENCODED_QUERY, sysId);
          gr.setValue(this.COLS.CI_COUNT, countCIs);
          gr.setValue(this.COLS.NAME, tableDisplayName);
          gr.insert();
      }
  },

  /**
   * Helper function to update/insert count of CIs in sn_cmdb_ws_group_query_metadata table
   * @params {String} sysId
   * @params {Number} countForGroup
   */
  insUpdCntGrpMetadata: function(sysId, countForGroup) {

      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_GROUP_METADATA);
      var foundGroup = gr.get(this.COLS.GROUP, sysId);

      if (foundGroup) {
          gr.setValue(this.COLS.CI_COUNT, countForGroup);
          gr.update();
      } else {
          gr.initialize();
          gr.setValue(this.COLS.GROUP, sysId);
          gr.setValue(this.COLS.CI_COUNT, countForGroup);
          gr.insert();
      }
  },


  /**
   * Helper function to removed orphaned records from sn_cmdb_ws_group_metadata table
   *
   */
  remOrphanedGrpRecords: function() {
      var grGrpMeta = new GlideRecord(this.TABLES.SN_CMDB_WS_GROUP_METADATA);
      grGrpMeta.query();

      while (grGrpMeta.next()) {
          var grGrp = new GlideRecord(this.TABLES.CMDB_GROUP);
          var hasGroupMatch = grGrp.get(grGrpMeta.group);
          if (!hasGroupMatch) {
              grGrpMeta.deleteRecord();
          }
      }
  },

  /**
   * Helper function to removed orphaned records from sn_cmdb_ws_group_metadata table
   *
   */
  remOrphanedGrpQryRecords: function() {
      var grGrpQry = new GlideRecord(this.TABLES.SN_CMDB_WS_GROUP_QUERY_METADATA);
      grGrpQry.query();

      while (grGrpQry.next()) {
          var grEncQry = new GlideRecord(this.TABLES.CMDB_GROUP_CONTAINS_ENCODED_QUERY);
          var hasGroupMatch = grEncQry.get(grGrpQry.encoded_query);
          if (!hasGroupMatch) {
              grGrpQry.deleteRecord();
          }
      }
  },

  populateManagementAggregates: function() {
      //only aggregating rejected CIs for now
      this._populateRejectedCIs();
  },

  _populateRejectedCIs: function() {
      var rejectedCis = this.getRowCount(this.TABLES.CMDB_CI, 'attestation_status=Rejected');

      this.updateBaseAggDataState(this.STATES.DRAFT, this.STATES.RETIRED, this.CATEGORY.REJECTED_CIS);
      this.populateBaseAggData(this.CATEGORY.REJECTED_CIS, null, rejectedCis, this.STATES.DRAFT);

      this.updateBaseAggDataState(this.STATES.READY, this.STATES.RETIRED, this.CATEGORY.REJECTED_CIS);
      this.updateBaseAggDataState(this.STATES.DRAFT, this.STATES.READY, this.CATEGORY.REJECTED_CIS);
  },

  /**
   * Helper function to check if CIs Managed By Me should be included on landing page
   * @return {Boolean}
   */
  showCIsManagedByMe: function() {
      var propEnabled = this.getSystemProperty(this.SYS_PROPERTIES_NAME.CIS_MANAGED_BY_ME, this.DATATYPE.BOOLEAN);
      var hasMinimumRole = this.userHasRole(this.ROLES.SN_CMDB_EDITOR);

      return propEnabled && hasMinimumRole;
  },

  /**
   * Helper function to check if Total CIs should be included on landing page
   * @return {Boolean}
   */
  showTotalCIs: function() {
      var propEnabled = this.getSystemProperty(this.SYS_PROPERTIES_NAME.ENABLE_TOTAL_CIS, this.DATATYPE.BOOLEAN, 'true');
      return propEnabled;
  },

  /**
   * Helper function to check if Important Actions should be included on the landing page
   * @return {Boolean}
   */
  showImportantActions: function() {
      var isAdminOrMaint = this.isUserAdminOrMaint();
      var isCMDBAdmin = this.userHasRole(this.ROLES.SN_CMDB_ADMIN);
      var isCMDBEditor = this.userHasRole(this.ROLES.SN_CMDB_EDITOR);

      return (isAdminOrMaint || isCMDBAdmin || isCMDBEditor);
  },

  /**
   * Helper function to check if CMDB Health should be included on the landing page
   * @return {Boolean}
   */
  showCmdbHealth: function() {
      var isAdminOrMaint = this.isUserAdminOrMaint();
      var hasAssetRole = this.userHasRole(this.ROLES.ASSET);
      var isCMDBEditor = this.userHasRole(this.ROLES.SN_CMDB_EDITOR);
      var isCMDBUser = this.userHasRole(this.ROLES.SN_CMDB_USER);

      return (isAdminOrMaint || isCMDBEditor || (isCMDBUser && hasAssetRole));
  },

  /**
   * Parse a string to a boolean
   * @params {String}
   * @return {boolean}
   */
  parseBooleanString: function(booleanString) {
      if (booleanString)
          return booleanString === 'true' ? true : false;
      return false;
  },

  /**
   * Parse a string to a number
   * @params {String}
   * @return {Number}
   */
  parseNumericString: function(numericString) {
      if (numericString) {
          var val = Number(numericString);
          return isNaN(val) ? null : val;
      }
      return;
  },

  /**
   * Helper function to check if current user has given role
   * @return {Boolean}
   */
  userHasRole: function(role) {
      return gs.hasRole(role);
  },

  /**
   * Helper function to check if current user is admin or maint
   * @return {Boolean}
   */
  isUserAdminOrMaint: function() {
      return this.userHasRole(this.ROLES.ADMIN) || this.userHasRole(this.ROLES.MAINT);
  },

  /**
   * Helper function to check if the given table has any action config that we have set
   * @return {Boolean}
   */
  tableHasActionConfig: function(table) {
      return this.hasRecord(this.TABLES.M2M_ACTION_ASSIGNMENT_ACTION_CONFIG, null, {
          'action_configuration': this.CMDB_WORKSPACE_ACTION_CONFIG_ID,
          'action_assignment.table': table
      });
  },

  /**
   * Helper function to get if the given table and query result in at least one valid record
   * @params {String} table
   * @params {String} encodedQuery
   * @params {Object} query
   * @return {boolean} whether the table has at least one record that satifies the queries
   */
  hasRecord: function(table, encodedQuery, query) {
      var gr = new GlideRecord(table);
      this.addQueryConditions(gr, encodedQuery, query);

      gr.setLimit(1);
      gr.query();
      return gr.hasNext();
  },

  /**
   * Helper function to get the row count on the given table with given queries
   * @params {String} table
   * @params {String} encodedQuery
   * @params {Object} query
   * @return {number} row count that satisfies the query condition
   */
  getRowCount: function(table, encodedQuery, query) {
      var ga = new GlideAggregate(table);
      this.addQueryConditions(ga, encodedQuery, query);
      ga.addAggregate(this.COUNT_AGGREGATE);
      ga.query();
      if (ga.next()) {
          return ga.getAggregate(this.COUNT_AGGREGATE);
      }
      return 0;
  },

  addQueryConditions: function(gr, encodedQuery, query) {
      if (encodedQuery) {
          gr.addEncodedQuery(encodedQuery);
      }
      if (query) {
          var fields = Object.keys(query);
          for (var i = 0; i < fields.length; i++) {
              gr.addQuery(fields[i], query[fields[i]]);
          }
      }
  },

  /**
   * Helper function to check if a plugin is active
   * @params {String} pluginId
   * @return {boolean} whether the given plugin is active or not
   */
  isPluginActive: function(pluginId) {
      return new GlidePluginManager().isActive(pluginId);
  },

  /**
   * Helper function to get the value for the given sys_property
   * @params {String} key
   * @params {DATATYPE} datatype
   * @params {any} defaultValue
   * @return {Object} value 
   */
  getSystemProperty: function(key, dataType, defaultValue) {
      var val = gs.getProperty(key, defaultValue);
      switch (dataType) {
          case this.DATATYPE.BOOLEAN:
              return this.parseBooleanString(val);
          case this.DATATYPE.NUMBER:
              return this.parseNumericString(val);
          default:
              return val;
      }
  },

  /**
   * Helper function to check if license is active for given appId
   * @params {String} appId
   * @return {Object} whether the license is active or not 
   */
  hasActiveLicense: function(appId) {
      return new sn_lef.GlideLicenseAPI().hasLicenseForFeature("app_id", appId);
  },

  populateBaseAggData: function(chart, groupby, count, state) {
      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_BASE_AGGREGATE_DATA);
      gr.initialize();
      gr.setValue(this.COLS.CHART, chart);
      if (groupby)
          gr.setValue(this.COLS.GROUP_BY, groupby);
      gr.setValue(this.COLS.COUNT, count);
      gr.setValue(this.COLS.STATE, state);
      gr.insert();
  },

  updateBaseAggDataState: function(sourceState, targetState, chart) {
      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_BASE_AGGREGATE_DATA);
      if (chart)
          gr.addQuery(this.COLS.CHART, chart);
      gr.addQuery(this.COLS.STATE, sourceState);
      gr.setValue(this.COLS.STATE, targetState);
      gr.updateMultiple();
  },

  deleteFeatureCategoryAggregate: function(featureId, state, groupby) {
      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_BASE_AGGREGATE_DATA);
      gr.addQuery(this.COLS.CHART, featureId);
      if (global.JSUtil.notNil(state)) {
          gr.addQuery(this.COLS.STATE, state);
      }
      if (global.JSUtil.notNil(groupby)) {
          gr.addQuery(this.COLS.GROUP_BY, groupby);
      }
      gr.query();
      while (gr.next()) {
          gr.deleteRecord();
      }
  },

  updateDraftFeatureCategoryAggregate: function(featureId, state, count, groupby) {
      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_BASE_AGGREGATE_DATA);
      gr.addQuery(this.COLS.CHART, featureId);
      gr.addQuery(this.COLS.STATE, this.STATES.DRAFT);
      if (global.JSUtil.notNil(groupby)) {
          gr.addQuery(this.COLS.GROUP_BY, groupby);
      }
      gr.query();
      while (gr.next()) {
          if (global.JSUtil.notNil(state)) {
              gr.setValue(this.COLS.STATE, state);
          }
          if (global.JSUtil.notNil(count)) {
              gr.setValue(this.COLS.COUNT, count);
          }
          gr.update();
      }
  },

  getAggregateData: function(featureId, groupByFilter, stateFilter) {
      var result = [];
      var gr = new GlideRecord(this.TABLES.SN_CMDB_WS_BASE_AGGREGATE_DATA);
      gr.addQuery(this.COLS.CHART, featureId);
      if (groupByFilter) {
          gr.addQuery(this.COLS.GROUP_BY, groupByFilter);
      }
      if (stateFilter) {
          gr.addQuery(this.COLS.STATE, stateFilter);
      }
      gr.query();

      while (gr.next()) {
          result.push({
              chart: gr.getValue(this.COLS.CHART),
              groupBy: gr.getValue(this.COLS.GROUP_BY),
              count: +gr.getValue(this.COLS.COUNT),
              updatedDt: gr.getValue(this.COLS.SYS_UPDATED_ON)
          });
      }

      return result;
  },

  isJson: function(str) {
      try {
          return JSON.parse(str);
      } catch (e) {
          return false;
      }
  },

  getRelative24hrEncodedQuery: function(column) {
      return column + "RELATIVEGT@hour@ago@24";
  },

  /**
   * Generic method to make outbound REST API call.
   * @params {String} url
   * @return {Object} response with status and body
   */
  httpGet: function(url) {
      var response;
      try {
          var sm = new sn_ws.RESTMessageV2();
          sm.setEndpoint(url);
          sm.setHttpMethod('get');
          response = sm.execute();
          var responseBody = response.haveError() ? response.getErrorMessage() : JSON.parse(response.getBody());
          response = {
              status: response.getStatusCode(),
              body: responseBody
          };
      } catch (ex) {
          var errorMessage = ex.getMessage ? ex.getMessage() : "Error fetching data";
          response = {
              status: 500,
              body: errorMessage
          };
      }
      return response;
  },

  /**
   * Gets AppStore url.
   * @return {String} appstore url
   */
  getAppStoreBaseUrl: function() {
      return this.getSystemProperty(this.SYS_PROPERTIES_NAME.APPSTORE_BASE_URL, this.DATATYPE.STRING, "https://store.service-now.com/");
  },

  /**
   * Given relative path for appstore, this returns the absolute url relevant for this instance
   */
  buildAppStoreUrl: function(relativePath) {
      var baseUrl = this.getAppStoreBaseUrl();
      baseUrl = baseUrl.replace(/\/+$/, "");
      var relPath = relativePath.replace(/^\/+/, "");
      return baseUrl + "/" + relPath;
  },

  invokePAJob: function(paJobId, jobName) {
      var paJobRecord = new GlideRecord(this.TABLES.SYSAUTO_PA);
      if (paJobRecord.get(paJobId)) {
          var paJobName = paJobRecord.getValue(this.COLS.NAME);
          gs.debug("Invoking job : '{0}' to calculate trends for aggregated charts", paJobName);
          var paTriggerId = gs.executeNow(paJobRecord);
          gs.info("Triggered job : '{0} ' with trigger id : {1} ", paJobName, paTriggerId);
      } else {
          gs.error("Failed to find PA job with sys Id {0} and cannot execute the {1} job", paJobId, jobName);
      }
  },

  invokeWsAggregatesMonthlyPAJob: function() {
      this.invokePAJob(this.SCHEDULED_JOBS.CMDB_WS_AGGREGATES_MONTHLY_PA_JOB, "CMDB Workspace Monthly Collection");
  },

  invokeWsAggregatesDailyPAJob: function() {
      this.invokePAJob(this.SCHEDULED_JOBS.CMDB_WS_AGGREGATES_DAILY_PA_JOB, "CMDB Workspace Aggregates Collection");
  },

  invokeScriptJob: function(scriptJobId, jobName) {
      var scriptJobRecord = new GlideRecord(this.TABLES.SYSAUTO_SCRIPT);
      if (scriptJobRecord.get(scriptJobId)) {
          var scriptJobName = scriptJobRecord.getValue(this.COLS.NAME);
          gs.debug("Invoking job : '{0}'", scriptJobName);
          var scriptTriggerId = gs.executeNow(scriptJobRecord);
          gs.info("Triggered job : '{0} ' with trigger id : {1} ", scriptJobName, scriptTriggerId);
      } else {
          gs.error("Failed to find script job with sys_id {0} and cannot execute the {1} job", scriptJobId, jobName);
      }
  },

  invokeWsPopulateAggregatesDailyScriptJob: function() {
      this.invokeScriptJob(this.SCHEDULED_JOBS.CMDB_WS_POPULATE_AGGREGATES_DAILY, "CMDB Workspace - Populate aggregates Daily");
  },

  invokeWsPopulateAggregatesMonthlyScriptJob: function() {
      this.invokeScriptJob(this.SCHEDULED_JOBS.CMDB_WS_POPULATE_AGGREGATES_MONTHLY, "CMDB Workspace - Populate aggregates Monthly");
  },

  /**
   * Abbreviates a number
   * @params {Number} num
   * @return {String} abbreviated number
   */
  abbreviateNumber: function(num) {
      if (global.JSUtil.nil(num)) return null;

      var formatNumStr = function(numberStr, suffix) {
          if (numberStr.endsWith('.0')) {
              return numberStr.slice(0, -2) + suffix;
          }
          return numberStr + suffix;
      };

      if (num < 1e3) {
          return num.toString();
      }

      if (num >= 1e3 && num < 1e6) {
          return formatNumStr((num / 1e3).toFixed(1), 'K');
      }

      if (num >= 1e6 && num < 1e9) {
          return formatNumStr((num / 1e6).toFixed(1), 'M');
      }

      return formatNumStr((num / 1e9).toFixed(1), 'B');
  },

  getJobLastCompletedInfo: function(jobId) {
  	var completedDateTime =  gs.getMessage("N/A");
  	var gr = new GlideRecord(this.TABLES.PA_JOB_LOGS);
  	gr.addQuery(this.COLS.JOB, jobId);
  	gr.addQuery(this.COLS.STATE, this.PA_JOB_LOGS_STATE.COLLECTED_OK);
  	gr.setLimit(1);
  	gr.orderByDesc(this.COLS.SYS_CREATED_ON);
  	gr.query();
  	if(gr.next()) {
  		completedDateTime = gr.getDisplayValue(this.COLS.COMPLETED);
  	}
  	return completedDateTime;
  },

  getCMDBWsPAJobsLastCompletedInfo: function() {
  	var result = {};
  	var jobs = {};
  	jobs[this.SCHEDULED_JOB_RUN_TYPE.AGGREGATES_DAILY_JOB] = this.SCHEDULED_JOBS.CMDB_WS_AGGREGATES_DAILY_PA_JOB;
  	jobs[this.SCHEDULED_JOB_RUN_TYPE.AGGREGATES_MONTHLY_JOB] = this.SCHEDULED_JOBS.CMDB_WS_AGGREGATES_MONTHLY_PA_JOB;

  	for (var runType in jobs) {
  		var dateTime = this.getJobLastCompletedInfo(jobs[runType]);
  		result[runType] = gs.getMessage("Last updated: {0}", dateTime);
  	}
  	return result;
  },

  getIndicatorDetails: function(indicatorId, breakdownId, breakdownElementId, includeScores, limit) {
      var scorecard = new PAScorecard();
      var uuid = indicatorId;

      if (!global.JSUtil.nil(breakdownId)) {
          uuid += ":" + breakdownId;

          if (!global.JSUtil.nil(breakdownElementId)) {
              uuid += ":" + breakdownElementId;
          }
      }

      if (!global.JSUtil.nil(limit)) {
          scorecard.addParam("limit", limit);
      }

      scorecard.addParam("uuid", uuid);
      scorecard.addParam("include_scores", global.JSUtil.nil(includeScores) ? true : includeScores);
      scorecard.addParam("display", "all");
      return scorecard.query();
  },

  /**
   * Checks if at least one score is greater than given value
   */
  atLeastOneScoreGreaterThan: function(scores, gtValue) {
      if (global.JSUtil.nil(scores)) {
          return false;
      }
      var val = global.JSUtil.nil(gtValue) ? 0 : gtValue;
      for (var idx in scores) {
          if (scores[idx].value > val) return true;
      }
      return false;
  },

  getSysChoiceId: function(encodedQuery, query) {
      var gr = new GlideRecord(this.TABLES.SYS_CHOICE);
      this.addQueryConditions(gr, encodedQuery, query);
      gr.setLimit(1);
      gr.query();
      return gr.next() ? gr.getUniqueValue() : '';
  },

  lookupIndicatorId: function(indicator) {
      var id = null;
      var gr = new GlideRecord(this.TABLES.PA_INDICATORS);
      if (gr.get(indicator)) {
          id = gr.getValue(this.COLS.ID);
      }
      return id;
  },

  /* Helper function to copy scores from one indicator to another,
   * @params {String} sourceIndicator
   * @params {String} targetIndicator
   * @return null - nothing to be returned, scores from sourceIndicator should be inserted to the pa_scores_l1 table, as scores for targetIndicator
   *
   */
  migratePaIndicatorScores: function(sourceIndicator, targetIndicator) {
      //first, look up 'id' of each indicator, because pa_scores_l1 takes in indicator id, and not the sys_id. Not sure why
      var sourceIndicatorId = this.lookupIndicatorId(sourceIndicator);
      var targetIndicatorId = this.lookupIndicatorId(targetIndicator);
      //grab the earliest date of the pa scores for new indicator
      var earliestScoreDate = null;
      var gr = new GlideRecord(this.TABLES.PA_SCORES_L1);
      gr.addQuery(this.COLS.INDICATOR, targetIndicatorId);
      gr.orderBy(this.COLS.START_AT);
      gr.setLimit(1);
      gr.query();

      //get scores before earliest date for old indicator
      var earliestDate = null;
      if (gr.next()) {
          earliestDate = gr.getValue(this.COLS.START_AT);
      }

      //grab all pa_scores_l1 records for sourceIndicator, before the earliest score date of new indicator
      //for each of the scores, create another score entry with the same scores and dates, for the new indicator
      gr = new GlideRecord(this.TABLES.PA_SCORES_L1);
      gr.addQuery(this.COLS.INDICATOR, sourceIndicatorId);
      if (earliestDate) {
          gr.addQuery(this.COLS.START_AT, this.OPERATORS.LESSTHAN, earliestDate);
      }

      gr.query();
      while (gr.next()) {
          var scoreDate = gr.getValue(this.COLS.START_AT);
          var scoreValue = gr.getValue(this.COLS.VALUE);
          //create new scores for the new indicator. 
          var newGr = new GlideRecord(this.TABLES.PA_SCORES_L1);
          newGr.initialize();
          newGr.setValue(this.COLS.INDICATOR, targetIndicatorId); //new indicator id
          newGr.setValue(this.COLS.START_AT, scoreDate);
          newGr.setValue(this.COLS.VALUE, scoreValue);
          newGr.insert();
      }

  },

  migrateTotalCIsIndicator: function() {
      var oldTotalCiIndicator = 'b6dd71e077511110ee0d0cc2fa5a990e';
      var newTotalCiIndicator = 'b1fb06f0ef572110785294c1ed97d705';
      this.migratePaIndicatorScores(oldTotalCiIndicator, newTotalCiIndicator);
  },

  type: 'CMDBWorkspaceUtil'
});

Sys ID

114ce4a553a720102365ddeeff7b122a

Offical Documentation

Official Docs: