Skip to content

Commit

Permalink
fix(reference): fix regression in OpenAPI 3.0.x dereferencing (#3977)
Browse files Browse the repository at this point in the history
Refs #3974
  • Loading branch information
char0n committed Mar 28, 2024
1 parent 2e4d15a commit 45d706f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,17 @@ const OpenApi3_0DereferenceVisitor = stampit({
*
* Cases to consider:
* 1. We're crossing document boundary
* 2. Fragment is a Reference Object. We need to follow it to get the eventual value
* 3. We are dereferencing the fragment lazily/eagerly depending on circular mode
* 2. Fragment is from non-root document
* 3. Fragment is a Reference Object. We need to follow it to get the eventual value
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
*/
const isNonRootDocument = reference.refSet.rootRef.uri !== reference.uri;
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
if (
(isExternalReference ||
isNonRootDocument ||
isReferenceElement(referencedElement) ||
['error', 'replace'].includes(this.options.dereference.circular)) &&
shouldDetectCircular) &&
!ancestorsLineage.includesCycle(referencedElement)
) {
// append referencing reference to ancestors lineage
Expand Down Expand Up @@ -401,13 +405,17 @@ const OpenApi3_0DereferenceVisitor = stampit({
*
* Cases to consider:
* 1. We're crossing document boundary
* 2. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
* 3. We are dereferencing the fragment lazily/eagerly depending on circular mode
* 2. Fragment is from non-root document
* 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
*/
const isNonRootDocument = reference.refSet.rootRef.uri !== reference.uri;
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
if (
(isExternalReference ||
isNonRootDocument ||
(isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref)) ||
['error', 'replace'].includes(this.options.dereference.circular)) &&
shouldDetectCircular) &&
!ancestorsLineage.includesCycle(referencedElement)
) {
// append referencing reference to ancestors lineage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assert } from 'chai';
import {
mediaTypes,
isParameterElement,
isSchemaElement,
OpenApi3_0Element,
} from '@swagger-api/apidom-ns-openapi-3-0';
import { toValue } from '@swagger-api/apidom-core';
Expand Down Expand Up @@ -123,23 +124,23 @@ describe('dereference', function () {
parse: { mediaType: mediaTypes.latest('json') },
});
const referenceElement = evaluate(
'/components/parameters/externalRef',
'/components/schemas/externalSchema',
parseResult.api as OpenApi3_0Element,
);
const dereferenced = await dereferenceApiDOM(referenceElement, {
parse: { mediaType: mediaTypes.latest('json') },
resolve: { baseURI: fixturePath },
});

assert.isTrue(isParameterElement(dereferenced));
assert.isTrue(isSchemaElement(dereferenced));
});

specify('should dereference and contain metadata about origin', async function () {
const parseResult = await parse(fixturePath, {
parse: { mediaType: mediaTypes.latest('json') },
});
const referenceElement = evaluate(
'/components/parameters/externalRef',
'/components/schemas/externalSchema',
parseResult.api as OpenApi3_0Element,
);
const dereferenced = await dereferenceApiDOM(referenceElement, {
Expand Down Expand Up @@ -173,23 +174,23 @@ describe('dereference', function () {
parse: { mediaType: mediaTypes.latest('json') },
});
const referenceElement = evaluate(
'/components/parameters/externalRef',
'/components/schemas/externalSchema',
parseResult.api as OpenApi3_0Element,
);
const dereferenced = await dereferenceApiDOM(referenceElement, {
parse: { mediaType: mediaTypes.latest('json') },
resolve: { baseURI: `http://localhost:${httpPort}/root.json` },
});

assert.isTrue(isParameterElement(dereferenced));
assert.isTrue(isSchemaElement(dereferenced));
});

specify('should dereference and contain metadata about origin', async function () {
const parseResult = await parse(fixturePath, {
parse: { mediaType: mediaTypes.latest('json') },
});
const referenceElement = evaluate(
'/components/parameters/externalRef',
'/components/schemas/externalSchema',
parseResult.api as OpenApi3_0Element,
);
const dereferenced = await dereferenceApiDOM(referenceElement, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@
{
"openapi": "3.0.3",
"components": {
"parameters": {
"externalRef": {
"name": "externalParameter",
"in": "query",
"description": "this is parameter stored in external file",
"required": true
"schemas": {
"externalSchema": {
"schema1": {
"type": "object",
"properties": {
"sentAt": {
"type": "string",
"format": "date-time",
"description": "Date and time when the message was sent."
}
}
},
"somePayload": {
"type": "object",
"properties": {
"sentAt": {
"type": "string",
"format": "date-time",
"description": "Date and time when the message was sent."
}
}
},
"sentAt": {
"type": "string",
"format": "date-time",
"description": "Date and time when the message was sent."
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
{
"externalParameter": {
"name": "externalParameter",
"in": "query",
"description": "this is parameter stored in external file",
"required": true
"openapi": "3.0.3",
"components": {
"schemas": {
"schema1": {
"$ref": "#/components/schemas/somePayload"
},
"somePayload": {
"type": "object",
"properties": {
"sentAt": {
"$ref": "#/components/schemas/sentAt"
}
}
},
"sentAt": {
"type": "string",
"format": "date-time",
"description": "Date and time when the message was sent."
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"openapi": "3.0.3",
"components": {
"parameters": {
"externalRef": {
"$ref": "./ex.json#/externalParameter"
"schemas": {
"externalSchema": {
"$ref": "./ex.json#/components/schemas"
}
}
}
Expand Down

0 comments on commit 45d706f

Please sign in to comment.