Skip to content

Commit

Permalink
fix(reference): add support for external cycles detection
Browse files Browse the repository at this point in the history
Refs #3863
  • Loading branch information
char0n committed Feb 28, 2024
1 parent ada33bc commit 1d7f125
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,22 @@ const AsyncApi2DereferenceVisitor = stampit({
reference: null,
options: null,
ancestors: null,
refractCache: null,
},
init({ indirections = [], reference, namespace, options, ancestors = new AncestorLineage() }) {
init({
indirections = [],
reference,
namespace,
options,
ancestors = new AncestorLineage(),
refractCache = new Map(),
}) {
this.indirections = indirections;
this.namespace = namespace;
this.reference = reference;
this.options = options;
this.ancestors = new AncestorLineage(...ancestors);
this.refractCache = refractCache;
},
methods: {
toBaseURI(uri: string): string {
Expand Down Expand Up @@ -148,15 +157,20 @@ const AsyncApi2DereferenceVisitor = stampit({
// applying semantics to a fragment
if (isPrimitiveElement(referencedElement)) {
const referencedElementType = toValue(referencingElement.meta.get('referenced-element'));
const cacheKey = `${referencedElementType}-${toValue(identityManager.identify(referencedElement))}`;

if (isReferenceLikeElement(referencedElement)) {
if (this.refractCache.has(cacheKey)) {
referencedElement = this.refractCache.get(cacheKey);
} else if (isReferenceLikeElement(referencedElement)) {
// handling indirect references
referencedElement = ReferenceElement.refract(referencedElement);
referencedElement.setMetaProperty('referenced-element', referencedElementType);
this.refractCache.set(cacheKey, referencedElement);
} else {
// handling direct references
const ElementClass = this.namespace.getElementClass(referencedElementType);
referencedElement = ElementClass.refract(referencedElement);
this.refractCache.set(cacheKey, referencedElement);
}
}

Expand All @@ -182,6 +196,7 @@ const AsyncApi2DereferenceVisitor = stampit({
indirections: [...this.indirections],
options: this.options,
ancestors: ancestorsLineage,
refractCache: this.refractCache,
});
referencedElement = await visitAsync(referencedElement, visitor, {
keyMap,
Expand Down Expand Up @@ -288,7 +303,14 @@ const AsyncApi2DereferenceVisitor = stampit({

// applying semantics to a referenced element
if (isPrimitiveElement(referencedElement)) {
referencedElement = ChannelItemElement.refract(referencedElement);
const cacheKey = `channel-${toValue(identityManager.identify(referencedElement))}`;

if (this.refractCache.has(cacheKey)) {
referencedElement = this.refractCache.get(cacheKey);
} else {
referencedElement = ChannelItemElement.refract(referencedElement);
this.refractCache.set(cacheKey, referencedElement);
}
}

// detect direct or indirect reference
Expand All @@ -313,6 +335,7 @@ const AsyncApi2DereferenceVisitor = stampit({
indirections: [...this.indirections],
options: this.options,
ancestors: ancestorsLineage,
refractCache: this.refractCache,
});
referencedElement = await visitAsync(referencedElement, visitor, {
keyMap,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"properties": {
"parent": {
"$ref": "#"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"asyncapi": "2.6.0",
"components": {
"schemas": {
"externalSchema": {
"$ref": "./ex.json"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ describe('dereference', function () {
});
});

context('given Reference Objects pointing to external cycles', function () {
const fixturePath = path.join(rootFixturePath, 'external-cycle');

specify('should dereference', async function () {
const rootFilePath = path.join(fixturePath, 'root.json');
const dereferenced = await dereference(rootFilePath, {
parse: { mediaType: mediaTypes.latest('json') },
});
const parent = evaluate(
'/0/components/schemas/externalSchema/properties',
dereferenced,
);
const cyclicParent = evaluate(
'/0/components/schemas/externalSchema/properties/parent/properties',
dereferenced,
);

assert.strictEqual(parent, cyclicParent);
});
});

context('given Reference Objects pointing to external indirections', function () {
const fixturePath = path.join(rootFixturePath, 'external-indirections');

Expand Down

0 comments on commit 1d7f125

Please sign in to comment.