Skip to content

Commit bac4850

Browse files
authored
fix(reference): fix internal/external URL determination for OpenAPI 3.0.x (#3455)
This change will handle cases where the referenced data is served from the external URL, but the definition is served on the the same external URL as well. Refs #3451
1 parent c720584 commit bac4850

File tree

5 files changed

+47
-103
lines changed

5 files changed

+47
-103
lines changed

packages/apidom-ns-openapi-3-0/src/index.ts

-3
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,13 @@ export {
3030
isInfoElement,
3131
isLicenseElement,
3232
isLinkElement,
33-
isLinkElementExternal,
3433
isOpenapiElement,
3534
isOpenApi3_0Element,
3635
isOperationElement,
3736
isParameterElement,
3837
isPathItemElement,
39-
isPathItemElementExternal,
4038
isPathsElement,
4139
isReferenceElement,
42-
isReferenceElementExternal,
4340
isRequestBodyElement,
4441
isResponseElement,
4542
isResponsesElement,

packages/apidom-ns-openapi-3-0/src/predicates.ts

+1-52
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import {
2-
BooleanElement,
3-
createPredicate,
4-
isBooleanElement,
5-
isStringElement,
6-
toValue,
7-
} from '@swagger-api/apidom-core';
1+
import { BooleanElement, createPredicate, isBooleanElement } from '@swagger-api/apidom-core';
82
import type { ElementPredicate } from '@swagger-api/apidom-core';
93

104
import CallbackElement from './elements/Callback';
@@ -124,21 +118,6 @@ export const isLinkElement = createPredicate(
124118
},
125119
);
126120

127-
export const isLinkElementExternal: ElementPredicate<LinkElement> = (
128-
element: unknown,
129-
): element is LinkElement => {
130-
if (!isLinkElement(element)) {
131-
return false;
132-
}
133-
if (!isStringElement(element.operationRef)) {
134-
return false;
135-
}
136-
137-
const value = toValue(element.operationRef);
138-
139-
return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
140-
};
141-
142121
export const isOpenapiElement = createPredicate(
143122
({ hasBasicElementProps, isElementType, primitiveEq }) => {
144123
return (element: unknown): element is OpenapiElement =>
@@ -191,21 +170,6 @@ export const isPathItemElement = createPredicate(
191170
},
192171
);
193172

194-
export const isPathItemElementExternal: ElementPredicate<PathItemElement> = (
195-
element: unknown,
196-
): element is PathItemElement => {
197-
if (!isPathItemElement(element)) {
198-
return false;
199-
}
200-
if (!isStringElement(element.$ref)) {
201-
return false;
202-
}
203-
204-
const value = toValue(element.$ref);
205-
206-
return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
207-
};
208-
209173
export const isPathsElement = createPredicate(
210174
({ hasBasicElementProps, isElementType, primitiveEq }) => {
211175
return (element: unknown): element is PathsElement =>
@@ -226,21 +190,6 @@ export const isReferenceElement = createPredicate(
226190
},
227191
);
228192

229-
export const isReferenceElementExternal: ElementPredicate<ReferenceElement> = (
230-
element: unknown,
231-
): element is ReferenceElement => {
232-
if (!isReferenceElement(element)) {
233-
return false;
234-
}
235-
if (!isStringElement(element.$ref)) {
236-
return false;
237-
}
238-
239-
const value = toValue(element.$ref);
240-
241-
return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
242-
};
243-
244193
export const isRequestBodyElement = createPredicate(
245194
({ hasBasicElementProps, isElementType, primitiveEq }) => {
246195
return (element: unknown): element is RequestBodyElement =>

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

+25-24
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ import {
2626
OperationElement,
2727
PathItemElement,
2828
isOperationElement,
29-
isReferenceElementExternal,
30-
isPathItemElementExternal,
31-
isLinkElementExternal,
3229
} from '@swagger-api/apidom-ns-openapi-3-0';
3330

3431
import { Reference as IReference } from '../../../types';
@@ -133,16 +130,16 @@ const OpenApi3_0DereferenceVisitor = stampit({
133130
return false;
134131
}
135132

136-
// ignore resolving external Reference Objects
137-
if (!this.options.resolve.external && isReferenceElementExternal(referencingElement)) {
138-
// skip traversing this schema but traverse all it's child schemas
139-
return undefined;
140-
}
141-
142133
const reference = await this.toReference(toValue(referencingElement.$ref));
143134
const { uri: retrievalURI } = reference;
144135
const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
145136

137+
// ignore resolving external Reference Objects
138+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
139+
// skip traversing this reference element but traverse all it's child elements
140+
return undefined;
141+
}
142+
146143
this.indirections.push(referencingElement);
147144

148145
const jsonPointer = uriToPointer($refBaseURI);
@@ -256,15 +253,16 @@ const OpenApi3_0DereferenceVisitor = stampit({
256253
return false;
257254
}
258255

259-
// ignore resolving external Path Item Elements
260-
if (!this.options.resolve.external && isPathItemElementExternal(referencingElement)) {
261-
return undefined;
262-
}
263-
264256
const reference = await this.toReference(toValue(referencingElement.$ref));
265257
const retrievalURI = reference.uri;
266258
const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
267259

260+
// ignore resolving external Path Item Objects
261+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
262+
// skip traversing this Path Item element but traverse all it's child elements
263+
return undefined;
264+
}
265+
268266
this.indirections.push(referencingElement);
269267

270268
const jsonPointer = uriToPointer($refBaseURI);
@@ -367,11 +365,6 @@ const OpenApi3_0DereferenceVisitor = stampit({
367365
return undefined;
368366
}
369367

370-
// ignore resolving external Path Item Elements
371-
if (!this.options.resolve.external && isLinkElementExternal(linkElement)) {
372-
return undefined;
373-
}
374-
375368
// operationRef and operationId fields are mutually exclusive
376369
if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
377370
throw new ApiDOMError(
@@ -385,6 +378,13 @@ const OpenApi3_0DereferenceVisitor = stampit({
385378
// possibly non-semantic referenced element
386379
const jsonPointer = uriToPointer(toValue(linkElement.operationRef));
387380
const reference = await this.toReference(toValue(linkElement.operationRef));
381+
382+
// ignore resolving external Operation Object reference
383+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== reference.uri) {
384+
// skip traversing this Link element but traverse all it's child elements
385+
return undefined;
386+
}
387+
388388
operationElement = evaluate(jsonPointer, reference.value.result);
389389
// applying semantics to a referenced element
390390
if (isPrimitiveElement(operationElement)) {
@@ -440,11 +440,6 @@ const OpenApi3_0DereferenceVisitor = stampit({
440440
return false;
441441
}
442442

443-
// ignore resolving ExampleElement externalValue
444-
if (!this.options.resolve.external && isStringElement(exampleElement.externalValue)) {
445-
return undefined;
446-
}
447-
448443
// value and externalValue fields are mutually exclusive
449444
if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
450445
throw new ApiDOMError(
@@ -454,6 +449,12 @@ const OpenApi3_0DereferenceVisitor = stampit({
454449

455450
const reference = await this.toReference(toValue(exampleElement.externalValue));
456451

452+
// ignore resolving external Example Objects
453+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== reference.uri) {
454+
// skip traversing this Example element but traverse all it's child elements
455+
return undefined;
456+
}
457+
457458
// shallow clone of the referenced element
458459
const valueElement = cloneShallow(reference.value.result);
459460
// annotate operation element with info about origin

packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const AsyncApi2ResolveVisitor = stampit({
8383
const uri = toValue(referenceElement.$ref);
8484
const baseURI = this.toBaseURI(uri);
8585

86-
// // ignore resolving external Reference Objects
86+
// ignore resolving external Reference Objects
8787
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
8888
return false;
8989
}

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

+20-23
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ import {
1414
PathItemElement,
1515
LinkElement,
1616
ExampleElement,
17-
isReferenceElementExternal,
18-
isPathItemElementExternal,
19-
isLinkElementExternal,
2017
} from '@swagger-api/apidom-ns-openapi-3-0';
2118

2219
import { Reference as IReference } from '../../../types';
@@ -86,14 +83,14 @@ const OpenApi3_0ResolveVisitor = stampit({
8683
},
8784

8885
ReferenceElement(referenceElement: ReferenceElement) {
89-
// ignore resolving external Reference Objects
90-
if (!this.options.resolve.external && isReferenceElementExternal(referenceElement)) {
91-
return false;
92-
}
93-
9486
const uri = toValue(referenceElement.$ref);
9587
const baseURI = this.toBaseURI(uri);
9688

89+
// ignore resolving external Reference Objects
90+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
91+
return undefined;
92+
}
93+
9794
if (!has(baseURI, this.crawlingMap)) {
9895
this.crawlingMap[baseURI] = this.toReference(uri);
9996
}
@@ -108,14 +105,14 @@ const OpenApi3_0ResolveVisitor = stampit({
108105
return undefined;
109106
}
110107

108+
const uri = toValue(pathItemElement.$ref);
109+
const baseURI = this.toBaseURI(uri);
110+
111111
// ignore resolving external Path Item Objects
112-
if (!this.options.resolve.external && isPathItemElementExternal(pathItemElement)) {
112+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
113113
return undefined;
114114
}
115115

116-
const uri = toValue(pathItemElement.$ref);
117-
const baseURI = this.toBaseURI(uri);
118-
119116
if (!has(baseURI, this.crawlingMap)) {
120117
this.crawlingMap[baseURI] = this.toReference(uri);
121118
}
@@ -130,20 +127,20 @@ const OpenApi3_0ResolveVisitor = stampit({
130127
return undefined;
131128
}
132129

133-
// ignore resolving external Path Item Elements
134-
if (!this.options.resolve.external && isLinkElementExternal(linkElement)) {
135-
return undefined;
136-
}
137-
138130
// operationRef and operationId are mutually exclusive
139131
if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
140132
throw new ApiDOMError('LinkElement operationRef and operationId are mutually exclusive.');
141133
}
142134

143-
if (isLinkElementExternal(linkElement)) {
135+
if (isStringElement(linkElement.operationRef)) {
144136
const uri = toValue(linkElement.operationRef);
145137
const baseURI = this.toBaseURI(uri);
146138

139+
// ignore resolving LinkElement.operationRef
140+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
141+
return undefined;
142+
}
143+
147144
if (!has(baseURI, this.crawlingMap)) {
148145
this.crawlingMap[baseURI] = this.toReference(uri);
149146
}
@@ -158,11 +155,6 @@ const OpenApi3_0ResolveVisitor = stampit({
158155
return undefined;
159156
}
160157

161-
// ignore resolving ExampleElement externalValue
162-
if (!this.options.resolve.external && isStringElement(exampleElement.externalValue)) {
163-
return undefined;
164-
}
165-
166158
// value and externalValue fields are mutually exclusive
167159
if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
168160
throw new ApiDOMError(
@@ -173,6 +165,11 @@ const OpenApi3_0ResolveVisitor = stampit({
173165
const uri = toValue(exampleElement.externalValue);
174166
const baseURI = this.toBaseURI(uri);
175167

168+
// ignore resolving ExampleElement externalValue
169+
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
170+
return undefined;
171+
}
172+
176173
if (!has(baseURI, this.crawlingMap)) {
177174
this.crawlingMap[baseURI] = this.toReference(uri);
178175
}

0 commit comments

Comments
 (0)