diff --git a/apidom/packages/apidom-parser-adapter-asyncapi-json-2-0/src/adapter.ts b/apidom/packages/apidom-parser-adapter-asyncapi-json-2-0/src/adapter.ts index 0634840c30..574d03686c 100644 --- a/apidom/packages/apidom-parser-adapter-asyncapi-json-2-0/src/adapter.ts +++ b/apidom/packages/apidom-parser-adapter-asyncapi-json-2-0/src/adapter.ts @@ -24,6 +24,7 @@ export const parse = async ( if (isNotUndefined(firstResultElement)) { const asyncApiElement = AsyncApi2_0Element.refract(firstResultElement, refractorOpts); + asyncApiElement.classes.push('result'); parseResultElement = transclude(firstResultElement, asyncApiElement, parseResultElement); } diff --git a/apidom/packages/apidom-parser-adapter-asyncapi-yaml-2-0/src/adapter.ts b/apidom/packages/apidom-parser-adapter-asyncapi-yaml-2-0/src/adapter.ts index 419429cf28..1c3111c8af 100644 --- a/apidom/packages/apidom-parser-adapter-asyncapi-yaml-2-0/src/adapter.ts +++ b/apidom/packages/apidom-parser-adapter-asyncapi-yaml-2-0/src/adapter.ts @@ -24,6 +24,7 @@ export const parse = async ( if (isNotUndefined(firstResultElement)) { const asyncApiElement = AsyncApi2_0Element.refract(firstResultElement, refractorOpts); + asyncApiElement.classes.push('result'); parseResultElement = transclude(firstResultElement, asyncApiElement, parseResultElement); } diff --git a/apidom/packages/apidom-parser-adapter-openapi-json-3-1/src/adapter.ts b/apidom/packages/apidom-parser-adapter-openapi-json-3-1/src/adapter.ts index a4cd14cf78..3d9b6d0d81 100644 --- a/apidom/packages/apidom-parser-adapter-openapi-json-3-1/src/adapter.ts +++ b/apidom/packages/apidom-parser-adapter-openapi-json-3-1/src/adapter.ts @@ -24,6 +24,7 @@ export const parse = async ( if (isNotUndefined(firstResultElement)) { const openApiElement = OpenApi3_1Element.refract(firstResultElement, refractorOpts); + openApiElement.classes.push('result'); parseResultElement = transclude(firstResultElement, openApiElement, parseResultElement); } diff --git a/apidom/packages/apidom-parser-adapter-openapi-yaml-3-1/src/adapter.ts b/apidom/packages/apidom-parser-adapter-openapi-yaml-3-1/src/adapter.ts index 72912b0097..b037e5fe68 100644 --- a/apidom/packages/apidom-parser-adapter-openapi-yaml-3-1/src/adapter.ts +++ b/apidom/packages/apidom-parser-adapter-openapi-yaml-3-1/src/adapter.ts @@ -24,6 +24,7 @@ export const parse = async ( if (isNotUndefined(firstResultElement)) { const openApiElement = OpenApi3_1Element.refract(firstResultElement, refractorOpts); + openApiElement.classes.push('result'); parseResultElement = transclude(firstResultElement, openApiElement, parseResultElement); } diff --git a/apidom/packages/apidom-reference/src/ReferenceSet.ts b/apidom/packages/apidom-reference/src/ReferenceSet.ts index 686f851ac4..0997d73ccf 100644 --- a/apidom/packages/apidom-reference/src/ReferenceSet.ts +++ b/apidom/packages/apidom-reference/src/ReferenceSet.ts @@ -48,6 +48,14 @@ const ReferenceSet: stampit.Stamp = stampit({ *values() { yield* this.refs; }, + + clean() { + this.refs.forEach((ref: IReference) => { + // eslint-disable-next-line no-param-reassign + ref.refSet = null; + }); + this.refs = []; + }, }, }); diff --git a/apidom/packages/apidom-reference/src/dereference.ts b/apidom/packages/apidom-reference/src/dereference.ts deleted file mode 100644 index 9b47cbc006..0000000000 --- a/apidom/packages/apidom-reference/src/dereference.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Element, transclude, visit } from 'apidom'; -import stampit from 'stampit'; -import { ReferenceElement, keyMap, getNodeType } from 'apidom-ns-openapi-3-1'; -import { hasIn, pathSatisfies } from 'ramda'; -import { isNotUndefined } from 'ramda-adjunct'; - -import { ReferenceOptions as IReferenceOptions } from './types'; -import parse from './parse'; -import * as url from './util/url'; -import { evaluate, uriToPointer } from './selectors/json-pointer'; - -// 1. - gather all Reference Objects -// 2. - for each Reference Object -// 2.1. - look at the $ref property -// 2.2. - translate the $ref JSON Pointer into absolute URI JSON Pointer -// 2.3. - resolve the absolute URI JSON Pointer against data -// 2.4. - if resolved data is again a Reference (Like) Object repeat from 2.1 - -// @ts-ignore -const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')]; - -const DereferenceVisitor = stampit({ - props: { - baseURI: '', - element: null, - indirections: [], - }, - init({ baseURI, element, indirections = [] }) { - this.baseURI = baseURI; - this.element = element; - this.indirections = indirections; - }, - methods: { - async ReferenceElement(referenceElement: ReferenceElement) { - const uri = referenceElement.$ref.toValue(); - - // if only hash is provided, reference is considered to be internal - if (url.getHash(uri) === uri) { - return this.ReferenceElementInternal(referenceElement); - } - // everything else is treated as an external reference - return this.ReferenceElementExternal(referenceElement); - }, - - async ReferenceElementInternal(referenceElement: ReferenceElement) { - this.indirections.push(referenceElement); - - const jsonPointer = uriToPointer(referenceElement.$ref.toValue()); - let fragment = evaluate(jsonPointer, this.element); - - // detect direct or circular reference - if (this.indirections.includes(fragment)) { - throw new Error('Recursive JSON Pointer detected'); - } - - // dive deep into the fragment - const visitor = DereferenceVisitor({ - baseURI: this.baseURI, - element: this.element, - indirections: [...this.indirections], - }); - await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType }); - - /** - * Re-evaluate the JSON Pointer against the element as the fragment could - * have been another reference and the previous deep dive into fragment - * dereferenced it. - */ - fragment = evaluate(jsonPointer, this.element); - - // override description and summary (outer has higher priority then inner) - const hasDescription = pathSatisfies(isNotUndefined, ['description'], referenceElement); - const hasSummary = pathSatisfies(isNotUndefined, ['summary'], referenceElement); - if (hasDescription || hasSummary) { - fragment = fragment.clone(); - - if (hasDescription && hasIn('description', fragment)) { - // @ts-ignore - fragment.description = referenceElement.description; - } - if (hasSummary && hasIn('summary', fragment)) { - // @ts-ignore - fragment.summary = referenceElement.summary; - } - } - - // transclude the element for a fragment - this.element = transclude(referenceElement, fragment, this.element); - - this.indirections.pop(); - }, - - async ReferenceElementExternal(referenceElement: ReferenceElement) { - this.indirections.push(referenceElement); - - const uri = referenceElement.$ref.toValue(); - const uriWithoutHash = url.stripHash(uri); - const sanitizedURI = url.isFileSystemPath(uriWithoutHash) - ? url.fromFileSystemPath(uriWithoutHash) - : uriWithoutHash; - const baseURI = url.resolve(this.baseURI, sanitizedURI); - const parseResult = await parse(baseURI, this.options); - const { first: element } = parseResult; - const jsonPointer = uriToPointer(uri); - // @ts-ignore - const fragment = evaluate(jsonPointer, element); - - // dive deep into the fragment - const visitor = DereferenceVisitor({ - baseURI, - element, - indirections: [...this.indirections], - }); - await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType }); - - this.indirections.pop(); - }, - }, -}); - -// eslint-disable-next-line import/prefer-default-export -export const dereferenceApiDOM = async ( - element: T, - options: IReferenceOptions, -): Promise => { - const visitor = DereferenceVisitor({ baseURI: options.resolve.baseURI, element }); - await visitAsync(element, visitor, { state: { options }, keyMap, nodeTypeGetter: getNodeType }); - - return element; -}; diff --git a/apidom/packages/apidom-reference/src/dereference/index.ts b/apidom/packages/apidom-reference/src/dereference/index.ts new file mode 100644 index 0000000000..c5ef11bbc8 --- /dev/null +++ b/apidom/packages/apidom-reference/src/dereference/index.ts @@ -0,0 +1,57 @@ +import { isEmpty } from 'ramda'; +import { Element, ParseResultElement } from 'apidom'; + +import File from '../util/File'; +import * as plugins from '../util/plugins'; +import { UnmatchedResolveStrategyError } from '../util/errors'; +import DereferenceError from '../util/errors/DereferenceError'; +import { ReferenceOptions as IReferenceOptions } from '../types'; +import parse from '../parse'; +import { merge as mergeOptions } from '../options/util'; + +/** + * Dereferences ApiDOM with all it's external references. + */ +export const dereferenceApiDOM = async ( + element: T, + options: IReferenceOptions, +): Promise => { + const file = File({ + uri: options.resolve.baseURI, + parseResult: element, + mediaType: options.parse.mediaType, + }); + + const dereferenceStrategies = plugins.filter( + 'canDereference', + file, + options.dereference.strategies, + ); + + // we couldn't find any dereference for this File + if (isEmpty(dereferenceStrategies)) { + throw new UnmatchedResolveStrategyError(file.uri); + } + + try { + const { result } = await plugins.run('dereference', [file, options], dereferenceStrategies); + return result; + } catch (error) { + throw new DereferenceError(`Error while dereferencing file "${file.uri}"`, error); + } +}; + +/** + * Dereferences a file with all it's external references. + */ +const dereference = async ( + uri: string, + options: IReferenceOptions, +): Promise => { + const parseResult = await parse(uri, options); + const mergedOptions = mergeOptions(options, { resolve: { baseURI: uri } }); + + return dereferenceApiDOM(parseResult, mergedOptions); +}; + +export default dereference; diff --git a/apidom/packages/apidom-reference/src/dereference/strategies/DereferenceStrategy.ts b/apidom/packages/apidom-reference/src/dereference/strategies/DereferenceStrategy.ts new file mode 100644 index 0000000000..0d5c37c20a --- /dev/null +++ b/apidom/packages/apidom-reference/src/dereference/strategies/DereferenceStrategy.ts @@ -0,0 +1,18 @@ +import stampit from 'stampit'; + +import { DereferenceStrategy as IDereferenceStrategy } from '../../types'; +import { NotImplementedError } from '../../util/errors'; + +const DereferenceStrategy: stampit.Stamp = stampit({ + methods: { + canDereference() { + return false; + }, + + async dereference(): Promise { + throw new NotImplementedError(); + }, + }, +}); + +export default DereferenceStrategy; diff --git a/apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/index.ts b/apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/index.ts new file mode 100644 index 0000000000..92b0ac419e --- /dev/null +++ b/apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/index.ts @@ -0,0 +1,61 @@ +import stampit from 'stampit'; +import { createNamespace, visit, Element } from 'apidom'; +import openApi3_1Namespace, { + getNodeType, + isOpenApi3_1Element, + keyMap, +} from 'apidom-ns-openapi-3-1'; + +import DereferenceStrategy from '../DereferenceStrategy'; +import { + DereferenceStrategy as IDereferenceStrategy, + File as IFile, + ReferenceOptions as IReferenceOptions, +} from '../../../types'; +import Reference from '../../../Reference'; +import ReferenceSet from '../../../ReferenceSet'; +import OpenApi3_1DereferenceVisitor from './visitor'; + +// @ts-ignore +const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')]; + +const OpenApi3_1DereferenceStrategy: stampit.Stamp = stampit( + DereferenceStrategy, + { + methods: { + canDereference(file: IFile): boolean { + // assert by media type + if (file.mediaType !== 'text/plain') { + return [ + 'application/vnd.oai.openapi;version=3.1.0', + 'application/vnd.oai.openapi+json;version=3.1.0', + 'application/vnd.oai.openapi+yaml;version=3.1.0', + ].includes(file.mediaType); + } + + // assert by inspecting ApiDOM + return isOpenApi3_1Element(file.parseResult?.api); + }, + + async dereference(file: IFile, options: IReferenceOptions): Promise { + const namespace = createNamespace(openApi3_1Namespace); + const reference = Reference({ uri: file.uri, value: file.parseResult }); + const visitor = OpenApi3_1DereferenceVisitor({ reference, namespace, options }); + const refSet = ReferenceSet(); + refSet.add(reference); + + const dereferencedElement = await visitAsync(refSet.rootRef.value, visitor, { + keyMap, + nodeTypeGetter: getNodeType, + }); + + // release all memory + refSet.clean(); + + return dereferencedElement; + }, + }, + }, +); + +export default OpenApi3_1DereferenceStrategy; diff --git a/apidom/packages/apidom-reference/test/dereference.ts b/apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts similarity index 66% rename from apidom/packages/apidom-reference/test/dereference.ts rename to apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts index a38ea5c76c..a2602b8809 100644 --- a/apidom/packages/apidom-reference/test/dereference.ts +++ b/apidom/packages/apidom-reference/src/dereference/strategies/openapi-3-1/visitor.ts @@ -1,36 +1,35 @@ -import util from 'util'; -import path from 'path'; import stampit from 'stampit'; import { hasIn, pathSatisfies, propEq } from 'ramda'; import { isNotUndefined } from 'ramda-adjunct'; -import { toValue, visit, createNamespace, isPrimitiveElement } from 'apidom'; -import openApi3_1Namespace, { - keyMap, +import { isPrimitiveElement, visit } from 'apidom'; +import { getNodeType, - ReferenceElement, isReferenceLikeElement, + keyMap, + ReferenceElement, } from 'apidom-ns-openapi-3-1'; -import { parse } from '../src'; -import ReferenceSet from '../src/ReferenceSet'; -import Reference from '../src/Reference'; -import * as url from '../src/util/url'; -import { evaluate, uriToPointer } from '../src/selectors/json-pointer'; -import { Reference as IReference } from '../src/types'; +import { Reference as IReference } from '../../../types'; +import * as url from '../../../util/url'; +import parse from '../../../parse'; +import Reference from '../../../Reference'; +import { evaluate, uriToPointer } from '../../../selectors/json-pointer'; // @ts-ignore const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')]; -const DereferenceVisitor = stampit({ +const OpenApi3_1DereferenceVisitor = stampit({ props: { indirections: [], namespace: null, reference: null, + options: null, }, - init({ reference, namespace, indirections = [] }) { + init({ reference, namespace, indirections = [], options }) { this.indirections = indirections; this.namespace = namespace; this.reference = reference; + this.options = options; }, methods: { async toReference(uri: string): Promise { @@ -47,9 +46,9 @@ const DereferenceVisitor = stampit({ } // register new Reference with ReferenceSet - const parseResult = await parse(baseURI); + const parseResult = await parse(baseURI, this.options); - return Reference({ uri: baseURI, value: parseResult.first, refSet }); + return Reference({ uri: baseURI, value: parseResult, refSet }); }, async ReferenceElement(referenceElement: ReferenceElement) { @@ -60,7 +59,7 @@ const DereferenceVisitor = stampit({ const jsonPointer = uriToPointer(referenceElement.$ref.toValue()); // possibly non-semantic fragment - let fragment = evaluate(jsonPointer, reference.value); + let fragment = evaluate(jsonPointer, reference.value.result); // applying semantics to a fragment if (referenceElement.meta.hasKey('referenced-element') && isPrimitiveElement(fragment)) { @@ -81,10 +80,11 @@ const DereferenceVisitor = stampit({ } // dive deep into the fragment - const visitor = DereferenceVisitor({ + const visitor = OpenApi3_1DereferenceVisitor({ reference, namespace: this.namespace, indirections: [...this.indirections], + options: this.options, }); fragment = await visitAsync(fragment, visitor, { keyMap, nodeTypeGetter: getNodeType }); @@ -112,24 +112,4 @@ const DereferenceVisitor = stampit({ }, }); -describe('dereference', function () { - specify('should dereference', async function () { - const fixturePath = path.join(__dirname, 'fixtures', 'dereference', 'reference-objects.json'); - const { api } = await parse(fixturePath, { - parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' }, - }); - const namespace = createNamespace(openApi3_1Namespace); - const reference = Reference({ uri: fixturePath, value: api }); - const visitor = DereferenceVisitor({ reference, namespace }); - const refSet = ReferenceSet(); - refSet.add(reference); - - const dereferenced = await visitAsync(refSet.rootRef.value, visitor, { - keyMap, - nodeTypeGetter: getNodeType, - }); - - // @ts-ignore - console.log(util.inspect(toValue(dereferenced), true, null, true)); - }); -}); +export default OpenApi3_1DereferenceVisitor; diff --git a/apidom/packages/apidom-reference/src/index.ts b/apidom/packages/apidom-reference/src/index.ts index b1ad8d3048..f0f5807b91 100644 --- a/apidom/packages/apidom-reference/src/index.ts +++ b/apidom/packages/apidom-reference/src/index.ts @@ -7,7 +7,7 @@ import defaultOptions from './options'; import { merge as mergeOptions } from './options/util'; import parseFn, { readFile as readFileFn } from './parse'; import resolveFn, { resolveApiDOM as resolveApiDOMFn } from './resolve'; -import { dereferenceApiDOM as dereferenceApiDOMFn } from './dereference'; +import dereferenceFn, { dereferenceApiDOM as dereferenceApiDOMFn } from './dereference'; export const readFile = async (uri: string, options = {}): Promise => { const mergedOptions = mergeOptions(defaultOptions, options); @@ -35,6 +35,11 @@ export const resolveApiDOM = async ( return resolveApiDOMFn(element, mergedOptions); }; +export const dereference = async (uri: string, options = {}): Promise => { + const mergedOptions = mergeOptions(defaultOptions, options); + return dereferenceFn(uri, mergedOptions); +}; + export const dereferenceApiDOM = async ( element: T, options = {}, diff --git a/apidom/packages/apidom-reference/src/options/index.ts b/apidom/packages/apidom-reference/src/options/index.ts index a09d3a66ba..bc1a7c6820 100644 --- a/apidom/packages/apidom-reference/src/options/index.ts +++ b/apidom/packages/apidom-reference/src/options/index.ts @@ -10,6 +10,8 @@ import AsyncApiYaml2_0Parser from '../parsers/apidom-reference-parser-asyncapi-y import JsonParser from '../parsers/apidom-reference-parser-json'; import YamlParser from '../parsers/apidom-reference-parser-yaml'; +import OpenApi3_1DereferenceStrategy from '../dereference/strategies/openapi-3-1'; + import { ReferenceOptions as IReferenceOptions } from '../types'; const defaultOptions: IReferenceOptions = { @@ -60,7 +62,7 @@ const defaultOptions: IReferenceOptions = { * Strategy is determined by media type. * * You can add additional resolver strategies of your own, replace an existing one with - * your own implementation, or remove any resolver by removing it from the list. + * your own implementation, or remove any resolve strategy by removing it from the list. */ strategies: [OpenApi3_1ResolveStrategy()], /** @@ -80,6 +82,21 @@ const defaultOptions: IReferenceOptions = { */ maxDepth: +Infinity, }, + dereference: { + /** + * If set to a specific dereference strategy, loop for suitable resolve strategy will be skipped + * and this strategy will be used directly. + */ + strategy: null, + /** + * Determines strategies how ApiDOM is dereferenced. + * Strategy is determined by media type or by inspecting ApiDOM to be dereferenced. + * + * You can add additional dereference strategies of your own, replace an existing one with + * your own implementation, or remove any dereference strategy by removing it from the list. + */ + strategies: [OpenApi3_1DereferenceStrategy()], + }, }; export default defaultOptions; diff --git a/apidom/packages/apidom-reference/src/types.ts b/apidom/packages/apidom-reference/src/types.ts index 5ce3db2576..05e9798f35 100644 --- a/apidom/packages/apidom-reference/src/types.ts +++ b/apidom/packages/apidom-reference/src/types.ts @@ -1,4 +1,4 @@ -import { ParseResultElement } from 'apidom'; +import { ParseResultElement, Element } from 'apidom'; export interface File { uri: string; @@ -36,6 +36,11 @@ export interface ResolveStrategy { resolve(file: File, options: ReferenceOptions): Promise; } +export interface DereferenceStrategy { + canDereference(file: File): boolean; + dereference(file: File, options: ReferenceOptions): Promise; +} + export interface ComposableResolveStrategy extends ResolveStrategy { readonly strategies: Array; } @@ -61,6 +66,7 @@ export interface ReferenceSet { has(uri: string): boolean; find(callback: (reference: Reference) => boolean): undefined | Reference; values(): IterableIterator; + clean(): void; } export interface ReferenceParserOptions { @@ -77,7 +83,13 @@ export interface ReferenceResolveOptions { readonly maxDepth: number; } +export interface ReferenceDereferenceOptions { + readonly strategy: null | DereferenceStrategy; + readonly strategies: Array; +} + export interface ReferenceOptions { readonly parse: ReferenceParserOptions; readonly resolve: ReferenceResolveOptions; + readonly dereference: ReferenceDereferenceOptions; } diff --git a/apidom/packages/apidom-reference/src/util/errors/DereferenceError.ts b/apidom/packages/apidom-reference/src/util/errors/DereferenceError.ts new file mode 100644 index 0000000000..356e96556c --- /dev/null +++ b/apidom/packages/apidom-reference/src/util/errors/DereferenceError.ts @@ -0,0 +1,10 @@ +class DereferenceError extends Error { + public cause: undefined | Error; + + constructor(message: string, cause?: Error) { + super(message); + this.cause = cause; + } +} + +export default DereferenceError; diff --git a/apidom/packages/apidom-reference/test/dereference/index.ts b/apidom/packages/apidom-reference/test/dereference/index.ts new file mode 100644 index 0000000000..c7216d7f64 --- /dev/null +++ b/apidom/packages/apidom-reference/test/dereference/index.ts @@ -0,0 +1,48 @@ +import { assert } from 'chai'; +import path from 'path'; +import { toValue } from 'apidom'; + +import { dereference, dereferenceApiDOM, parse } from '../../src'; +import { loadJsonFile } from '../helpers'; + +describe('dereference', function () { + it('should dereference a file', async function () { + const rootFilePath = path.join( + __dirname, + 'strategies', + 'openapi-3-1', + 'fixtures', + 'basic', + 'root.json', + ); + const expected = loadJsonFile( + path.join(__dirname, 'strategies', 'openapi-3-1', 'fixtures', 'basic', 'dereferenced.json'), + ); + + const actual = await dereference(rootFilePath, { + parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' }, + }); + + assert.deepEqual(toValue(actual), expected); + }); + + it('should dereference an ApiDOM fragment', async function () { + const rootFilePath = path.join( + __dirname, + 'strategies', + 'openapi-3-1', + 'fixtures', + 'basic', + 'root.json', + ); + const expected = loadJsonFile( + path.join(__dirname, 'strategies', 'openapi-3-1', 'fixtures', 'basic', 'dereferenced.json'), + ); + const fragment = await parse(rootFilePath, { + parse: { mediaType: 'application/vnd.oai.openapi+json;version=3.1.0' }, + }); + const actual = await dereferenceApiDOM(fragment, { resolve: { baseURI: rootFilePath } }); + + assert.deepEqual(toValue(actual), expected); + }); +}); diff --git a/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/dereferenced.json b/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/dereferenced.json new file mode 100644 index 0000000000..4baa2dc952 --- /dev/null +++ b/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/dereferenced.json @@ -0,0 +1,39 @@ +[ + { + "openapi": "3.1.0", + "components": { + "parameters": { + "userId": { + "name": "userId", + "in": "query", + "description": "override", + "required": true + }, + "indirection1": { + "name": "userId", + "in": "query", + "description": "ID of the user", + "required": true + }, + "indirection2": { + "name": "userId", + "in": "query", + "description": "ID of the user", + "required": true + }, + "userIdRef": { + "name": "userId", + "in": "query", + "description": "ID of the user", + "required": true + }, + "externalRef": { + "name": "externalParameter", + "in": "query", + "description": "another ref", + "required": true + } + } + } + } +] diff --git a/apidom/packages/apidom-reference/test/fixtures/dereference/external-reference-objects.json b/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/ex.json similarity index 100% rename from apidom/packages/apidom-reference/test/fixtures/dereference/external-reference-objects.json rename to apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/ex.json diff --git a/apidom/packages/apidom-reference/test/fixtures/dereference/reference-objects.json b/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/root.json similarity index 90% rename from apidom/packages/apidom-reference/test/fixtures/dereference/reference-objects.json rename to apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/root.json index c18a2c03f5..74feea8442 100644 --- a/apidom/packages/apidom-reference/test/fixtures/dereference/reference-objects.json +++ b/apidom/packages/apidom-reference/test/dereference/strategies/openapi-3-1/fixtures/basic/root.json @@ -21,7 +21,7 @@ "required": true }, "externalRef": { - "$ref": "./external-reference-objects.json#/externalParameter", + "$ref": "./ex.json#/externalParameter", "description": "another ref" } } diff --git a/apidom/packages/apidom-reference/test/helpers.ts b/apidom/packages/apidom-reference/test/helpers.ts new file mode 100644 index 0000000000..ac0d72a70c --- /dev/null +++ b/apidom/packages/apidom-reference/test/helpers.ts @@ -0,0 +1,5 @@ +import fs from 'fs'; + +export const loadFile = (uri: string) => fs.readFileSync(uri).toString(); + +export const loadJsonFile = (uri: string) => JSON.parse(loadFile(uri));