diff --git a/src/index.js b/src/index.js index d872dee6cc..bd08e67482 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ import Url from 'url'; import Http, { makeHttp, serializeRes, serializeHeaders } from './http/index.js'; -import Resolver, { clearCache } from './resolver.js'; +import Resolver from './resolver/index.js'; +import { clearCache } from './resolver/strategies/openapi-2--3-0.js'; import resolveSubtree from './subtree-resolver/index.js'; import { makeApisTagOperation } from './interfaces.js'; import { execute, buildRequest, baseUrl } from './execute/index.js'; diff --git a/src/resolver.js b/src/resolver.js deleted file mode 100644 index 6f2ab784cc..0000000000 --- a/src/resolver.js +++ /dev/null @@ -1,98 +0,0 @@ -import Http from './http/index.js'; -import mapSpec, { plugins } from './specmap/index.js'; -// eslint-disable-next-line camelcase -import normalizeOpenAPI2__30 from './helpers/normalize/openapi-2--3-0.js'; -import { ACCEPT_HEADER_VALUE_FOR_DOCUMENTS } from './constants.js'; - -export function makeFetchJSON(http, opts = {}) { - const { requestInterceptor, responseInterceptor } = opts; - // Set credentials with 'http.withCredentials' value - const credentials = http.withCredentials ? 'include' : 'same-origin'; - return (docPath) => - http({ - url: docPath, - loadSpec: true, - requestInterceptor, - responseInterceptor, - headers: { - Accept: ACCEPT_HEADER_VALUE_FOR_DOCUMENTS, - }, - credentials, - }).then((res) => res.body); -} - -// Wipe out the http cache -export function clearCache() { - plugins.refs.clearCache(); -} - -export default function resolve(obj) { - const { - fetch, - spec, - url, - mode, - allowMetaPatches = true, - pathDiscriminator, - modelPropertyMacro, - parameterMacro, - requestInterceptor, - responseInterceptor, - skipNormalization, - useCircularStructures, - } = obj; - - let { http, baseDoc } = obj; - - // @TODO Swagger-UI uses baseDoc instead of url, this is to allow both - // need to fix and pick one. - baseDoc = baseDoc || url; - - // Provide a default fetch implementation - // TODO fetch should be removed, and http used instead - http = fetch || http || Http; - - if (!spec) { - return makeFetchJSON(http, { requestInterceptor, responseInterceptor })(baseDoc).then( - doResolve - ); - } - - return doResolve(spec); - - function doResolve(_spec) { - if (baseDoc) { - plugins.refs.docCache[baseDoc] = _spec; - } - - // Build a json-fetcher ( ie: give it a URL and get json out ) - plugins.refs.fetchJSON = makeFetchJSON(http, { requestInterceptor, responseInterceptor }); - - const plugs = [plugins.refs]; - - if (typeof parameterMacro === 'function') { - plugs.push(plugins.parameters); - } - - if (typeof modelPropertyMacro === 'function') { - plugs.push(plugins.properties); - } - - if (mode !== 'strict') { - plugs.push(plugins.allOf); - } - - // mapSpec is where the hard work happens - return mapSpec({ - spec: _spec, - context: { baseDoc }, - plugins: plugs, - allowMetaPatches, // allows adding .meta patches, which include adding `$$ref`s to the spec - pathDiscriminator, // for lazy resolution - parameterMacro, - modelPropertyMacro, - useCircularStructures, - // eslint-disable-next-line camelcase - }).then(skipNormalization ? async (a) => a : normalizeOpenAPI2__30); - } -} diff --git a/src/resolver/index.js b/src/resolver/index.js new file mode 100644 index 0000000000..1afec9dd95 --- /dev/null +++ b/src/resolver/index.js @@ -0,0 +1,18 @@ +// eslint-disable-next-line camelcase +import resolveOpenAPI2_30Strategy from './strategies/openapi-2--3-0.js'; +import { makeFetchJSON } from './utils/index.js'; +import * as optionsUtil from './utils/options.js'; + +const resolve = async (options) => { + const { spec, requestInterceptor, responseInterceptor } = options; + + const retrievalURI = optionsUtil.retrievalURI(options); + const httpClient = optionsUtil.httpClient(options); + const retrievedSpec = + spec || + (await makeFetchJSON(httpClient, { requestInterceptor, responseInterceptor })(retrievalURI)); + + return resolveOpenAPI2_30Strategy({ ...options, spec: retrievedSpec }); +}; + +export default resolve; diff --git a/src/resolver/strategies/openapi-2--3-0.js b/src/resolver/strategies/openapi-2--3-0.js new file mode 100644 index 0000000000..8cd3867ba0 --- /dev/null +++ b/src/resolver/strategies/openapi-2--3-0.js @@ -0,0 +1,67 @@ +import mapSpec, { plugins } from '../../specmap/index.js'; +// eslint-disable-next-line camelcase +import normalizeOpenAPI2__30 from '../../helpers/normalize/openapi-2--3-0.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) { + const { + spec, + mode, + allowMetaPatches = true, + pathDiscriminator, + modelPropertyMacro, + parameterMacro, + requestInterceptor, + responseInterceptor, + skipNormalization, + useCircularStructures, + } = obj; + + const retrievalURI = optionsUtil.retrievalURI(obj); + const httpClient = optionsUtil.httpClient(obj); + + return doResolve(spec); + + function doResolve(_spec) { + if (retrievalURI) { + plugins.refs.docCache[retrievalURI] = _spec; + } + + // Build a json-fetcher ( ie: give it a URL and get json out ) + plugins.refs.fetchJSON = makeFetchJSON(httpClient, { requestInterceptor, responseInterceptor }); + + const plugs = [plugins.refs]; + + if (typeof parameterMacro === 'function') { + plugs.push(plugins.parameters); + } + + if (typeof modelPropertyMacro === 'function') { + plugs.push(plugins.properties); + } + + if (mode !== 'strict') { + plugs.push(plugins.allOf); + } + + // mapSpec is where the hard work happens + return mapSpec({ + spec: _spec, + context: { baseDoc: retrievalURI }, + plugins: plugs, + allowMetaPatches, // allows adding .meta patches, which include adding `$$ref`s to the spec + pathDiscriminator, // for lazy resolution + parameterMacro, + modelPropertyMacro, + useCircularStructures, + // eslint-disable-next-line camelcase + }).then(skipNormalization ? async (a) => a : normalizeOpenAPI2__30); + } +} diff --git a/src/resolver/utils/index.js b/src/resolver/utils/index.js new file mode 100644 index 0000000000..38c056b611 --- /dev/null +++ b/src/resolver/utils/index.js @@ -0,0 +1,19 @@ +import { ACCEPT_HEADER_VALUE_FOR_DOCUMENTS } from '../../constants.js'; + +// eslint-disable-next-line import/prefer-default-export +export function makeFetchJSON(http, opts = {}) { + const { requestInterceptor, responseInterceptor } = opts; + // Set credentials with 'http.withCredentials' value + const credentials = http.withCredentials ? 'include' : 'same-origin'; + return (docPath) => + http({ + url: docPath, + loadSpec: true, + requestInterceptor, + responseInterceptor, + headers: { + Accept: ACCEPT_HEADER_VALUE_FOR_DOCUMENTS, + }, + credentials, + }).then((res) => res.body); +} diff --git a/src/resolver/utils/options.js b/src/resolver/utils/options.js new file mode 100644 index 0000000000..bbe1330c52 --- /dev/null +++ b/src/resolver/utils/options.js @@ -0,0 +1,17 @@ +import Http from '../../http/index.js'; + +export const retrievalURI = (options) => { + const { baseDoc, url } = options; + + // @TODO Swagger-UI uses baseDoc instead of url, this is to allow both + // need to fix and pick one. + return baseDoc || url; +}; + +export const httpClient = (options) => { + const { fetch, http } = options; + + // @TODO fetch should be removed, and http used instead + // provide a default fetch implementation + return fetch || http || Http; +}; diff --git a/src/subtree-resolver/index.js b/src/subtree-resolver/index.js index be83436661..205e544c95 100644 --- a/src/subtree-resolver/index.js +++ b/src/subtree-resolver/index.js @@ -23,7 +23,7 @@ import get from 'lodash/get'; -import resolve from '../resolver.js'; +import resolve from '../resolver/index.js'; // eslint-disable-next-line camelcase import normalizeOpenAPI2__30 from '../helpers/normalize/openapi-2--3-0.js'; diff --git a/test/resolver.js b/test/resolver/resolver.js similarity index 99% rename from test/resolver.js rename to test/resolver/resolver.js index 21e3074276..947b4afce2 100644 --- a/test/resolver.js +++ b/test/resolver/resolver.js @@ -3,7 +3,7 @@ import path from 'path'; import fs from 'fs'; import jsYaml from 'js-yaml'; -import Swagger from '../src/index.js'; +import Swagger from '../../src/index.js'; describe('resolver', () => { afterEach(() => { @@ -721,7 +721,7 @@ describe('resolver', () => { test('should not throw errors on resvered-keywords in freely-named-fields', () => { // Given const ReservedKeywordSpec = jsYaml.load( - fs.readFileSync(path.resolve(__dirname, './data/reserved-keywords.yaml'), 'utf8') + fs.readFileSync(path.resolve(__dirname, '../data/reserved-keywords.yaml'), 'utf8') ); // When