Skip to content

Commit 45d706f

Browse files
authored
fix(reference): fix regression in OpenAPI 3.0.x dereferencing (#3977)
Refs #3974
1 parent 2e4d15a commit 45d706f

File tree

5 files changed

+71
-26
lines changed

5 files changed

+71
-26
lines changed

packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,17 @@ const OpenApi3_0DereferenceVisitor = stampit({
234234
*
235235
* Cases to consider:
236236
* 1. We're crossing document boundary
237-
* 2. Fragment is a Reference Object. We need to follow it to get the eventual value
238-
* 3. We are dereferencing the fragment lazily/eagerly depending on circular mode
237+
* 2. Fragment is from non-root document
238+
* 3. Fragment is a Reference Object. We need to follow it to get the eventual value
239+
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
239240
*/
241+
const isNonRootDocument = reference.refSet.rootRef.uri !== reference.uri;
242+
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
240243
if (
241244
(isExternalReference ||
245+
isNonRootDocument ||
242246
isReferenceElement(referencedElement) ||
243-
['error', 'replace'].includes(this.options.dereference.circular)) &&
247+
shouldDetectCircular) &&
244248
!ancestorsLineage.includesCycle(referencedElement)
245249
) {
246250
// append referencing reference to ancestors lineage
@@ -401,13 +405,17 @@ const OpenApi3_0DereferenceVisitor = stampit({
401405
*
402406
* Cases to consider:
403407
* 1. We're crossing document boundary
404-
* 2. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
405-
* 3. We are dereferencing the fragment lazily/eagerly depending on circular mode
408+
* 2. Fragment is from non-root document
409+
* 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
410+
* 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
406411
*/
412+
const isNonRootDocument = reference.refSet.rootRef.uri !== reference.uri;
413+
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
407414
if (
408415
(isExternalReference ||
416+
isNonRootDocument ||
409417
(isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref)) ||
410-
['error', 'replace'].includes(this.options.dereference.circular)) &&
418+
shouldDetectCircular) &&
411419
!ancestorsLineage.includesCycle(referencedElement)
412420
) {
413421
// append referencing reference to ancestors lineage

packages/apidom-reference/test/dereference/strategies/openapi-3-0/reference-object/dereference-apidom.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { assert } from 'chai';
33
import {
44
mediaTypes,
55
isParameterElement,
6+
isSchemaElement,
67
OpenApi3_0Element,
78
} from '@swagger-api/apidom-ns-openapi-3-0';
89
import { toValue } from '@swagger-api/apidom-core';
@@ -123,23 +124,23 @@ describe('dereference', function () {
123124
parse: { mediaType: mediaTypes.latest('json') },
124125
});
125126
const referenceElement = evaluate(
126-
'/components/parameters/externalRef',
127+
'/components/schemas/externalSchema',
127128
parseResult.api as OpenApi3_0Element,
128129
);
129130
const dereferenced = await dereferenceApiDOM(referenceElement, {
130131
parse: { mediaType: mediaTypes.latest('json') },
131132
resolve: { baseURI: fixturePath },
132133
});
133134

134-
assert.isTrue(isParameterElement(dereferenced));
135+
assert.isTrue(isSchemaElement(dereferenced));
135136
});
136137

137138
specify('should dereference and contain metadata about origin', async function () {
138139
const parseResult = await parse(fixturePath, {
139140
parse: { mediaType: mediaTypes.latest('json') },
140141
});
141142
const referenceElement = evaluate(
142-
'/components/parameters/externalRef',
143+
'/components/schemas/externalSchema',
143144
parseResult.api as OpenApi3_0Element,
144145
);
145146
const dereferenced = await dereferenceApiDOM(referenceElement, {
@@ -173,23 +174,23 @@ describe('dereference', function () {
173174
parse: { mediaType: mediaTypes.latest('json') },
174175
});
175176
const referenceElement = evaluate(
176-
'/components/parameters/externalRef',
177+
'/components/schemas/externalSchema',
177178
parseResult.api as OpenApi3_0Element,
178179
);
179180
const dereferenced = await dereferenceApiDOM(referenceElement, {
180181
parse: { mediaType: mediaTypes.latest('json') },
181182
resolve: { baseURI: `http://localhost:${httpPort}/root.json` },
182183
});
183184

184-
assert.isTrue(isParameterElement(dereferenced));
185+
assert.isTrue(isSchemaElement(dereferenced));
185186
});
186187

187188
specify('should dereference and contain metadata about origin', async function () {
188189
const parseResult = await parse(fixturePath, {
189190
parse: { mediaType: mediaTypes.latest('json') },
190191
});
191192
const referenceElement = evaluate(
192-
'/components/parameters/externalRef',
193+
'/components/schemas/externalSchema',
193194
parseResult.api as OpenApi3_0Element,
194195
);
195196
const dereferenced = await dereferenceApiDOM(referenceElement, {

packages/apidom-reference/test/dereference/strategies/openapi-3-0/reference-object/fixtures/external-only/dereferenced.json

+27-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,33 @@
22
{
33
"openapi": "3.0.3",
44
"components": {
5-
"parameters": {
6-
"externalRef": {
7-
"name": "externalParameter",
8-
"in": "query",
9-
"description": "this is parameter stored in external file",
10-
"required": true
5+
"schemas": {
6+
"externalSchema": {
7+
"schema1": {
8+
"type": "object",
9+
"properties": {
10+
"sentAt": {
11+
"type": "string",
12+
"format": "date-time",
13+
"description": "Date and time when the message was sent."
14+
}
15+
}
16+
},
17+
"somePayload": {
18+
"type": "object",
19+
"properties": {
20+
"sentAt": {
21+
"type": "string",
22+
"format": "date-time",
23+
"description": "Date and time when the message was sent."
24+
}
25+
}
26+
},
27+
"sentAt": {
28+
"type": "string",
29+
"format": "date-time",
30+
"description": "Date and time when the message was sent."
31+
}
1132
}
1233
}
1334
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
{
2-
"externalParameter": {
3-
"name": "externalParameter",
4-
"in": "query",
5-
"description": "this is parameter stored in external file",
6-
"required": true
2+
"openapi": "3.0.3",
3+
"components": {
4+
"schemas": {
5+
"schema1": {
6+
"$ref": "#/components/schemas/somePayload"
7+
},
8+
"somePayload": {
9+
"type": "object",
10+
"properties": {
11+
"sentAt": {
12+
"$ref": "#/components/schemas/sentAt"
13+
}
14+
}
15+
},
16+
"sentAt": {
17+
"type": "string",
18+
"format": "date-time",
19+
"description": "Date and time when the message was sent."
20+
}
21+
}
722
}
823
}

packages/apidom-reference/test/dereference/strategies/openapi-3-0/reference-object/fixtures/external-only/root.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"openapi": "3.0.3",
33
"components": {
4-
"parameters": {
5-
"externalRef": {
6-
"$ref": "./ex.json#/externalParameter"
4+
"schemas": {
5+
"externalSchema": {
6+
"$ref": "./ex.json#/components/schemas"
77
}
88
}
99
}

0 commit comments

Comments
 (0)