Skip to content

Commit

Permalink
feat(resolver): expose resolve strategies (#2854)
Browse files Browse the repository at this point in the history
Part of this commit is consolidation of resolve
strategies and introduction of new 'strategies'
option for resolve and resolveSubTree functions.

The way this has been implemented allow effective
tree shaking.

Full API backward compatibility has remained.
  • Loading branch information
char0n authored Feb 23, 2023
1 parent 986c2ab commit 2a39fb4
Show file tree
Hide file tree
Showing 34 changed files with 141 additions and 43 deletions.
23 changes: 19 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down
13 changes: 6 additions & 7 deletions src/resolver/index.js
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
22 changes: 22 additions & 0 deletions src/resolver/strategies/generic/index.js
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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);

Expand Down
20 changes: 20 additions & 0 deletions src/resolver/strategies/openapi-2/index.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions src/resolver/strategies/openapi-2/normalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line no-restricted-exports
export { default } from '../generic/normalize.js';
5 changes: 5 additions & 0 deletions src/resolver/strategies/openapi-2/resolve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import resolveGenericStrategy from '../generic/resolve.js';

export default async function resolveOpenAPI2Strategy(options) {
return resolveGenericStrategy(options);
}
20 changes: 20 additions & 0 deletions src/resolver/strategies/openapi-3-0/index.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions src/resolver/strategies/openapi-3-0/normalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line no-restricted-exports
export { default } from '../generic/normalize.js';
5 changes: 5 additions & 0 deletions src/resolver/strategies/openapi-3-0/resolve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import resolveGenericStrategy from '../generic/resolve.js';

export default async function resolveOpenAPI30Strategy(options) {
return resolveGenericStrategy(options);
}
17 changes: 17 additions & 0 deletions src/resolver/strategies/openapi-3-1-apidom/index.js
Original file line number Diff line number Diff line change
@@ -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;
30 changes: 14 additions & 16 deletions src/subtree-resolver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -36,27 +36,25 @@ 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,
responseInterceptor,
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,
Expand Down
6 changes: 3 additions & 3 deletions test/execute/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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',
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__');

Expand Down

0 comments on commit 2a39fb4

Please sign in to comment.