From f0a698053f66b830b385c36fac8beecab31c4191 Mon Sep 17 00:00:00 2001 From: Vladimir Gorej Date: Fri, 24 Nov 2023 12:44:44 +0100 Subject: [PATCH] fix(reference): fix internal/external URL determ for OpenAPI 3.0.x Refs #3451 --- packages/apidom-ns-openapi-3-0/src/index.ts | 3 -- .../apidom-ns-openapi-3-0/src/predicates.ts | 53 +------------------ .../strategies/openapi-3-0/visitor.ts | 49 ++++++++--------- .../resolve/strategies/asyncapi-2/visitor.ts | 2 +- .../resolve/strategies/openapi-3-0/visitor.ts | 43 +++++++-------- 5 files changed, 47 insertions(+), 103 deletions(-) diff --git a/packages/apidom-ns-openapi-3-0/src/index.ts b/packages/apidom-ns-openapi-3-0/src/index.ts index aa0cd2e9e6..1fb93bff46 100644 --- a/packages/apidom-ns-openapi-3-0/src/index.ts +++ b/packages/apidom-ns-openapi-3-0/src/index.ts @@ -30,16 +30,13 @@ export { isInfoElement, isLicenseElement, isLinkElement, - isLinkElementExternal, isOpenapiElement, isOpenApi3_0Element, isOperationElement, isParameterElement, isPathItemElement, - isPathItemElementExternal, isPathsElement, isReferenceElement, - isReferenceElementExternal, isRequestBodyElement, isResponseElement, isResponsesElement, diff --git a/packages/apidom-ns-openapi-3-0/src/predicates.ts b/packages/apidom-ns-openapi-3-0/src/predicates.ts index 9287f91f98..92f516fa97 100644 --- a/packages/apidom-ns-openapi-3-0/src/predicates.ts +++ b/packages/apidom-ns-openapi-3-0/src/predicates.ts @@ -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'; @@ -124,21 +118,6 @@ export const isLinkElement = createPredicate( }, ); -export const isLinkElementExternal: ElementPredicate = ( - 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 => @@ -191,21 +170,6 @@ export const isPathItemElement = createPredicate( }, ); -export const isPathItemElementExternal: ElementPredicate = ( - 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 => @@ -226,21 +190,6 @@ export const isReferenceElement = createPredicate( }, ); -export const isReferenceElementExternal: ElementPredicate = ( - 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 => diff --git a/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts b/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts index d1d6c4ccba..0eb4448613 100644 --- a/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts +++ b/packages/apidom-reference/src/dereference/strategies/openapi-3-0/visitor.ts @@ -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'; @@ -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); @@ -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); @@ -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( @@ -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)) { @@ -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( @@ -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 diff --git a/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts b/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts index 52b958f79b..11b5842d5c 100644 --- a/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts +++ b/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts @@ -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; } diff --git a/packages/apidom-reference/src/resolve/strategies/openapi-3-0/visitor.ts b/packages/apidom-reference/src/resolve/strategies/openapi-3-0/visitor.ts index 5635ae4508..7e5326faae 100644 --- a/packages/apidom-reference/src/resolve/strategies/openapi-3-0/visitor.ts +++ b/packages/apidom-reference/src/resolve/strategies/openapi-3-0/visitor.ts @@ -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'; @@ -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); } @@ -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); } @@ -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); } @@ -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( @@ -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); }