diff --git a/src/index.js b/src/index.js index 7487924f3..f42920e5f 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,11 @@ import Url from 'url'; import Http, { makeHttp, serializeRes, serializeHeaders } from './http/index.js'; -import Resolver from './resolver/index.js'; -import { clearCache } from './resolver/strategies/openapi-2--3-0/index.js'; +import resolve from './resolver/index.js'; +import genericResolveStrategy from './resolver/strategies/generic/index.js'; +import openApi2ResolveStrategy, { clearCache } from './resolver/strategies/openapi-2/index.js'; +import openApi30ResolveStrategy from './resolver/strategies/openapi-3-0/index.js'; +import openApi31ApiDOMResolveStrategy from './resolver/strategies/openapi-3-1-apidom/index.js'; import resolveSubtree from './subtree-resolver/index.js'; import { makeApisTagOperation } from './interfaces.js'; import { execute, buildRequest, baseUrl } from './execute/index.js'; @@ -17,8 +20,20 @@ import OpenApi3_1SwaggerClientDereferenceStrategy from './resolver/apidom/refere Swagger.http = Http; Swagger.makeHttp = makeHttp.bind(null, Swagger.http); -Swagger.resolve = Resolver; -Swagger.resolveSubtree = resolveSubtree; +Swagger.resolveStrategies = { + 'openapi-3-1-apidom': openApi31ApiDOMResolveStrategy, + 'openapi-3-0': openApi30ResolveStrategy, + 'openapi-2-0': openApi2ResolveStrategy, + generic: genericResolveStrategy, +}; +Swagger.resolve = async (options) => { + const mergedOptions = { strategies: Object.values(Swagger.resolveStrategies), ...options }; + return resolve(mergedOptions); +}; +Swagger.resolveSubtree = async (obj, path, options = {}) => { + const mergedOptions = { strategies: Object.values(Swagger.resolveStrategies), ...options }; + return resolveSubtree(obj, path, mergedOptions); +}; Swagger.execute = execute; Swagger.serializeRes = serializeRes; Swagger.serializeHeaders = serializeHeaders; diff --git a/src/resolver/index.js b/src/resolver/index.js index 9c85776a0..3cbf7ed06 100644 --- a/src/resolver/index.js +++ b/src/resolver/index.js @@ -1,9 +1,8 @@ -// eslint-disable-next-line camelcase -import resolveOpenAPI2_30Strategy from './strategies/openapi-2--3-0/index.js'; -import resolveOpenAPI31Strategy from './strategies/openapi-3-1/index.js'; import { makeFetchJSON } from './utils/index.js'; import * as optionsUtil from './utils/options.js'; -import { isOpenAPI31 } from '../helpers/openapi-predicates.js'; +import genericStrategy from './strategies/generic/index.js'; +import openApi2Strategy from './strategies/openapi-2/index.js'; +import openApi30Strategy from './strategies/openapi-3-0/index.js'; const resolve = async (options) => { const { spec, requestInterceptor, responseInterceptor } = options; @@ -14,10 +13,10 @@ const resolve = async (options) => { spec || (await makeFetchJSON(httpClient, { requestInterceptor, responseInterceptor })(retrievalURI)); const strategyOptions = { ...options, spec: retrievedSpec }; + const strategies = options.strategies || [openApi30Strategy, openApi2Strategy, genericStrategy]; + const strategy = strategies.find((strg) => strg.match(strategyOptions)); - return isOpenAPI31(retrievedSpec) - ? resolveOpenAPI31Strategy(strategyOptions) - : resolveOpenAPI2_30Strategy(strategyOptions); + return strategy.resolve(strategyOptions); }; export default resolve; diff --git a/src/resolver/strategies/generic/index.js b/src/resolver/strategies/generic/index.js new file mode 100644 index 000000000..bf15ef51c --- /dev/null +++ b/src/resolver/strategies/generic/index.js @@ -0,0 +1,22 @@ +import resolveGenericStrategy from './resolve.js'; +import normalize from './normalize.js'; +import { plugins } from '../../../specmap/index.js'; + +export function clearCache() { + plugins.refs.clearCache(); +} + +const genericStrategy = { + name: 'generic', + match() { + return true; + }, + normalize({ spec }) { + const { spec: normalized } = normalize({ spec }); + return normalized; + }, + async resolve(options) { + return resolveGenericStrategy(options); + }, +}; +export default genericStrategy; diff --git a/src/resolver/strategies/openapi-2--3-0/normalize.js b/src/resolver/strategies/generic/normalize.js similarity index 100% rename from src/resolver/strategies/openapi-2--3-0/normalize.js rename to src/resolver/strategies/generic/normalize.js diff --git a/src/resolver/strategies/openapi-2--3-0/index.js b/src/resolver/strategies/generic/resolve.js similarity index 81% rename from src/resolver/strategies/openapi-2--3-0/index.js rename to src/resolver/strategies/generic/resolve.js index 34015919f..92822183d 100644 --- a/src/resolver/strategies/openapi-2--3-0/index.js +++ b/src/resolver/strategies/generic/resolve.js @@ -1,16 +1,9 @@ import mapSpec, { plugins } from '../../../specmap/index.js'; -// eslint-disable-next-line camelcase import normalize from './normalize.js'; import { makeFetchJSON } from '../../utils/index.js'; import * as optionsUtil from '../../utils/options.js'; -// Wipe out the http cache -export function clearCache() { - plugins.refs.clearCache(); -} - -// eslint-disable-next-line camelcase -export default function resolveOpenAPI2_30Strategy(obj) { +export default async function resolveGenericStrategy(options) { const { spec, mode, @@ -22,10 +15,10 @@ export default function resolveOpenAPI2_30Strategy(obj) { responseInterceptor, skipNormalization, useCircularStructures, - } = obj; + } = options; - const retrievalURI = optionsUtil.retrievalURI(obj); - const httpClient = optionsUtil.httpClient(obj); + const retrievalURI = optionsUtil.retrievalURI(options); + const httpClient = optionsUtil.httpClient(options); return doResolve(spec); diff --git a/src/resolver/strategies/openapi-2/index.js b/src/resolver/strategies/openapi-2/index.js new file mode 100644 index 000000000..9cd82c450 --- /dev/null +++ b/src/resolver/strategies/openapi-2/index.js @@ -0,0 +1,20 @@ +import resolveOpenAPI2Strategy from './resolve.js'; +import normalize from './normalize.js'; +import { isOpenAPI2 } from '../../../helpers/openapi-predicates.js'; + +export { clearCache } from '../generic/index.js'; + +const openApi2Strategy = { + name: 'openapi-2', + match({ spec }) { + return isOpenAPI2(spec); + }, + normalize({ spec }) { + const { spec: normalized } = normalize({ spec }); + return normalized; + }, + async resolve(options) { + return resolveOpenAPI2Strategy(options); + }, +}; +export default openApi2Strategy; diff --git a/src/resolver/strategies/openapi-2/normalize.js b/src/resolver/strategies/openapi-2/normalize.js new file mode 100644 index 000000000..db30ae539 --- /dev/null +++ b/src/resolver/strategies/openapi-2/normalize.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export { default } from '../generic/normalize.js'; diff --git a/src/resolver/strategies/openapi-2/resolve.js b/src/resolver/strategies/openapi-2/resolve.js new file mode 100644 index 000000000..eccb1a2be --- /dev/null +++ b/src/resolver/strategies/openapi-2/resolve.js @@ -0,0 +1,5 @@ +import resolveGenericStrategy from '../generic/resolve.js'; + +export default async function resolveOpenAPI2Strategy(options) { + return resolveGenericStrategy(options); +} diff --git a/src/resolver/strategies/openapi-3-0/index.js b/src/resolver/strategies/openapi-3-0/index.js new file mode 100644 index 000000000..991e30264 --- /dev/null +++ b/src/resolver/strategies/openapi-3-0/index.js @@ -0,0 +1,20 @@ +import resolveOpenAPI30Strategy from './resolve.js'; +import normalize from './normalize.js'; +import { isOpenAPI30 } from '../../../helpers/openapi-predicates.js'; + +export { clearCache } from '../generic/index.js'; + +const openApi30Strategy = { + name: 'openapi-3-0', + match({ spec }) { + return isOpenAPI30(spec); + }, + normalize({ spec }) { + const { spec: normalized } = normalize({ spec }); + return normalized; + }, + async resolve(options) { + return resolveOpenAPI30Strategy(options); + }, +}; +export default openApi30Strategy; diff --git a/src/resolver/strategies/openapi-3-0/normalize.js b/src/resolver/strategies/openapi-3-0/normalize.js new file mode 100644 index 000000000..db30ae539 --- /dev/null +++ b/src/resolver/strategies/openapi-3-0/normalize.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export { default } from '../generic/normalize.js'; diff --git a/src/resolver/strategies/openapi-3-0/resolve.js b/src/resolver/strategies/openapi-3-0/resolve.js new file mode 100644 index 000000000..00b4a19f0 --- /dev/null +++ b/src/resolver/strategies/openapi-3-0/resolve.js @@ -0,0 +1,5 @@ +import resolveGenericStrategy from '../generic/resolve.js'; + +export default async function resolveOpenAPI30Strategy(options) { + return resolveGenericStrategy(options); +} diff --git a/src/resolver/strategies/openapi-3-1-apidom/index.js b/src/resolver/strategies/openapi-3-1-apidom/index.js new file mode 100644 index 000000000..8c2b18c7a --- /dev/null +++ b/src/resolver/strategies/openapi-3-1-apidom/index.js @@ -0,0 +1,17 @@ +import resolveOpenAPI31Strategy from './resolve.js'; +import normalize, { pojoAdapter } from './normalize.js'; +import { isOpenAPI31 } from '../../../helpers/openapi-predicates.js'; + +const openApi31ApiDOMStrategy = { + name: 'openapi-3-1-apidom', + match({ spec }) { + return isOpenAPI31(spec); + }, + normalize({ spec }) { + return pojoAdapter(normalize)(spec); + }, + async resolve(options) { + return resolveOpenAPI31Strategy(options); + }, +}; +export default openApi31ApiDOMStrategy; diff --git a/src/resolver/strategies/openapi-3-1/normalize.js b/src/resolver/strategies/openapi-3-1-apidom/normalize.js similarity index 100% rename from src/resolver/strategies/openapi-3-1/normalize.js rename to src/resolver/strategies/openapi-3-1-apidom/normalize.js diff --git a/src/resolver/strategies/openapi-3-1/index.js b/src/resolver/strategies/openapi-3-1-apidom/resolve.js similarity index 100% rename from src/resolver/strategies/openapi-3-1/index.js rename to src/resolver/strategies/openapi-3-1-apidom/resolve.js diff --git a/src/subtree-resolver/index.js b/src/subtree-resolver/index.js index bfbc46b37..84e9463f8 100644 --- a/src/subtree-resolver/index.js +++ b/src/subtree-resolver/index.js @@ -22,12 +22,12 @@ // TODO: move the remarks above into project documentation import get from 'lodash/get'; -import { isOpenAPI31 } from '../helpers/openapi-predicates.js'; import resolve from '../resolver/index.js'; -import normalizeOpenAPI2__30 from '../resolver/strategies/openapi-2--3-0/normalize.js'; // eslint-disable-line camelcase -import normalizeOpenAPI31, { pojoAdapter } from '../resolver/strategies/openapi-3-1/normalize.js'; +import genericResolverStrategy from '../resolver/strategies/generic/index.js'; +import openApi2ResolverStrategy from '../resolver/strategies/openapi-2/index.js'; +import openApi30ResolverStrategy from '../resolver/strategies/openapi-3-0/index.js'; -export default async function resolveSubtree(obj, path, opts = {}) { +export default async function resolveSubtree(obj, path, options = {}) { const { returnEntireTree, baseDoc, @@ -36,9 +36,14 @@ export default async function resolveSubtree(obj, path, opts = {}) { parameterMacro, modelPropertyMacro, useCircularStructures, - } = opts; - + } = options; + const strategies = options.strategies || [ + openApi30ResolverStrategy, + openApi2ResolverStrategy, + genericResolverStrategy, + ]; const resolveOptions = { + spec: obj, pathDiscriminator: path, baseDoc, requestInterceptor, @@ -46,17 +51,10 @@ export default async function resolveSubtree(obj, path, opts = {}) { parameterMacro, modelPropertyMacro, useCircularStructures, + strategies, }; - - let normalized; - if (isOpenAPI31(obj)) { - normalized = pojoAdapter(normalizeOpenAPI31)(obj); - } else { - ({ spec: normalized } = normalizeOpenAPI2__30({ - spec: obj, - })); - } - + const strategy = strategies.find((strg) => strg.match(resolveOptions)); + const normalized = strategy.normalize(resolveOptions); const result = await resolve({ ...resolveOptions, spec: normalized, diff --git a/test/execute/main.js b/test/execute/main.js index 94944b604..43d1db137 100644 --- a/test/execute/main.js +++ b/test/execute/main.js @@ -3,7 +3,7 @@ import AbortController from 'abort-controller'; import { execute, buildRequest, self as stubs } from '../../src/execute/index.js'; // eslint-disable-next-line camelcase -import normalizeOpenAPI2__30 from '../../src/resolver/strategies/openapi-2--3-0/normalize.js'; +import normalize from '../../src/resolver/strategies/generic/normalize.js'; // Supported shape... { spec, operationId, parameters, securities, fetch } // One can use operationId or pathItem + method @@ -2033,7 +2033,7 @@ describe('execute', () => { }, }; - const resultSpec = normalizeOpenAPI2__30(spec); + const resultSpec = normalize(spec); const warnSpy = jest.spyOn(console, 'warn'); const req = buildRequest({ spec: resultSpec.spec, @@ -2087,7 +2087,7 @@ describe('execute', () => { const oriWarn = global.console.warn; global.console.warn = jest.fn(); - const resultSpec = normalizeOpenAPI2__30(spec); + const resultSpec = normalize(spec); const req = buildRequest({ spec: resultSpec.spec, operationId: 'getPetsById', diff --git a/test/resolver/strategies/openapi-2--3-0/index.js b/test/resolver/strategies/generic/index.js similarity index 100% rename from test/resolver/strategies/openapi-2--3-0/index.js rename to test/resolver/strategies/generic/index.js diff --git a/test/resolver/strategies/openapi-2--3-0/normalize.js b/test/resolver/strategies/generic/normalize.js similarity index 99% rename from test/resolver/strategies/openapi-2--3-0/normalize.js rename to test/resolver/strategies/generic/normalize.js index 6dc97baea..bba98f204 100644 --- a/test/resolver/strategies/openapi-2--3-0/normalize.js +++ b/test/resolver/strategies/generic/normalize.js @@ -1,5 +1,5 @@ // eslint-disable-next-line camelcase -import normalize from '../../../../src/resolver/strategies/openapi-2--3-0/normalize.js'; +import normalize from '../../../../src/resolver/strategies/openapi-2/normalize.js'; describe('helpers', () => { describe('normalize', () => { diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/circular-structures.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/circular-structures.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/circular-structures.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/circular-structures.json diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/model-property-macro-error.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/model-property-macro-error.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/model-property-macro-error.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/model-property-macro-error.json diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/model-property-macro.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/model-property-macro.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/model-property-macro.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/model-property-macro.json diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/parameter-macro-no-operation.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/parameter-macro-no-operation.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/parameter-macro-no-operation.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/parameter-macro-no-operation.json diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/parameter-macro.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/parameter-macro.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/parameter-macro.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/parameter-macro.json diff --git a/test/resolver/strategies/openapi-3-1/__fixtures__/petstore.json b/test/resolver/strategies/openapi-3-1-apidom/__fixtures__/petstore.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/__fixtures__/petstore.json rename to test/resolver/strategies/openapi-3-1-apidom/__fixtures__/petstore.json diff --git a/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap b/test/resolver/strategies/openapi-3-1-apidom/__snapshots__/index.js.snap similarity index 100% rename from test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap rename to test/resolver/strategies/openapi-3-1-apidom/__snapshots__/index.js.snap diff --git a/test/resolver/strategies/openapi-3-1/index.js b/test/resolver/strategies/openapi-3-1-apidom/index.js similarity index 100% rename from test/resolver/strategies/openapi-3-1/index.js rename to test/resolver/strategies/openapi-3-1-apidom/index.js diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/header-examples.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/header-examples.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/header-examples.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/header-examples.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/operation-ids.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/operation-ids.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/operation-ids.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/operation-ids.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/parameter-examples.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/parameter-examples.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/parameter-examples.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/parameter-examples.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/parameters.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/parameters.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/parameters.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/parameters.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/security-requirements.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/security-requirements.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/security-requirements.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/security-requirements.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__fixtures__/servers.json b/test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/servers.json similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__fixtures__/servers.json rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__fixtures__/servers.json diff --git a/test/resolver/strategies/openapi-3-1/normalize/__snapshots__/index.js.snap b/test/resolver/strategies/openapi-3-1-apidom/normalize/__snapshots__/index.js.snap similarity index 100% rename from test/resolver/strategies/openapi-3-1/normalize/__snapshots__/index.js.snap rename to test/resolver/strategies/openapi-3-1-apidom/normalize/__snapshots__/index.js.snap diff --git a/test/resolver/strategies/openapi-3-1/normalize/index.js b/test/resolver/strategies/openapi-3-1-apidom/normalize/index.js similarity index 99% rename from test/resolver/strategies/openapi-3-1/normalize/index.js rename to test/resolver/strategies/openapi-3-1-apidom/normalize/index.js index bfb7650be..a4ad6cbbd 100644 --- a/test/resolver/strategies/openapi-3-1/normalize/index.js +++ b/test/resolver/strategies/openapi-3-1-apidom/normalize/index.js @@ -3,7 +3,7 @@ import path from 'node:path'; import { toValue, StringElement } from '@swagger-api/apidom-core'; import { OpenApi3_1Element } from '@swagger-api/apidom-ns-openapi-3-1'; -import normalize from '../../../../../src/resolver/strategies/openapi-3-1/normalize.js'; +import normalize from '../../../../../src/resolver/strategies/openapi-3-1-apidom/normalize.js'; const fixturesPath = path.join(__dirname, '__fixtures__');