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;
}