UNPKG

swagger-client

Version:

SwaggerJS - a collection of interfaces for OAI specs

677 lines (628 loc) 35.1 kB
/* eslint-disable camelcase */ import { RefElement, isObjectElement, isPrimitiveElement, isStringElement, isMemberElement, IdentityManager, visit, includesClasses, toValue, cloneShallow, cloneDeep } from '@swagger-api/apidom-core'; import { ApiDOMError } from '@swagger-api/apidom-error'; import { isReferenceLikeElement, isReferenceElement, isBooleanJsonSchemaElement, isPathItemElement, isSchemaElement, ReferenceElement, PathItemElement, SchemaElement, getNodeType, keyMap } from '@swagger-api/apidom-ns-openapi-3-2'; import { evaluate as jsonPointerEvaluate, URIFragmentIdentifier } from '@swagger-api/apidom-json-pointer/modern'; import { url, MaximumDereferenceDepthError, File } from '@swagger-api/apidom-reference/configuration/empty'; import { OpenAPI3_2DereferenceVisitor, resolveSchema$refField, maybeRefractToSchemaElement } from '@swagger-api/apidom-reference/dereference/strategies/openapi-3-2'; import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from '@swagger-api/apidom-reference/dereference/strategies/openapi-3-2/selectors/$anchor'; import { evaluate as uriEvaluate, EvaluationJsonSchemaUriError } from '@swagger-api/apidom-reference/dereference/strategies/openapi-3-2/selectors/uri'; import toPath from '../utils/to-path.js'; import getRootCause from '../utils/get-root-cause.js'; import specMapMod from '../../../../../../specmap/lib/refs.js'; import SchemaRefError from '../errors/SchemaRefError.js'; const { wrapError } = specMapMod; const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')]; // initialize element identity manager const identityManager = new IdentityManager(); // custom mutation replacer const mutationReplacer = (newElement, oldElement, key, parent) => { if (isMemberElement(parent)) { parent.value = newElement; // eslint-disable-line no-param-reassign } else if (Array.isArray(parent)) { parent[key] = newElement; // eslint-disable-line no-param-reassign } }; class OpenAPI3_2SwaggerClientDereferenceVisitor extends OpenAPI3_2DereferenceVisitor { useCircularStructures; allowMetaPatches; basePath; constructor({ allowMetaPatches = true, useCircularStructures = false, basePath = null, ...rest }) { super(rest); this.allowMetaPatches = allowMetaPatches; this.useCircularStructures = useCircularStructures; this.basePath = basePath; } async ReferenceElement(referencingElement, key, parent, path, ancestors, link) { try { // skip current referencing element as it's already been access if (this.indirections.includes(referencingElement)) { return false; } const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]); const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref)); const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI; const isExternalReference = !isInternalReference; // ignore resolving internal Reference Objects if (!this.options.resolve.internal && isInternalReference) { return false; } // ignore resolving external Reference Objects if (!this.options.resolve.external && isExternalReference) { return false; } const reference = await this.toReference(toValue(referencingElement.$ref)); const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref)); this.indirections.push(referencingElement); const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI); // possibly non-semantic fragment let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer); referencedElement.id = identityManager.identify(referencedElement); // applying semantics to a fragment if (isPrimitiveElement(referencedElement)) { const referencedElementType = toValue(referencingElement.meta.get('referenced-element')); const cacheKey = `${referencedElementType}-${toValue(identityManager.identify(referencedElement))}`; if (this.refractCache.has(cacheKey)) { referencedElement = this.refractCache.get(cacheKey); } else if (isReferenceLikeElement(referencedElement)) { // handling indirect references referencedElement = ReferenceElement.refract(referencedElement); referencedElement.setMetaProperty('referenced-element', referencedElementType); this.refractCache.set(cacheKey, referencedElement); } else { // handling direct references const ElementClass = this.namespace.getElementClass(referencedElementType); referencedElement = ElementClass.refract(referencedElement); this.refractCache.set(cacheKey, referencedElement); } } // detect direct or indirect reference if (referencingElement === referencedElement) { 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}"`); } // detect second deep dive into the same fragment and avoid it if (ancestorsLineage.includes(referencedElement)) { reference.refSet.circular = true; if (this.options.dereference.circular === 'error') { throw new ApiDOMError('Circular reference detected'); } else if (this.options.dereference.circular === 'replace') { var _this$options$derefer, _this$options$derefer2; const refElement = new RefElement(referencedElement.id, { type: 'reference', uri: reference.uri, $ref: toValue(referencingElement.$ref), baseURI: $refBaseURI, referencingElement }); const replacer = (_this$options$derefer = (_this$options$derefer2 = this.options.dereference.strategyOpts['openapi-3-2']) === null || _this$options$derefer2 === void 0 ? void 0 : _this$options$derefer2.circularReplacer) !== null && _this$options$derefer !== void 0 ? _this$options$derefer : this.options.dereference.circularReplacer; const replacement = replacer(refElement); link.replaceWith(refElement, mutationReplacer); return !parent ? replacement : false; } } /** * Dive deep into the fragment. * * Cases to consider: * 1. We're crossing document boundary * 2. Fragment is from non-root document * 3. Fragment is a Reference Object. We need to follow it to get the eventual value * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode */ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri; const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular); if ((isExternalReference || isNonRootDocument || isReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) { var _this$basePath; // append referencing reference to ancestors lineage directAncestors.add(referencingElement); const visitor = new OpenAPI3_2SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], options: this.options, refractCache: this.refractCache, ancestors: ancestorsLineage, allowMetaPatches: this.allowMetaPatches, useCircularStructures: this.useCircularStructures, basePath: (_this$basePath = this.basePath) !== null && _this$basePath !== void 0 ? _this$basePath : [...toPath([...ancestors, parent, referencingElement]), '$ref'] }); referencedElement = await visitAsync(referencedElement, visitor, { keyMap, nodeTypeGetter: getNodeType }); // remove referencing reference from ancestors lineage directAncestors.delete(referencingElement); } this.indirections.pop(); const mergedElement = cloneShallow(referencedElement); // annotate fragment with info about original Reference element mergedElement.setMetaProperty('ref-fields', { $ref: toValue(referencingElement.$ref), description: toValue(referencingElement.description), summary: toValue(referencingElement.summary) }); // annotate fragment with info about origin mergedElement.setMetaProperty('ref-origin', reference.uri); // annotate fragment with info about referencing element mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement))); // override description and summary (outer has higher priority then inner) if (isObjectElement(referencedElement)) { if (referencingElement.hasKey('description') && 'description' in referencedElement) { mergedElement.remove('description'); mergedElement.set('description', referencingElement.get('description')); } if (referencingElement.hasKey('summary') && 'summary' in referencedElement) { mergedElement.remove('summary'); mergedElement.set('summary', referencingElement.get('summary')); } } // apply meta patches if (this.allowMetaPatches && isObjectElement(mergedElement)) { // apply meta patch only when not already applied if (!mergedElement.hasKey('$$ref')) { const baseURI = url.resolve(retrievalURI, $refBaseURI); mergedElement.set('$$ref', baseURI); } } /** * Transclude referencing element with merged referenced element. */ link.replaceWith(mergedElement, mutationReplacer); /** * We're at the root of the tree, so we're just replacing the entire tree. */ return !parent ? mergedElement : false; } catch (error) { var _this$basePath2, _this$options$derefer3, _this$options$derefer4; const rootCause = getRootCause(error); const wrappedError = wrapError(rootCause, { baseDoc: this.reference.uri, $ref: toValue(referencingElement.$ref), pointer: URIFragmentIdentifier.fromURIReference(toValue(referencingElement.$ref)), fullPath: (_this$basePath2 = this.basePath) !== null && _this$basePath2 !== void 0 ? _this$basePath2 : [...toPath([...ancestors, parent, referencingElement]), '$ref'] }); (_this$options$derefer3 = this.options.dereference.dereferenceOpts) === null || _this$options$derefer3 === void 0 || (_this$options$derefer3 = _this$options$derefer3.errors) === null || _this$options$derefer3 === void 0 || (_this$options$derefer4 = _this$options$derefer3.push) === null || _this$options$derefer4 === void 0 || _this$options$derefer4.call(_this$options$derefer3, wrappedError); return undefined; } } async PathItemElement(pathItemElement, key, parent, path, ancestors, link) { try { // ignore PathItemElement without $ref field if (!isStringElement(pathItemElement.$ref)) { return undefined; } // skip current referencing element as it's already been access if (this.indirections.includes(pathItemElement)) { return false; } // skip already identified cycled Path Item Objects if (includesClasses(['cycle'], pathItemElement.$ref)) { return false; } const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]); const retrievalURI = this.toBaseURI(toValue(pathItemElement.$ref)); const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI; const isExternalReference = !isInternalReference; // ignore resolving internal Path Item Elements if (!this.options.resolve.internal && isInternalReference) { return undefined; } // ignore resolving external Path Item Elements if (!this.options.resolve.external && isExternalReference) { return undefined; } const reference = await this.toReference(toValue(pathItemElement.$ref)); const $refBaseURI = url.resolve(retrievalURI, toValue(pathItemElement.$ref)); this.indirections.push(pathItemElement); const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI); // possibly non-semantic referenced element let referencedElement = jsonPointerEvaluate(reference.value.result, jsonPointer); referencedElement.id = identityManager.identify(referencedElement); // applying semantics to a referenced element if (isPrimitiveElement(referencedElement)) { const cacheKey = `path-item-${toValue(identityManager.identify(referencedElement))}`; if (this.refractCache.has(cacheKey)) { referencedElement = this.refractCache.get(cacheKey); } else { referencedElement = PathItemElement.refract(referencedElement); this.refractCache.set(cacheKey, referencedElement); } } // detect direct or indirect reference if (pathItemElement === referencedElement) { throw new ApiDOMError('Recursive Path 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}"`); } // detect second deep dive into the same fragment and avoid it if (ancestorsLineage.includes(referencedElement)) { reference.refSet.circular = true; if (this.options.dereference.circular === 'error') { throw new ApiDOMError('Circular reference detected'); } else if (this.options.dereference.circular === 'replace') { var _this$options$derefer5, _this$options$derefer6; const refElement = new RefElement(referencedElement.id, { type: 'path-item', uri: reference.uri, $ref: toValue(pathItemElement.$ref), baseURI: $refBaseURI, referencingElement: pathItemElement }); const replacer = (_this$options$derefer5 = (_this$options$derefer6 = this.options.dereference.strategyOpts['openapi-3-2']) === null || _this$options$derefer6 === void 0 ? void 0 : _this$options$derefer6.circularReplacer) !== null && _this$options$derefer5 !== void 0 ? _this$options$derefer5 : this.options.dereference.circularReplacer; const replacement = replacer(refElement); link.replaceWith(refElement, mutationReplacer); return !parent ? replacement : false; } } /** * Dive deep into the fragment. * * Cases to consider: * 1. We're crossing document boundary * 2. Fragment is from non-root document * 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode */ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri; const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular); if ((isExternalReference || isNonRootDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) { var _this$basePath3; // append referencing schema to ancestors lineage directAncestors.add(pathItemElement); // dive deep into the referenced element const visitor = new OpenAPI3_2SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], options: this.options, ancestors: ancestorsLineage, allowMetaPatches: this.allowMetaPatches, useCircularStructures: this.useCircularStructures, basePath: (_this$basePath3 = this.basePath) !== null && _this$basePath3 !== void 0 ? _this$basePath3 : [...toPath([...ancestors, parent, pathItemElement]), '$ref'] }); referencedElement = await visitAsync(referencedElement, visitor, { keyMap, nodeTypeGetter: getNodeType }); // remove referencing schema from ancestors lineage directAncestors.delete(pathItemElement); } this.indirections.pop(); /** * Creating a new version of Path Item by merging fields from referenced Path Item with referencing one. */ if (isPathItemElement(referencedElement)) { const mergedElement = new PathItemElement([...referencedElement.content], cloneDeep(referencedElement.meta), cloneDeep(referencedElement.attributes)); // existing keywords from referencing PathItemElement overrides ones from referenced element pathItemElement.forEach((value, keyElement, item) => { mergedElement.remove(toValue(keyElement)); mergedElement.content.push(item); }); mergedElement.remove('$ref'); // annotate referenced element with info about original referencing element mergedElement.setMetaProperty('ref-fields', { $ref: toValue(pathItemElement.$ref) }); // annotate referenced element with info about origin mergedElement.setMetaProperty('ref-origin', reference.uri); // annotate fragment with info about referencing element mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(pathItemElement))); // apply meta patches if (this.allowMetaPatches) { // apply meta patch only when not already applied if (typeof mergedElement.get('$$ref') === 'undefined') { const baseURI = url.resolve(retrievalURI, $refBaseURI); mergedElement.set('$$ref', baseURI); } } referencedElement = mergedElement; } /** * Transclude referencing element with merged referenced element. */ link.replaceWith(referencedElement, mutationReplacer); /** * We're at the root of the tree, so we're just replacing the entire tree. */ return !parent ? referencedElement : undefined; } catch (error) { var _this$basePath4, _this$options$derefer7, _this$options$derefer8; const rootCause = getRootCause(error); const wrappedError = wrapError(rootCause, { baseDoc: this.reference.uri, $ref: toValue(pathItemElement.$ref), pointer: URIFragmentIdentifier.fromURIReference(toValue(pathItemElement.$ref)), fullPath: (_this$basePath4 = this.basePath) !== null && _this$basePath4 !== void 0 ? _this$basePath4 : [...toPath([...ancestors, parent, pathItemElement]), '$ref'] }); (_this$options$derefer7 = this.options.dereference.dereferenceOpts) === null || _this$options$derefer7 === void 0 || (_this$options$derefer7 = _this$options$derefer7.errors) === null || _this$options$derefer7 === void 0 || (_this$options$derefer8 = _this$options$derefer7.push) === null || _this$options$derefer8 === void 0 || _this$options$derefer8.call(_this$options$derefer7, wrappedError); return undefined; } } async SchemaElement(referencingElement, key, parent, path, ancestors, link) { try { // skip current referencing schema as $ref keyword was not defined if (!isStringElement(referencingElement.$ref)) { // skip traversing this schema but traverse all it's child schemas return undefined; } // skip current referencing element as it's already been access if (this.indirections.includes(referencingElement)) { return false; } const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]); // compute baseURI using rules around $id and $ref keywords let reference = await this.toReference(url.unsanitize(this.reference.uri)); let { uri: retrievalURI } = reference; const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement); const $refBaseURIStrippedHash = url.stripHash($refBaseURI); const file = new File({ uri: $refBaseURIStrippedHash }); const isUnknownURI = !this.options.resolve.resolvers.some(r => r.canRead(file)); const isURL = !isUnknownURI; let isInternalReference = url.stripHash(this.reference.uri) === $refBaseURI; let isExternalReference = !isInternalReference; this.indirections.push(referencingElement); // determining reference, proper evaluation and selection mechanism let referencedElement; try { if (isUnknownURI || isURL) { // we're dealing with canonical URI or URL with possible fragment retrievalURI = this.toBaseURI($refBaseURI); const selector = $refBaseURI; const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result); referencedElement = uriEvaluate(selector, referenceAsSchema); referencedElement = maybeRefractToSchemaElement(referencedElement); referencedElement.id = identityManager.identify(referencedElement); // ignore resolving internal Schema Objects if (!this.options.resolve.internal && isInternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } // ignore resolving external Schema Objects if (!this.options.resolve.external && isExternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } } else { // we're assuming here that we're dealing with JSON Pointer here retrievalURI = this.toBaseURI($refBaseURI); isInternalReference = url.stripHash(this.reference.uri) === retrievalURI; isExternalReference = !isInternalReference; // ignore resolving internal Schema Objects if (!this.options.resolve.internal && isInternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } // ignore resolving external Schema Objects if (!this.options.resolve.external && isExternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } reference = await this.toReference(url.unsanitize($refBaseURI)); const selector = URIFragmentIdentifier.fromURIReference($refBaseURI); const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result); referencedElement = jsonPointerEvaluate(referenceAsSchema, selector); referencedElement = maybeRefractToSchemaElement(referencedElement); referencedElement.id = identityManager.identify(referencedElement); } } catch (error) { /** * No SchemaElement($id=URL) was not found, so we're going to try to resolve * the URL and assume the returned response is a JSON Schema. */ if (isURL && error instanceof EvaluationJsonSchemaUriError) { if (isAnchor(uriToAnchor($refBaseURI))) { // we're dealing with JSON Schema $anchor here isInternalReference = url.stripHash(this.reference.uri) === retrievalURI; isExternalReference = !isInternalReference; // ignore resolving internal Schema Objects if (!this.options.resolve.internal && isInternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } // ignore resolving external Schema Objects if (!this.options.resolve.external && isExternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } reference = await this.toReference(url.unsanitize($refBaseURI)); const selector = uriToAnchor($refBaseURI); const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result); referencedElement = $anchorEvaluate(selector, referenceAsSchema); referencedElement = maybeRefractToSchemaElement(referencedElement); referencedElement.id = identityManager.identify(referencedElement); } else { // we're assuming here that we're dealing with JSON Pointer here retrievalURI = this.toBaseURI(toValue($refBaseURI)); isInternalReference = url.stripHash(this.reference.uri) === retrievalURI; isExternalReference = !isInternalReference; // ignore resolving internal Schema Objects if (!this.options.resolve.internal && isInternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } // ignore resolving external Schema Objects if (!this.options.resolve.external && isExternalReference) { // skip traversing this schema element but traverse all it's child elements return undefined; } reference = await this.toReference(url.unsanitize($refBaseURI)); const selector = URIFragmentIdentifier.fromURIReference($refBaseURI); const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result); referencedElement = jsonPointerEvaluate(referenceAsSchema, selector); referencedElement = maybeRefractToSchemaElement(referencedElement); referencedElement.id = identityManager.identify(referencedElement); } } else { throw error; } } // detect direct or indirect reference if (referencingElement === referencedElement) { throw new ApiDOMError('Recursive Schema 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}"`); } // detect second deep dive into the same fragment and avoid it if (ancestorsLineage.includes(referencedElement)) { reference.refSet.circular = true; if (this.options.dereference.circular === 'error') { throw new ApiDOMError('Circular reference detected'); } else if (this.options.dereference.circular === 'replace') { var _this$options$derefer9, _this$options$derefer0; const refElement = new RefElement(referencedElement.id, { type: 'json-schema', uri: reference.uri, $ref: toValue(referencingElement.$ref), baseURI: url.resolve(retrievalURI, $refBaseURI), referencingElement }); const replacer = (_this$options$derefer9 = (_this$options$derefer0 = this.options.dereference.strategyOpts['openapi-3-2']) === null || _this$options$derefer0 === void 0 ? void 0 : _this$options$derefer0.circularReplacer) !== null && _this$options$derefer9 !== void 0 ? _this$options$derefer9 : this.options.dereference.circularReplacer; const replacement = replacer(refElement); link.replaceWith(replacement, mutationReplacer); return !parent ? replacement : false; } } /** * Dive deep into the fragment. * * Cases to consider: * 1. We're crossing document boundary * 2. Fragment is from non-root document * 3. Fragment is a Schema Object with $ref field. We need to follow it to get the eventual value * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode */ const isNonRootDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri; const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular); if ((isExternalReference || isNonRootDocument || isSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) { var _this$basePath5; // append referencing schema to ancestors lineage directAncestors.add(referencingElement); // dive deep into the fragment const mergeVisitor = new OpenAPI3_2SwaggerClientDereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], options: this.options, useCircularStructures: this.useCircularStructures, allowMetaPatches: this.allowMetaPatches, ancestors: ancestorsLineage, basePath: (_this$basePath5 = this.basePath) !== null && _this$basePath5 !== void 0 ? _this$basePath5 : [...toPath([...ancestors, parent, referencingElement]), '$ref'] }); referencedElement = await visitAsync(referencedElement, mergeVisitor, { keyMap, nodeTypeGetter: getNodeType }); // remove referencing schema from ancestors lineage directAncestors.delete(referencingElement); } this.indirections.pop(); if (isBooleanJsonSchemaElement(referencedElement)) { const booleanJsonSchemaElement = cloneDeep(referencedElement); // annotate referenced element with info about original referencing element booleanJsonSchemaElement.setMetaProperty('ref-fields', { $ref: toValue(referencingElement.$ref) }); // annotate referenced element with info about origin booleanJsonSchemaElement.setMetaProperty('ref-origin', reference.uri); // annotate fragment with info about referencing element booleanJsonSchemaElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement))); link.replaceWith(booleanJsonSchemaElement, mutationReplacer); return !parent ? booleanJsonSchemaElement : false; } /** * Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one. */ if (isSchemaElement(referencedElement)) { // Schema Object - merge keywords from referenced schema with referencing schema const mergedElement = new SchemaElement([...referencedElement.content], cloneDeep(referencedElement.meta), cloneDeep(referencedElement.attributes)); // existing keywords from referencing schema overrides ones from referenced schema referencingElement.forEach((value, keyElement, item) => { mergedElement.remove(toValue(keyElement)); mergedElement.content.push(item); }); mergedElement.remove('$ref'); // annotate referenced element with info about original referencing element mergedElement.setMetaProperty('ref-fields', { $ref: toValue(referencingElement.$ref) }); // annotate fragment with info about origin mergedElement.setMetaProperty('ref-origin', reference.uri); // annotate fragment with info about referencing element mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement))); // allowMetaPatches option processing if (this.allowMetaPatches) { // apply meta patch only when not already applied if (typeof mergedElement.get('$$ref') === 'undefined') { const baseURI = url.resolve(retrievalURI, $refBaseURI); mergedElement.set('$$ref', baseURI); } } referencedElement = mergedElement; } /** * Transclude referencing element with merged referenced element. */ link.replaceWith(referencedElement, mutationReplacer); /** * We're at the root of the tree, so we're just replacing the entire tree. */ return !parent ? referencedElement : undefined; } catch (error) { var _this$basePath6, _this$options$derefer1, _this$options$derefer10; const rootCause = getRootCause(error); const wrappedError = new SchemaRefError(`Could not resolve reference: ${rootCause.message}`, { baseDoc: this.reference.uri, $ref: toValue(referencingElement.$ref), fullPath: (_this$basePath6 = this.basePath) !== null && _this$basePath6 !== void 0 ? _this$basePath6 : [...toPath([...ancestors, parent, referencingElement]), '$ref'], cause: rootCause }); (_this$options$derefer1 = this.options.dereference.dereferenceOpts) === null || _this$options$derefer1 === void 0 || (_this$options$derefer1 = _this$options$derefer1.errors) === null || _this$options$derefer1 === void 0 || (_this$options$derefer10 = _this$options$derefer1.push) === null || _this$options$derefer10 === void 0 || _this$options$derefer10.call(_this$options$derefer1, wrappedError); return undefined; } } // eslint-disable-next-line class-methods-use-this async LinkElement() { /** * OpenApi3_2DereferenceVisitor is doing lookup of Operation Objects * and assigns them to Link Object metadata. This is not needed in * swagger-client context, so we're disabling it here. */ return undefined; } async ExampleElement(exampleElement, key, parent, path, ancestors, link) { try { return await super.ExampleElement(exampleElement, key, parent, path, ancestors, link); } catch (error) { var _this$basePath7, _this$options$derefer11, _this$options$derefer12; const rootCause = getRootCause(error); const wrappedError = wrapError(rootCause, { baseDoc: this.reference.uri, externalValue: toValue(exampleElement.externalValue), fullPath: (_this$basePath7 = this.basePath) !== null && _this$basePath7 !== void 0 ? _this$basePath7 : [...toPath([...ancestors, parent, exampleElement]), 'externalValue'] }); (_this$options$derefer11 = this.options.dereference.dereferenceOpts) === null || _this$options$derefer11 === void 0 || (_this$options$derefer11 = _this$options$derefer11.errors) === null || _this$options$derefer11 === void 0 || (_this$options$derefer12 = _this$options$derefer11.push) === null || _this$options$derefer12 === void 0 || _this$options$derefer12.call(_this$options$derefer11, wrappedError); return undefined; } } } export default OpenAPI3_2SwaggerClientDereferenceVisitor; /* eslint-enable camelcase */