Skip to content

Commit

Permalink
fix(reference): fix internal/external URL determination for OpenAPI 3…
Browse files Browse the repository at this point in the history
….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
  • Loading branch information
char0n committed Nov 24, 2023
1 parent c720584 commit bac4850
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 103 deletions.
3 changes: 0 additions & 3 deletions packages/apidom-ns-openapi-3-0/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,13 @@ export {
isInfoElement,
isLicenseElement,
isLinkElement,
isLinkElementExternal,
isOpenapiElement,
isOpenApi3_0Element,
isOperationElement,
isParameterElement,
isPathItemElement,
isPathItemElementExternal,
isPathsElement,
isReferenceElement,
isReferenceElementExternal,
isRequestBodyElement,
isResponseElement,
isResponsesElement,
Expand Down
53 changes: 1 addition & 52 deletions packages/apidom-ns-openapi-3-0/src/predicates.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
BooleanElement,
createPredicate,
isBooleanElement,
isStringElement,
toValue,
} from '@swagger-api/apidom-core';
import { BooleanElement, createPredicate, isBooleanElement } from '@swagger-api/apidom-core';
import type { ElementPredicate } from '@swagger-api/apidom-core';

import CallbackElement from './elements/Callback';
Expand Down Expand Up @@ -124,21 +118,6 @@ export const isLinkElement = createPredicate(
},
);

export const isLinkElementExternal: ElementPredicate<LinkElement> = (
element: unknown,
): element is LinkElement => {
if (!isLinkElement(element)) {
return false;
}
if (!isStringElement(element.operationRef)) {
return false;
}

const value = toValue(element.operationRef);

return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
};

export const isOpenapiElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: unknown): element is OpenapiElement =>
Expand Down Expand Up @@ -191,21 +170,6 @@ export const isPathItemElement = createPredicate(
},
);

export const isPathItemElementExternal: ElementPredicate<PathItemElement> = (
element: unknown,
): element is PathItemElement => {
if (!isPathItemElement(element)) {
return false;
}
if (!isStringElement(element.$ref)) {
return false;
}

const value = toValue(element.$ref);

return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
};

export const isPathsElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: unknown): element is PathsElement =>
Expand All @@ -226,21 +190,6 @@ export const isReferenceElement = createPredicate(
},
);

export const isReferenceElementExternal: ElementPredicate<ReferenceElement> = (
element: unknown,
): element is ReferenceElement => {
if (!isReferenceElement(element)) {
return false;
}
if (!isStringElement(element.$ref)) {
return false;
}

const value = toValue(element.$ref);

return typeof value === 'string' && value.length > 0 && !value.startsWith('#');
};

export const isRequestBodyElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: unknown): element is RequestBodyElement =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ import {
OperationElement,
PathItemElement,
isOperationElement,
isReferenceElementExternal,
isPathItemElementExternal,
isLinkElementExternal,
} from '@swagger-api/apidom-ns-openapi-3-0';

import { Reference as IReference } from '../../../types';
Expand Down Expand Up @@ -133,16 +130,16 @@ const OpenApi3_0DereferenceVisitor = stampit({
return false;
}

// ignore resolving external Reference Objects
if (!this.options.resolve.external && isReferenceElementExternal(referencingElement)) {
// skip traversing this schema but traverse all it's child schemas
return undefined;
}

const reference = await this.toReference(toValue(referencingElement.$ref));
const { uri: retrievalURI } = reference;
const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));

// ignore resolving external Reference Objects
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
// skip traversing this reference element but traverse all it's child elements
return undefined;
}

this.indirections.push(referencingElement);

const jsonPointer = uriToPointer($refBaseURI);
Expand Down Expand Up @@ -256,15 +253,16 @@ const OpenApi3_0DereferenceVisitor = stampit({
return false;
}

// ignore resolving external Path Item Elements
if (!this.options.resolve.external && isPathItemElementExternal(referencingElement)) {
return undefined;
}

const reference = await this.toReference(toValue(referencingElement.$ref));
const retrievalURI = reference.uri;
const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));

// ignore resolving external Path Item Objects
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
// skip traversing this Path Item element but traverse all it's child elements
return undefined;
}

this.indirections.push(referencingElement);

const jsonPointer = uriToPointer($refBaseURI);
Expand Down Expand Up @@ -367,11 +365,6 @@ const OpenApi3_0DereferenceVisitor = stampit({
return undefined;
}

// ignore resolving external Path Item Elements
if (!this.options.resolve.external && isLinkElementExternal(linkElement)) {
return undefined;
}

// operationRef and operationId fields are mutually exclusive
if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
throw new ApiDOMError(
Expand All @@ -385,6 +378,13 @@ const OpenApi3_0DereferenceVisitor = stampit({
// possibly non-semantic referenced element
const jsonPointer = uriToPointer(toValue(linkElement.operationRef));
const reference = await this.toReference(toValue(linkElement.operationRef));

// ignore resolving external Operation Object reference
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== reference.uri) {
// skip traversing this Link element but traverse all it's child elements
return undefined;
}

operationElement = evaluate(jsonPointer, reference.value.result);
// applying semantics to a referenced element
if (isPrimitiveElement(operationElement)) {
Expand Down Expand Up @@ -440,11 +440,6 @@ const OpenApi3_0DereferenceVisitor = stampit({
return false;
}

// ignore resolving ExampleElement externalValue
if (!this.options.resolve.external && isStringElement(exampleElement.externalValue)) {
return undefined;
}

// value and externalValue fields are mutually exclusive
if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
throw new ApiDOMError(
Expand All @@ -454,6 +449,12 @@ const OpenApi3_0DereferenceVisitor = stampit({

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

// ignore resolving external Example Objects
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== reference.uri) {
// skip traversing this Example element but traverse all it's child elements
return undefined;
}

// shallow clone of the referenced element
const valueElement = cloneShallow(reference.value.result);
// annotate operation element with info about origin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const AsyncApi2ResolveVisitor = stampit({
const uri = toValue(referenceElement.$ref);
const baseURI = this.toBaseURI(uri);

// // ignore resolving external Reference Objects
// ignore resolving external Reference Objects
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ import {
PathItemElement,
LinkElement,
ExampleElement,
isReferenceElementExternal,
isPathItemElementExternal,
isLinkElementExternal,
} from '@swagger-api/apidom-ns-openapi-3-0';

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

ReferenceElement(referenceElement: ReferenceElement) {
// ignore resolving external Reference Objects
if (!this.options.resolve.external && isReferenceElementExternal(referenceElement)) {
return false;
}

const uri = toValue(referenceElement.$ref);
const baseURI = this.toBaseURI(uri);

// ignore resolving external Reference Objects
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
return undefined;
}

if (!has(baseURI, this.crawlingMap)) {
this.crawlingMap[baseURI] = this.toReference(uri);
}
Expand All @@ -108,14 +105,14 @@ const OpenApi3_0ResolveVisitor = stampit({
return undefined;
}

const uri = toValue(pathItemElement.$ref);
const baseURI = this.toBaseURI(uri);

// ignore resolving external Path Item Objects
if (!this.options.resolve.external && isPathItemElementExternal(pathItemElement)) {
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
return undefined;
}

const uri = toValue(pathItemElement.$ref);
const baseURI = this.toBaseURI(uri);

if (!has(baseURI, this.crawlingMap)) {
this.crawlingMap[baseURI] = this.toReference(uri);
}
Expand All @@ -130,20 +127,20 @@ const OpenApi3_0ResolveVisitor = stampit({
return undefined;
}

// ignore resolving external Path Item Elements
if (!this.options.resolve.external && isLinkElementExternal(linkElement)) {
return undefined;
}

// operationRef and operationId are mutually exclusive
if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
throw new ApiDOMError('LinkElement operationRef and operationId are mutually exclusive.');
}

if (isLinkElementExternal(linkElement)) {
if (isStringElement(linkElement.operationRef)) {
const uri = toValue(linkElement.operationRef);
const baseURI = this.toBaseURI(uri);

// ignore resolving LinkElement.operationRef
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
return undefined;
}

if (!has(baseURI, this.crawlingMap)) {
this.crawlingMap[baseURI] = this.toReference(uri);
}
Expand All @@ -158,11 +155,6 @@ const OpenApi3_0ResolveVisitor = stampit({
return undefined;
}

// ignore resolving ExampleElement externalValue
if (!this.options.resolve.external && isStringElement(exampleElement.externalValue)) {
return undefined;
}

// value and externalValue fields are mutually exclusive
if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
throw new ApiDOMError(
Expand All @@ -173,6 +165,11 @@ const OpenApi3_0ResolveVisitor = stampit({
const uri = toValue(exampleElement.externalValue);
const baseURI = this.toBaseURI(uri);

// ignore resolving ExampleElement externalValue
if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== baseURI) {
return undefined;
}

if (!has(baseURI, this.crawlingMap)) {
this.crawlingMap[baseURI] = this.toReference(uri);
}
Expand Down

0 comments on commit bac4850

Please sign in to comment.