Name

global.DataExtractionContractValidationImpl

Description

No description available

Script

var DataExtractionContractValidationImpl = Class.create();
DataExtractionContractValidationImpl.prototype = {
  initialize: function() {
      this.CONTRACT_VALIDATION_ERRORS = {};
      this.UTIL = new DataExtractionUtil();
      this.CONSTANTS = new global.DataExtractionConstants();
  },

  // All JSON validations.
  isJSONValid: function(contract, useCase) {

      if (gs.nil(contract) || !this.UTIL.isJSONObject(contract)) {
          this.addError("CONTRACT_DEFINITION_ERROR", "No valid contract was provided");
          return this.getJSONError();
      }

      try {

          // converting just the keys to upper case to avoid errors while validating encodedqueries (case sensitive)
          contract = this.UTIL.ConvertContractKeysToUpperCase(contract);

          //validating if the contract has USECASE key. USECASE won't be mandatory in the contract.
          if (this.UTIL.isValidJSONKey(contract, "USE_CASE") &&
              this.CONSTANTS.SUPPORTED_USE_CASES.hasOwnProperty(contract.USE_CASE.toUpperCase()))
              useCase = contract.USE_CASE.toUpperCase();

          //get useCase based in the contract
          if (gs.nil(useCase))
              useCase = this.getUseCase(contract);

          if (!this.CONSTANTS.SUPPORTED_USE_CASES.hasOwnProperty(useCase) ||
              useCase == this.CONSTANTS.UNKNOWN_TAG ||
              !this.isContractByUseCaseValid(contract, useCase))
              return this.getJSONError(useCase);

          if (GlideDomainSupport.isDataOrProcessSeparationEnabled() && this.UTIL.isValidJSONKey(contract, "DOMAIN_SYS_ID") && !this.UTIL.isValidDomain(contract.DOMAIN_SYS_ID)) {
              this.addError("CONTRACT_DEFINITION_ERROR", "The domain_sys_id " + contract.DOMAIN_SYS_ID + " is not valid.");
              return this.getJSONError(useCase);
          }

      } catch (err) {
          this.addError("CONTRACT_DEFINITION_ERROR", "Please check the contract definition");
          validJSON = false;
      }

      // All Glide validations.
      try {
          if (!this.hasReadAccess(contract, useCase) ||
              !this.areEncodedQueriesValid(contract, useCase) ||
              !this.areFieldsValid(contract, useCase))
              return this.getJSONError(useCase);

      } catch (err) {
          this.addError("SECURITY_ERROR", "Please check if you have read access to the tables, fields or if encodedQueries are valid " +
              err);
          return this.getJSONError(useCase);
      }

      return isValid = {
          "isValid": true,
          "useCase": useCase,
          "errors": {}
      };

  },

  getJSONError: function(useCase) {
      return {
          "isValid": false,
          "useCase": gs.nil(useCase) ? this.CONSTANTS.UNKNOWN_TAG : useCase,
          "errors": this.CONTRACT_VALIDATION_ERRORS
      };
  },

  //Identify the use case based in the target, source and join objects in the contract
  //Inputs: (JSON) required. contract that describes the use case and extra features to query
  getUseCase: function(contract) {

      var useCase = this.CONSTANTS.UNKNOWN_TAG;
      try {
          if (!this.UTIL.isValidJSONKey(contract, "TARGET") ||
              !this.UTIL.isValidJSONKey(contract.TARGET, "TABLE")) {
              this.addError("USE_CASE_ERROR", "TARGET.TABLE is mandatory for all use cases.");

          } else if (!this.UTIL.isValidJSONKey(contract, "SOURCE") ||
              !this.UTIL.isValidJSONKey(contract.SOURCE, "TABLE")) {
              useCase = "SINGLE_TABLE";

          } else if (!this.UTIL.isValidJSONKey(contract.SOURCE, "TABLE") ||
              !this.UTIL.isValidJSONKey(contract.SOURCE, "JOIN") ||
              !this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TARGET_ID_FIELD")) {
              this.addError("USE_CASE_ERROR", "SOURCE,SOURCE.JOIN and SOURCE.JOIN.TARGET_ID_FIELD are mandatory if TWO_TABLE_JOIN.");

          } else if (!this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TABLE") ||
              contract.SOURCE.TABLE == contract.SOURCE.JOIN.TABLE) {
              useCase = "TWO_TABLE_JOIN";

          } else if (!this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "SOURCE_ID_FIELD")) {
              this.addError("USE_CASE_ERROR", "SOURCE.JOIN.SOURCE_ID_FIELD is mandatory if TWO_TABLE_JOIN_M2M.");

          } else if (contract.SOURCE.TABLE != contract.SOURCE.JOIN.TABLE) {
              useCase = "TWO_TABLE_JOIN_WITH_M2M";

          } else {
              this.addError("USE_CASE_ERROR", "Unknown error");
          }
      } catch (err) {
          this.addError("USE_CASE_ERROR", "Unknown error");
      }
      return useCase;
  },

  //Check for all the important keys in the contract according to use case
  isContractByUseCaseValid: function(contract, useCase) {

      if (gs.nil(contract) || gs.nil(useCase)) {
          this.addError("CONTRACT_DEFINITION_ERROR", "No contract or useCase was provided");
          return false;
      }

      var validContract = true;
      switch (useCase) {
          case "SINGLE_TABLE":
              // just features or other_features are mandatory
              if ((!this.UTIL.isValidJSONKey(contract.TARGET, "FEATURES") ||
                      contract.TARGET.FEATURES.length <= 0) &&
                  (!this.UTIL.isValidJSONKey(contract.TARGET, "OTHER_FEATURES") ||
                      contract.TARGET.OTHER_FEATURES.length <= 0)) {
                  validContract = false;
                  this.addError("CONTRACT_DEFINITION_ERROR", "TARGET.FEATURES or TARGET.OTHER_FEATURES are mandatory for SINGLE_TABLE");
              }
              break;

          case "TWO_TABLE_JOIN":
          case "TWO_TABLE_JOIN_WITH_M2M":
              // features or other_features are mandatory to be present in either source or target
               if (!this.UTIL.isValidJSONArrayKey(contract.TARGET, "FEATURES") &&
  				!this.UTIL.isValidJSONArrayKey(contract.TARGET, "OTHER_FEATURES") &&
  				!this.UTIL.isValidJSONArrayKey(contract.SOURCE, "FEATURES") &&
                  !this.UTIL.isValidJSONArrayKey(contract.SOURCE, "OTHER_FEATURES")) {
                  validContract = false;
                  this.addError("CONTRACT_DEFINITION_ERROR", "SOURCE.FEATURES or SOURCE.OTHER_FEATURES are mandatory for TWO_TABLE_JOIN");
              }

              if (this.UTIL.isValidJSONArrayKey(contract.SOURCE, "OTHER_FEATURES")) {
                  var isOtherFeaturesInSourceValid = this.otherFeaturesValidations(contract.SOURCE.OTHER_FEATURES, "KEY");
                  if (!isOtherFeaturesInSourceValid)
                      validContract = false;
              }
              break;
      }

      // validating other features
      if (this.UTIL.isValidJSONArrayKey(contract.TARGET, "OTHER_FEATURES")) {
          var isOtherFeaturesInTargetValid = this.otherFeaturesValidations(contract.TARGET.OTHER_FEATURES, "KEY");
          if (!isOtherFeaturesInTargetValid)
              validContract = false;
      }

      if ((this.UTIL.isValidJSONKey(contract.TARGET, "ORDER") &&
              !this.CONSTANTS.ORDER_TYPES.hasOwnProperty(contract.TARGET.ORDER))) {
          validContract = false;
          this.addError("CONTRACT_DEFINITION_ERROR", "TARGET.ORDER is not supported");
      }

      return validContract;
  },

  //Security check for all tables in the contract
  hasReadAccess: function(contract, useCase) {

      if (gs.nil(contract)) {
          this.addError("SECURITY_ERROR", "No contract was provided.");
          return false;
      }

      var hasAccess = true;

      if (!this.UTIL.hasTableAccess(contract.TARGET.TABLE)) {
          this.addError("SECURITY_ERROR", "No read access for TARGET.TABLE. " + contract.TARGET.TABLE);
          hasAccess = false;
      }

      // validating access to other features for target
      if (this.UTIL.isValidJSONArrayKey(contract.TARGET, "OTHER_FEATURES") &&
          !this.otherFeaturesValidations(contract.TARGET.OTHER_FEATURES, "ACCESS")) {
          hasAccess = false;
      }

      if (useCase == "TWO_TABLE_JOIN" || useCase == "TWO_TABLE_JOIN_WITH_M2M") {
          if (this.UTIL.isValidJSONKey(contract.SOURCE, "TABLE") &&
              !this.UTIL.hasTableAccess(contract.SOURCE.TABLE)) {
              this.addError("SECURITY_ERROR", "No read access for SOURCE.TABLE. " + contract.SOURCE.TABLE);
              hasAccess = false;

          }
          // validating access to other features for source
          if (this.UTIL.isValidJSONKey(contract.SOURCE, "OTHER_FEATURES") &&
              contract.SOURCE.OTHER_FEATURES.length > 0 &&
              !this.otherFeaturesValidations(contract.SOURCE.OTHER_FEATURES, "ACCESS")) {
              hasAccess = false;
          }
      }

      if (useCase == "TWO_TABLE_JOIN_WITH_M2M") {
          if (this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TABLE") &&
              (contract.SOURCE.TABLE != contract.SOURCE.JOIN.TABLE) &&
              !this.UTIL.hasTableAccess(contract.SOURCE.JOIN.TABLE)) {
              this.addError("SECURITY_ERROR", "No read access for the M2M table SOURCE.JOIN.TABLE. " + contract.SOURCE.JOIN.TABLE);
              hasAccess = false;
          }
      }

      return hasAccess;
  },

  //Security check for encodedQueries. 
  areEncodedQueriesValid: function(contract, useCase) {
      var encodedQueryValid = true;

      if (this.UTIL.isValidJSONKey(contract.TARGET, "ENCODED_QUERY") &&
          this.UTIL.isValidJSONKey(contract.TARGET, "TABLE") &&
          !this.UTIL.isValidEncodedQuery(contract.TARGET.TABLE, contract.TARGET.ENCODED_QUERY)
      ) {
          this.addError("SECURITY_ERROR", "TARGET.ENCODED_QUERY is invalid, remember encodedQueries are case sensitive. " +
              contract.TARGET.ENCODED_QUERY);
          encodedQueryValid = false;
      }

      if (this.UTIL.isValidJSONArrayKey(contract.TARGET, "OTHER_FEATURES") &&
          !this.otherFeaturesValidations(contract.TARGET.OTHER_FEATURES, "ENCODED_QUERY")) {
          encodedQueryValid = false;
      }

      if (useCase == "TWO_TABLE_JOIN" || useCase == "TWO_TABLE_JOIN_WITH_M2M") {
          if (this.UTIL.isValidJSONKey(contract.SOURCE, "ENCODED_QUERY") &&
              this.UTIL.isValidJSONKey(contract.SOURCE, "TABLE") &&
              !this.UTIL.isValidEncodedQuery(contract.SOURCE.TABLE, contract.SOURCE.ENCODED_QUERY)
          ) {
              this.addError("SECURITY_ERROR", "SOURCE.ENCODED_QUERY is invalid, remember encodedQueries are case sensitive. " +
                  contract.SOURCE.ENCODED_QUERY);
              encodedQueryValid = false;
          }
          if (this.UTIL.isValidJSONKey(contract.SOURCE, "TABLE") &&
              this.UTIL.isValidJSONArrayKey(contract.SOURCE, "OTHER_FEATURES") &&
              !this.otherFeaturesValidations(contract.SOURCE.OTHER_FEATURES, "ENCODED_QUERY")) {
              encodedQueryValid = false;
          }
      }

      if (useCase == "TWO_TABLE_JOIN_WITH_M2M") {
          if (this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "ENCODED_QUERY") &&
              this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TABLE") &&
              !this.UTIL.isValidEncodedQuery(contract.SOURCE.JOIN.TABLE, contract.SOURCE.JOIN.ENCODED_QUERY)
          ) {
              this.addError("SECURITY_ERROR", "SOURCE.JOIN.ENCODED_QUERY is invalid, remember encodedQueries are case sensitive. " +
                  contract.SOURCE.JOIN.ENCODED_QUERY);
              encodedQueryValid = false;
          }
      }

      return encodedQueryValid;
  },

  //Security check for fields in the contract
  areFieldsValid: function(contract, useCase) {
      var fieldsValid = true;

      var fieldsToEvaluate = [];
      if (this.UTIL.isValidJSONKey(contract.TARGET, "LABELS"))
          fieldsToEvaluate = fieldsToEvaluate.concat(contract.TARGET.LABELS);

      if (this.UTIL.isValidJSONKey(contract.TARGET, "FEATURES"))
          fieldsToEvaluate = fieldsToEvaluate.concat(contract.TARGET.FEATURES);

      if (fieldsToEvaluate.length > 0 &&
          !this.UTIL.areFieldsValid(contract.TARGET.TABLE, fieldsToEvaluate)) {
          this.addError("SECURITY_ERROR", "TARGET.LABELS or TARGET.FEATURES are invalid. " + fieldsToEvaluate.toString());
          fieldsValid = false;
      }

      if (this.UTIL.isValidJSONArrayKey(contract.TARGET, "OTHER_FEATURES") &&
          !this.otherFeaturesValidations(contract.TARGET.OTHER_FEATURES, "FIELDS")) {
          fieldsValid = false;
      }

      if (useCase != "SINGLE_TABLE") {
          fieldsToEvaluate = [];

          if (this.UTIL.isValidJSONArrayKey(contract.SOURCE, "FEATURES")) {
              if (!this.UTIL.areFieldsValid(contract.SOURCE.TABLE, contract.SOURCE.FEATURES)) {
                  this.addError("SECURITY_ERROR", " SOURCE.FEATURES are invalid. " + contract.SOURCE.FEATURES.toString());
                  fieldsValid = false;
              }
          }
          if (useCase == "TWO_TABLE_JOIN") {
              fieldsToEvaluate.push(contract.SOURCE.JOIN.TARGET_ID_FIELD);
              if (this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TARGET_TABLE_FIELD"))
                  fieldsToEvaluate.push(contract.SOURCE.JOIN.TARGET_TABLE_FIELD);

              if (fieldsToEvaluate.length > 0 &&
                  !this.UTIL.areFieldsValid(contract.SOURCE.TABLE, fieldsToEvaluate)) {
                  this.addError("SECURITY_ERROR", " SOURCE.FEATURES, SOURCE.JOIN.TARGET_ID_FIELD or SOURCE.JOIN.TARGET_TABLE_FIELD are invalid. " +
                      fieldsToEvaluate.toString());
                  fieldsValid = false;
              }
          }

          if (useCase == "TWO_TABLE_JOIN_WITH_M2M") {
              fieldsToEvaluate.push(contract.SOURCE.JOIN.TARGET_ID_FIELD);
              fieldsToEvaluate.push(contract.SOURCE.JOIN.SOURCE_ID_FIELD);
              if (this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "TARGET_TABLE_FIELD"))
                  fieldsToEvaluate.push(contract.SOURCE.JOIN.TARGET_TABLE_FIELD);
              if (this.UTIL.isValidJSONKey(contract.SOURCE.JOIN, "SOURCE_TABLE_FIELD"))
                  fieldsToEvaluate.push(contract.SOURCE.JOIN.SOURCE_TABLE_FIELD);
              if (fieldsToEvaluate.length > 0 &&
                  !this.UTIL.areFieldsValid(contract.SOURCE.JOIN.TABLE, fieldsToEvaluate)) {
                  this.addError("SECURITY_ERROR", "SOURCE.FEATURES, SOURCE.JOIN.TARGET_ID_FIELD, SOURCE.JOIN.SOURCE_ID_FIELD, SOURCE.JOIN.TARGET_TABLE_FIELD or SOURCE.JOIN.SOURCE_TABLE_FIELD are invalid.");
                  fieldsValid = false;
              }
          }

          if (this.UTIL.isValidJSONArrayKey(contract.SOURCE, "OTHER_FEATURES") &&
              !this.otherFeaturesValidations(contract.SOURCE.OTHER_FEATURES, "FIELDS")) {
              fieldsValid = false;
          }
      }

      return fieldsValid;
  },

  //validation for other_features objects
  otherFeaturesValidations: function(otherFeatures, validationType) {
      if (gs.nil(otherFeatures) || otherFeatures.length == 0 || gs.nil(validationType))
          return false;

      var valid = true;

      for (var i = 0; i < otherFeatures.length; i++) {
          var otherFeature = otherFeatures[i];
          var otherFeatureTable = this.UTIL.getOtherFeatureTable(otherFeature);

          switch (validationType) {
              case "KEY":
                  if (!this.isOtherFeatureValid(otherFeature))
                      valid = false;
                  break;
              case "ACCESS":
                  if (!this.UTIL.hasTableAccess(otherFeatureTable)) {
                      this.addError("SECURITY_ERROR", "No read access to SOURCE.OTHER_FEATURES.TABLE. " + otherFeatureTable);
                      valid = false;
                  }
                  break;
              case "ENCODED_QUERY":
                  if (this.UTIL.isValidJSONKey(otherFeature, "ENCODED_QUERY") &&
                      !this.UTIL.isValidEncodedQuery(otherFeatureTable, otherFeature.ENCODED_QUERY)) {
                      this.addError("SECURITY_ERROR", "OTHER_FEATURES.ENCODED_QUERY is invalid, remember encodedQueries are case sensitive. " + otherFeature.ENCODED_QUERY);
                      valid = false;
                  }
                  break;

              case "FIELDS":
                  if (this.UTIL.isValidJSONKey(otherFeature, "FEATURE") &&
                      (!this.UTIL.areFieldsValid(otherFeatureTable, otherFeature.FEATURE.split(" ")) ||
                          (!this.UTIL.areFieldsValid(otherFeatureTable, otherFeature.JOIN_ID_FIELD.split(" "))))) {
                      this.addError("SECURITY_ERROR", "OTHER_FEATURES.FEATURE or OTHER_FEATURES.JOIN_ID_FIELD are invalid. " +
                          otherFeature.FEATURE + "," + otherFeature.JOIN_ID_FIELD);
                      valid = false;
                  }
                  break;

              default:
                  valid = false;
          }
      }
      return valid;

  },

  //Validating indivual other_feature
  isOtherFeatureValid: function(otherFeature) {
      var otherFeatureValid = true;

      if (!this.UTIL.isValidJSONKey(otherFeature, "TYPE") ||
          !this.CONSTANTS.OTHER_FEATURES_TYPES.hasOwnProperty(otherFeature.TYPE)) {
          this.addError("CONTRACT_DEFINITION_ERROR", "OTHER_FEATURES.TYPE is not supported");
          return false;
      }

      switch (otherFeature.TYPE) {
          case "ATTACHMENT":
              if (this.UTIL.isValidJSONKey(otherFeature, "ATTACHMENT_TYPES")) {
                  var attachmentTypes = otherFeature.ATTACHMENT_TYPES;
                  for (i = 0; i < attachmentTypes.length; i++) {
                      if (!this.CONSTANTS.ALL_SUPPORTED_ATTACHMENT_TYPES.hasOwnProperty(attachmentTypes[i])) {
                          this.addError("CONTRACT_DEFINITION_ERROR", "OTHER_FEATURES.ATTACHMENT_TYPES " +
                              attachmentTypes[i] + " is not supported");
                          otherFeatureValid = false;
                      }
                  }
              }
              break;
          case "ARRAY":
              if (!this.UTIL.isValidJSONKey(otherFeature, "LABEL") ||
                  !this.UTIL.isValidJSONKey(otherFeature, "TABLE") ||
                  !this.UTIL.isValidJSONKey(otherFeature, "FEATURE") ||
                  !this.UTIL.isValidJSONKey(otherFeature, "JOIN_ID_FIELD")) {
                  this.addError("CONTRACT_DEFINITION_ERROR",
                      "OTHER_FEATURES.LABEL, OTHER_FEATURES.TABLE, OTHER_FEATURES.FEATURE or OTHER_FEATURES.JOIN_ID_FIELD are invalid");
                  otherFeatureValid = false;

              }
              break;
      }
      return otherFeatureValid;
  },

  //Adding errors to CONTRACT_VALIDATION_ERRORS and the log
  addError: function(type, message) {
      if (gs.nil(type) || !this.CONSTANTS.ERROR_TYPES.hasOwnProperty(type))
          type = this.CONSTANTS.UNKNOWN_TAG;

      message = this.CONSTANTS.ERROR_TYPES[type] + ". " + message;
      if (!this.CONTRACT_VALIDATION_ERRORS.hasOwnProperty(this.CONSTANTS.ERROR_TYPE_KEY)) {
          this.CONTRACT_VALIDATION_ERRORS[this.CONSTANTS.ERROR_TYPE_KEY] = type;
          this.CONTRACT_VALIDATION_ERRORS[this.CONSTANTS.ERROR_MESSAGE_KEY] = [message];
      } else {
          this.CONTRACT_VALIDATION_ERRORS[this.CONSTANTS.ERROR_MESSAGE_KEY].push(message);
      }
  },

  _debug: function(msg) {
      new DataExtractionDebugUtil().debug(msg);
  },

  type: 'DataExtractionContractValidationImpl'
};

Sys ID

5074d1a1a934e9d4f877bee6906445e0

Offical Documentation

Official Docs: