export function getSubsetRelation()

in packages/extensions/core/src/lib/plugins/subset-schemas-deduplicator.ts [142:250]


export function getSubsetRelation(
  oldSchema: any,
  newSchema: any,
  additiveFieldsList: Array<string>,
  skipList: Array<string>,
): SubsetCheckResult {
  const result: SubsetCheckResult = { isSubset: true, nonAdditiveFieldsUpdates: {}, additiveFieldsMatches: {} };
  const failResult = { isSubset: false, nonAdditiveFieldsUpdates: {}, additiveFieldsMatches: {} };
  const skipSet = new Set(skipList);
  const additiveFieldsSet = new Set(additiveFieldsList);
  const oldSchemaFields = Object.keys(oldSchema);
  for (const fieldName of oldSchemaFields) {
    // if the newSchema does not contain a field from the oldSchema, it is not a subset
    if (newSchema[fieldName] === undefined) {
      return failResult;
    }

    // skip Fields have priority over additive fields
    if (skipSet.has(fieldName) && !areSimilar(oldSchema[fieldName], newSchema[fieldName], ...skipList)) {
      if (Array.isArray(oldSchema[fieldName])) {
        if (!Array.isArray(newSchema[fieldName])) {
          return failResult;
        }

        result.nonAdditiveFieldsUpdates[fieldName] = [];
        result.nonAdditiveFieldsUpdates[fieldName].push(newSchema[fieldName]);
      } else {
        result.nonAdditiveFieldsUpdates[fieldName] = {};
        result.nonAdditiveFieldsUpdates[fieldName] = newSchema[fieldName];
      }
    } else if (additiveFieldsSet.has(fieldName)) {
      // array example use case of additive field: required and allOf fields
      if (Array.isArray(oldSchema[fieldName])) {
        if (!Array.isArray(newSchema[fieldName])) {
          return failResult;
        }

        if (!result.additiveFieldsMatches[fieldName]) {
          result.additiveFieldsMatches[fieldName] = [];
        }

        let foundMatch = false;
        for (const oldValue of oldSchema[fieldName]) {
          foundMatch = false;
          for (const newValue of newSchema[fieldName]) {
            if (areSimilar(oldValue, newValue, ...skipList)) {
              result.additiveFieldsMatches[fieldName].push(newValue);
              foundMatch = true;
              break;
            }
          }
        }

        // one of the oldValues was not found in the new array.
        if (!foundMatch) {
          return failResult;
        }
      } else {
        const oldFieldKeys = Object.keys(oldSchema[fieldName]);

        for (const key of oldFieldKeys) {
          if (!newSchema[fieldName][key]) {
            return failResult;
          }

          if (!areSimilar(newSchema[fieldName][key], oldSchema[fieldName][key], ...skipList)) {
            return failResult;
          }

          if (!result.additiveFieldsMatches[fieldName]) {
            result.additiveFieldsMatches[fieldName] = {};
          }

          result.additiveFieldsMatches[fieldName][key] = newSchema[fieldName][key];
        }
      }
      // it can't be a subset if fields are different
    } else if (newSchema[fieldName] !== oldSchema[fieldName]) {
      return failResult;
    }
  }

  const newSchemaFields = Object.keys(newSchema);
  for (const fieldName of newSchemaFields) {
    // Add fields from the newSchema in the skipList that were not present in the old schema.
    // For example, a description that the latest model has but the newest does not have.
    if (
      oldSchema[fieldName] === undefined &&
      result.nonAdditiveFieldsUpdates[fieldName] === undefined &&
      result.additiveFieldsMatches[fieldName] === undefined &&
      skipSet.has(fieldName)
    ) {
      result.nonAdditiveFieldsUpdates[fieldName] = newSchema[fieldName];
    }

    // for additive fields not in the oldmodel, we should treat them as empty objects/arrays.
    // Example: if the newer schema has an allOf and the old doesn't we should still treat as a match.
    // It's equivalent to saying that the old one has also an allOf, but it's an empty array.
    if (
      oldSchema[fieldName] === undefined &&
      result.additiveFieldsMatches[fieldName] === undefined &&
      !skipSet.has(fieldName)
    ) {
      result.additiveFieldsMatches[fieldName] = newSchema[fieldName];
    }
  }

  return result;
}