in packages/libs/deduplication/src/main.ts [264:381]
private async deduplicateComponent(componentUid: string, type: string) {
switch (type) {
case "schemas":
case "responses":
case "parameters":
case "examples":
case "requestBodies":
case "headers":
case "links":
case "callbacks":
case "securitySchemes":
if (!this.deduplicatedComponents[type].has(componentUid)) {
if (!this.crawledComponents[type].has(componentUid)) {
await YieldCPU();
await this.crawlComponent(componentUid, type);
}
// Just try to deduplicate if it was not deduplicated while crawling another component.
if (!this.deduplicatedComponents[type].has(componentUid)) {
// deduplicate crawled component
const xMsMetadata = "x-ms-metadata";
const component = this.target.components[type][componentUid];
// extract metadata to be merged
let apiVersions = component[xMsMetadata].apiVersions;
let filename = component[xMsMetadata].filename;
let originalLocations = component[xMsMetadata].originalLocations;
let name = component[xMsMetadata].name;
// extract component properties excluding metadata
const { "x-ms-metadata": metadataCurrent, ...filteredComponent } = component;
// iterate over all the components of the same type of the component
for (const { key: anotherComponentUid, value: anotherComponent } of visit(this.target.components[type])) {
// ignore merge with itself && anotherComponent already deleted (i.e. undefined)
if (anotherComponent !== undefined && componentUid !== anotherComponentUid) {
// extract the another component's properties excluding metadata
const { "x-ms-metadata": metadataSchema, ...filteredAnotherComponent } = anotherComponent;
// TODO: Add more keys to ignore.
const keysToIgnore: Array<string> = ["description", "x-ms-original", "x-ms-examples"];
const namesMatch = this.deduplicateInlineModels
? anotherComponent[xMsMetadata].name.replace(/\d*$/, "") ===
component[xMsMetadata].name.replace(/\d*$/, "") ||
(anotherComponent[xMsMetadata].name.indexOf("·") > -1 &&
component[xMsMetadata].name.indexOf("·") > -1)
: anotherComponent[xMsMetadata].name.replace(/\d*$/, "") ===
component[xMsMetadata].name.replace(/\d*$/, "");
// const t1 = component['type'];
// const t2 = anotherComponent['type'];
// const typesAreSame = t1 === t2;
// const isObjectSchema = t1 === 'object' || t1 === undefined;
// const isStringSchemaWithFormat = !!(t1 === 'string' && component['format']);
// (type === 'schemas' && t1 === t2 && (t1 === 'object' || (t1 === 'string' && component['format']) || t1 === undefined))
// they should have the same name to be merged and they should be similar
if (
namesMatch &&
// typesAreSame &&
// (isObjectSchema || isStringSchemaWithFormat) &&
areSimilar(filteredAnotherComponent, filteredComponent, ...keysToIgnore)
) {
// if the primary has a synthetic name, and the secondary doesn't use the secondary's name
if (name.indexOf("·") > 0 && anotherComponent[xMsMetadata].name.indexOf("·") === -1) {
name = anotherComponent[xMsMetadata].name;
}
// merge metadata
apiVersions = apiVersions.concat(anotherComponent[xMsMetadata].apiVersions);
filename = filename.concat(anotherComponent[xMsMetadata].filename);
originalLocations = originalLocations.concat(anotherComponent[xMsMetadata].originalLocations);
// the discriminator to take contents is the api version
const maxApiVersionComponent = maximum(component[xMsMetadata].apiVersions);
const maxApiVersionAnotherComponent = maximum(anotherComponent[xMsMetadata].apiVersions);
let uidComponentToDelete = anotherComponentUid;
if (
compareVersions(toSemver(maxApiVersionComponent), toSemver(maxApiVersionAnotherComponent)) === -1
) {
// if the current component max api version is less than the another component, swap ids.
uidComponentToDelete = componentUid;
componentUid = anotherComponentUid;
}
// finish up
delete this.target.components[type][uidComponentToDelete];
this.refs[`#/components/${type}/${uidComponentToDelete}`] = `#/components/${type}/${componentUid}`;
this.updateRefs(this.target);
this.updateMappings(
`/components/${type}/${uidComponentToDelete}`,
`/components/${type}/${componentUid}`,
);
this.deduplicatedComponents[type].add(uidComponentToDelete);
}
}
}
this.target.components[type][componentUid][xMsMetadata] = {
apiVersions: [...new Set([...apiVersions])],
filename: [...new Set([...filename])],
name,
originalLocations: [...new Set([...originalLocations])],
};
this.deduplicatedComponents[type].add(componentUid);
}
}
break;
default:
if (isExtensionKey(type)) {
// Skip unknown extension type
return;
}
throw new Error(`Unknown component type: '${type}'`);
}
}