diff --git a/packages/apidom-reference/src/resolve/strategies/asyncapi-2/index.ts b/packages/apidom-reference/src/resolve/strategies/asyncapi-2/index.ts index 2f30783f76..b6dc2f417f 100644 --- a/packages/apidom-reference/src/resolve/strategies/asyncapi-2/index.ts +++ b/packages/apidom-reference/src/resolve/strategies/asyncapi-2/index.ts @@ -46,7 +46,6 @@ const AsyncApi2ResolveStrategy: stampit.Stamp = stampit(Resolv keyMap, nodeTypeGetter: getNodeType, }); - await visitor.crawl(); return refSet; }, 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 ef5225a937..e2607189cc 100644 --- a/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts +++ b/packages/apidom-reference/src/resolve/strategies/asyncapi-2/visitor.ts @@ -1,234 +1,7 @@ import stampit from 'stampit'; -import { propEq, values, has, pipe } from 'ramda'; -import { allP } from 'ramda-adjunct'; -import { isPrimitiveElement, isStringElement, visit, toValue } from '@swagger-api/apidom-core'; -import { ApiDOMError } from '@swagger-api/apidom-error'; -import { evaluate, uriToPointer } from '@swagger-api/apidom-json-pointer'; -import { - getNodeType, - isReferenceElement, - isChannelItemElement, - isReferenceLikeElement, - keyMap, - ReferenceElement, - ChannelItemElement, -} from '@swagger-api/apidom-ns-asyncapi-2'; -import { Reference as IReference } from '../../../types'; -import MaximumDereferenceDepthError from '../../../errors/MaximumDereferenceDepthError'; -import MaximumResolveDepthError from '../../../errors/MaximumResolveDepthError'; -import * as url from '../../../util/url'; -import parse from '../../../parse'; -import Reference from '../../../Reference'; +import AsyncAPI2DereferenceVisitor from '../../../dereference/strategies/asyncapi-2/visitor'; -// @ts-ignore -const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')]; +const AsyncAPI2ResolveVisitor = stampit(AsyncAPI2DereferenceVisitor); -const AsyncApi2ResolveVisitor = stampit({ - props: { - indirections: [], - namespace: null, - reference: null, - crawledElements: null, - crawlingMap: null, - options: null, - }, - init({ reference, namespace, indirections = [], options }) { - this.indirections = indirections; - this.namespace = namespace; - this.reference = reference; - this.crawledElements = []; - this.crawlingMap = {}; - this.options = options; - }, - methods: { - toBaseURI(uri: string): string { - return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri))); - }, - - async toReference(uri: string): Promise { - // detect maximum depth of resolution - if (this.reference.depth >= this.options.resolve.maxDepth) { - throw new MaximumResolveDepthError( - `Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, - ); - } - - const baseURI = this.toBaseURI(uri); - const { refSet } = this.reference; - - // we've already processed this Reference in past - if (refSet.has(baseURI)) { - return refSet.find(propEq(baseURI, 'uri')); - } - - const parseResult = await parse(url.unsanitize(baseURI), { - ...this.options, - parse: { ...this.options.parse, mediaType: 'text/plain' }, - }); - - // register new Reference with ReferenceSet - const reference = Reference({ - uri: baseURI, - value: parseResult, - depth: this.reference.depth + 1, - }); - - refSet.add(reference); - - return reference; - }, - - ReferenceElement(referenceElement: ReferenceElement) { - const uri = toValue(referenceElement.$ref); - const retrievalURI = this.toBaseURI(uri); - - // ignore resolving external Reference Objects - if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) { - return false; - } - - if (!has(retrievalURI, this.crawlingMap)) { - this.crawlingMap[retrievalURI] = this.toReference(uri); - } - this.crawledElements.push(referenceElement); - - return undefined; - }, - - ChannelItemElement(channelItemElement: ChannelItemElement) { - // ignore PathItemElement without $ref field - if (!isStringElement(channelItemElement.$ref)) { - return undefined; - } - - const uri = toValue(channelItemElement.$ref); - const retrievalURI = this.toBaseURI(uri); - - // ignore resolving external Channel Item Objects - if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) { - return undefined; - } - - if (!has(retrievalURI, this.crawlingMap)) { - this.crawlingMap[retrievalURI] = this.toReference(uri); - } - this.crawledElements.push(channelItemElement); - - return undefined; - }, - - async crawlReferenceElement(referenceElement: ReferenceElement) { - // @ts-ignore - const reference = await this.toReference(toValue(referenceElement.$ref)); - - this.indirections.push(referenceElement); - - const jsonPointer = uriToPointer(toValue(referenceElement.$ref)); - - // possibly non-semantic fragment - let fragment = evaluate(jsonPointer, reference.value.result); - - // applying semantics to a fragment - if (isPrimitiveElement(fragment)) { - const referencedElementType = toValue(referenceElement.meta.get('referenced-element')); - - if (isReferenceLikeElement(fragment)) { - // handling indirect references - fragment = ReferenceElement.refract(fragment); - fragment.setMetaProperty('referenced-element', referencedElementType); - } else { - // handling direct references - const ElementClass = this.namespace.getElementClass(referencedElementType); - fragment = ElementClass.refract(fragment); - } - } - - // detect direct or circular reference - if (this.indirections.includes(fragment)) { - throw new ApiDOMError('Recursive Reference Object detected'); - } - - // detect maximum depth of dereferencing - if (this.indirections.length > this.options.dereference.maxDepth) { - throw new MaximumDereferenceDepthError( - `Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, - ); - } - - // dive deep into the fragment - const visitor = AsyncApi2ResolveVisitor({ - reference, - namespace: this.namespace, - indirections: [...this.indirections], - options: this.options, - }); - await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType }); - await visitor.crawl(); - - this.indirections.pop(); - }, - - async crawlChannelItemElement(channelItemElement: ChannelItemElement) { - const reference = await this.toReference(toValue(channelItemElement.$ref)); - - this.indirections.push(channelItemElement); - - const jsonPointer = uriToPointer(toValue(channelItemElement.$ref)); - - // possibly non-semantic referenced element - let referencedElement = evaluate(jsonPointer, reference.value.result); - - // applying semantics to a referenced element - if (isPrimitiveElement(referencedElement)) { - referencedElement = ChannelItemElement.refract(referencedElement); - } - - // detect direct or indirect reference - if (this.indirections.includes(referencedElement)) { - throw new ApiDOMError('Recursive Channel Item Object reference detected'); - } - - // detect maximum depth of dereferencing - if (this.indirections.length > this.options.dereference.maxDepth) { - throw new MaximumDereferenceDepthError( - `Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`, - ); - } - - // dive deep into the referenced element - const visitor: any = AsyncApi2ResolveVisitor({ - reference, - namespace: this.namespace, - indirections: [...this.indirections], - options: this.options, - }); - await visitAsync(referencedElement, visitor, { keyMap, nodeTypeGetter: getNodeType }); - await visitor.crawl(); - - this.indirections.pop(); - }, - - async crawl() { - /** - * Synchronize all parallel resolutions in this place. - * After synchronization happened we can be sure that refSet - * contains resolved Reference objects. - */ - await pipe(values, allP)(this.crawlingMap); - this.crawlingMap = null; - - /* eslint-disable no-await-in-loop */ - for (const element of this.crawledElements) { - if (isReferenceElement(element)) { - await this.crawlReferenceElement(element); - } else if (isChannelItemElement(element)) { - await this.crawlChannelItemElement(element); - } - } - /* eslint-enabled */ - }, - }, -}); - -export default AsyncApi2ResolveVisitor; +export default AsyncAPI2ResolveVisitor;