Skip to content

Commit

Permalink
feat(referece): add support for setting max depth of resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
char0n committed Dec 29, 2020
1 parent dfdb887 commit 0e71846
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 0 deletions.
5 changes: 5 additions & 0 deletions apidom/packages/apidom-reference/src/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ const defaultOptions: IReferenceOptions = {
/**
* Determines the maximum depth of resolve algorithms.
* By default there is no limit.
*
* It can be set to any positive integer number.
*
* The resolver should throw MaximumResolverDepthError if resolution depth
* is exceeded by this option.
*/
maxDepth: +Infinity,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { merge as mergeOptions } from '../../../options/util';
import Reference from '../../../Reference';
import { isExternalReferenceElement, isExternalReferenceLikeElement } from './predicates';
import ReferenceSet from '../../../ReferenceSet';
import { MaximumResolverDepthError } from '../../../util/errors';

const ReferenceObjectsResolveStrategy: stampit.Stamp<IResolveStrategy> = stampit(
ResolveStrategy,
Expand All @@ -39,6 +40,13 @@ const ReferenceObjectsResolveStrategy: stampit.Stamp<IResolveStrategy> = stampit
const resolvedURI = url.resolve(options.resolve.baseURI, $ref);
const withoutHash = url.stripHash(resolvedURI);

// maximum resolution depth has been exceeded
if (depth > options.resolve.maxDepth) {
throw new MaximumResolverDepthError(
`Maximum resolution depth of ${options.resolve.maxDepth} has been exceeded by file "${withoutHash}"`,
);
}

// return early if we already recognize this reference
if (refSet.has(withoutHash)) {
const reference = refSet.find(propEq('uri', withoutHash));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ResolverError from './ResolverError';

class MaximumResolverDepthError extends ResolverError {}

export default MaximumResolverDepthError;
1 change: 1 addition & 0 deletions apidom/packages/apidom-reference/src/util/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { default as UnmatchedResolverError } from './UnmatchedResolverError';
export { default as ParserError } from './ParserError';
export { default as PluginError } from './PluginError';
export { default as InvalidSelectorError } from './InvalidSelectorError';
export { default as MaximumResolverDepthError } from './MaximumResolverDepthError';
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import File from '../../../../src/util/File';
import ReferenceObjectsResolveStrategy from '../../../../src/resolve-strategies/openapi-3-1/reference-objects';
import { parse } from '../../../../src';
import defaultOptions from '../../../../src/options';
import { merge as mergeOptions } from '../../../../src/options/util';
import * as url from '../../../../src/util/url';
import {
File as IFile,
ReferenceSet as IReferenceSet,
Reference as IReference,
} from '../../../../src/types';
import { MaximumResolverDepthError } from '../../../../src/util/errors';

describe('resolve-strategies', function () {
context('openapi-3-1', function () {
Expand Down Expand Up @@ -470,6 +472,31 @@ describe('resolve-strategies', function () {
});
},
);

context('given maxDepth option is set to 2', function () {
context('and OpenApi 3.1.x document with depth 4 external references', function () {
specify('should throw MaximumResolverDepthError', async function () {
const uri = path.join(
__dirname,
'fixtures',
'external-reference-depth-4',
'root.json',
);
const mediaType = 'application/vnd.oai.openapi;version=3.1.0';
const data = fs.readFileSync(uri);
const parseResult = await parse(uri, { parse: { mediaType } });
const rootFile = File({ uri, mediaType, data, parseResult });
const options = mergeOptions(defaultOptions, { resolve: { maxDepth: 2 } });

try {
await strategy.resolve(rootFile, options);
assert.fail('Should throw error here');
} catch (error) {
assert.instanceOf(error, MaximumResolverDepthError);
}
});
});
});
});
});
});
Expand Down

0 comments on commit 0e71846

Please sign in to comment.