Merge pull request #556 from postmanlabs/chore/RefactoringBundleRulesResolver3031

Refactor rule resolver 3.0 and 3.1
This commit is contained in:
Carlos-Veloz
2022-06-16 14:30:52 -05:00
committed by GitHub
8 changed files with 202 additions and 226 deletions

View File

@@ -1,102 +0,0 @@
const COMPONENTS_KEYS_30 = [
'schemas',
'responses',
'parameters',
'examples',
'requestBodies',
'headers',
'securitySchemes',
'links',
'callbacks'
],
SCHEMA_CONTAINERS = [
'allOf',
'oneOf',
'anyOf',
'not',
'additionalProperties',
'items',
'schema'
],
EXAMPLE_CONTAINERS = [
'example'
],
PROPERTY_DEFINITION = [
'properties'
],
RESPONSE_DEFINITION = [
'responses'
],
REQUEST_BODY_CONTAINER = [
'requestBody'
],
LINKS_CONTAINER = [
'links'
],
HEADER_DEFINITION = [
'headers'
],
CALLBACK_DEFINITION = [
'callbacks'
];
module.exports = {
/**
* Generates the trace to the key that will wrap de component using 3.0 version
* @param {array} traceFromParent - The trace from the parent key
* @param {string} filePathName - The filePath name from the file
* @param {string} localPart - The local path part
* @param {function} jsonPointerDecodeAndReplace - Function to decode a json pointer
* @returns {array} The trace to the container key
*/
getKeyInComponents30: function (traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace) {
let res = traceFromParent,
trace = [],
traceToKey = [],
matchFound = false,
isInComponents = traceFromParent[0] === 'components';
if (isInComponents) {
return [];
}
res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`));
trace = [...res].reverse();
for (let [index, item] of trace.entries()) {
if (SCHEMA_CONTAINERS.includes(item)) {
item = 'schemas';
}
if (EXAMPLE_CONTAINERS.includes(item)) {
item = 'examples';
}
if (REQUEST_BODY_CONTAINER.includes(item)) {
item = 'requestBodies';
}
if (LINKS_CONTAINER.includes(trace[index + 2])) {
trace[index + 1] = 'links';
}
if (PROPERTY_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'schemas';
}
traceToKey.push(item);
if (COMPONENTS_KEYS_30.includes(item)) {
matchFound = true;
break;
}
if (RESPONSE_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'responses';
}
if (HEADER_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'headers';
}
if (CALLBACK_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'callbacks';
}
}
return matchFound ?
traceToKey.reverse() :
[];
},
COMPONENTS_KEYS_30
};

View File

@@ -1,109 +0,0 @@
const COMPONENTS_KEYS_31 = [
'schemas',
'responses',
'parameters',
'examples',
'requestBodies',
'headers',
'securitySchemes',
'links',
'callbacks',
'pathItems'
],
SCHEMA_CONTAINERS = [
'allOf',
'oneOf',
'anyOf',
'not',
'additionalProperties',
'items',
'schema'
],
EXAMPLE_CONTAINERS = [
'example'
],
PROPERTY_DEFINITION = [
'properties'
],
RESPONSE_DEFINITION = [
'responses'
],
REQUEST_BODY_CONTAINER = [
'requestBody'
],
LINKS_CONTAINER = [
'links'
],
HEADER_DEFINITION = [
'headers'
],
CALLBACK_DEFINITION = [
'callbacks'
],
PATH_ITEM_CONTAINER = [
'paths'
];
module.exports = {
/**
* Generates the trace to the key that will wrap de component using 3.0 version
* @param {array} traceFromParent - The trace from the parent key
* @param {string} filePathName - The filePath name from the file
* @param {string} localPart - The local path part
* @param {function} jsonPointerDecodeAndReplace - Function to decode a json pointer
* @returns {array} The trace to the container key
*/
getKeyInComponents31: function (traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace) {
let res = traceFromParent,
trace = [],
traceToKey = [],
matchFound = false,
isInComponents = traceFromParent[0] === 'components';
if (isInComponents) {
return [];
}
res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`));
trace = [...res].reverse();
for (let [index, item] of trace.entries()) {
if (SCHEMA_CONTAINERS.includes(item)) {
item = 'schemas';
}
if (EXAMPLE_CONTAINERS.includes(item)) {
item = 'examples';
}
if (REQUEST_BODY_CONTAINER.includes(item)) {
item = 'requestBodies';
}
if (LINKS_CONTAINER.includes(trace[index + 2])) {
trace[index + 1] = 'links';
}
if (PATH_ITEM_CONTAINER.includes(trace[index + 2])) {
trace[index + 1] = 'pathItems';
}
if (PROPERTY_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'schemas';
}
traceToKey.push(item);
if (COMPONENTS_KEYS_31.includes(item)) {
matchFound = true;
break;
}
if (RESPONSE_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'responses';
}
if (HEADER_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'headers';
}
if (CALLBACK_DEFINITION.includes(trace[index + 2])) {
trace[index + 1] = 'callbacks';
}
}
return matchFound ?
traceToKey.reverse() :
[];
},
COMPONENTS_KEYS_31
};

View File

@@ -1,5 +1,4 @@
const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'),
{
const {
isExtRef,
getKeyInComponents,
getJsonPointerRelationToRoot,
@@ -11,8 +10,8 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'),
} = require('./jsonPointer'),
traverseUtility = require('traverse'),
parse = require('./parse.js'),
{ COMPONENTS_KEYS_31 } = require('./31XUtils/componentsParentMatcher'),
{ ParseError } = require('./common/ParseError');
const { getBundleRulesDataByVersion } = require('./common/versionUtils');
let path = require('path'),
pathBrowserify = require('path-browserify'),
@@ -156,7 +155,7 @@ function getContentFromTrace(content, partial) {
* @returns {null} It modifies components global context
*/
function setValueInComponents(keyInComponents, components, value, version) {
const COMPONENTS_KEYS = version === '3.1' ? COMPONENTS_KEYS_31 : COMPONENTS_KEYS_30;
const { COMPONENTS_KEYS } = getBundleRulesDataByVersion(version);
let currentPlace = components,
target = keyInComponents[keyInComponents.length - 2],
key = keyInComponents.length === 2 && COMPONENTS_KEYS.includes(keyInComponents[0]) ?

View File

@@ -0,0 +1,33 @@
module.exports = {
/**
* Resolve the scenario if the item is in second level
* @param {array} trace The keyInComponents
* @param {number} index the current index
* @return {undefined}
*/
resolveSecondLevelChild: function (trace, index, definitions) {
const item = definitions[trace[index + 2]];
if (item) {
trace[index + 1] = item;
}
},
/**
* If the provided item is included in any defined container it returns the container name
* else it return the same item
* @param {string} item The current item in the iteration
* @returns {string} the name of container where item is included or the item if it's not included
* in any container
*/
resolveFirstLevelChild: function(item, containers) {
for (let [key, containerItems] of Object.entries(containers)) {
if (containerItems.includes(item)) {
return key;
}
}
return item;
}
};

52
lib/bundleRules/spec30.js Normal file
View File

@@ -0,0 +1,52 @@
const SCHEMA_CONTAINERS = [
'allOf',
'oneOf',
'anyOf',
'not',
'additionalProperties',
'items',
'schema'
],
EXAMPLE_CONTAINERS = [
'example'
],
REQUEST_BODY_CONTAINER = [
'requestBody'
],
CONTAINERS = {
schemas: SCHEMA_CONTAINERS,
examples: EXAMPLE_CONTAINERS,
requestBodies: REQUEST_BODY_CONTAINER
},
DEFINITIONS = {
headers: 'headers',
responses: 'responses',
callbacks: 'callbacks',
properties: 'schemas',
links: 'links'
},
INLINE = [],
ROOT_CONTAINERS_KEYS = [
'components'
],
COMPONENTS_KEYS = [
'schemas',
'responses',
'parameters',
'examples',
'requestBodies',
'headers',
'securitySchemes',
'links',
'callbacks'
];
module.exports = {
RULES_30: {
CONTAINERS,
DEFINITIONS,
COMPONENTS_KEYS,
INLINE,
ROOT_CONTAINERS_KEYS
}
};

54
lib/bundleRules/spec31.js Normal file
View File

@@ -0,0 +1,54 @@
const SCHEMA_CONTAINERS = [
'allOf',
'oneOf',
'anyOf',
'not',
'additionalProperties',
'items',
'schema'
],
EXAMPLE_CONTAINERS = [
'example'
],
REQUEST_BODY_CONTAINER = [
'requestBody'
],
CONTAINERS = {
schemas: SCHEMA_CONTAINERS,
examples: EXAMPLE_CONTAINERS,
requestBodies: REQUEST_BODY_CONTAINER
},
DEFINITIONS = {
headers: 'headers',
responses: 'responses',
callbacks: 'callbacks',
properties: 'schemas',
links: 'links',
paths: 'pathItems'
},
INLINE = [],
ROOT_CONTAINERS_KEYS = [
'components'
],
COMPONENTS_KEYS = [
'schemas',
'responses',
'parameters',
'examples',
'requestBodies',
'headers',
'securitySchemes',
'links',
'callbacks',
'pathItems'
];
module.exports = {
RULES_31: {
CONTAINERS,
DEFINITIONS,
COMPONENTS_KEYS,
INLINE,
ROOT_CONTAINERS_KEYS
}
};

View File

@@ -6,7 +6,9 @@ const VERSION_30 = { key: 'openapi', version: '3.0' },
DEFAULT_SPEC_VERSION = VERSION_30.version,
SWAGGER_VERSION = VERSION_20.version,
VERSION_3_1 = VERSION_31.version,
fs = require('fs');
fs = require('fs'),
{ RULES_30 } = require('./../bundleRules/spec30'),
{ RULES_31 } = require('./../bundleRules/spec31');
/**
* gets the version key and the version and generates a regular expression that
@@ -268,6 +270,21 @@ function validateSupportedVersion(version) {
return isValid !== undefined;
}
/**
* Return the bundling rules to follow in the bundling process
* @param {string} version The spec version we are bundling
* @returns {object} The bundling rules related with the spec
*/
function getBundleRulesDataByVersion(version) {
const is31 = compareVersion(version, VERSION_31.version);
if (is31) {
return RULES_31;
}
else {
return RULES_30;
}
}
module.exports = {
getSpecVersion,
getConcreteSchemaUtils,
@@ -277,5 +294,6 @@ module.exports = {
getVersionRegexBySpecificationVersion,
SWAGGER_VERSION,
VERSION_3_1,
validateSupportedVersion
validateSupportedVersion,
getBundleRulesDataByVersion
};

View File

@@ -6,9 +6,11 @@ const slashes = /\//g,
escapedTilde = /~0/g,
jsonPointerLevelSeparator = '/',
escapedTildeString = '~0',
{ getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'),
{ getKeyInComponents31 } = require('./31XUtils/componentsParentMatcher'),
{ VERSION_3_1 } = require('./common/versionUtils');
{ getBundleRulesDataByVersion } = require('./common/versionUtils'),
{
resolveFirstLevelChild,
resolveSecondLevelChild
} = require('./bundleRules/resolvers');
/**
* Encodes a filepath name so it can be a json pointer
@@ -51,15 +53,44 @@ function jsonPointerDecodeAndReplace(filePathName) {
*/
function getKeyInComponents(traceFromParent, filePathName, localPath, version) {
const localPart = localPath ? `${localPointer}${localPath}` : '',
is31 = version === VERSION_3_1;
let result;
{
CONTAINERS,
DEFINITIONS,
COMPONENTS_KEYS,
INLINE,
ROOT_CONTAINERS_KEYS
} = getBundleRulesDataByVersion(version);
let result,
trace = [
...traceFromParent,
jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)
].reverse(),
traceToKey = [],
matchFound = false,
isRootAndReusableItemsContainer = ROOT_CONTAINERS_KEYS.includes(traceFromParent[0]);
if (is31) {
result = getKeyInComponents31(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace);
if (isRootAndReusableItemsContainer) {
return [];
}
else {
result = getKeyInComponents30(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace);
for (let [index, item] of trace.entries()) {
let itemShouldBeResolvedInline = INLINE.includes(item);
if (itemShouldBeResolvedInline) {
matchFound = false;
break;
}
item = resolveFirstLevelChild(item, CONTAINERS);
resolveSecondLevelChild(trace, index, DEFINITIONS);
traceToKey.push(item);
if (COMPONENTS_KEYS.includes(item)) {
matchFound = true;
break;
}
}
result = matchFound ?
traceToKey.reverse() :
[];
return result.map(generateObjectName);
}