From caf725e9978874aefe602e907071c897959ea10d Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 1 Jun 2022 12:17:53 -0500 Subject: [PATCH 01/56] Adding new tests for 2.0 version --- lib/30XUtils/componentsParentMatcher.js | 59 +++++ lib/bundle.js | 66 +++--- lib/jsonPointer.js | 84 ++----- lib/schemaUtils.js | 20 +- lib/swaggerUtils/componentParentMatcher.js | 47 ++++ .../basicExample/bundleExpected.json | 29 +++ .../basicExample/bundleExpected.json | 29 +++ .../swagger20/basicExample/index.yaml | 9 + .../swagger20/basicExample/info.yaml | 3 + .../swagger20/basicExample/paths.yaml | 9 + .../nestedLocalRef/bundleExpected.json | 66 ++++++ .../swagger20/nestedLocalRef/index.yaml | 18 ++ .../swagger20/nestedLocalRef/info.yaml | 3 + .../swagger20/nestedLocalRef/paths.yaml | 9 + .../nestedLocalRef/schemas/favorite_food.yaml | 11 + .../swagger20/nestedLocalRef/schemas/pet.yaml | 16 ++ .../swagger20/nestedRefs/bundleExpected.json | 63 ++++++ .../swagger20/nestedRefs/index.yaml | 18 ++ .../swagger20/nestedRefs/info.yaml | 3 + .../swagger20/nestedRefs/paths.yaml | 9 + .../nestedRefs/schemas/favorite_food.yaml | 8 + .../swagger20/nestedRefs/schemas/pet.yaml | 14 ++ .../swagger20/simpleRef/bundleExpected.json | 52 +++++ .../swagger20/simpleRef/index.yaml | 18 ++ .../swagger20/simpleRef/info.yaml | 3 + .../swagger20/simpleRef/paths.yaml | 9 + .../swagger20/simpleRef/pet.yaml | 12 + .../bundleExpected.json | 82 +++++++ .../withParametersAndItems/index.yaml | 9 + .../withParametersAndItems/info.yaml | 3 + .../parameters/parameters.yaml | 16 ++ .../withParametersAndItems/paths.yaml | 14 ++ .../swagger20/withParametersAndItems/pet.yaml | 12 + test/unit/bundle.test.js | 86 ++++++- test/unit/bundle20.test.js | 212 ++++++++++++++++++ test/unit/jsonPointer.test.js | 39 ++-- 36 files changed, 1033 insertions(+), 127 deletions(-) create mode 100644 lib/30XUtils/componentsParentMatcher.js create mode 100644 lib/swaggerUtils/componentParentMatcher.js create mode 100644 test/data/swaggerMultifile/basicExample/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/basicExample/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/basicExample/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/basicExample/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/basicExample/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/favorite_food.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/schemas/favorite_food.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedRefs/schemas/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/simpleRef/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/simpleRef/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/simpleRef/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/simpleRef/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/parameters/parameters.yaml create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/withParametersAndItems/pet.yaml create mode 100644 test/unit/bundle20.test.js diff --git a/lib/30XUtils/componentsParentMatcher.js b/lib/30XUtils/componentsParentMatcher.js new file mode 100644 index 0000000..4da8c5a --- /dev/null +++ b/lib/30XUtils/componentsParentMatcher.js @@ -0,0 +1,59 @@ +const COMPONENTS_KEYS_30 = [ + 'schemas', + 'responses', + 'parameters', + 'examples', + 'requestBodies', + 'headers', + 'securitySchemes', + 'links', + 'callbacks' + ], + SCHEMA_CONTAINERS = [ + 'allOf', + 'oneOf', + 'anyOf', + 'not', + 'additionalProperties', + 'items', + 'schema' + ]; + +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 + * @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 item of trace) { + if (SCHEMA_CONTAINERS.includes(item)) { + item = 'schemas'; + } + traceToKey.push(item); + if (COMPONENTS_KEYS_30.includes(item)) { + matchFound = true; + break; + } + } + return matchFound ? + traceToKey.reverse() : + []; + }, + COMPONENTS_KEYS_30 +}; diff --git a/lib/bundle.js b/lib/bundle.js index b1a90d9..b33866d 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -1,4 +1,5 @@ -const { +const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), + { isExtRef, getKeyInComponents, getJsonPointerRelationToRoot, @@ -9,24 +10,13 @@ const { isLocalRef } = require('./jsonPointer'), traverseUtility = require('traverse'), - parse = require('./parse.js'); + parse = require('./parse.js'), + { COMPONENTS_KEYS_20 } = require('./swaggerUtils/componentParentMatcher'); let path = require('path'), pathBrowserify = require('path-browserify'), BROWSER = 'browser', { DFS } = require('./dfs'), - COMPONENTS_KEYS = [ - 'schemas', - 'schema', - 'responses', - 'parameters', - 'examples', - 'requestBodies', - 'headers', - 'securitySchemes', - 'links', - 'callbacks' - ], deref = require('./deref.js'); @@ -147,9 +137,11 @@ function getContentFromTrace(content, partial) { * @param {array} keyInComponents - The trace to the key in components * @param {object} components - A global components object * @param {object} value - The value from node matched with data + * @param {string} version - The current version * @returns {null} It modifies components global context */ -function setValueInComponents(keyInComponents, components, value) { +function setValueInComponents(keyInComponents, components, value, version) { + const COMPONENTS_KEYS = version === '2.0' ? COMPONENTS_KEYS_20 : COMPONENTS_KEYS_30; let currentPlace = components, target = keyInComponents[keyInComponents.length - 2], key = keyInComponents.length === 2 && COMPONENTS_KEYS.includes(keyInComponents[0]) ? @@ -181,9 +173,10 @@ function setValueInComponents(keyInComponents, components, value) { * Return a trace from the current node's root to the place where we find a $ref * @param {object} nodeContext - The current node we are processing * @param {object} property - The current property that contains the $ref + * @param {string} version - The current version of the spec * @returns {array} The trace to the place where the $ref appears */ -function getTraceFromParentKeyInComponents(nodeContext, property) { +function getTraceFromParentKeyInComponents(nodeContext, property, version) { const parents = [...nodeContext.parents].reverse(), isArrayKeyRegexp = new RegExp('^\\d$', 'g'), key = nodeContext.key, @@ -196,8 +189,8 @@ function getTraceFromParentKeyInComponents(nodeContext, property) { [key, ...parentKeys], nodeTrace = getRootFileTrace(nodeParentsKey), [file, local] = property.split(localPointer), - [keyTraceInComponents, inComponents] = getKeyInComponents(nodeTrace, file, local); - return [keyTraceInComponents, inComponents]; + keyTraceInComponents = getKeyInComponents(nodeTrace, file, local, version); + return keyTraceInComponents; } /** @@ -206,11 +199,10 @@ function getTraceFromParentKeyInComponents(nodeContext, property) { * @param {Function} isOutOfRoot - A filter to know if the current components was called from root or not * @param {Function} pathSolver - function to resolve the Path * @param {string} parentFilename - The parent's filename - * @param {object} globalComponentsContext - The global context from root file - * @param {array} allData The data from files provided in the input - * @returns {object} - {path : $ref value} + * @param {object} version - The version of the spec we are bundling + * @returns {object} - The references in current node and the new content from the node */ -function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename) { +function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version) { let referencesInNode = [], nodeReferenceDirectory = {}; traverseUtility(currentNode).forEach(function (property) { @@ -227,11 +219,12 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename) { ); if (hasReferenceTypeKey) { const tempRef = calculatePath(parentFilename, property.$ref), - [nodeTrace] = getTraceFromParentKeyInComponents(this, tempRef), + nodeTrace = getTraceFromParentKeyInComponents(this, tempRef, version), referenceInDocument = getJsonPointerRelationToRoot( jsonPointerEncodeAndReplace, tempRef, - nodeTrace + nodeTrace, + version ), traceToParent = [...this.parents.map((item) => { return item.key; @@ -270,9 +263,10 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename) { * @param {object} currentNode - current { path, content} object * @param {Array} allData - array of { path, content} objects * @param {object} specRoot - root file information + * @param {string} version - The current version * @returns {object} - Detect root files result object */ -function getNodeContentAndReferences (currentNode, allData, specRoot) { +function getNodeContentAndReferences (currentNode, allData, specRoot, version) { let graphAdj = [], missingNodes = [], nodeContent; @@ -288,7 +282,8 @@ function getNodeContentAndReferences (currentNode, allData, specRoot) { nodeContent, currentNode.fileName !== specRoot.fileName, removeLocalReferenceFromPath, - currentNode.fileName + currentNode.fileName, + version ); referencesInNode.forEach((reference) => { @@ -318,9 +313,10 @@ function getNodeContentAndReferences (currentNode, allData, specRoot) { * @param {object} rootContent - The root's parsed content * @param {function} refTypeResolver - The resolver function to test if node has a reference * @param {object} components - The global components object + * @param {string} version - The current version * @returns {object} The components object related to the file */ -function generateComponentsObject (documentContext, rootContent, refTypeResolver, components) { +function generateComponentsObject (documentContext, rootContent, refTypeResolver, components, version) { [rootContent, components].forEach((contentData) => { traverseUtility(contentData).forEach(function (property) { if (property) { @@ -366,7 +362,8 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver setValueInComponents( refData.keyInComponents, components, - refData.nodeContent + refData.nodeContent, + version ); } } @@ -383,9 +380,10 @@ module.exports = { * @param {object} specRoot - root file information * @param {Array} allData - array of { path, content} objects * @param {Array} origin - process origin (BROWSER or node) + * @param {string} version - The version we are using * @returns {object} - Detect root files result object */ - getBundleContentAndComponents: function (specRoot, allData, origin) { + getBundleContentAndComponents: function (specRoot, allData, origin, version) { if (origin === BROWSER) { path = pathBrowserify; } @@ -394,12 +392,18 @@ module.exports = { rootContextData; rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { - return getNodeContentAndReferences(currentNode, allData, specRoot); + return getNodeContentAndReferences(currentNode, allData, specRoot, version); }); if (specRoot.parsed.oasObject.hasOwnProperty('components')) { components = specRoot.parsed.oasObject.components; } - generateComponentsObject(rootContextData, rootContextData.nodeContents[specRoot.fileName], isExtRef, components); + generateComponentsObject( + rootContextData, + rootContextData.nodeContents[specRoot.fileName], + isExtRef, + components, + version + ); return { fileContent: rootContextData.nodeContents[specRoot.fileName], components diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 44cba59..1489f8e 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -1,4 +1,5 @@ -const slashes = /\//g, +const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher'), + slashes = /\//g, tildes = /~/g, escapedSlash = /~1/g, escapedSlashString = '~1', @@ -6,26 +7,7 @@ const slashes = /\//g, escapedTilde = /~0/g, jsonPointerLevelSeparator = '/', escapedTildeString = '~0', - COMPONENTS_KEYS = [ - 'schemas', - 'responses', - 'parameters', - 'examples', - 'requestBodies', - 'headers', - 'securitySchemes', - 'links', - 'callbacks' - ], - SCHEMA_PARENT_KEYS_IN_DOC = [ - 'allOf', - 'oneOf', - 'anyOf', - 'not', - 'additionalProperties', - 'items', - 'schema' - ]; + { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'); /** * Encodes a filepath name so it can be a json pointer @@ -52,49 +34,21 @@ function jsonPointerDecodeAndReplace(filePathName) { * @param {string} traceFromParent the node trace from root. * @param {string} filePathName the filePathName of the file * @param {string} localPath the local path that the pointer will reach +* @param {string} version - The current spec version * @returns {Array} - the calculated keys in an array representing each nesting property name */ -function getKeyInComponents(traceFromParent, filePathName, localPath) { - const localPart = localPath ? `${localPointer}${localPath}` : ''; - let res = traceFromParent, - trace = [], - traceToKey = [], - matchFound = false, - inComponents = false; +function getKeyInComponents(traceFromParent, filePathName, localPath, version) { + const localPart = localPath ? `${localPointer}${localPath}` : '', + is20 = version === '2.0'; + let result; - if (traceFromParent[0] === 'components') { - inComponents = true; - return [[], inComponents]; + if (is20) { + result = getKeyInComponents20(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } - - res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); - trace = [...res].reverse(); - - for (let item of trace) { - if (SCHEMA_PARENT_KEYS_IN_DOC.includes(item)) { - item = 'schemas'; - } - traceToKey.push(item); - if (COMPONENTS_KEYS.includes(item)) { - matchFound = true; - break; - } + else { + result = getKeyInComponents30(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } - return [matchFound ? - traceToKey.reverse() : - [], inComponents]; -} - -/** -* returns the local path of a pointer #/definitions/dog etc. -* @param {string} jsonPointer the complet pointer -* @returns {string} - the calculated key -*/ -function getLocalPath(jsonPointer) { - if (jsonPointer.includes(localPointer)) { - return jsonPointer.split(localPointer)[1]; - } - return ''; + return result; } /** @@ -102,13 +56,14 @@ function getLocalPath(jsonPointer) { * @constructor * @param {Function} encodeFunction function to encode url * @param {string} traceFromParent the trace from parent. +* @param {string} targetInRoot - The root element where we will point * @returns {string} - the concatenated json pointer */ -function concatJsonPointer(encodeFunction, traceFromParent) { +function concatJsonPointer(encodeFunction, traceFromParent, targetInRoot) { const traceFromParentAsString = traceFromParent.map((trace) => { return encodeFunction(trace); }).join('/'); - return localPointer + '/components' + jsonPointerLevelSeparator + traceFromParentAsString; + return localPointer + targetInRoot + jsonPointerLevelSeparator + traceFromParentAsString; } /** @@ -117,14 +72,15 @@ function concatJsonPointer(encodeFunction, traceFromParent) { * @param {Function} encodeFunction function to encode url * @param {string} refValue the type of component e.g. schemas, parameters, etc. * @param {string} traceFromKey the trace from the parent node. +* @param {string} version - The version we are working on * @returns {string} - the concatenated json pointer */ -function getJsonPointerRelationToRoot(encodeFunction, refValue, traceFromKey) { +function getJsonPointerRelationToRoot(encodeFunction, refValue, traceFromKey, version) { + let targetInRoot = version === '2.0' ? '' : '/components'; if (refValue.startsWith(localPointer)) { return refValue; } - const localPath = getLocalPath(refValue); - return concatJsonPointer(encodeFunction, traceFromKey, localPath); + return concatJsonPointer(encodeFunction, traceFromKey, targetInRoot); } /** diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 80ff378..61749db 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4828,13 +4828,20 @@ module.exports = { return data; }, - getBundledFileData(parsedRootFiles, inputData, origin, format) { + getBundledFileData(parsedRootFiles, inputData, origin, format, version) { const data = parsedRootFiles.map((root) => { - let bundleData = getBundleContentAndComponents(root, inputData, origin); + let bundleData = getBundleContentAndComponents(root, inputData, origin, version); return bundleData; }); let bundledFile = data[0].fileContent; - bundledFile.components = data[0].components; + if (version === '2.0') { + Object.entries(data[0].components).forEach(([key, value]) => { + bundledFile[key] = value; + }); + } + else { + bundledFile.components = data[0].components; + } if (format === parse.YAML_FORMAT) { bundledFile = parse.toYAML(bundledFile); } @@ -4863,11 +4870,14 @@ module.exports = { let parsedContent = this.parseFileOrThrow(rootFile.content); return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent }; }).filter((rootWithParsedContent) => { + let fileVersion = version === '2.0' ? + rootWithParsedContent.parsed.oasObject.swagger : + rootWithParsedContent.parsed.oasObject.openapi; bundleFormat = bundleFormat ? bundleFormat : rootWithParsedContent.parsed.inputFormat; - return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi); + return compareVersion(version, fileVersion); }), data = toBundle ? - this.getBundledFileData(parsedRootFiles, inputData, origin, bundleFormat.toLowerCase()) : + this.getBundledFileData(parsedRootFiles, inputData, origin, bundleFormat.toLowerCase(), version) : this.getRelatedFilesData(parsedRootFiles, inputData, origin); return data; diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js new file mode 100644 index 0000000..2d95b47 --- /dev/null +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -0,0 +1,47 @@ +const COMPONENTS_KEYS_20 = [ + 'definitions', + 'parameters', + 'responses' + ], + SCHEMA_PARENTS = [ + 'schema', + 'items', + 'allOf', + 'additionalProperties' + ]; + +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 +* @returns {array} The trace to the container key +*/ + getKeyInComponents20: function (traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace) { + let res = traceFromParent, + trace = [], + traceToKey = [], + matchFound = false, + isRootKey = false; + + res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); + trace = [...res].reverse(); + + for (let [index, item] of trace.entries()) { + if (SCHEMA_PARENTS.includes(item)) { + item = 'definitions'; + } + traceToKey.push(item); + if (COMPONENTS_KEYS_20.includes(item)) { + matchFound = true; + isRootKey = trace[index + 1] === undefined; + break; + } + } + return matchFound && !isRootKey ? + traceToKey.reverse() : + []; + }, + COMPONENTS_KEYS_20 +}; diff --git a/test/data/swaggerMultifile/basicExample/bundleExpected.json b/test/data/swaggerMultifile/basicExample/bundleExpected.json new file mode 100644 index 0000000..f7cc6f3 --- /dev/null +++ b/test/data/swaggerMultifile/basicExample/bundleExpected.json @@ -0,0 +1,29 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/basicExample/bundleExpected.json b/test/data/toBundleExamples/swagger20/basicExample/bundleExpected.json new file mode 100644 index 0000000..f7cc6f3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/basicExample/bundleExpected.json @@ -0,0 +1,29 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/basicExample/index.yaml b/test/data/toBundleExamples/swagger20/basicExample/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/toBundleExamples/swagger20/basicExample/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/basicExample/info.yaml b/test/data/toBundleExamples/swagger20/basicExample/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/basicExample/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/basicExample/paths.yaml b/test/data/toBundleExamples/swagger20/basicExample/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/basicExample/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json new file mode 100644 index 0000000..5ab6064 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json @@ -0,0 +1,66 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/definitions/~1schemas~1pet.yaml" + } + } + } + } + } + }, + "definitions": { + "/schemas/pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "favoriteFood": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" + } + } + }, + "foodPrice": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/index.yaml b/test/data/toBundleExamples/swagger20/nestedLocalRef/index.yaml new file mode 100644 index 0000000..75cbdc0 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/index.yaml @@ -0,0 +1,18 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "./schemas/pet.yaml" diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/info.yaml b/test/data/toBundleExamples/swagger20/nestedLocalRef/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/paths.yaml b/test/data/toBundleExamples/swagger20/nestedLocalRef/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/favorite_food.yaml b/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/favorite_food.yaml new file mode 100644 index 0000000..331fbe2 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/favorite_food.yaml @@ -0,0 +1,11 @@ +FavoriteFood: + type: object + properties: + name: + type: + string + brand: + type: + string +Price: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/pet.yaml b/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/pet.yaml new file mode 100644 index 0000000..c34b1ef --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/schemas/pet.yaml @@ -0,0 +1,16 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + favoriteFood: + $ref: "./favorite_food.yaml#/FavoriteFood" + foodPrice: + $ref: "./favorite_food.yaml#/Price" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json new file mode 100644 index 0000000..c5d8dbd --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json @@ -0,0 +1,63 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/definitions/~1schemas~1pet.yaml" + } + } + } + } + } + }, + "definitions": { + "/schemas/pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "favoriteFood": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/index.yaml b/test/data/toBundleExamples/swagger20/nestedRefs/index.yaml new file mode 100644 index 0000000..75cbdc0 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/index.yaml @@ -0,0 +1,18 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "./schemas/pet.yaml" diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/info.yaml b/test/data/toBundleExamples/swagger20/nestedRefs/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/paths.yaml b/test/data/toBundleExamples/swagger20/nestedRefs/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/schemas/favorite_food.yaml b/test/data/toBundleExamples/swagger20/nestedRefs/schemas/favorite_food.yaml new file mode 100644 index 0000000..32b48e5 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/schemas/favorite_food.yaml @@ -0,0 +1,8 @@ +type: object +properties: + name: + type: + string + brand: + type: + string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/schemas/pet.yaml b/test/data/toBundleExamples/swagger20/nestedRefs/schemas/pet.yaml new file mode 100644 index 0000000..41e7d3c --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedRefs/schemas/pet.yaml @@ -0,0 +1,14 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + favoriteFood: + $ref: "./favorite_food.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json new file mode 100644 index 0000000..d0a7b42 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json @@ -0,0 +1,52 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/definitions/~1pet.yaml" + } + } + } + } + } + }, + "definitions": { + "/pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/simpleRef/index.yaml b/test/data/toBundleExamples/swagger20/simpleRef/index.yaml new file mode 100644 index 0000000..466509a --- /dev/null +++ b/test/data/toBundleExamples/swagger20/simpleRef/index.yaml @@ -0,0 +1,18 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "./pet.yaml" diff --git a/test/data/toBundleExamples/swagger20/simpleRef/info.yaml b/test/data/toBundleExamples/swagger20/simpleRef/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/simpleRef/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/simpleRef/paths.yaml b/test/data/toBundleExamples/swagger20/simpleRef/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/simpleRef/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/simpleRef/pet.yaml b/test/data/toBundleExamples/swagger20/simpleRef/pet.yaml new file mode 100644 index 0000000..dcdc8a0 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/simpleRef/pet.yaml @@ -0,0 +1,12 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json b/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json new file mode 100644 index 0000000..229d1f3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json @@ -0,0 +1,82 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to", + "operationId": "findPets", + "parameters": [ + { + "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter1" + }, + { + "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter2" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1pet.yaml" + } + } + } + } + } + } + }, + "parameters": { + "/parameters/parameters.yaml#/Parameter1": { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "type": "array", + "collectionFormat": "csv", + "items": { + "type": "string" + } + }, + "/parameters/parameters.yaml#/Parameter2": { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "type": "integer", + "format": "int32" + } + }, + "definitions": { + "/pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/index.yaml b/test/data/toBundleExamples/swagger20/withParametersAndItems/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/info.yaml b/test/data/toBundleExamples/swagger20/withParametersAndItems/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/parameters/parameters.yaml b/test/data/toBundleExamples/swagger20/withParametersAndItems/parameters/parameters.yaml new file mode 100644 index 0000000..b6a3840 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/parameters/parameters.yaml @@ -0,0 +1,16 @@ +Parameter1: + name: tags + in: query + description: tags to filter by + required: false + type: array + collectionFormat: csv + items: + type: string +Parameter2: + name: limit + in: query + description: maximum number of results to return + required: false + type: integer + format: int32 \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/paths.yaml b/test/data/toBundleExamples/swagger20/withParametersAndItems/paths.yaml new file mode 100644 index 0000000..d67649e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/paths.yaml @@ -0,0 +1,14 @@ +/pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + parameters: + - $ref: "./parameters/parameters.yaml#/Parameter1" + - $ref: "./parameters/parameters.yaml#/Parameter2" + responses: + 200: + description: pet response + schema: + type: array + items: + $ref: "./pet.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/pet.yaml b/test/data/toBundleExamples/swagger20/withParametersAndItems/pet.yaml new file mode 100644 index 0000000..dcdc8a0 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/pet.yaml @@ -0,0 +1,12 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 2cdb984..5940726 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -22,8 +22,10 @@ let expect = require('chai').expect, refTags = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags'), refInfo = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info'), refPaths = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths'), - refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'); - + refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), + SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', + basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), + simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -748,10 +750,8 @@ describe('bundle files method - 3.0', function () { }); }); - describe('getReferences method when node does not have any reference', function() { - it('Should return ' + - ' - schema_from_response', function() { + it('Should return reference data empty if there are not any reference', function() { const userData = 'type: object\n' + 'properties:\n' + ' id:\n' + @@ -771,8 +771,7 @@ describe('getReferences method when node does not have any reference', function( expect(Object.keys(result.nodeReferenceDirectory).length).to.equal(0); }); - it('Should return ' + - ' - schema_from_response', function() { + it('Should return the reference data - schema_from_response', function() { const userData = 'User:\n' + ' $ref: \"./user.yaml\"\n' + '\n' + @@ -807,3 +806,76 @@ describe('getReferences method when node does not have any reference', function( expect(result.referencesInNode[0].newValue.$ref).to.equal('the/parent/user.yaml'); }); }); + +describe('bundle files method - 2.0', function() { + it('Should return bundled result from - basicExample', async function() { + let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), + info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), + paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), + expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - simpleRef', async function() { + let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); +}); diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js new file mode 100644 index 0000000..a4e5a04 --- /dev/null +++ b/test/unit/bundle20.test.js @@ -0,0 +1,212 @@ +const expect = require('chai').expect, + Converter = require('../../index.js'), + fs = require('fs'), + path = require('path'), + SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', + basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), + nestedRefs = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedRefs'), + nestedLocalRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedLocalRef'), + withParametersAndItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/withParametersAndItems'), + simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); + +describe('bundle files method - 2.0', function() { + it('Should return bundled result from - withParametersAndItems', async function() { + let contentRootFile = fs.readFileSync(withParametersAndItems + '/index.yaml', 'utf8'), + info = fs.readFileSync(withParametersAndItems + '/info.yaml', 'utf8'), + paths = fs.readFileSync(withParametersAndItems + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(withParametersAndItems + '/pet.yaml', 'utf8'), + parameters = fs.readFileSync(withParametersAndItems + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(withParametersAndItems + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedLocalRef', async function() { + let contentRootFile = fs.readFileSync(nestedLocalRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedLocalRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedLocalRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedLocalRef + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedLocalRef + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedLocalRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedRefs', async function() { + let contentRootFile = fs.readFileSync(nestedRefs + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedRefs + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedRefs + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedRefs + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedRefs + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedRefs + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - basicExample', async function() { + let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), + info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), + paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), + expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - simpleRef', async function() { + let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); +}); diff --git a/test/unit/jsonPointer.test.js b/test/unit/jsonPointer.test.js index 64b06d3..2742680 100644 --- a/test/unit/jsonPointer.test.js +++ b/test/unit/jsonPointer.test.js @@ -6,27 +6,21 @@ const expect = require('chai').expect, getKeyInComponents } = require('./../../lib/jsonPointer'); describe('getKeyInComponents function', function () { - it('should return [[], true] when is pointing to an element in components', function () { - const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml'); - expect(result).to.be.an('array').with.length(2); - expect(result[0].length).to.equal(0); - expect(result[1]).to.equal(true); + it('should return [] when is pointing to an element in components', function () { + const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '', '3.0'); + expect(result).to.be.an('array').with.length(0); }); - it('should return [[], true] when is pointing to a local ref in components', + it('should return [] when is pointing to a local ref in components', function () { - const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world'); - expect(result).to.be.an('array').with.length(2); - expect(result[0].length).to.equal(0); - expect(result[1]).to.equal(true); + const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world', '3.0'); + expect(result).to.be.an('array').with.length(0); }); - it('should return [["schemas", "folder/pet.yaml"], false] when there is an scaped slash', function () { - const result = getKeyInComponents(['path', 'schemas'], 'folder~1pet.yaml'); + it('should return ["schemas", "folder/pet.yaml"] when there is an scaped slash', function () { + const result = getKeyInComponents(['path', 'schemas'], 'folder~1pet.yaml', '', '3.0'); expect(result).to.be.an('array').with.length(2); - expect(result[0].length).to.equal(2); - expect(result[0][0]).to.equal('schemas'); - expect(result[1]).to.equal(false); + expect(result[0]).to.equal('schemas'); }); }); @@ -62,7 +56,8 @@ describe('concatJsonPointer function ', function () { it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () { let res = concatJsonPointer( jsonPointerEncodeAndReplace, - ['schemas', 'Pets.yaml'] + ['schemas', 'Pets.yaml'], + '/components' ); expect(res).to.equal('#/components/schemas/Pets.yaml'); }); @@ -70,21 +65,24 @@ describe('concatJsonPointer function ', function () { it('should return "#/components/schemas/other~1Pets.yaml" no local path and schema folder in filename', function () { let res = concatJsonPointer( jsonPointerEncodeAndReplace, - ['schemas', 'other/Pets.yaml'] + ['schemas', 'other/Pets.yaml'], + '/components' ); expect(res).to.equal('#/components/schemas/other~1Pets.yaml'); }); it('should return "#/components/schemas/some~1Pet" no local path and schema folder in filename', function () { let res = concatJsonPointer( jsonPointerEncodeAndReplace, - ['schemas', 'some/Pet.yaml'] + ['schemas', 'some/Pet.yaml'], + '/components' ); expect(res).to.equal('#/components/schemas/some~1Pet.yaml'); }); it('should return "#/components/schemas/hello.yaml" no local path and schema', function () { let res = concatJsonPointer( jsonPointerEncodeAndReplace, - ['schemas', 'hello.yaml'] + ['schemas', 'hello.yaml'], + '/components' ); expect(res).to.equal('#/components/schemas/hello.yaml'); }); @@ -92,7 +90,8 @@ describe('concatJsonPointer function ', function () { it('should return "#/components/schemas/~1Pets.yaml" no local path and schema', function () { let res = concatJsonPointer( jsonPointerEncodeAndReplace, - ['schemas', '/Pets.yaml'] + ['schemas', '/Pets.yaml'], + '/components' ); expect(res).to.equal('#/components/schemas/~1Pets.yaml'); }); From aee2d3d325278796214aad861e1bc597ea761abe Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:27:16 -0500 Subject: [PATCH 02/56] Added test for 2.0 referenced properties from root Added test for 2.0 referenced properties from root --- lib/schemaUtils.js | 3 +- .../referenced_info_2_0/expected.json | 78 ++++++++++++++++ .../referenced_info_2_0/info/info.yaml | 11 +++ .../referenced_info_2_0/root.yaml | 42 +++++++++ .../referenced_tags_2_0/expected.json | 89 +++++++++++++++++++ .../referenced_tags_2_0/root.yaml | 54 +++++++++++ .../referenced_tags_2_0/tags/tags.yaml | 6 ++ test/unit/bundle.test.js | 61 ++++++++++++- 8 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 test/data/toBundleExamples/referenced_info_2_0/expected.json create mode 100644 test/data/toBundleExamples/referenced_info_2_0/info/info.yaml create mode 100644 test/data/toBundleExamples/referenced_info_2_0/root.yaml create mode 100644 test/data/toBundleExamples/referenced_tags_2_0/expected.json create mode 100644 test/data/toBundleExamples/referenced_tags_2_0/root.yaml create mode 100644 test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 80ff378..28cd0b0 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4864,7 +4864,8 @@ module.exports = { return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent }; }).filter((rootWithParsedContent) => { bundleFormat = bundleFormat ? bundleFormat : rootWithParsedContent.parsed.inputFormat; - return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi); + return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi ? + rootWithParsedContent.parsed.oasObject.openapi : rootWithParsedContent.parsed.oasObject.swagger); }), data = toBundle ? this.getBundledFileData(parsedRootFiles, inputData, origin, bundleFormat.toLowerCase()) : diff --git a/test/data/toBundleExamples/referenced_info_2_0/expected.json b/test/data/toBundleExamples/referenced_info_2_0/expected.json new file mode 100644 index 0000000..ce26f83 --- /dev/null +++ b/test/data/toBundleExamples/referenced_info_2_0/expected.json @@ -0,0 +1,78 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_info_2_0/info/info.yaml b/test/data/toBundleExamples/referenced_info_2_0/info/info.yaml new file mode 100644 index 0000000..938fb64 --- /dev/null +++ b/test/data/toBundleExamples/referenced_info_2_0/info/info.yaml @@ -0,0 +1,11 @@ +version: 1.0.0 +title: Swagger Petstore +description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification +termsOfService: http://swagger.io/terms/ +contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io +license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html diff --git a/test/data/toBundleExamples/referenced_info_2_0/root.yaml b/test/data/toBundleExamples/referenced_info_2_0/root.yaml new file mode 100644 index 0000000..97a29cc --- /dev/null +++ b/test/data/toBundleExamples/referenced_info_2_0/root.yaml @@ -0,0 +1,42 @@ +swagger: '2.0' +info: + "$ref": "./info/info.yaml" +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + description: pet response + schema: + type: array + items: + $ref: '#/definitions/Pet' + default: + description: unexpected error + schema: + $ref: '#/definitions/Error' +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/referenced_tags_2_0/expected.json b/test/data/toBundleExamples/referenced_tags_2_0/expected.json new file mode 100644 index 0000000..4798368 --- /dev/null +++ b/test/data/toBundleExamples/referenced_tags_2_0/expected.json @@ -0,0 +1,89 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "tags": [ + { + "name": "Authorization", + "x-bx-tag": "authorization", + "x-bx-priority": true + }, + { + "name": "Bx Sign", + "x-bx-tag": "sign_requests" + } + ], + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} diff --git a/test/data/toBundleExamples/referenced_tags_2_0/root.yaml b/test/data/toBundleExamples/referenced_tags_2_0/root.yaml new file mode 100644 index 0000000..5eec1ae --- /dev/null +++ b/test/data/toBundleExamples/referenced_tags_2_0/root.yaml @@ -0,0 +1,54 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + description: pet response + schema: + type: array + items: + $ref: '#/definitions/Pet' + default: + description: unexpected error + schema: + $ref: '#/definitions/Error' +tags: + "$ref": "./tags/tags.yaml" +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml b/test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml new file mode 100644 index 0000000..a123fd4 --- /dev/null +++ b/test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml @@ -0,0 +1,6 @@ +- name: Authorization + x-bx-tag: authorization + x-bx-priority: true + +- name: Bx Sign + x-bx-tag: sign_requests diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 2cdb984..4adbb86 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -22,7 +22,9 @@ let expect = require('chai').expect, refTags = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags'), refInfo = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info'), refPaths = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths'), - refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'); + refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), + refInfo20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info_2_0'), + refTags20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags_2_0'); describe('bundle files method - 3.0', function () { @@ -748,6 +750,63 @@ describe('bundle files method - 3.0', function () { }); }); +describe('bundle files method - 2.0', function () { + it('Should return bundled file with referenced info from root', async function () { + let contentRootFile = fs.readFileSync(refInfo20 + '/root.yaml', 'utf8'), + info = fs.readFileSync(refInfo20 + '/info/info.yaml', 'utf8'), + expected = fs.readFileSync(refInfo20 + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info/info.yaml', + content: info + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled file with referenced tags from root', async function () { + let contentRootFile = fs.readFileSync(refTags20 + '/root.yaml', 'utf8'), + tags = fs.readFileSync(refTags20 + '/tags/tags.yaml', 'utf8'), + expected = fs.readFileSync(refTags20 + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/tags/tags.yaml', + content: tags + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); +}); describe('getReferences method when node does not have any reference', function() { it('Should return ' + From 080b9c751e235af93de997ce7da82774bab0f368 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:32:23 -0500 Subject: [PATCH 03/56] Update expected.json --- test/data/toBundleExamples/referenced_tags_2_0/expected.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/toBundleExamples/referenced_tags_2_0/expected.json b/test/data/toBundleExamples/referenced_tags_2_0/expected.json index 4798368..cd62dc5 100644 --- a/test/data/toBundleExamples/referenced_tags_2_0/expected.json +++ b/test/data/toBundleExamples/referenced_tags_2_0/expected.json @@ -86,4 +86,4 @@ } } } -} +} \ No newline at end of file From 4bc535ee255331684797bcd404015b6a08553e60 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:41:16 -0500 Subject: [PATCH 04/56] Update bundle.test.js Fix merge error --- test/unit/bundle.test.js | 145 ++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 72 deletions(-) diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 0fb0e77..2a62a70 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -27,7 +27,8 @@ let expect = require('chai').expect, refTags20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags_2_0'), SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), - simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); + simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), + refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -753,6 +754,77 @@ describe('bundle files method - 3.0', function () { }); describe('bundle files method - 2.0', function () { + it('Should return bundled result from - basicExample', async function() { + let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), + info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), + paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), + expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - simpleRef', async function() { + let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + it('Should return bundled file with referenced info from root', async function () { let contentRootFile = fs.readFileSync(refInfo20 + '/root.yaml', 'utf8'), info = fs.readFileSync(refInfo20 + '/info/info.yaml', 'utf8'), @@ -865,75 +937,4 @@ describe('getReferences method when node does not have any reference', function( expect(result.referencesInNode[0].path).to.equal('./user.yaml'); expect(result.referencesInNode[0].newValue.$ref).to.equal('the/parent/user.yaml'); }); - - it('Should return bundled result from - basicExample', async function() { - let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), - info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), - paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), - expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); - - it('Should return bundled result from - simpleRef', async function() { - let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), - info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), - paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), - expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); }); From e43bb9aba61b644b617e8340ae5fb493fe9a3cc2 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:00:30 -0500 Subject: [PATCH 05/56] added path referenced from root 2.0 added path referenced from root 2.0 --- .../referenced_paths_2_0/expected.json | 107 ++++++++++++++++++ .../referenced_paths_2_0/paths/path.yaml | 31 +++++ .../referenced_paths_2_0/paths/paths.yaml | 3 + .../referenced_paths_2_0/root.yaml | 38 +++++++ test/unit/bundle.test.js | 35 +++++- 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/referenced_paths_2_0/expected.json create mode 100644 test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml create mode 100644 test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml create mode 100644 test/data/toBundleExamples/referenced_paths_2_0/root.yaml diff --git a/test/data/toBundleExamples/referenced_paths_2_0/expected.json b/test/data/toBundleExamples/referenced_paths_2_0/expected.json new file mode 100644 index 0000000..16927f5 --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_2_0/expected.json @@ -0,0 +1,107 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml b/test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml new file mode 100644 index 0000000..dab6ca5 --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml @@ -0,0 +1,31 @@ +description: Returns all pets alesuada ac... +operationId: findPets +responses: + "200": + description: pet response + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + default: + description: unexpected error + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml b/test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml new file mode 100644 index 0000000..e826f5c --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml @@ -0,0 +1,3 @@ +/pets: + get: + "$ref": "./path.yaml" diff --git a/test/data/toBundleExamples/referenced_paths_2_0/root.yaml b/test/data/toBundleExamples/referenced_paths_2_0/root.yaml new file mode 100644 index 0000000..6652cb6 --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_2_0/root.yaml @@ -0,0 +1,38 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + "$ref": "./paths/paths.yaml" +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 2a62a70..78c932e 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -28,7 +28,7 @@ let expect = require('chai').expect, SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), - refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); + refPaths20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_2_0'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -880,6 +880,39 @@ describe('bundle files method - 2.0', function () { expect(res.result).to.be.true; expect(res.output.data.bundledContent).to.be.equal(expected); }); + + it('Should return bundled file with referenced paths from root', async function () { + let contentRootFile = fs.readFileSync(refPaths20 + '/root.yaml', 'utf8'), + paths = fs.readFileSync(refPaths20 + '/paths/paths.yaml', 'utf8'), + path = fs.readFileSync(refPaths20 + '/paths/path.yaml', 'utf8'), + expected = fs.readFileSync(refPaths20 + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/paths/paths.yaml', + content: paths + }, + { + path: '/paths/path.yaml', + content: path + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 6fa20ccb48670d3cae977f7bdba3f82b8e550cf1 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:05:44 -0500 Subject: [PATCH 06/56] move 2.0 test files to folder move 2.0 test files to folder --- .../referenced_info}/expected.json | 0 .../referenced_info}/info/info.yaml | 0 .../referenced_info}/root.yaml | 0 .../referenced_paths}/expected.json | 0 .../referenced_paths}/paths/path.yaml | 0 .../referenced_paths}/paths/paths.yaml | 0 .../referenced_paths}/root.yaml | 0 .../referenced_tags}/expected.json | 0 .../referenced_tags}/root.yaml | 0 .../referenced_tags}/tags/tags.yaml | 0 test/unit/bundle.test.js | 8 ++++---- 11 files changed, 4 insertions(+), 4 deletions(-) rename test/data/toBundleExamples/{referenced_info_2_0 => swagger20/referenced_info}/expected.json (100%) rename test/data/toBundleExamples/{referenced_info_2_0 => swagger20/referenced_info}/info/info.yaml (100%) rename test/data/toBundleExamples/{referenced_info_2_0 => swagger20/referenced_info}/root.yaml (100%) rename test/data/toBundleExamples/{referenced_paths_2_0 => swagger20/referenced_paths}/expected.json (100%) rename test/data/toBundleExamples/{referenced_paths_2_0 => swagger20/referenced_paths}/paths/path.yaml (100%) rename test/data/toBundleExamples/{referenced_paths_2_0 => swagger20/referenced_paths}/paths/paths.yaml (100%) rename test/data/toBundleExamples/{referenced_paths_2_0 => swagger20/referenced_paths}/root.yaml (100%) rename test/data/toBundleExamples/{referenced_tags_2_0 => swagger20/referenced_tags}/expected.json (100%) rename test/data/toBundleExamples/{referenced_tags_2_0 => swagger20/referenced_tags}/root.yaml (100%) rename test/data/toBundleExamples/{referenced_tags_2_0 => swagger20/referenced_tags}/tags/tags.yaml (100%) diff --git a/test/data/toBundleExamples/referenced_info_2_0/expected.json b/test/data/toBundleExamples/swagger20/referenced_info/expected.json similarity index 100% rename from test/data/toBundleExamples/referenced_info_2_0/expected.json rename to test/data/toBundleExamples/swagger20/referenced_info/expected.json diff --git a/test/data/toBundleExamples/referenced_info_2_0/info/info.yaml b/test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_info_2_0/info/info.yaml rename to test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml diff --git a/test/data/toBundleExamples/referenced_info_2_0/root.yaml b/test/data/toBundleExamples/swagger20/referenced_info/root.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_info_2_0/root.yaml rename to test/data/toBundleExamples/swagger20/referenced_info/root.yaml diff --git a/test/data/toBundleExamples/referenced_paths_2_0/expected.json b/test/data/toBundleExamples/swagger20/referenced_paths/expected.json similarity index 100% rename from test/data/toBundleExamples/referenced_paths_2_0/expected.json rename to test/data/toBundleExamples/swagger20/referenced_paths/expected.json diff --git a/test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml b/test/data/toBundleExamples/swagger20/referenced_paths/paths/path.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_paths_2_0/paths/path.yaml rename to test/data/toBundleExamples/swagger20/referenced_paths/paths/path.yaml diff --git a/test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml b/test/data/toBundleExamples/swagger20/referenced_paths/paths/paths.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_paths_2_0/paths/paths.yaml rename to test/data/toBundleExamples/swagger20/referenced_paths/paths/paths.yaml diff --git a/test/data/toBundleExamples/referenced_paths_2_0/root.yaml b/test/data/toBundleExamples/swagger20/referenced_paths/root.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_paths_2_0/root.yaml rename to test/data/toBundleExamples/swagger20/referenced_paths/root.yaml diff --git a/test/data/toBundleExamples/referenced_tags_2_0/expected.json b/test/data/toBundleExamples/swagger20/referenced_tags/expected.json similarity index 100% rename from test/data/toBundleExamples/referenced_tags_2_0/expected.json rename to test/data/toBundleExamples/swagger20/referenced_tags/expected.json diff --git a/test/data/toBundleExamples/referenced_tags_2_0/root.yaml b/test/data/toBundleExamples/swagger20/referenced_tags/root.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_tags_2_0/root.yaml rename to test/data/toBundleExamples/swagger20/referenced_tags/root.yaml diff --git a/test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml b/test/data/toBundleExamples/swagger20/referenced_tags/tags/tags.yaml similarity index 100% rename from test/data/toBundleExamples/referenced_tags_2_0/tags/tags.yaml rename to test/data/toBundleExamples/swagger20/referenced_tags/tags/tags.yaml diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 78c932e..32cd898 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -22,13 +22,13 @@ let expect = require('chai').expect, refTags = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags'), refInfo = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info'), refPaths = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths'), - refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), - refInfo20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info_2_0'), - refTags20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags_2_0'), SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', + refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), + refInfo20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_info'), + refTags20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_tags'), basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), - refPaths20 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_2_0'); + refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { From d289dd0fdc60c9b35815c679ca050b4d0d4c58f7 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:09:35 -0500 Subject: [PATCH 07/56] Fix defintions replacing instead of merging Fix defintions replacing instead of merging --- lib/bundle.js | 32 +++++-- lib/common/versionUtils.js | 4 +- lib/jsonPointer.js | 8 +- lib/schemaUtils.js | 6 +- lib/swaggerUtils/componentParentMatcher.js | 3 +- .../expected.json | 85 +++++++++++++++++++ .../paths/path.yaml | 13 +++ .../paths/paths.yaml | 3 + .../referenced_paths_local_schema/root.yaml | 38 +++++++++ test/unit/bundle.test.js | 36 +++++++- 10 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/path.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_paths_local_schema/root.yaml diff --git a/lib/bundle.js b/lib/bundle.js index b33866d..489fc8a 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -17,7 +17,8 @@ let path = require('path'), pathBrowserify = require('path-browserify'), BROWSER = 'browser', { DFS } = require('./dfs'), - deref = require('./deref.js'); + deref = require('./deref.js'), + { SWAGGER_VERSION } = require('./common/versionUtils'); /** @@ -141,7 +142,7 @@ function getContentFromTrace(content, partial) { * @returns {null} It modifies components global context */ function setValueInComponents(keyInComponents, components, value, version) { - const COMPONENTS_KEYS = version === '2.0' ? COMPONENTS_KEYS_20 : COMPONENTS_KEYS_30; + const COMPONENTS_KEYS = version === SWAGGER_VERSION ? COMPONENTS_KEYS_20 : COMPONENTS_KEYS_30; let currentPlace = components, target = keyInComponents[keyInComponents.length - 2], key = keyInComponents.length === 2 && COMPONENTS_KEYS.includes(keyInComponents[0]) ? @@ -372,6 +373,29 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver }); } +/** + * Generates the components object wrapper + * @param {object} parsedOasObject The parsed root + * @param {string} version - The current version + * @returns {object} The components object wrapper + */ +function generateComponentsWrapper(parsedOasObject, version) { + let components = {}; + + if (version === SWAGGER_VERSION) { + COMPONENTS_KEYS_20.forEach((property) => { + if (parsedOasObject.hasOwnProperty(property)) { + components[property] = parsedOasObject[property]; + } + }); + } + else if (parsedOasObject.hasOwnProperty('components')) { + components = parsedOasObject.components; + } + + return components; +} + module.exports = { /** * Takes in an spec root file and an array of data files @@ -394,9 +418,7 @@ module.exports = { rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { return getNodeContentAndReferences(currentNode, allData, specRoot, version); }); - if (specRoot.parsed.oasObject.hasOwnProperty('components')) { - components = specRoot.parsed.oasObject.components; - } + components = generateComponentsWrapper(specRoot.parsed.oasObject, version); generateComponentsObject( rootContextData, rootContextData.nodeContents[specRoot.fileName], diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 3b53f76..b6431c7 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -4,6 +4,7 @@ const VERSION_30 = { key: 'openapi', version: '3.0' }, GENERIC_VERSION2 = { key: 'swagger', version: '2.' }, GENERIC_VERSION3 = { key: 'openapi', version: '3.' }, DEFAULT_SPEC_VERSION = VERSION_30.version, + SWAGGER_VERSION = VERSION_20.version, fs = require('fs'); /** @@ -257,5 +258,6 @@ module.exports = { filterOptionsByVersion, isSwagger, compareVersion, - getVersionRegexBySpecificationVersion + getVersionRegexBySpecificationVersion, + SWAGGER_VERSION }; diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 1489f8e..265963f 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -7,7 +7,9 @@ const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher' escapedTilde = /~0/g, jsonPointerLevelSeparator = '/', escapedTildeString = '~0', - { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'); + { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'), + { SWAGGER_VERSION } = require('./common/versionUtils'); + /** * Encodes a filepath name so it can be a json pointer @@ -39,7 +41,7 @@ function jsonPointerDecodeAndReplace(filePathName) { */ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { const localPart = localPath ? `${localPointer}${localPath}` : '', - is20 = version === '2.0'; + is20 = version === SWAGGER_VERSION; let result; if (is20) { @@ -76,7 +78,7 @@ function concatJsonPointer(encodeFunction, traceFromParent, targetInRoot) { * @returns {string} - the concatenated json pointer */ function getJsonPointerRelationToRoot(encodeFunction, refValue, traceFromKey, version) { - let targetInRoot = version === '2.0' ? '' : '/components'; + let targetInRoot = version === SWAGGER_VERSION ? '' : '/components'; if (refValue.startsWith(localPointer)) { return refValue; } diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 61749db..5dfb051 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -6,7 +6,7 @@ const { ParseError } = require('./common/ParseError.js'); const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/schemaUtilsCommon.js'), - { getConcreteSchemaUtils } = require('./common/versionUtils.js'), + { getConcreteSchemaUtils, SWAGGER_VERSION } = require('./common/versionUtils.js'), async = require('async'), sdk = require('postman-collection'), schemaFaker = require('../assets/json-schema-faker.js'), @@ -4834,7 +4834,7 @@ module.exports = { return bundleData; }); let bundledFile = data[0].fileContent; - if (version === '2.0') { + if (version === SWAGGER_VERSION) { Object.entries(data[0].components).forEach(([key, value]) => { bundledFile[key] = value; }); @@ -4870,7 +4870,7 @@ module.exports = { let parsedContent = this.parseFileOrThrow(rootFile.content); return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent }; }).filter((rootWithParsedContent) => { - let fileVersion = version === '2.0' ? + let fileVersion = version === SWAGGER_VERSION ? rootWithParsedContent.parsed.oasObject.swagger : rootWithParsedContent.parsed.oasObject.openapi; bundleFormat = bundleFormat ? bundleFormat : rootWithParsedContent.parsed.inputFormat; diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js index 2d95b47..cc0799f 100644 --- a/lib/swaggerUtils/componentParentMatcher.js +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -1,7 +1,8 @@ const COMPONENTS_KEYS_20 = [ 'definitions', 'parameters', - 'responses' + 'responses', + 'securityDefinitions' ], SCHEMA_PARENTS = [ 'schema', diff --git a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json new file mode 100644 index 0000000..700e64a --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json @@ -0,0 +1,85 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/~1paths~1path.yaml%23~1definitions~1Error" + } + }, + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/definitions/~1paths~1path.yaml%23~1definitions~1Pet" + } + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + }, + "/paths/path.yaml#/definitions/Error": { + "$ref": "#/definitions/Error" + }, + "/paths/path.yaml#/definitions/Pet": { + "$ref": "#/definitions/Pet" + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/path.yaml b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/path.yaml new file mode 100644 index 0000000..ef92b58 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/path.yaml @@ -0,0 +1,13 @@ +description: Returns all pets +content: + application/json: + schema: + $ref: "#/definitions/Error" + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "#/definitions/Pet" diff --git a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/paths.yaml b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/paths.yaml new file mode 100644 index 0000000..e826f5c --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/paths/paths.yaml @@ -0,0 +1,3 @@ +/pets: + get: + "$ref": "./path.yaml" diff --git a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/root.yaml b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/root.yaml new file mode 100644 index 0000000..6652cb6 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/root.yaml @@ -0,0 +1,38 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + "$ref": "./paths/paths.yaml" +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 32cd898..d39a713 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -28,7 +28,8 @@ let expect = require('chai').expect, refTags20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_tags'), basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), - refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'); + refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'), + refPathsRefToLocalSchema20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths_local_schema'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -913,6 +914,39 @@ describe('bundle files method - 2.0', function () { expect(res.result).to.be.true; expect(res.output.data.bundledContent).to.be.equal(expected); }); + + it('Should return bundled file with referenced paths from root - path references local schema', async function () { + let contentRootFile = fs.readFileSync(refPathsRefToLocalSchema20 + '/root.yaml', 'utf8'), + paths = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/paths.yaml', 'utf8'), + path = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/path.yaml', 'utf8'), + expected = fs.readFileSync(refPathsRefToLocalSchema20 + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/paths/paths.yaml', + content: paths + }, + { + path: '/paths/path.yaml', + content: path + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 66419af8d4cb61e952b3d25a0390135ca5853507 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:34:17 -0500 Subject: [PATCH 08/56] Add examples example Add examples example --- lib/30XUtils/componentsParentMatcher.js | 6 ++ .../referenced_examples/examples.yaml | 5 + .../referenced_examples/expected.json | 100 ++++++++++++++++++ .../referenced_examples/root.yaml | 60 +++++++++++ test/unit/bundle.test.js | 31 +++++- 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/referenced_examples/examples.yaml create mode 100644 test/data/toBundleExamples/referenced_examples/expected.json create mode 100644 test/data/toBundleExamples/referenced_examples/root.yaml diff --git a/lib/30XUtils/componentsParentMatcher.js b/lib/30XUtils/componentsParentMatcher.js index 4da8c5a..ef49803 100644 --- a/lib/30XUtils/componentsParentMatcher.js +++ b/lib/30XUtils/componentsParentMatcher.js @@ -17,6 +17,9 @@ const COMPONENTS_KEYS_30 = [ 'additionalProperties', 'items', 'schema' + ], + EXAMPLE_CONTAINERS = [ + 'example' ]; module.exports = { @@ -45,6 +48,9 @@ module.exports = { if (SCHEMA_CONTAINERS.includes(item)) { item = 'schemas'; } + if (EXAMPLE_CONTAINERS.includes(item)) { + item = 'examples'; + } traceToKey.push(item); if (COMPONENTS_KEYS_30.includes(item)) { matchFound = true; diff --git a/test/data/toBundleExamples/referenced_examples/examples.yaml b/test/data/toBundleExamples/referenced_examples/examples.yaml new file mode 100644 index 0000000..c01b2fd --- /dev/null +++ b/test/data/toBundleExamples/referenced_examples/examples.yaml @@ -0,0 +1,5 @@ +foo: + summary: sum + value: + code: 1 + message: test error message diff --git a/test/data/toBundleExamples/referenced_examples/expected.json b/test/data/toBundleExamples/referenced_examples/expected.json new file mode 100644 index 0000000..2067a82 --- /dev/null +++ b/test/data/toBundleExamples/referenced_examples/expected.json @@ -0,0 +1,100 @@ +{ + "openapi": "3.0.2", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "example": { + "$ref": "#/components/examples/~1examples.yaml%23~1foo" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "examples": { + "/examples.yaml#/foo": { + "summary": "sum", + "value": { + "code": 1, + "message": "test error message" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_examples/root.yaml b/test/data/toBundleExamples/referenced_examples/root.yaml new file mode 100644 index 0000000..afb13e9 --- /dev/null +++ b/test/data/toBundleExamples/referenced_examples/root.yaml @@ -0,0 +1,60 @@ + +openapi: "3.0.2" +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Pet" + example: + $ref: "examples.yaml#/foo" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d39a713..0c21423 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -29,7 +29,8 @@ let expect = require('chai').expect, basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'), - refPathsRefToLocalSchema20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths_local_schema'); + refPathsRefToLocalSchema20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths_local_schema'), + refExample = path.join(__dirname, BUNDLES_FOLDER + '/referenced_examples'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -752,6 +753,34 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.data.bundledContent).to.be.equal(expected); }); + + it('Should return bundled file with referenced example', async function () { + let contentRootFile = fs.readFileSync(refExample + '/root.yaml', 'utf8'), + example = fs.readFileSync(refExample + '/examples.yaml', 'utf8'), + expected = fs.readFileSync(refExample + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/examples.yaml', + content: example + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); }); describe('bundle files method - 2.0', function () { From e01f02afb8504c7f899cff117c05cfe18a2bf831 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 1 Jun 2022 17:16:04 -0500 Subject: [PATCH 09/56] Adding swagger 2.0 similar scenarios to 3.0 --- lib/bundle.js | 21 +- .../bundleExpected.json | 70 ++++ .../index.yaml | 10 + .../info.yaml | 3 + .../paths.yaml | 13 + .../pet.yaml | 25 ++ .../bundleExpected.json | 127 +++++++ .../food.yaml | 22 ++ .../index.yaml | 10 + .../info.yaml | 3 + .../parameters/parameters.yaml | 16 + .../paths.yaml | 16 + .../pet.yaml | 29 ++ .../bundleExpected.json | 76 ++++ .../index.yaml | 10 + .../info.yaml | 3 + .../paths.yaml | 13 + .../pet.yaml | 27 ++ .../bundleExpected.json | 92 +++++ .../multipleRefFromRootComponents/index.yaml | 28 ++ .../multipleRefFromRootComponents/info.yaml | 3 + .../parameters/parameters.yaml | 16 + .../multipleRefFromRootComponents/paths.yaml | 9 + .../multipleRefFromRootComponents/pet.yaml | 25 ++ .../bundleExpected.json | 121 ++++++ .../sameRefDifferentSource/index.yaml | 9 + .../sameRefDifferentSource/info.yaml | 3 + .../otherSchemas/client.yaml | 12 + .../otherSchemas/detail.yaml | 10 + .../sameRefDifferentSource/paths.yaml | 22 ++ .../schemas/detail.yaml | 10 + .../sameRefDifferentSource/schemas/user.yaml | 12 + test/unit/bundle.test.js | 359 ++++++++++++++++++ test/unit/bundle20.test.js | 212 ----------- 34 files changed, 1221 insertions(+), 216 deletions(-) create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/food.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/parameters/parameters.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/parameters/parameters.yaml create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/client.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/detail.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/detail.yaml create mode 100644 test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/user.yaml delete mode 100644 test/unit/bundle20.test.js diff --git a/lib/bundle.js b/lib/bundle.js index b33866d..6cb1a74 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -372,6 +372,22 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver }); } +function generateComponentsWrapper(parsedOasObject, version) { + let components = {}; + if (version === '2.0') { + ['definitions', 'parameters', 'responses', 'securityDefinitions'].forEach((property) => { + if (parsedOasObject.hasOwnProperty(property)) { + components[property] = parsedOasObject[property]; + } + }); + } + else if (parsedOasObject.hasOwnProperty('components')) { + components = parsedOasObject.components; + } + return components; +} + + module.exports = { /** * Takes in an spec root file and an array of data files @@ -390,13 +406,10 @@ module.exports = { let algorithm = new DFS(), components = {}, rootContextData; - rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { return getNodeContentAndReferences(currentNode, allData, specRoot, version); }); - if (specRoot.parsed.oasObject.hasOwnProperty('components')) { - components = specRoot.parsed.oasObject.components; - } + components = generateComponentsWrapper(specRoot.parsed.oasObject, version); generateComponentsObject( rootContextData, rootContextData.nodeContents[specRoot.fileName], diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json new file mode 100644 index 0000000..eb6ddbb --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json @@ -0,0 +1,70 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1pet.yaml%23~1Pet" + } + } + } + } + } + } + }, + "definitions": { + "/pet.yaml#/Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/index.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/index.yaml new file mode 100644 index 0000000..1042333 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/index.yaml @@ -0,0 +1,10 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: "./paths.yaml" + \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/info.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/paths.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/paths.yaml new file mode 100644 index 0000000..9531244 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/paths.yaml @@ -0,0 +1,13 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "./pet.yaml#/Pet" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/pet.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/pet.yaml new file mode 100644 index 0000000..61c94b1 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/pet.yaml @@ -0,0 +1,25 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Color: + $ref: "#/Color" + +Color: + type: object + properties: + name: + type: string + uses: + type: string + color: + type: string diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json new file mode 100644 index 0000000..8554787 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json @@ -0,0 +1,127 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "parameters": [ + { + "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter1" + }, + { + "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter2" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1pet.yaml%23~1Pet" + } + } + } + } + } + } + }, + "parameters": { + "/parameters/parameters.yaml#/Parameter1": { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "type": "array", + "collectionFormat": "csv", + "items": { + "type": "string" + } + }, + "/parameters/parameters.yaml#/Parameter2": { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "type": "integer", + "format": "int32" + } + }, + "definitions": { + "/pet.yaml#/Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "FavoriteFood": { + "type": "object", + "properties": { + "brand": { + "type": "string" + }, + "benefits": { + "type": "array", + "items": { + "$ref": "#/definitions/~1food.yaml%23~1Benefit" + } + }, + "cost": { + "type": "string" + } + } + } + } + }, + "/food.yaml#/Benefit": { + "type": "object", + "properties": { + "benefit": { + "type": "string" + }, + "detail": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/food.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/food.yaml new file mode 100644 index 0000000..a7db36f --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/food.yaml @@ -0,0 +1,22 @@ +Food: + type: object + properties: + brand: + $ref: "#/Brand" + benefits: + type: array + items: + $ref: "#/Benefit" + cost: + type: string + +Brand: + type: string + +Benefit: + type: object + properties: + benefit: + type: string + detail: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/index.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/index.yaml new file mode 100644 index 0000000..1042333 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/index.yaml @@ -0,0 +1,10 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: "./paths.yaml" + \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/info.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/parameters/parameters.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/parameters/parameters.yaml new file mode 100644 index 0000000..b6a3840 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/parameters/parameters.yaml @@ -0,0 +1,16 @@ +Parameter1: + name: tags + in: query + description: tags to filter by + required: false + type: array + collectionFormat: csv + items: + type: string +Parameter2: + name: limit + in: query + description: maximum number of results to return + required: false + type: integer + format: int32 \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/paths.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/paths.yaml new file mode 100644 index 0000000..57e8f5d --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/paths.yaml @@ -0,0 +1,16 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + parameters: + - $ref: "./parameters/parameters.yaml#/Parameter1" + - $ref: "./parameters/parameters.yaml#/Parameter2" + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "./pet.yaml#/Pet" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/pet.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/pet.yaml new file mode 100644 index 0000000..6dbe34e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/pet.yaml @@ -0,0 +1,29 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Color: + $ref: "#/Color" + FavoriteFood: + $ref: "./food.yaml#/Food" + + + +Color: + type: object + properties: + name: + type: string + uses: + type: string + color: + type: string diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json new file mode 100644 index 0000000..b43a148 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json @@ -0,0 +1,76 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1pet.yaml%23~1Pet" + } + } + } + } + } + } + }, + "definitions": { + "/pet.yaml#/Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "Colors": { + "type": "array", + "items": { + "$ref": "#/definitions/~1pet.yaml%23~1Color" + } + } + } + }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/index.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/index.yaml new file mode 100644 index 0000000..1042333 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/index.yaml @@ -0,0 +1,10 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: "./paths.yaml" + \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/info.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/paths.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/paths.yaml new file mode 100644 index 0000000..9531244 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/paths.yaml @@ -0,0 +1,13 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "./pet.yaml#/Pet" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/pet.yaml b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/pet.yaml new file mode 100644 index 0000000..b158bf0 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/pet.yaml @@ -0,0 +1,27 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Colors: + type: array + items: + $ref: "#/Color" + +Color: + type: object + properties: + name: + type: string + uses: + type: string + color: + type: string diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json new file mode 100644 index 0000000..f8a5b21 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json @@ -0,0 +1,92 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": null, + "get": { + "description": "Returns all pets from the system that the user has access to", + "operationId": "findPets", + "parameters": [ + { + "$ref": "#/parameters/Parameter1" + }, + { + "$ref": "#/parameters/Parameter2" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + } + } + } + }, + "definitions": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + } + } + } + }, + "parameters": { + "Parameter1": { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "type": "array", + "collectionFormat": "csv", + "items": { + "type": "string" + } + }, + "Parameter2": { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "type": "integer", + "format": "int32" + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/index.yaml b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/index.yaml new file mode 100644 index 0000000..259a70f --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/index.yaml @@ -0,0 +1,28 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to + operationId: findPets + parameters: + - $ref: "#/parameters/Parameter1" + - $ref: "#/parameters/Parameter2" + responses: + 200: + description: pet response + schema: + $ref: "#/definitions/Pet" +definitions: + Pet: + $ref: "./pet.yaml#/Pet" +parameters: + Parameter1: + $ref: "./parameters/parameters.yaml#/Parameter1" + Parameter2: + $ref: "./parameters/parameters.yaml#/Parameter2" diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/info.yaml b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/parameters/parameters.yaml b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/parameters/parameters.yaml new file mode 100644 index 0000000..b6a3840 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/parameters/parameters.yaml @@ -0,0 +1,16 @@ +Parameter1: + name: tags + in: query + description: tags to filter by + required: false + type: array + collectionFormat: csv + items: + type: string +Parameter2: + name: limit + in: query + description: maximum number of results to return + required: false + type: integer + format: int32 \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/paths.yaml b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/pet.yaml b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/pet.yaml new file mode 100644 index 0000000..61c94b1 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/pet.yaml @@ -0,0 +1,25 @@ +Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Color: + $ref: "#/Color" + +Color: + type: object + properties: + name: + type: string + uses: + type: string + color: + type: string diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json new file mode 100644 index 0000000..f0a84b2 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json @@ -0,0 +1,121 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/~1schemas~1user.yaml" + } + } + } + } + }, + "/clients": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/~1otherSchemas~1client.yaml" + } + } + } + } + } + }, + "definitions": { + "/schemas/user.yaml": { + "User": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer" + }, + "detail": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" + } + } + } + } + }, + "/otherSchemas/client.yaml": { + "Client": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" + } + } + } + } + }, + "/schemas/detail.yaml#/Detail": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "userName": { + "type": "string" + }, + "userDescription": { + "type": "string" + } + } + }, + "/otherSchemas/detail.yaml#/Detail": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "clientName": { + "type": "string" + }, + "clientDescription": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/index.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/info.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/client.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/client.yaml new file mode 100644 index 0000000..7a2d1f5 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/client.yaml @@ -0,0 +1,12 @@ +Client: + type: object + required: + - id + - name + properties: + id: + type: integer + details: + type: array + items: + $ref: "./detail.yaml#/Detail" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/detail.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/detail.yaml new file mode 100644 index 0000000..2898012 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/otherSchemas/detail.yaml @@ -0,0 +1,10 @@ +Detail: + type: object + properties: + id: + type: integer + format: int64 + clientName: + type: string + clientDescription: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml new file mode 100644 index 0000000..06abba8 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml @@ -0,0 +1,22 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "./schemas/user.yaml" +/clients: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "./otherSchemas/client.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/detail.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/detail.yaml new file mode 100644 index 0000000..5a0406e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/detail.yaml @@ -0,0 +1,10 @@ +Detail: + type: object + properties: + id: + type: integer + format: int64 + userName: + type: string + userDescription: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/user.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/user.yaml new file mode 100644 index 0000000..fe4c4ad --- /dev/null +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/schemas/user.yaml @@ -0,0 +1,12 @@ +User: + type: object + required: + - id + - name + properties: + id: + type: integer + detail: + type: array + items: + $ref: "./detail.yaml#/Detail" \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 5940726..d30331f 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -25,6 +25,16 @@ let expect = require('chai').expect, refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), + nestedRefs = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedRefs'), + nestedLocalRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedLocalRef'), + withParametersAndItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/withParametersAndItems'), + bringLocalFromExternal = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/bringLocalDependenciesFromExternal'), + bringLocalFromExternalWithItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/bringLocalDependenciesFromExternalWithItems'), + bringLocalFromExternalMultiple = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/bringLocalDependenciesFromExternalMultiple'), + multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), + sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); describe('bundle files method - 3.0', function () { @@ -808,6 +818,355 @@ describe('getReferences method when node does not have any reference', function( }); describe('bundle files method - 2.0', function() { + it('Should return bundled result from - sameRefDifferentSource', async function() { + let contentRootFile = fs.readFileSync(sameRefDifferentSource + '/index.yaml', 'utf8'), + info = fs.readFileSync(sameRefDifferentSource + '/info.yaml', 'utf8'), + paths = fs.readFileSync(sameRefDifferentSource + '/paths.yaml', 'utf8'), + user = fs.readFileSync(sameRefDifferentSource + '/schemas/user.yaml', 'utf8'), + userDetail = fs.readFileSync(sameRefDifferentSource + '/schemas/detail.yaml', 'utf8'), + client = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/client.yaml', 'utf8'), + clientDetail = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/detail.yaml', 'utf8'), + expected = fs.readFileSync(sameRefDifferentSource + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/detail.yaml', + content: userDetail + }, + { + path: '/otherSchemas/client.yaml', + content: client + }, + { + path: '/otherSchemas/detail.yaml', + content: clientDetail + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - multipleRefFromRootComponents', async function() { + let contentRootFile = fs.readFileSync(multipleRefFromRootComponents + '/index.yaml', 'utf8'), + info = fs.readFileSync(multipleRefFromRootComponents + '/info.yaml', 'utf8'), + paths = fs.readFileSync(multipleRefFromRootComponents + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(multipleRefFromRootComponents + '/pet.yaml', 'utf8'), + parameters = fs.readFileSync(multipleRefFromRootComponents + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(multipleRefFromRootComponents + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternalMultiple', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternalMultiple + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternalMultiple + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternalMultiple + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternalMultiple + '/pet.yaml', 'utf8'), + food = fs.readFileSync(bringLocalFromExternalMultiple + '/food.yaml', 'utf8'), + parameters = fs.readFileSync(bringLocalFromExternalMultiple + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternalMultiple + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/food.yaml', + content: food + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternalWithItems', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternalWithItems + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternalWithItems + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternalWithItems + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternalWithItems + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternalWithItems + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternal', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternal + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternal + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternal + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternal + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternal + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - withParametersAndItems', async function() { + let contentRootFile = fs.readFileSync(withParametersAndItems + '/index.yaml', 'utf8'), + info = fs.readFileSync(withParametersAndItems + '/info.yaml', 'utf8'), + paths = fs.readFileSync(withParametersAndItems + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(withParametersAndItems + '/pet.yaml', 'utf8'), + parameters = fs.readFileSync(withParametersAndItems + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(withParametersAndItems + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedLocalRef', async function() { + let contentRootFile = fs.readFileSync(nestedLocalRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedLocalRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedLocalRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedLocalRef + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedLocalRef + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedLocalRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedRefs', async function() { + let contentRootFile = fs.readFileSync(nestedRefs + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedRefs + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedRefs + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedRefs + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedRefs + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedRefs + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + it('Should return bundled result from - basicExample', async function() { let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js deleted file mode 100644 index a4e5a04..0000000 --- a/test/unit/bundle20.test.js +++ /dev/null @@ -1,212 +0,0 @@ -const expect = require('chai').expect, - Converter = require('../../index.js'), - fs = require('fs'), - path = require('path'), - SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', - basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), - nestedRefs = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedRefs'), - nestedLocalRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedLocalRef'), - withParametersAndItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/withParametersAndItems'), - simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); - -describe('bundle files method - 2.0', function() { - it('Should return bundled result from - withParametersAndItems', async function() { - let contentRootFile = fs.readFileSync(withParametersAndItems + '/index.yaml', 'utf8'), - info = fs.readFileSync(withParametersAndItems + '/info.yaml', 'utf8'), - paths = fs.readFileSync(withParametersAndItems + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(withParametersAndItems + '/pet.yaml', 'utf8'), - parameters = fs.readFileSync(withParametersAndItems + '/parameters/parameters.yaml', 'utf8'), - expected = fs.readFileSync(withParametersAndItems + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - }, - { - path: '/parameters/parameters.yaml', - content: parameters - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); - - it('Should return bundled result from - nestedLocalRef', async function() { - let contentRootFile = fs.readFileSync(nestedLocalRef + '/index.yaml', 'utf8'), - info = fs.readFileSync(nestedLocalRef + '/info.yaml', 'utf8'), - paths = fs.readFileSync(nestedLocalRef + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(nestedLocalRef + '/schemas/pet.yaml', 'utf8'), - favoriteFood = fs.readFileSync(nestedLocalRef + '/schemas/favorite_food.yaml', 'utf8'), - expected = fs.readFileSync(nestedLocalRef + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/pet.yaml', - content: pet - }, - { - path: '/schemas/favorite_food.yaml', - content: favoriteFood - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); - - it('Should return bundled result from - nestedRefs', async function() { - let contentRootFile = fs.readFileSync(nestedRefs + '/index.yaml', 'utf8'), - info = fs.readFileSync(nestedRefs + '/info.yaml', 'utf8'), - paths = fs.readFileSync(nestedRefs + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(nestedRefs + '/schemas/pet.yaml', 'utf8'), - favoriteFood = fs.readFileSync(nestedRefs + '/schemas/favorite_food.yaml', 'utf8'), - expected = fs.readFileSync(nestedRefs + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/pet.yaml', - content: pet - }, - { - path: '/schemas/favorite_food.yaml', - content: favoriteFood - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); - - it('Should return bundled result from - basicExample', async function() { - let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), - info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), - paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), - expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); - - it('Should return bundled result from - simpleRef', async function() { - let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), - info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), - paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), - expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), - input = { - type: 'folder', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml', - content: contentRootFile - } - ], - data: [ - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(res.output.data.bundledContent).to.be.equal(expected); - }); -}); From d2e2be3c0686da094b232ff7eb21d93d7f091894 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Thu, 2 Jun 2022 13:10:34 -0500 Subject: [PATCH 10/56] add example of using examples in swagger 2.0 add example of using examples in swagger 2.0 --- lib/30XUtils/componentsParentMatcher.js | 1 + lib/bundle.js | 7 +- lib/swaggerUtils/componentParentMatcher.js | 8 ++ .../referenced_example/examples.yaml | 2 + .../referenced_example/expected.json | 88 +++++++++++++++++++ .../swagger20/referenced_example/root.yaml | 57 ++++++++++++ test/unit/bundle.test.js | 31 ++++++- 7 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 test/data/toBundleExamples/swagger20/referenced_example/examples.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_example/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_example/root.yaml diff --git a/lib/30XUtils/componentsParentMatcher.js b/lib/30XUtils/componentsParentMatcher.js index ef49803..b831583 100644 --- a/lib/30XUtils/componentsParentMatcher.js +++ b/lib/30XUtils/componentsParentMatcher.js @@ -28,6 +28,7 @@ module.exports = { * @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) { diff --git a/lib/bundle.js b/lib/bundle.js index 5530e08..2677712 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -7,7 +7,8 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), removeLocalReferenceFromPath, localPointer, jsonPointerLevelSeparator, - isLocalRef + isLocalRef, + jsonPointerDecodeAndReplace } = require('./jsonPointer'), traverseUtility = require('traverse'), parse = require('./parse.js'), @@ -127,7 +128,9 @@ function getContentFromTrace(content, partial) { return content; } partial = partial[0] === jsonPointerLevelSeparator ? partial.substring(1) : partial; - const trace = partial.split(jsonPointerLevelSeparator); + const trace = partial.split(jsonPointerLevelSeparator).map((item) => { + return jsonPointerDecodeAndReplace(item); + }); let currentValue = content; currentValue = deref._getEscaped(content, trace, undefined); return currentValue; diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js index cc0799f..e87f946 100644 --- a/lib/swaggerUtils/componentParentMatcher.js +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -9,6 +9,9 @@ const COMPONENTS_KEYS_20 = [ 'items', 'allOf', 'additionalProperties' + ], + INLINE = [ + 'examples' ]; module.exports = { @@ -17,6 +20,7 @@ module.exports = { * @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 */ getKeyInComponents20: function (traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace) { @@ -33,6 +37,10 @@ module.exports = { if (SCHEMA_PARENTS.includes(item)) { item = 'definitions'; } + if (INLINE.includes(item)) { + matchFound = false; + break; + } traceToKey.push(item); if (COMPONENTS_KEYS_20.includes(item)) { matchFound = true; diff --git a/test/data/toBundleExamples/swagger20/referenced_example/examples.yaml b/test/data/toBundleExamples/swagger20/referenced_example/examples.yaml new file mode 100644 index 0000000..972a8ef --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example/examples.yaml @@ -0,0 +1,2 @@ +application/json: + name: Puma diff --git a/test/data/toBundleExamples/swagger20/referenced_example/expected.json b/test/data/toBundleExamples/swagger20/referenced_example/expected.json new file mode 100644 index 0000000..da7e196 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example/expected.json @@ -0,0 +1,88 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + }, + "examples": { + "application/json": { + "name": "Puma" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + }, + "examples": { + "application/json": { + "name": "Puma" + } + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_example/root.yaml b/test/data/toBundleExamples/swagger20/referenced_example/root.yaml new file mode 100644 index 0000000..844351a --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example/root.yaml @@ -0,0 +1,57 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + description: pet response + schema: + type: array + items: + $ref: '#/definitions/Pet' + examples: + $ref: "examples.yaml" + default: + description: unexpected error + schema: + $ref: '#/definitions/Error' + examples: + application/json: + $ref: "examples.yaml#/application~1json" +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 20d4067..3ec2bf2 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -40,7 +40,8 @@ let expect = require('chai').expect, '/bringLocalDependenciesFromExternalMultiple'), multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), - simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'); + simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), + refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -1335,6 +1336,34 @@ describe('bundle files method - 2.0', function() { expect(res.result).to.be.true; expect(res.output.data.bundledContent).to.be.equal(expected); }); + + it('Should return bundled file with referenced example', async function () { + let contentRootFile = fs.readFileSync(refExample20 + '/root.yaml', 'utf8'), + examples = fs.readFileSync(refExample20 + '/examples.yaml', 'utf8'), + expected = fs.readFileSync(refExample20 + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/examples.yaml', + content: examples + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From c82d50ed8d0cc0c78cb1b47b90694df9bfa8b3ba Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 2 Jun 2022 14:29:03 -0500 Subject: [PATCH 11/56] Adding safe parsing to the node's content parsing --- lib/bundle.js | 20 +++++++++++++++++--- lib/schemaUtils.js | 13 ++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index 2677712..17f53bd 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -12,7 +12,8 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), } = require('./jsonPointer'), traverseUtility = require('traverse'), parse = require('./parse.js'), - { COMPONENTS_KEYS_20 } = require('./swaggerUtils/componentParentMatcher'); + { COMPONENTS_KEYS_20 } = require('./swaggerUtils/componentParentMatcher'), + { ParseError } = require('./common/ParseError'); let path = require('path'), pathBrowserify = require('path-browserify'), @@ -32,6 +33,18 @@ function comparePaths(path1, path2) { return path1 === path2; } +/** + * Parses a node content or throw ParseError if there's any error + * @param {string} fileContent The content from the current node + * @returns {object} The parsed content + */ +function parseFileOrThrow(fileContent) { + const result = parse.getOasObject(fileContent); + if (result.result === false) { + throw new ParseError(result.reason); + } + return result; +} /** * Calculates the path relative to parent @@ -279,7 +292,7 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { nodeContent = currentNode.parsed.oasObject; } else { - nodeContent = parse.getOasObject(currentNode.content).oasObject; + nodeContent = parseFileOrThrow(currentNode.content).oasObject; } const { referencesInNode, nodeReferenceDirectory } = getReferences( @@ -433,5 +446,6 @@ module.exports = { components }; }, - getReferences + getReferences, + parseFileOrThrow }; diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 5dfb051..61321b3 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -92,7 +92,7 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc { getRelatedFiles } = require('./relatedFiles'), { compareVersion } = require('./common/versionUtils.js'), parse = require('./parse'), - { getBundleContentAndComponents } = require('./bundle.js'); + { getBundleContentAndComponents, parseFileOrThrow } = require('./bundle.js'); /* eslint-enable */ // See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options @@ -4867,7 +4867,7 @@ module.exports = { mapProcessRelatedFiles(rootFiles, inputData, origin, version, format, toBundle = false) { let bundleFormat = format, parsedRootFiles = rootFiles.map((rootFile) => { - let parsedContent = this.parseFileOrThrow(rootFile.content); + let parsedContent = parseFileOrThrow(rootFile.content); return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent }; }).filter((rootWithParsedContent) => { let fileVersion = version === SWAGGER_VERSION ? @@ -4931,14 +4931,5 @@ module.exports = { else { return res; } - }, - - parseFileOrThrow(fileContent) { - const result = parse.getOasObject(fileContent); - if (result.result === false) { - throw new ParseError(result.reason); - } - return result; } - }; From b7e2597cc18d9f653dd02df2d16f161c4482b643 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 3 Jun 2022 12:54:10 -0500 Subject: [PATCH 12/56] Add input validation for Multifile APIs Add input validation for Multifile APIs --- lib/schemaUtils.js | 43 ++++++++++++++++++++++ lib/schemapack.js | 9 ++--- test/unit/bundle.test.js | 38 ++++++++++++++++++++ test/unit/detectRelatedFiles.test.js | 53 +++++++++++++++++++++++++--- test/unit/detectRoot.test.js | 33 ++++++++++++++++- 5 files changed, 166 insertions(+), 10 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 61321b3..5018706 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4815,6 +4815,16 @@ module.exports = { }; }, + /** + * + * @description Takes in a the root files obtains the related files and + * generates the result object + * @param {object} parsedRootFiles - found parsed root files + * @param {array} inputData - file data information [{path, content}] + * @param {Array} origin - process origin (BROWSER or node) + * + * @returns {object} process result { rootFile, relatedFiles, missingRelatedFiles } + */ getRelatedFilesData(parsedRootFiles, inputData, origin) { const data = parsedRootFiles.map((root) => { let relatedData = getRelatedFiles(root, inputData, origin), @@ -4828,6 +4838,17 @@ module.exports = { return data; }, + /** + * + * @description Takes in parsed root files and bundle it + * @param {object} parsedRootFiles - found parsed root files + * @param {array} inputData - file data information [{path, content}] + * @param {Array} origin - process origin (BROWSER or node) + * @param {string} format - output format could be either YAML or JSON + * @param {string} version - specification version specified in the input + * + * @returns {object} process result { rootFile, bundledContent } + */ getBundledFileData(parsedRootFiles, inputData, origin, format, version) { const data = parsedRootFiles.map((root) => { let bundleData = getBundleContentAndComponents(root, inputData, origin, version); @@ -4931,5 +4952,27 @@ module.exports = { else { return res; } + }, + + /** + * + * @description Validates the input for multi file APIs + * @param {string} processInput - Process input data + * + * @returns {undefined} - nothing + */ + validateInputMultiFileAPI(processInput) { + if (_.isEmpty(processInput)) { + throw new Error('Input object must have "type" and "data" information'); + } + if (!processInput.type) { + throw new Error('"Type" parameter should be provided'); + } + if (!processInput.data || processInput.data.length === 0) { + throw new Error('"Data" parameter should be provided'); + } + if (processInput.data[0].path === '') { + throw new Error('"Path" of the data element should be provided'); + } } }; diff --git a/lib/schemapack.js b/lib/schemapack.js index 38bd0f3..9874e1b 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -629,14 +629,11 @@ class SchemaPack { */ async detectRootFiles() { const input = this.input; - if (input.data[0].path === '') { - throw new Error('undefined input'); - } + schemaUtils.validateInputMultiFileAPI(input); if (!this.hasDefinedVersion && ('content' in input.data[0])) { return schemaUtils.mapGetRootFilesOutputToDetectRootFilesOutput([], input.specificationVersion); } - let files = {}, rootFiles, res, @@ -668,6 +665,8 @@ class SchemaPack { */ async detectRelatedFiles() { const input = this.input; + + schemaUtils.validateInputMultiFileAPI(input); if (!input.rootFiles || input.rootFiles.length === 0) { let rootFiles = await this.detectRootFiles(input); if (rootFiles.output.data) { @@ -698,6 +697,8 @@ class SchemaPack { */ async bundle() { const input = this.input; + + schemaUtils.validateInputMultiFileAPI(input); if (!input.rootFiles || input.rootFiles.length === 0) { let rootFiles = await this.detectRootFiles(input); if (rootFiles.output.data) { diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 3ec2bf2..183d8c5 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -792,6 +792,44 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.data.bundledContent).to.be.equal(expected); }); + + it('should return error when "type" parameter is not sent', async function () { + let contentRootFile = fs.readFileSync(refExample + '/root.yaml', 'utf8'), + example = fs.readFileSync(refExample + '/examples.yaml', 'utf8'), + input = { + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/examples.yaml', + content: example + } + ], + options: {}, + bundleFormat: 'JSON' + }; + try { + await Converter.bundle(input); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('"Type" parameter should be provided'); + } + }); + + it('should return error when input is an empty object', async function () { + try { + await Converter.bundle({}); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('Input object must have "type" and "data" information'); + } + }); }); describe('bundle files method - 2.0', function() { diff --git a/test/unit/detectRelatedFiles.test.js b/test/unit/detectRelatedFiles.test.js index d0b1286..7ffc0c8 100644 --- a/test/unit/detectRelatedFiles.test.js +++ b/test/unit/detectRelatedFiles.test.js @@ -139,7 +139,9 @@ describe('detectRelatedFiles method', function () { content: contentFileMissedRef } ], - data: [ + data: [{ + + } ] }, res = await Converter.detectRelatedFiles(input); @@ -243,6 +245,9 @@ describe('detectRelatedFiles method', function () { } ], data: [ + { + + } ] }; const res = await Converter.detectRelatedFiles(input); @@ -266,8 +271,9 @@ describe('detectRelatedFiles method', function () { content: contentFileHop } ], - data: [ - ] + data: [{ + + }] }; const res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; @@ -286,8 +292,8 @@ describe('detectRelatedFiles method', function () { content: contentFile } ], - data: [ - ] + data: [{ + }] }; const res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; @@ -368,4 +374,41 @@ describe('detectRelatedFiles method', function () { expect(res.output.data[0].missingRelatedFiles.length).to.equal(6); }); + it('should return error when "type" parameter is not sent', async function () { + let contentRootFile = fs.readFileSync(petstoreMultipleFiles, 'utf8'), + contentFileResPets = fs.readFileSync(resourcesPets, 'utf8'), + input = { + rootFiles: [ + { + path: '/openapi.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/resources/pets.yaml', + content: contentFileResPets + } + ] + }; + + try { + await Converter.detectRelatedFiles(input); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('"Type" parameter should be provided'); + } + }); + + it('should return error when input is an empty object', async function () { + try { + await Converter.detectRelatedFiles({}); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('Input object must have "type" and "data" information'); + } + }); + }); diff --git a/test/unit/detectRoot.test.js b/test/unit/detectRoot.test.js index f0d200a..bced46d 100644 --- a/test/unit/detectRoot.test.js +++ b/test/unit/detectRoot.test.js @@ -218,7 +218,7 @@ describe('detectRoot method', function() { await Converter.detectRootFiles(input); } catch (ex) { - expect(ex.message).to.equal('undefined input'); + expect(ex.message).to.equal('"Path" of the data element should be provided'); } }); @@ -266,4 +266,35 @@ describe('detectRoot method', function() { }); + it('should return error when "type" parameter is not sent', async function () { + let input = { + data: [ + { + path: validPetstore + }, + { + path: validHopService31x + } + ] + }; + + try { + await Converter.detectRootFiles(input); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('"Type" parameter should be provided'); + } + }); + + it('should return error when input is an empty object', async function () { + try { + await Converter.detectRootFiles({}); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('Input object must have "type" and "data" information'); + } + }); + }); From 0bba8a4895ff9b93aec84dba2b6f3e7d59d11af9 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:07:21 -0500 Subject: [PATCH 13/56] Update detectRelatedFiles.test.js Standardize object creation --- test/unit/detectRelatedFiles.test.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/test/unit/detectRelatedFiles.test.js b/test/unit/detectRelatedFiles.test.js index 7ffc0c8..41cc27c 100644 --- a/test/unit/detectRelatedFiles.test.js +++ b/test/unit/detectRelatedFiles.test.js @@ -139,10 +139,7 @@ describe('detectRelatedFiles method', function () { content: contentFileMissedRef } ], - data: [{ - - } - ] + data: [{}] }, res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; @@ -244,11 +241,7 @@ describe('detectRelatedFiles method', function () { content: contentFileHop } ], - data: [ - { - - } - ] + data: [{}] }; const res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; @@ -271,9 +264,7 @@ describe('detectRelatedFiles method', function () { content: contentFileHop } ], - data: [{ - - }] + data: [{}] }; const res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; @@ -292,8 +283,7 @@ describe('detectRelatedFiles method', function () { content: contentFile } ], - data: [{ - }] + data: [{}] }; const res = await Converter.detectRelatedFiles(input); expect(res).to.not.be.empty; From 7b5e68b2b69f424bb9a501114575b0068b561748 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 3 Jun 2022 16:34:23 -0500 Subject: [PATCH 14/56] Issue with empty roots bundle --- lib/schemaUtils.js | 28 ++++++++++++++------------ lib/schemapack.js | 16 +-------------- test/unit/bundle.test.js | 43 ++++++++++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 5018706..f3b9ae4 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4931,23 +4931,21 @@ module.exports = { try { res.output.data = this.mapProcessRelatedFiles(inputRelatedFiles.rootFiles, inputRelatedFiles.data, inputRelatedFiles.origin, version, inputRelatedFiles.bundleFormat, toBundle); + if (res.output.data === undefined || res.output.data.length === 0 || + res.output.data.result === false) { + res.result = false; + } + return res; } catch (error) { if (error instanceof ParseError) { - return { - result: false, - reason: error.message - }; - } - else { throw (error); } + let newError = new Error('There was an error during the process'); + newError.stack = error.stack; + throw (newError); } - if (res.output.data.result === false) { - res.result = res.output.data.result; - res.error = res.output.data.error; - } - return res; + } else { return res; @@ -4957,11 +4955,12 @@ module.exports = { /** * * @description Validates the input for multi file APIs - * @param {string} processInput - Process input data + * @param {object} processInput - Process input data + * @param {boolean} bundleProcess - Wheter the process input corresponds to bundel process * * @returns {undefined} - nothing */ - validateInputMultiFileAPI(processInput) { + validateInputMultiFileAPI(processInput, bundleProcess = false) { if (_.isEmpty(processInput)) { throw new Error('Input object must have "type" and "data" information'); } @@ -4974,5 +4973,8 @@ module.exports = { if (processInput.data[0].path === '') { throw new Error('"Path" of the data element should be provided'); } + if (bundleProcess && (!processInput.rootFiles || processInput.rootFiles.length === 0)) { + throw new Error('"RootFiles" parameter should be provided'); + } } }; diff --git a/lib/schemapack.js b/lib/schemapack.js index 9874e1b..ead7229 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -698,21 +698,7 @@ class SchemaPack { async bundle() { const input = this.input; - schemaUtils.validateInputMultiFileAPI(input); - if (!input.rootFiles || input.rootFiles.length === 0) { - let rootFiles = await this.detectRootFiles(input); - if (rootFiles.output.data) { - let inputContent = []; - rootFiles.output.data.forEach((rootFile) => { - let founInData = input.data.find((dataFile) => { return dataFile.fileName === rootFile.path; }); - if (founInData) { - inputContent.push({ fileName: founInData.fileName, content: founInData.content }); - } - }); - input.rootFiles = inputContent; - return schemaUtils.processRelatedFiles(input, true); - } - } + schemaUtils.validateInputMultiFileAPI(input, true); let adaptedRootFiles = input.rootFiles.map((rootFile) => { return { fileName: rootFile.path, content: rootFile.content }; }); diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 183d8c5..f19ddce 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -419,12 +419,14 @@ describe('bundle files method - 3.0', function () { } ] }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.false; - expect(res.reason).to.equal('Invalid format. Input must be in YAML or JSON ' + - 'format. Specification is not a valid YAML. YAMLException: duplicated mapping' + - ' key at line 30, column -54:\n components:\n ^'); + try { + await Converter.bundle(input); + } + catch (error) { + expect(error.message).to.equal('Invalid format. Input must be in YAML or JSON ' + + 'format. Specification is not a valid YAML. YAMLException: duplicated mapping' + + ' key at line 30, column -54:\n components:\n ^'); + } }); it('Should return bundled file from same_ref_different_source', async function () { @@ -830,6 +832,35 @@ describe('bundle files method - 3.0', function () { expect(error.message).to.equal('Input object must have "type" and "data" information'); } }); + + it('should return error when input has no root files', async function () { + let contentRootFile = fs.readFileSync(refExample + '/root.yaml', 'utf8'), + input = { + type: 'folder', + specificationVersion: '3.0', + rootFiles: [], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/root.yaml', + content: contentRootFile + } + + ], + options: {}, + bundleFormat: 'JSON' + }; + try { + await Converter.bundle(input); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('"RootFiles" parameter should be provided'); + } + }); }); describe('bundle files method - 2.0', function() { From 2e79c2755c2785c734968945067606bef0418bf2 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 3 Jun 2022 17:10:19 -0500 Subject: [PATCH 15/56] Update schemaUtils.js --- lib/schemaUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index f3b9ae4..b3aeeb7 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4931,8 +4931,8 @@ module.exports = { try { res.output.data = this.mapProcessRelatedFiles(inputRelatedFiles.rootFiles, inputRelatedFiles.data, inputRelatedFiles.origin, version, inputRelatedFiles.bundleFormat, toBundle); - if (res.output.data === undefined || res.output.data.length === 0 || - res.output.data.result === false) { + if (res.output.data === undefined || res.output.data.result === false || + res.output.data.length === 0) { res.result = false; } return res; From b227ed326c647172b7a05f9abc947cc62746a01d Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Mon, 6 Jun 2022 19:29:42 -0500 Subject: [PATCH 16/56] Fixing properties resolving Properties should be resolved as independant schema not always inline --- lib/30XUtils/componentsParentMatcher.js | 8 +- lib/bundle.js | 12 ++ lib/swaggerUtils/componentParentMatcher.js | 14 +- .../expected.json | 21 +- .../expected.json | 78 ++++---- .../expected.json | 54 ++++-- .../nestedProperties/expected.json | 126 ++++++++++++ .../nestedProperties/properties/country.yaml | 6 + .../properties/lastNested.yaml | 10 + .../properties/nestedProp.yaml | 7 + .../nestedProperties/properties/prop.yaml | 14 ++ .../nestedProperties/properties/warrior.yaml | 6 + .../nestedProperties/root.yaml | 23 +++ .../nestedProperties/schemas/user.yaml | 8 + .../toBundleExamples/properties/expected.json | 69 +++++++ .../toBundleExamples/properties/root.yaml | 23 +++ .../properties/schemas/prop.yaml | 8 + .../properties/schemas/user.yaml | 8 + .../same_ref_different_source/expected.json | 55 +++--- .../same_source_different_place/expected.json | 94 +++++++++ .../same_source_different_place/root.yaml | 33 ++++ .../schemas/client/client.yaml | 8 + .../schemas/user/special.yaml | 4 + .../schemas/user/user.yaml | 8 + .../expected.json | 52 +++++ .../expected.yaml | 32 +++ .../headers/headers.yaml | 6 + .../root.yaml | 26 +++ .../schemas/user.yaml | 6 + .../bundleExpected.json | 27 +-- .../bundleExpected.json | 63 +++--- .../bundleExpected.json | 27 +-- .../nestedLocalRef/bundleExpected.json | 26 ++- .../nestedProperties20/bundleExpected.json | 68 +++++++ .../swagger20/nestedProperties20/index.yaml | 9 + .../swagger20/nestedProperties20/info.yaml | 3 + .../swagger20/nestedProperties20/paths.yaml | 13 ++ .../nestedProperties20/schemas/age.yaml | 1 + .../nestedProperties20/schemas/hobbies.yaml | 3 + .../nestedProperties20/schemas/hobby.yaml | 6 + .../nestedProperties20/schemas/user.yaml | 6 + .../swagger20/nestedRefs/bundleExpected.json | 21 +- .../bundleExpected.json | 94 +++++---- .../sameRefDifferentSource/paths.yaml | 4 +- test/unit/bundle.test.js | 183 +++++++++++++++++- 45 files changed, 1162 insertions(+), 211 deletions(-) create mode 100644 test/data/toBundleExamples/nestedProperties/expected.json create mode 100644 test/data/toBundleExamples/nestedProperties/properties/country.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/properties/lastNested.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/properties/nestedProp.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/properties/prop.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/properties/warrior.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/root.yaml create mode 100644 test/data/toBundleExamples/nestedProperties/schemas/user.yaml create mode 100644 test/data/toBundleExamples/properties/expected.json create mode 100644 test/data/toBundleExamples/properties/root.yaml create mode 100644 test/data/toBundleExamples/properties/schemas/prop.yaml create mode 100644 test/data/toBundleExamples/properties/schemas/user.yaml create mode 100644 test/data/toBundleExamples/same_source_different_place/expected.json create mode 100644 test/data/toBundleExamples/same_source_different_place/root.yaml create mode 100644 test/data/toBundleExamples/same_source_different_place/schemas/client/client.yaml create mode 100644 test/data/toBundleExamples/same_source_different_place/schemas/user/special.yaml create mode 100644 test/data/toBundleExamples/same_source_different_place/schemas/user/user.yaml create mode 100644 test/data/toBundleExamples/schema_from_response_with_headers/expected.json create mode 100644 test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml create mode 100644 test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml create mode 100644 test/data/toBundleExamples/schema_from_response_with_headers/root.yaml create mode 100644 test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml create mode 100644 test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml diff --git a/lib/30XUtils/componentsParentMatcher.js b/lib/30XUtils/componentsParentMatcher.js index b831583..38e6ad0 100644 --- a/lib/30XUtils/componentsParentMatcher.js +++ b/lib/30XUtils/componentsParentMatcher.js @@ -20,6 +20,9 @@ const COMPONENTS_KEYS_30 = [ ], EXAMPLE_CONTAINERS = [ 'example' + ], + PROPERTY_DEFINITION = [ + 'properties' ]; module.exports = { @@ -45,13 +48,16 @@ module.exports = { res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); trace = [...res].reverse(); - for (let item of trace) { + for (let [index, item] of trace.entries()) { if (SCHEMA_CONTAINERS.includes(item)) { item = 'schemas'; } if (EXAMPLE_CONTAINERS.includes(item)) { item = 'examples'; } + if (PROPERTY_DEFINITION.includes(trace[index + 2])) { + trace[index + 1] = 'schemas'; + } traceToKey.push(item); if (COMPONENTS_KEYS_30.includes(item)) { matchFound = true; diff --git a/lib/bundle.js b/lib/bundle.js index 17f53bd..7abf1de 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -334,6 +334,18 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { * @returns {object} The components object related to the file */ function generateComponentsObject (documentContext, rootContent, refTypeResolver, components, version) { + let notInLine = Object.entries(documentContext.globalReferences).filter(([, value]) => { + return value.keyInComponents.length !== 0; + }); + notInLine.forEach(([key, value]) => { + let [, partial] = key.split('#'); + setValueInComponents( + value.keyInComponents, + components, + getContentFromTrace(documentContext.nodeContents[key], partial), + version + ); + }); [rootContent, components].forEach((contentData) => { traverseUtility(contentData).forEach(function (property) { if (property) { diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js index e87f946..9aaa383 100644 --- a/lib/swaggerUtils/componentParentMatcher.js +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -12,6 +12,9 @@ const COMPONENTS_KEYS_20 = [ ], INLINE = [ 'examples' + ], + PROPERTY_DEFINITION = [ + 'properties' ]; module.exports = { @@ -28,7 +31,8 @@ module.exports = { trace = [], traceToKey = [], matchFound = false, - isRootKey = false; + isRootKey = false, + traceModified = false; res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); trace = [...res].reverse(); @@ -36,6 +40,11 @@ module.exports = { for (let [index, item] of trace.entries()) { if (SCHEMA_PARENTS.includes(item)) { item = 'definitions'; + traceModified = true; + } + if (PROPERTY_DEFINITION.includes(trace[index + 2])) { + trace[index + 1] = 'definitions'; + traceModified = true; } if (INLINE.includes(item)) { matchFound = false; @@ -44,7 +53,8 @@ module.exports = { traceToKey.push(item); if (COMPONENTS_KEYS_20.includes(item)) { matchFound = true; - isRootKey = trace[index + 1] === undefined; + isRootKey = trace[index + 1] === undefined && + !traceModified; break; } } diff --git a/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json b/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json index 4866525..7aa1b10 100644 --- a/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json +++ b/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json @@ -49,19 +49,22 @@ "type": "object", "properties": { "theUsersPet": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "breed": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Pet" } } } } + }, + "/schemas/user.yaml#/Pet": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "breed": { + "type": "string" + } + } } } } diff --git a/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json b/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json index 194fcb4..19b6f4c 100644 --- a/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json +++ b/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json @@ -49,43 +49,58 @@ "type": "object", "properties": { "favoriteFood": { - "type": "object", - "properties": { - "brand": { - "type": "string" - }, - "benefits": { - "type": "array", - "items": { - "$ref": "#/components/schemas/~1schemas~1food.yaml%23~1Benefit" - } - }, - "cost": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1schemas~1food.yaml%23Food" }, "theUsersPet": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "breed": { - "type": "string" - }, - "color": { - "type": "array", - "items": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Color" - } - } - } + "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Pet" } } } } }, + "/schemas/food.yaml#Food": { + "type": "object", + "properties": { + "brand": { + "$ref": "#/components/schemas/~1schemas~1food.yaml%23~1Brand" + }, + "benefits": { + "type": "array", + "items": { + "$ref": "#/components/schemas/~1schemas~1food.yaml%23~1Benefit" + } + }, + "cost": { + "type": "string" + } + } + }, + "/schemas/user.yaml#/Pet": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "breed": { + "type": "string" + }, + "color": { + "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Colors" + } + } + }, + "/schemas/user.yaml#/Colors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Color" + } + }, + "/schemas/user.yaml#/Color": { + "type": "string" + }, + "/schemas/food.yaml#/Brand": { + "type": "string" + }, "/schemas/food.yaml#/Benefit": { "type": "object", "properties": { @@ -96,9 +111,6 @@ "type": "string" } } - }, - "/schemas/user.yaml#/Color": { - "type": "string" } } } diff --git a/test/data/toBundleExamples/multiple_references_from_root_components/expected.json b/test/data/toBundleExamples/multiple_references_from_root_components/expected.json index a60e361..4081bea 100644 --- a/test/data/toBundleExamples/multiple_references_from_root_components/expected.json +++ b/test/data/toBundleExamples/multiple_references_from_root_components/expected.json @@ -80,31 +80,13 @@ "type": "object", "properties": { "userInfo": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "userName": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1schemas~1user.yaml" }, "carType": { - "type": "object", - "properties": { - "model": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1schemas~1carType.yaml" }, "work": { - "type": "object", - "properties": { - "office": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1otherSchemas~1work.yaml" } } }, @@ -118,6 +100,36 @@ "type": "string" } } + }, + "/schemas/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + }, + "/schemas/carType.yaml": { + "type": "object", + "properties": { + "model": { + "$ref": "#/components/schemas/~1otherSchemas~1model.yaml" + } + } + }, + "/otherSchemas/work.yaml": { + "type": "object", + "properties": { + "office": { + "type": "string" + } + } + }, + "/otherSchemas/model.yaml": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/nestedProperties/expected.json b/test/data/toBundleExamples/nestedProperties/expected.json new file mode 100644 index 0000000..9edf318 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/expected.json @@ -0,0 +1,126 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1user.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "complexProp": { + "$ref": "#/components/schemas/~1properties~1prop.yaml" + } + } + }, + "/properties/prop.yaml": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "secondName": { + "type": "string" + }, + "age": { + "type": "integer" + }, + "nestedProp": { + "$ref": "#/components/schemas/~1properties~1nestedProp.yaml" + }, + "country": { + "$ref": "#/components/schemas/~1properties~1country.yaml" + }, + "warrior": { + "$ref": "#/components/schemas/~1properties~1warrior.yaml" + } + } + }, + "/properties/nestedProp.yaml": { + "type": "object", + "rock": { + "type": "boolean" + }, + "friendly": { + "type": "string" + }, + "lastNested": { + "type": "object", + "properties": { + "this": { + "type": "string" + }, + "is": { + "type": "string" + }, + "the": { + "type": "string" + }, + "last": { + "type": "integer" + } + } + } + }, + "/properties/country.yaml": { + "type": "object", + "properties": { + "region": { + "type": "string" + }, + "flag": { + "type": "string" + } + } + }, + "/properties/warrior.yaml": { + "type": "object", + "properties": { + "power": { + "type": "string" + }, + "weapon": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/nestedProperties/properties/country.yaml b/test/data/toBundleExamples/nestedProperties/properties/country.yaml new file mode 100644 index 0000000..63eb6c6 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/properties/country.yaml @@ -0,0 +1,6 @@ +type: object +properties: + region: + type: string + flag: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/nestedProperties/properties/lastNested.yaml b/test/data/toBundleExamples/nestedProperties/properties/lastNested.yaml new file mode 100644 index 0000000..ec593dc --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/properties/lastNested.yaml @@ -0,0 +1,10 @@ +type: object +properties: + this: + type: string + is: + type: string + the: + type: string + last: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/nestedProperties/properties/nestedProp.yaml b/test/data/toBundleExamples/nestedProperties/properties/nestedProp.yaml new file mode 100644 index 0000000..35bc146 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/properties/nestedProp.yaml @@ -0,0 +1,7 @@ +type: object +rock: + type: boolean +friendly: + type: string +lastNested: + $ref: "./lastNested.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/nestedProperties/properties/prop.yaml b/test/data/toBundleExamples/nestedProperties/properties/prop.yaml new file mode 100644 index 0000000..f1c75c9 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/properties/prop.yaml @@ -0,0 +1,14 @@ +type: object +properties: + firstName: + type: string + secondName: + type: string + age: + type: integer + nestedProp: + $ref: "./nestedProp.yaml" + country: + $ref: "./country.yaml" + warrior: + $ref: "./warrior.yaml" diff --git a/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml b/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml new file mode 100644 index 0000000..0308186 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml @@ -0,0 +1,6 @@ +type: object +properties: + power: + type: string + weapon: + type: string diff --git a/test/data/toBundleExamples/nestedProperties/root.yaml b/test/data/toBundleExamples/nestedProperties/root.yaml new file mode 100644 index 0000000..3666bf2 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/root.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/nestedProperties/schemas/user.yaml b/test/data/toBundleExamples/nestedProperties/schemas/user.yaml new file mode 100644 index 0000000..70f6021 --- /dev/null +++ b/test/data/toBundleExamples/nestedProperties/schemas/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + id: + type: integer + userName: + type: string + complexProp: + $ref: "../properties/prop.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/properties/expected.json b/test/data/toBundleExamples/properties/expected.json new file mode 100644 index 0000000..48c9877 --- /dev/null +++ b/test/data/toBundleExamples/properties/expected.json @@ -0,0 +1,69 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1user.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "complexProp": { + "$ref": "#/components/schemas/~1schemas~1prop.yaml" + } + } + }, + "/schemas/prop.yaml": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "secondName": { + "type": "string" + }, + "age": { + "type": "integer" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/properties/root.yaml b/test/data/toBundleExamples/properties/root.yaml new file mode 100644 index 0000000..3666bf2 --- /dev/null +++ b/test/data/toBundleExamples/properties/root.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/properties/schemas/prop.yaml b/test/data/toBundleExamples/properties/schemas/prop.yaml new file mode 100644 index 0000000..799a814 --- /dev/null +++ b/test/data/toBundleExamples/properties/schemas/prop.yaml @@ -0,0 +1,8 @@ +type: object +properties: + firstName: + type: string + secondName: + type: string + age: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/properties/schemas/user.yaml b/test/data/toBundleExamples/properties/schemas/user.yaml new file mode 100644 index 0000000..2241722 --- /dev/null +++ b/test/data/toBundleExamples/properties/schemas/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + id: + type: integer + userName: + type: string + complexProp: + $ref: ./prop.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/same_ref_different_source/expected.json b/test/data/toBundleExamples/same_ref_different_source/expected.json index 811eb70..5b81da0 100644 --- a/test/data/toBundleExamples/same_ref_different_source/expected.json +++ b/test/data/toBundleExamples/same_ref_different_source/expected.json @@ -63,12 +63,7 @@ "type": "string" }, "special": { - "type": "object", - "properties": { - "specialUserId": { - "type": "string" - } - } + "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" } } }, @@ -82,23 +77,37 @@ "type": "string" }, "special": { - "type": "object", - "properties": { - "specialClientId": { - "type": "string" - }, - "magic": { - "type": "object", - "properties": { - "magicNumber": { - "type": "integer" - }, - "magicString": { - "type": "string" - } - } - } - } + "$ref": "#/components/schemas/~1schemas~1client~1special.yaml" + } + } + }, + "/schemas/client/special.yaml": { + "type": "object", + "properties": { + "specialClientId": { + "type": "string" + }, + "magic": { + "$ref": "#/components/schemas/~1schemas~1client~1magic.yaml" + } + } + }, + "/schemas/client/magic.yaml": { + "type": "object", + "properties": { + "magicNumber": { + "type": "integer" + }, + "magicString": { + "type": "string" + } + } + }, + "/schemas/user/special.yaml": { + "type": "object", + "properties": { + "specialUserId": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/same_source_different_place/expected.json b/test/data/toBundleExamples/same_source_different_place/expected.json new file mode 100644 index 0000000..d701d31 --- /dev/null +++ b/test/data/toBundleExamples/same_source_different_place/expected.json @@ -0,0 +1,94 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1user~1user.yaml" + } + } + } + } + } + } + }, + "/clients": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1client~1client.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "/schemas/user/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "special": { + "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" + } + } + }, + "/schemas/client/client.yaml": { + "type": "object", + "properties": { + "idClient": { + "type": "integer" + }, + "clientName": { + "type": "string" + }, + "special": { + "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" + } + } + }, + "/schemas/user/special.yaml": { + "type": "object", + "properties": { + "specialUserId": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/same_source_different_place/root.yaml b/test/data/toBundleExamples/same_source_different_place/root.yaml new file mode 100644 index 0000000..1b9d218 --- /dev/null +++ b/test/data/toBundleExamples/same_source_different_place/root.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user/user.yaml" + /clients: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/client/client.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/same_source_different_place/schemas/client/client.yaml b/test/data/toBundleExamples/same_source_different_place/schemas/client/client.yaml new file mode 100644 index 0000000..fc6eed6 --- /dev/null +++ b/test/data/toBundleExamples/same_source_different_place/schemas/client/client.yaml @@ -0,0 +1,8 @@ +type: object +properties: + idClient: + type: integer + clientName: + type: string + special: + $ref: ../user/special.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/same_source_different_place/schemas/user/special.yaml b/test/data/toBundleExamples/same_source_different_place/schemas/user/special.yaml new file mode 100644 index 0000000..d35d2f1 --- /dev/null +++ b/test/data/toBundleExamples/same_source_different_place/schemas/user/special.yaml @@ -0,0 +1,4 @@ +type: object +properties: + specialUserId: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/same_source_different_place/schemas/user/user.yaml b/test/data/toBundleExamples/same_source_different_place/schemas/user/user.yaml new file mode 100644 index 0000000..04c5121 --- /dev/null +++ b/test/data/toBundleExamples/same_source_different_place/schemas/user/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + id: + type: integer + userName: + type: string + special: + $ref: ./special.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/expected.json b/test/data/toBundleExamples/schema_from_response_with_headers/expected.json new file mode 100644 index 0000000..46c7d7f --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/expected.json @@ -0,0 +1,52 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1user.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml new file mode 100644 index 0000000..e405d31 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + title: Sample API + description: >- + Optional multiline or single-line description in + [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: 'http://api.example.com/v1' + description: 'Optional server description, e.g. Main (production) server' + - url: 'http://staging-api.example.com' + description: 'Optional server description, e.g. Internal staging server for testing' +paths: + '/users/{userId}': + get: + summary: Get a user by ID + responses: + '200': + description: A single user. + content: + application/json: + schema: + $ref: '#/components/schemas/~1schemas~1user.yaml' +components: + schemas: + /schemas/user.yaml: + type: object + properties: + id: + type: integer + userName: + type: string diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml new file mode 100644 index 0000000..5d1da1b --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml @@ -0,0 +1,6 @@ +headerTest: + schema: + type: integer + example: 5000 + description: >- + The test header \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml new file mode 100644 index 0000000..0529fe4 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + headers: + ratelimit-limit: + $ref: '../headers.yaml#/headerTest' + content: + application/json: + schema: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml new file mode 100644 index 0000000..81370de --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json index eb6ddbb..74a6a8a 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json @@ -51,18 +51,21 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" + } + } + }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json index 8554787..2579a1c 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json @@ -80,38 +80,47 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" }, "FavoriteFood": { - "type": "object", - "properties": { - "brand": { - "type": "string" - }, - "benefits": { - "type": "array", - "items": { - "$ref": "#/definitions/~1food.yaml%23~1Benefit" - } - }, - "cost": { - "type": "string" - } - } + "$ref": "#/definitions/~1food.yaml%23~1Food" } } }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "/food.yaml#/Food": { + "type": "object", + "properties": { + "brand": { + "$ref": "#/definitions/~1food.yaml%23~1Brand" + }, + "benefits": { + "type": "array", + "items": { + "$ref": "#/definitions/~1food.yaml%23~1Benefit" + } + }, + "cost": { + "type": "string" + } + } + }, + "/food.yaml#/Brand": { + "type": "string" + }, "/food.yaml#/Benefit": { "type": "object", "properties": { diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json index f8a5b21..d673521 100644 --- a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json @@ -52,18 +52,21 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" + } + } + }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json index 5ab6064..647921b 100644 --- a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json @@ -47,20 +47,26 @@ "type": "string" }, "favoriteFood": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "brand": { - "type": "string" - } - } + "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1FavoriteFood" }, "foodPrice": { - "type": "integer" + "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1Price" } } + }, + "/schemas/favorite_food.yaml#/FavoriteFood": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" + } + } + }, + "/schemas/favorite_food.yaml#/Price": { + "type": "integer" } } } \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json new file mode 100644 index 0000000..eca6b1e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json @@ -0,0 +1,68 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1user.yaml" + } + } + } + } + } + } + }, + "definitions": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "age": { + "$ref": "#/definitions/~1schemas~1age.yaml" + }, + "hobbies": { + "$ref": "#/definitions/~1schemas~1hobbies.yaml" + } + } + }, + "/schemas/age.yaml": { + "type": "string" + }, + "/schemas/hobbies.yaml": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1hobby.yaml" + } + }, + "/schemas/hobby.yaml": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml new file mode 100644 index 0000000..1a7dc22 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml @@ -0,0 +1,13 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml new file mode 100644 index 0000000..2d8bb0e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml @@ -0,0 +1 @@ +type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml new file mode 100644 index 0000000..1b4d1b3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml @@ -0,0 +1,3 @@ +type: array +items: + $ref: "./hobby.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml new file mode 100644 index 0000000..8545a7b --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml @@ -0,0 +1,6 @@ +type: object +properties: + name: + type: string + position: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml new file mode 100644 index 0000000..8c13a61 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + age: + $ref: "./age.yaml" + hobbies: + $ref: "./hobbies.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json index c5d8dbd..90b2aa1 100644 --- a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json @@ -47,15 +47,18 @@ "type": "string" }, "favoriteFood": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "brand": { - "type": "string" - } - } + "$ref": "#/definitions/~1schemas~1favorite_food.yaml" + } + } + }, + "/schemas/favorite_food.yaml": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json index f0a84b2..620a304 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json @@ -22,7 +22,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1schemas~1user.yaml" + "$ref": "#/definitions/~1schemas~1user.yaml%23User" } } } @@ -39,7 +39,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1otherSchemas~1client.yaml" + "$ref": "#/definitions/~1otherSchemas~1client.yaml%23Client" } } } @@ -47,58 +47,39 @@ } }, "definitions": { - "/schemas/user.yaml": { - "User": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer" - }, - "detail": { - "type": "array", - "items": { - "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" - } - } - } - } - }, - "/otherSchemas/client.yaml": { - "Client": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" - } - } - } - } - }, - "/schemas/detail.yaml#/Detail": { + "/schemas/user.yaml#User": { "type": "object", + "required": [ + "id", + "name" + ], "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "integer" }, - "userName": { - "type": "string" + "detail": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" + } + } + } + }, + "/otherSchemas/client.yaml#Client": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer" }, - "userDescription": { - "type": "string" + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" + } } } }, @@ -116,6 +97,21 @@ "type": "string" } } + }, + "/schemas/detail.yaml#/Detail": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "userName": { + "type": "string" + }, + "userDescription": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml index 06abba8..cd37e5f 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml @@ -8,7 +8,7 @@ 200: description: OK schema: - $ref: "./schemas/user.yaml" + $ref: "./schemas/user.yaml#User" /clients: get: summary: Returns a list of users. @@ -19,4 +19,4 @@ 200: description: OK schema: - $ref: "./otherSchemas/client.yaml" \ No newline at end of file + $ref: "./otherSchemas/client.yaml#Client" \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index f19ddce..b6216c6 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -40,8 +40,12 @@ let expect = require('chai').expect, '/bringLocalDependenciesFromExternalMultiple'), multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), + nestedProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedProperties20'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), - refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'); + refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'), + properties = path.join(__dirname, BUNDLES_FOLDER + '/properties'), + sameSourceDifferentPlace = path.join(__dirname, BUNDLES_FOLDER + '/same_source_different_place'), + nestedProperties = path.join(__dirname, BUNDLES_FOLDER + '/nestedProperties'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -861,9 +865,186 @@ describe('bundle files method - 3.0', function () { expect(error.message).to.equal('"RootFiles" parameter should be provided'); } }); + + it('Should return bundled file as json - sameSourceDifferentPlace', async function () { + let contentRootFile = fs.readFileSync(sameSourceDifferentPlace + '/root.yaml', 'utf8'), + user = fs.readFileSync(sameSourceDifferentPlace + '/schemas/user/user.yaml', 'utf8'), + special = fs.readFileSync(sameSourceDifferentPlace + '/schemas/user/special.yaml', 'utf8'), + client = fs.readFileSync(sameSourceDifferentPlace + '/schemas/client/client.yaml', 'utf8'), + expected = fs.readFileSync(sameSourceDifferentPlace + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/schemas/user/user.yaml', + content: user + }, + { + path: '/schemas/user/special.yaml', + content: special + }, + { + path: '/schemas/client/client.yaml', + content: client + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled file as json - nestedProperties', async function () { + let contentRootFile = fs.readFileSync(nestedProperties + '/root.yaml', 'utf8'), + user = fs.readFileSync(nestedProperties + '/schemas/user.yaml', 'utf8'), + prop = fs.readFileSync(nestedProperties + '/properties/prop.yaml', 'utf8'), + nestedProp = fs.readFileSync(nestedProperties + '/properties/nestedProp.yaml', 'utf8'), + lastNested = fs.readFileSync(nestedProperties + '/properties/lastNested.yaml', 'utf8'), + warrior = fs.readFileSync(nestedProperties + '/properties/warrior.yaml', 'utf8'), + country = fs.readFileSync(nestedProperties + '/properties/country.yaml', 'utf8'), + expected = fs.readFileSync(nestedProperties + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/properties/prop.yaml', + content: prop + }, + { + path: '/properties/nestedProp.yaml', + content: nestedProp + }, + { + path: '/properties/country.yaml', + content: country + }, + { + path: '/properties/lastNested.yaml', + content: lastNested + }, + { + path: '/properties/warrior.yaml', + content: warrior + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + + it('Should return bundled file as json - properties', async function () { + let contentRootFile = fs.readFileSync(properties + '/root.yaml', 'utf8'), + user = fs.readFileSync(properties + '/schemas/user.yaml', 'utf8'), + prop = fs.readFileSync(properties + '/schemas/prop.yaml', 'utf8'), + expected = fs.readFileSync(properties + '/expected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/prop.yaml', + content: prop + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); }); describe('bundle files method - 2.0', function() { + it('Should return bundled result from - nestedProperties20', async function() { + let contentRootFile = fs.readFileSync(nestedProperties20 + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedProperties20 + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedProperties20 + '/paths.yaml', 'utf8'), + age = fs.readFileSync(nestedProperties20 + '/schemas/age.yaml', 'utf8'), + hobbies = fs.readFileSync(nestedProperties20 + '/schemas/hobbies.yaml', 'utf8'), + hobby = fs.readFileSync(nestedProperties20 + '/schemas/hobby.yaml', 'utf8'), + user = fs.readFileSync(nestedProperties20 + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(nestedProperties20 + '/bundleExpected.json', 'utf8'), + input = { + type: 'folder', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml', + content: contentRootFile + } + ], + data: [ + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/age.yaml', + content: age + }, + { + path: '/schemas/hobbies.yaml', + content: hobbies + }, + { + path: '/schemas/hobby.yaml', + content: hobby + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data.bundledContent).to.be.equal(expected); + }); + it('Should return bundled result from - sameRefDifferentSource', async function() { let contentRootFile = fs.readFileSync(sameRefDifferentSource + '/index.yaml', 'utf8'), info = fs.readFileSync(sameRefDifferentSource + '/info.yaml', 'utf8'), From 7cccce71682230a4bf525924efdc2071b84855aa Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 9 Jun 2022 18:40:55 -0500 Subject: [PATCH 17/56] fix test file swagger20/sameRefDifferentSource --- .../bundleExpected.json | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json index f0a84b2..06c61ac 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json @@ -87,21 +87,6 @@ } } }, - "/schemas/detail.yaml#/Detail": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "userName": { - "type": "string" - }, - "userDescription": { - "type": "string" - } - } - }, "/otherSchemas/detail.yaml#/Detail": { "type": "object", "properties": { @@ -116,6 +101,21 @@ "type": "string" } } + }, + "/schemas/detail.yaml#/Detail": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "userName": { + "type": "string" + }, + "userDescription": { + "type": "string" + } + } } } } \ No newline at end of file From 144138c63da3ccacaa107c3b4b75ef83ab8ed705 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Fri, 10 Jun 2022 20:44:24 -0500 Subject: [PATCH 18/56] Applying naming changes to expected files - Fix expected files - Fix jsonPointer tests - Adding a missing test in bundle - Adding changes related with the naming --- lib/bundle.js | 2 - lib/jsonPointer.js | 28 +++--- .../petstore separate yaml/bundleExp.yaml | 32 +++---- .../expected.json | 8 +- .../expected.json | 28 +++--- .../expected.json | 16 ++-- .../nestedProperties/expected.json | 20 ++--- .../toBundleExamples/properties/expected.json | 8 +- .../referenced_examples/expected.json | 4 +- .../expected.json | 8 +- .../same_ref_different_source/expected.json | 20 ++--- .../same_source_different_place/expected.json | 14 +-- .../schema_from_response/expected.json | 4 +- .../schema_from_response/expected.yaml | 4 +- .../schema_from_response/expected3_1.json | 4 +- .../bundleExpected.json | 8 +- .../bundleExpected.json | 28 +++--- .../bundleExpected.json | 8 +- .../bundleExpected.json | 4 +- .../nestedLocalRef/bundleExpected.json | 12 +-- .../nestedProperties20/bundleExpected.json | 16 ++-- .../swagger20/nestedRefs/bundleExpected.json | 8 +- .../expected.json | 8 +- .../bundleExpected.json | 16 ++-- .../swagger20/simpleRef/bundleExpected.json | 4 +- .../bundleExpected.json | 12 +-- .../with_parameters/expected.json | 12 +-- .../with_ref_in_items/expected.json | 8 +- test/unit/bundle.test.js | 62 +++++++++++++ test/unit/jsonPointer.test.js | 87 ++++++++++++++----- 30 files changed, 302 insertions(+), 191 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index 877a92f..c980333 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -3,7 +3,6 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), isExtRef, getKeyInComponents, getJsonPointerRelationToRoot, - jsonPointerEncodeAndReplace, removeLocalReferenceFromPath, localPointer, jsonPointerLevelSeparator, @@ -238,7 +237,6 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve const tempRef = calculatePath(parentFilename, property.$ref), nodeTrace = getTraceFromParentKeyInComponents(this, tempRef, version), referenceInDocument = getJsonPointerRelationToRoot( - jsonPointerEncodeAndReplace, tempRef, nodeTrace, version diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 265963f..98fe488 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -21,6 +21,17 @@ function jsonPointerEncodeAndReplace(filePathName) { return encodeURIComponent(filePathName.replace(tildes, escapedTildeString).replace(slashes, escapedSlashString)); } +/** + * Get a path and return a valid key name in openapi spec + * @param {string} filePathName - The filePath from the ref called + * @returns {string} A valid in openapi object name + */ +function generateObjectName(filePathName) { + let validName = filePathName.replace(/\//g, '_').replace(/#/g, '-'); + validName = validName.replace(/[^a-zA-Z0-9\.\-_]/g, ''); + return validName; +} + /** * Decodes a json pointer * replaces ~0 and ~1 for tildes and slashes @@ -50,39 +61,35 @@ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { else { result = getKeyInComponents30(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } - return result; + return result.map(generateObjectName); } /** * concats the inputs to generate the json pointer * @constructor -* @param {Function} encodeFunction function to encode url * @param {string} traceFromParent the trace from parent. * @param {string} targetInRoot - The root element where we will point * @returns {string} - the concatenated json pointer */ -function concatJsonPointer(encodeFunction, traceFromParent, targetInRoot) { - const traceFromParentAsString = traceFromParent.map((trace) => { - return encodeFunction(trace); - }).join('/'); +function concatJsonPointer(traceFromParent, targetInRoot) { + const traceFromParentAsString = traceFromParent.join('/'); return localPointer + targetInRoot + jsonPointerLevelSeparator + traceFromParentAsString; } /** * genereates the json pointer relative to the root * @constructor -* @param {Function} encodeFunction function to encode url * @param {string} refValue the type of component e.g. schemas, parameters, etc. * @param {string} traceFromKey the trace from the parent node. * @param {string} version - The version we are working on * @returns {string} - the concatenated json pointer */ -function getJsonPointerRelationToRoot(encodeFunction, refValue, traceFromKey, version) { +function getJsonPointerRelationToRoot(refValue, traceFromKey, version) { let targetInRoot = version === SWAGGER_VERSION ? '' : '/components'; if (refValue.startsWith(localPointer)) { return refValue; } - return concatJsonPointer(encodeFunction, traceFromKey, targetInRoot); + return concatJsonPointer(traceFromKey, targetInRoot); } /** @@ -195,5 +202,6 @@ module.exports = { getEntityName, isRemoteRef, localPointer, - jsonPointerLevelSeparator + jsonPointerLevelSeparator, + generateObjectName }; diff --git a/test/data/petstore separate yaml/bundleExp.yaml b/test/data/petstore separate yaml/bundleExp.yaml index e1d4781..c0c565a 100644 --- a/test/data/petstore separate yaml/bundleExp.yaml +++ b/test/data/petstore separate yaml/bundleExp.yaml @@ -53,19 +53,19 @@ paths: euismod sapien. operationId: findPets parameters: - - $ref: '#/components/parameters/~1spec~1parameters.yaml%23~1tagsParam' - - $ref: '#/components/parameters/~1spec~1parameters.yaml%23~1limitsParam' + - $ref: '#/components/parameters/_spec_parameters.yaml-_tagsParam' + - $ref: '#/components/parameters/_spec_parameters.yaml-_limitsParam' responses: '200': description: pet response schema: type: array items: - $ref: '#/components/schemas/~1spec~1Pet.yaml' + $ref: '#/components/schemas/_spec_Pet.yaml' default: description: unexpected error schema: - $ref: '#/components/schemas/~1common~1Error.yaml' + $ref: '#/components/schemas/_common_Error.yaml' post: description: Creates a new pet in the store. Duplicates are allowed operationId: addPet @@ -75,16 +75,16 @@ paths: description: Pet to add to the store required: true schema: - $ref: '#/components/schemas/~1spec~1NewPet.yaml' + $ref: '#/components/schemas/_spec_NewPet.yaml' responses: '200': description: pet response schema: - $ref: '#/components/schemas/~1spec~1Pet.yaml' + $ref: '#/components/schemas/_spec_Pet.yaml' default: description: unexpected error schema: - $ref: '#/components/schemas/~1common~1Error.yaml' + $ref: '#/components/schemas/_common_Error.yaml' '/pets/{id}': get: description: >- @@ -102,11 +102,11 @@ paths: '200': description: pet response schema: - $ref: '#/components/schemas/~1spec~1Pet.yaml' + $ref: '#/components/schemas/_spec_Pet.yaml' default: description: unexpected error schema: - $ref: '#/components/schemas/~1common~1Error.yaml' + $ref: '#/components/schemas/_common_Error.yaml' delete: description: deletes a single pet based on the ID supplied operationId: deletePet @@ -123,10 +123,10 @@ paths: default: description: unexpected error schema: - $ref: '#/components/schemas/~1common~1Error.yaml' + $ref: '#/components/schemas/_common_Error.yaml' components: parameters: - /spec/parameters.yaml#/tagsParam: + _spec_parameters.yaml-_tagsParam: name: tags in: query description: tags to filter by @@ -135,7 +135,7 @@ components: collectionFormat: csv items: type: string - /spec/parameters.yaml#/limitsParam: + _spec_parameters.yaml-_limitsParam: name: limit in: query description: maximum number of results to return @@ -143,7 +143,7 @@ components: type: integer format: int32 schemas: - /spec/Pet.yaml: + _spec_Pet.yaml: type: object required: - id @@ -156,7 +156,7 @@ components: type: string tag: type: string - /common/Error.yaml: + _common_Error.yaml: type: object required: - code @@ -167,10 +167,10 @@ components: format: int32 message: type: string - /spec/NewPet.yaml: + _spec_NewPet.yaml: type: object allOf: - - $ref: '#/components/schemas/~1spec~1Pet.yaml' + - $ref: '#/components/schemas/_spec_Pet.yaml' - required: - name properties: diff --git a/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json b/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json index 7aa1b10..33664b7 100644 --- a/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json +++ b/test/data/toBundleExamples/bring_local_dependencies_from_external/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1User" + "$ref": "#/components/schemas/_schemas_user.yaml-_User" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml#/User": { + "_schemas_user.yaml-_User": { "type": "object", "properties": { "id": { @@ -49,13 +49,13 @@ "type": "object", "properties": { "theUsersPet": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Pet" + "$ref": "#/components/schemas/_schemas_user.yaml-_Pet" } } } } }, - "/schemas/user.yaml#/Pet": { + "_schemas_user.yaml-_Pet": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json b/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json index 19b6f4c..5dcd820 100644 --- a/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json +++ b/test/data/toBundleExamples/bring_local_dependencies_from_external_multiple_local/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1User" + "$ref": "#/components/schemas/_schemas_user.yaml-_User" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml#/User": { + "_schemas_user.yaml-_User": { "type": "object", "properties": { "id": { @@ -49,25 +49,25 @@ "type": "object", "properties": { "favoriteFood": { - "$ref": "#/components/schemas/~1schemas~1food.yaml%23Food" + "$ref": "#/components/schemas/_schemas_food.yaml-Food" }, "theUsersPet": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Pet" + "$ref": "#/components/schemas/_schemas_user.yaml-_Pet" } } } } }, - "/schemas/food.yaml#Food": { + "_schemas_food.yaml-Food": { "type": "object", "properties": { "brand": { - "$ref": "#/components/schemas/~1schemas~1food.yaml%23~1Brand" + "$ref": "#/components/schemas/_schemas_food.yaml-_Brand" }, "benefits": { "type": "array", "items": { - "$ref": "#/components/schemas/~1schemas~1food.yaml%23~1Benefit" + "$ref": "#/components/schemas/_schemas_food.yaml-_Benefit" } }, "cost": { @@ -75,7 +75,7 @@ } } }, - "/schemas/user.yaml#/Pet": { + "_schemas_user.yaml-_Pet": { "type": "object", "properties": { "name": { @@ -85,23 +85,23 @@ "type": "string" }, "color": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Colors" + "$ref": "#/components/schemas/_schemas_user.yaml-_Colors" } } }, - "/schemas/user.yaml#/Colors": { + "_schemas_user.yaml-_Colors": { "type": "array", "items": { - "$ref": "#/components/schemas/~1schemas~1user.yaml%23~1Color" + "$ref": "#/components/schemas/_schemas_user.yaml-_Color" } }, - "/schemas/user.yaml#/Color": { + "_schemas_user.yaml-_Color": { "type": "string" }, - "/schemas/food.yaml#/Brand": { + "_schemas_food.yaml-_Brand": { "type": "string" }, - "/schemas/food.yaml#/Benefit": { + "_schemas_food.yaml-_Benefit": { "type": "object", "properties": { "benefit": { diff --git a/test/data/toBundleExamples/multiple_references_from_root_components/expected.json b/test/data/toBundleExamples/multiple_references_from_root_components/expected.json index 4081bea..63389b7 100644 --- a/test/data/toBundleExamples/multiple_references_from_root_components/expected.json +++ b/test/data/toBundleExamples/multiple_references_from_root_components/expected.json @@ -80,13 +80,13 @@ "type": "object", "properties": { "userInfo": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" }, "carType": { - "$ref": "#/components/schemas/~1schemas~1carType.yaml" + "$ref": "#/components/schemas/_schemas_carType.yaml" }, "work": { - "$ref": "#/components/schemas/~1otherSchemas~1work.yaml" + "$ref": "#/components/schemas/_otherSchemas_work.yaml" } } }, @@ -101,7 +101,7 @@ } } }, - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { @@ -112,15 +112,15 @@ } } }, - "/schemas/carType.yaml": { + "_schemas_carType.yaml": { "type": "object", "properties": { "model": { - "$ref": "#/components/schemas/~1otherSchemas~1model.yaml" + "$ref": "#/components/schemas/_otherSchemas_model.yaml" } } }, - "/otherSchemas/work.yaml": { + "_otherSchemas_work.yaml": { "type": "object", "properties": { "office": { @@ -128,7 +128,7 @@ } } }, - "/otherSchemas/model.yaml": { + "_otherSchemas_model.yaml": { "type": "string" } } diff --git a/test/data/toBundleExamples/nestedProperties/expected.json b/test/data/toBundleExamples/nestedProperties/expected.json index 9edf318..8fbb408 100644 --- a/test/data/toBundleExamples/nestedProperties/expected.json +++ b/test/data/toBundleExamples/nestedProperties/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { @@ -46,11 +46,11 @@ "type": "string" }, "complexProp": { - "$ref": "#/components/schemas/~1properties~1prop.yaml" + "$ref": "#/components/schemas/_properties_prop.yaml" } } }, - "/properties/prop.yaml": { + "_properties_prop.yaml": { "type": "object", "properties": { "firstName": { @@ -63,17 +63,17 @@ "type": "integer" }, "nestedProp": { - "$ref": "#/components/schemas/~1properties~1nestedProp.yaml" + "$ref": "#/components/schemas/_properties_nestedProp.yaml" }, "country": { - "$ref": "#/components/schemas/~1properties~1country.yaml" + "$ref": "#/components/schemas/_properties_country.yaml" }, "warrior": { - "$ref": "#/components/schemas/~1properties~1warrior.yaml" + "$ref": "#/components/schemas/_properties_warrior.yaml" } } }, - "/properties/nestedProp.yaml": { + "_properties_nestedProp.yaml": { "type": "object", "rock": { "type": "boolean" @@ -99,7 +99,7 @@ } } }, - "/properties/country.yaml": { + "_properties_country.yaml": { "type": "object", "properties": { "region": { @@ -110,7 +110,7 @@ } } }, - "/properties/warrior.yaml": { + "_properties_warrior.yaml": { "type": "object", "properties": { "power": { diff --git a/test/data/toBundleExamples/properties/expected.json b/test/data/toBundleExamples/properties/expected.json index 48c9877..87fc3ce 100644 --- a/test/data/toBundleExamples/properties/expected.json +++ b/test/data/toBundleExamples/properties/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { @@ -46,11 +46,11 @@ "type": "string" }, "complexProp": { - "$ref": "#/components/schemas/~1schemas~1prop.yaml" + "$ref": "#/components/schemas/_schemas_prop.yaml" } } }, - "/schemas/prop.yaml": { + "_schemas_prop.yaml": { "type": "object", "properties": { "firstName": { diff --git a/test/data/toBundleExamples/referenced_examples/expected.json b/test/data/toBundleExamples/referenced_examples/expected.json index 2067a82..9ace3da 100644 --- a/test/data/toBundleExamples/referenced_examples/expected.json +++ b/test/data/toBundleExamples/referenced_examples/expected.json @@ -32,7 +32,7 @@ } }, "example": { - "$ref": "#/components/examples/~1examples.yaml%23~1foo" + "$ref": "#/components/examples/_examples.yaml-_foo" } } } @@ -88,7 +88,7 @@ } }, "examples": { - "/examples.yaml#/foo": { + "_examples.yaml-_foo": { "summary": "sum", "value": { "code": 1, diff --git a/test/data/toBundleExamples/referenced_paths_local_schema/expected.json b/test/data/toBundleExamples/referenced_paths_local_schema/expected.json index 2797fb7..b79488f 100644 --- a/test/data/toBundleExamples/referenced_paths_local_schema/expected.json +++ b/test/data/toBundleExamples/referenced_paths_local_schema/expected.json @@ -28,7 +28,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/~1paths~1path.yaml%23~1components~1schemas~1Pet" + "$ref": "#/components/schemas/_paths_path.yaml-_components_schemas_Pet" } } } @@ -39,7 +39,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1paths~1path.yaml%23~1components~1schemas~1Error" + "$ref": "#/components/schemas/_paths_path.yaml-_components_schemas_Error" } } } @@ -83,10 +83,10 @@ } } }, - "/paths/path.yaml#/components/schemas/Pet": { + "_paths_path.yaml-_components_schemas_Pet": { "$ref": "#/components/schemas/Pet" }, - "/paths/path.yaml#/components/schemas/Error": { + "_paths_path.yaml-_components_schemas_Error": { "$ref": "#/components/schemas/Error" } } diff --git a/test/data/toBundleExamples/same_ref_different_source/expected.json b/test/data/toBundleExamples/same_ref_different_source/expected.json index 5b81da0..5be8104 100644 --- a/test/data/toBundleExamples/same_ref_different_source/expected.json +++ b/test/data/toBundleExamples/same_ref_different_source/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user~1user.yaml" + "$ref": "#/components/schemas/_schemas_user_user.yaml" } } } @@ -42,7 +42,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1client~1client.yaml" + "$ref": "#/components/schemas/_schemas_client_client.yaml" } } } @@ -53,7 +53,7 @@ }, "components": { "schemas": { - "/schemas/user/user.yaml": { + "_schemas_user_user.yaml": { "type": "object", "properties": { "id": { @@ -63,11 +63,11 @@ "type": "string" }, "special": { - "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" + "$ref": "#/components/schemas/_schemas_user_special.yaml" } } }, - "/schemas/client/client.yaml": { + "_schemas_client_client.yaml": { "type": "object", "properties": { "idClient": { @@ -77,22 +77,22 @@ "type": "string" }, "special": { - "$ref": "#/components/schemas/~1schemas~1client~1special.yaml" + "$ref": "#/components/schemas/_schemas_client_special.yaml" } } }, - "/schemas/client/special.yaml": { + "_schemas_client_special.yaml": { "type": "object", "properties": { "specialClientId": { "type": "string" }, "magic": { - "$ref": "#/components/schemas/~1schemas~1client~1magic.yaml" + "$ref": "#/components/schemas/_schemas_client_magic.yaml" } } }, - "/schemas/client/magic.yaml": { + "_schemas_client_magic.yaml": { "type": "object", "properties": { "magicNumber": { @@ -103,7 +103,7 @@ } } }, - "/schemas/user/special.yaml": { + "_schemas_user_special.yaml": { "type": "object", "properties": { "specialUserId": { diff --git a/test/data/toBundleExamples/same_source_different_place/expected.json b/test/data/toBundleExamples/same_source_different_place/expected.json index d701d31..4fc83ea 100644 --- a/test/data/toBundleExamples/same_source_different_place/expected.json +++ b/test/data/toBundleExamples/same_source_different_place/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user~1user.yaml" + "$ref": "#/components/schemas/_schemas_user_user.yaml" } } } @@ -42,7 +42,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1client~1client.yaml" + "$ref": "#/components/schemas/_schemas_client_client.yaml" } } } @@ -53,7 +53,7 @@ }, "components": { "schemas": { - "/schemas/user/user.yaml": { + "_schemas_user_user.yaml": { "type": "object", "properties": { "id": { @@ -63,11 +63,11 @@ "type": "string" }, "special": { - "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" + "$ref": "#/components/schemas/_schemas_user_special.yaml" } } }, - "/schemas/client/client.yaml": { + "_schemas_client_client.yaml": { "type": "object", "properties": { "idClient": { @@ -77,11 +77,11 @@ "type": "string" }, "special": { - "$ref": "#/components/schemas/~1schemas~1user~1special.yaml" + "$ref": "#/components/schemas/_schemas_user_special.yaml" } } }, - "/schemas/user/special.yaml": { + "_schemas_user_special.yaml": { "type": "object", "properties": { "specialUserId": { diff --git a/test/data/toBundleExamples/schema_from_response/expected.json b/test/data/toBundleExamples/schema_from_response/expected.json index 46c7d7f..e8f6e09 100644 --- a/test/data/toBundleExamples/schema_from_response/expected.json +++ b/test/data/toBundleExamples/schema_from_response/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { diff --git a/test/data/toBundleExamples/schema_from_response/expected.yaml b/test/data/toBundleExamples/schema_from_response/expected.yaml index e405d31..5e2ea37 100644 --- a/test/data/toBundleExamples/schema_from_response/expected.yaml +++ b/test/data/toBundleExamples/schema_from_response/expected.yaml @@ -20,10 +20,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/~1schemas~1user.yaml' + $ref: '#/components/schemas/_schemas_user.yaml' components: schemas: - /schemas/user.yaml: + _schemas_user.yaml: type: object properties: id: diff --git a/test/data/toBundleExamples/schema_from_response/expected3_1.json b/test/data/toBundleExamples/schema_from_response/expected3_1.json index f4550a4..996cd3b 100644 --- a/test/data/toBundleExamples/schema_from_response/expected3_1.json +++ b/test/data/toBundleExamples/schema_from_response/expected3_1.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json index 74a6a8a..1841277 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json @@ -24,7 +24,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/~1pet.yaml%23~1Pet" + "$ref": "#/definitions/_pet.yaml-_Pet" } } } @@ -33,7 +33,7 @@ } }, "definitions": { - "/pet.yaml#/Pet": { + "_pet.yaml-_Pet": { "type": "object", "required": [ "id", @@ -51,11 +51,11 @@ "type": "string" }, "Color": { - "$ref": "#/definitions/~1pet.yaml%23~1Color" + "$ref": "#/definitions/_pet.yaml-_Color" } } }, - "/pet.yaml#/Color": { + "_pet.yaml-_Color": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json index 2579a1c..e6508db 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json @@ -17,10 +17,10 @@ "description": "Optional extended description in Markdown.", "parameters": [ { - "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter1" + "$ref": "#/parameters/_parameters_parameters.yaml-_Parameter1" }, { - "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter2" + "$ref": "#/parameters/_parameters_parameters.yaml-_Parameter2" } ], "produces": [ @@ -32,7 +32,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/~1pet.yaml%23~1Pet" + "$ref": "#/definitions/_pet.yaml-_Pet" } } } @@ -41,7 +41,7 @@ } }, "parameters": { - "/parameters/parameters.yaml#/Parameter1": { + "_parameters_parameters.yaml-_Parameter1": { "name": "tags", "in": "query", "description": "tags to filter by", @@ -52,7 +52,7 @@ "type": "string" } }, - "/parameters/parameters.yaml#/Parameter2": { + "_parameters_parameters.yaml-_Parameter2": { "name": "limit", "in": "query", "description": "maximum number of results to return", @@ -62,7 +62,7 @@ } }, "definitions": { - "/pet.yaml#/Pet": { + "_pet.yaml-_Pet": { "type": "object", "required": [ "id", @@ -80,14 +80,14 @@ "type": "string" }, "Color": { - "$ref": "#/definitions/~1pet.yaml%23~1Color" + "$ref": "#/definitions/_pet.yaml-_Color" }, "FavoriteFood": { - "$ref": "#/definitions/~1food.yaml%23~1Food" + "$ref": "#/definitions/_food.yaml-_Food" } } }, - "/pet.yaml#/Color": { + "_pet.yaml-_Color": { "type": "object", "properties": { "name": { @@ -101,16 +101,16 @@ } } }, - "/food.yaml#/Food": { + "_food.yaml-_Food": { "type": "object", "properties": { "brand": { - "$ref": "#/definitions/~1food.yaml%23~1Brand" + "$ref": "#/definitions/_food.yaml-_Brand" }, "benefits": { "type": "array", "items": { - "$ref": "#/definitions/~1food.yaml%23~1Benefit" + "$ref": "#/definitions/_food.yaml-_Benefit" } }, "cost": { @@ -118,10 +118,10 @@ } } }, - "/food.yaml#/Brand": { + "_food.yaml-_Brand": { "type": "string" }, - "/food.yaml#/Benefit": { + "_food.yaml-_Benefit": { "type": "object", "properties": { "benefit": { diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json index b43a148..d2395ea 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalWithItems/bundleExpected.json @@ -24,7 +24,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/~1pet.yaml%23~1Pet" + "$ref": "#/definitions/_pet.yaml-_Pet" } } } @@ -33,7 +33,7 @@ } }, "definitions": { - "/pet.yaml#/Pet": { + "_pet.yaml-_Pet": { "type": "object", "required": [ "id", @@ -53,12 +53,12 @@ "Colors": { "type": "array", "items": { - "$ref": "#/definitions/~1pet.yaml%23~1Color" + "$ref": "#/definitions/_pet.yaml-_Color" } } } }, - "/pet.yaml#/Color": { + "_pet.yaml-_Color": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json index d673521..f8046e8 100644 --- a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json @@ -52,11 +52,11 @@ "type": "string" }, "Color": { - "$ref": "#/definitions/~1pet.yaml%23~1Color" + "$ref": "#/definitions/_pet.yaml-_Color" } } }, - "/pet.yaml#/Color": { + "_pet.yaml-_Color": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json index 647921b..a84aa62 100644 --- a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json @@ -21,7 +21,7 @@ "200": { "description": "A list of pets.,", "schema": { - "$ref": "#/definitions/~1schemas~1pet.yaml" + "$ref": "#/definitions/_schemas_pet.yaml" } } } @@ -29,7 +29,7 @@ } }, "definitions": { - "/schemas/pet.yaml": { + "_schemas_pet.yaml": { "type": "object", "required": [ "id", @@ -47,14 +47,14 @@ "type": "string" }, "favoriteFood": { - "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1FavoriteFood" + "$ref": "#/definitions/_schemas_favorite_food.yaml-_FavoriteFood" }, "foodPrice": { - "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1Price" + "$ref": "#/definitions/_schemas_favorite_food.yaml-_Price" } } }, - "/schemas/favorite_food.yaml#/FavoriteFood": { + "_schemas_favorite_food.yaml-_FavoriteFood": { "type": "object", "properties": { "name": { @@ -65,7 +65,7 @@ } } }, - "/schemas/favorite_food.yaml#/Price": { + "_schemas_favorite_food.yaml-_Price": { "type": "integer" } } diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json index eca6b1e..2013c18 100644 --- a/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json @@ -24,7 +24,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/~1schemas~1user.yaml" + "$ref": "#/definitions/_schemas_user.yaml" } } } @@ -33,27 +33,27 @@ } }, "definitions": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "age": { - "$ref": "#/definitions/~1schemas~1age.yaml" + "$ref": "#/definitions/_schemas_age.yaml" }, "hobbies": { - "$ref": "#/definitions/~1schemas~1hobbies.yaml" + "$ref": "#/definitions/_schemas_hobbies.yaml" } } }, - "/schemas/age.yaml": { + "_schemas_age.yaml": { "type": "string" }, - "/schemas/hobbies.yaml": { + "_schemas_hobbies.yaml": { "type": "array", "items": { - "$ref": "#/definitions/~1schemas~1hobby.yaml" + "$ref": "#/definitions/_schemas_hobby.yaml" } }, - "/schemas/hobby.yaml": { + "_schemas_hobby.yaml": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json index 90b2aa1..69dc891 100644 --- a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json @@ -21,7 +21,7 @@ "200": { "description": "A list of pets.,", "schema": { - "$ref": "#/definitions/~1schemas~1pet.yaml" + "$ref": "#/definitions/_schemas_pet.yaml" } } } @@ -29,7 +29,7 @@ } }, "definitions": { - "/schemas/pet.yaml": { + "_schemas_pet.yaml": { "type": "object", "required": [ "id", @@ -47,11 +47,11 @@ "type": "string" }, "favoriteFood": { - "$ref": "#/definitions/~1schemas~1favorite_food.yaml" + "$ref": "#/definitions/_schemas_favorite_food.yaml" } } }, - "/schemas/favorite_food.yaml": { + "_schemas_favorite_food.yaml": { "type": "object", "properties": { "name": { diff --git a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json index 700e64a..649315c 100644 --- a/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json +++ b/test/data/toBundleExamples/swagger20/referenced_paths_local_schema/expected.json @@ -22,7 +22,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/definitions/~1paths~1path.yaml%23~1definitions~1Error" + "$ref": "#/definitions/_paths_path.yaml-_definitions_Error" } }, "description": "Returns all pets from the system that the user has access to,", @@ -33,7 +33,7 @@ "200": { "description": "A list of pets.,", "schema": { - "$ref": "#/definitions/~1paths~1path.yaml%23~1definitions~1Pet" + "$ref": "#/definitions/_paths_path.yaml-_definitions_Pet" } } } @@ -75,10 +75,10 @@ } } }, - "/paths/path.yaml#/definitions/Error": { + "_paths_path.yaml-_definitions_Error": { "$ref": "#/definitions/Error" }, - "/paths/path.yaml#/definitions/Pet": { + "_paths_path.yaml-_definitions_Pet": { "$ref": "#/definitions/Pet" } } diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json index 620a304..f749d5f 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json @@ -22,7 +22,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1schemas~1user.yaml%23User" + "$ref": "#/definitions/_schemas_user.yaml-User" } } } @@ -39,7 +39,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1otherSchemas~1client.yaml%23Client" + "$ref": "#/definitions/_otherSchemas_client.yaml-Client" } } } @@ -47,7 +47,7 @@ } }, "definitions": { - "/schemas/user.yaml#User": { + "_schemas_user.yaml-User": { "type": "object", "required": [ "id", @@ -60,12 +60,12 @@ "detail": { "type": "array", "items": { - "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" + "$ref": "#/definitions/_schemas_detail.yaml-_Detail" } } } }, - "/otherSchemas/client.yaml#Client": { + "_otherSchemas_client.yaml-Client": { "type": "object", "required": [ "id", @@ -78,12 +78,12 @@ "details": { "type": "array", "items": { - "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" + "$ref": "#/definitions/_otherSchemas_detail.yaml-_Detail" } } } }, - "/otherSchemas/detail.yaml#/Detail": { + "_otherSchemas_detail.yaml-_Detail": { "type": "object", "properties": { "id": { @@ -98,7 +98,7 @@ } } }, - "/schemas/detail.yaml#/Detail": { + "_schemas_detail.yaml-_Detail": { "type": "object", "properties": { "id": { diff --git a/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json index d0a7b42..9977af4 100644 --- a/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/simpleRef/bundleExpected.json @@ -21,7 +21,7 @@ "200": { "description": "A list of pets.,", "schema": { - "$ref": "#/definitions/~1pet.yaml" + "$ref": "#/definitions/_pet.yaml" } } } @@ -29,7 +29,7 @@ } }, "definitions": { - "/pet.yaml": { + "_pet.yaml": { "type": "object", "required": [ "id", diff --git a/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json b/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json index 229d1f3..756036b 100644 --- a/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/withParametersAndItems/bundleExpected.json @@ -17,10 +17,10 @@ "operationId": "findPets", "parameters": [ { - "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter1" + "$ref": "#/parameters/_parameters_parameters.yaml-_Parameter1" }, { - "$ref": "#/parameters/~1parameters~1parameters.yaml%23~1Parameter2" + "$ref": "#/parameters/_parameters_parameters.yaml-_Parameter2" } ], "responses": { @@ -29,7 +29,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/~1pet.yaml" + "$ref": "#/definitions/_pet.yaml" } } } @@ -38,7 +38,7 @@ } }, "parameters": { - "/parameters/parameters.yaml#/Parameter1": { + "_parameters_parameters.yaml-_Parameter1": { "name": "tags", "in": "query", "description": "tags to filter by", @@ -49,7 +49,7 @@ "type": "string" } }, - "/parameters/parameters.yaml#/Parameter2": { + "_parameters_parameters.yaml-_Parameter2": { "name": "limit", "in": "query", "description": "maximum number of results to return", @@ -59,7 +59,7 @@ } }, "definitions": { - "/pet.yaml": { + "_pet.yaml": { "type": "object", "required": [ "id", diff --git a/test/data/toBundleExamples/with_parameters/expected.json b/test/data/toBundleExamples/with_parameters/expected.json index f73fee2..6c29166 100644 --- a/test/data/toBundleExamples/with_parameters/expected.json +++ b/test/data/toBundleExamples/with_parameters/expected.json @@ -21,10 +21,10 @@ "summary": "Get a user by ID", "parameters": [ { - "$ref": "#/components/parameters/~1parameters~1index.yaml%23tagsParam" + "$ref": "#/components/parameters/_parameters_index.yaml-tagsParam" }, { - "$ref": "#/components/parameters/~1parameters~1index.yaml%23limitsParam" + "$ref": "#/components/parameters/_parameters_index.yaml-limitsParam" } ], "responses": { @@ -33,7 +33,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -44,7 +44,7 @@ }, "components": { "parameters": { - "/parameters/index.yaml#tagsParam": { + "_parameters_index.yaml-tagsParam": { "name": "tags", "in": "query", "description": "tags to filter by", @@ -55,7 +55,7 @@ "type": "string" } }, - "/parameters/index.yaml#limitsParam": { + "_parameters_index.yaml-limitsParam": { "name": "limit", "in": "query", "description": "maximum number of results to return", @@ -65,7 +65,7 @@ } }, "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { diff --git a/test/data/toBundleExamples/with_ref_in_items/expected.json b/test/data/toBundleExamples/with_ref_in_items/expected.json index 8570de7..c09d83f 100644 --- a/test/data/toBundleExamples/with_ref_in_items/expected.json +++ b/test/data/toBundleExamples/with_ref_in_items/expected.json @@ -25,7 +25,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/~1schemas~1user.yaml" + "$ref": "#/components/schemas/_schemas_user.yaml" } } } @@ -36,7 +36,7 @@ }, "components": { "schemas": { - "/schemas/user.yaml": { + "_schemas_user.yaml": { "type": "object", "properties": { "id": { @@ -48,12 +48,12 @@ "superProps": { "type": "array", "items": { - "$ref": "#/components/schemas/~1schemas~1superProp.yaml" + "$ref": "#/components/schemas/_schemas_superProp.yaml" } } } }, - "/schemas/superProp.yaml": { + "_schemas_superProp.yaml": { "type": "object", "properties": { "superInt": { diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 3669c61..f43f951 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -1491,6 +1491,52 @@ describe('bundle files method - 3.0', function () { expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expectedJSON); expect(res.output.data[1].bundledContent).to.be.equal(expectedYAML); }); + + it('Should take the root file from data array root file prop undefined', async function () { + let contentRootFile = fs.readFileSync(sameRefDiffSource + '/root.yaml', 'utf8'), + user = fs.readFileSync(sameRefDiffSource + '/schemas/user/user.yaml', 'utf8'), + client = fs.readFileSync(sameRefDiffSource + '/schemas/client/client.yaml', 'utf8'), + specialUser = fs.readFileSync(sameRefDiffSource + '/schemas/user/special.yaml', 'utf8'), + specialClient = fs.readFileSync(sameRefDiffSource + '/schemas/client/special.yaml', 'utf8'), + magic = fs.readFileSync(sameRefDiffSource + '/schemas/client/magic.yaml', 'utf8'), + expected = fs.readFileSync(sameRefDiffSource + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas/user/user.yaml', + content: user + }, + { + path: '/schemas/user/special.yaml', + content: specialUser + }, + { + path: '/schemas/client/client.yaml', + content: client + }, + { + path: '/schemas/client/special.yaml', + content: specialClient + }, + { + path: '/schemas/client/magic.yaml', + content: magic + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('bundle files method - 2.0', function() { @@ -1545,6 +1591,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1601,6 +1648,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1647,6 +1695,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1698,6 +1747,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1739,6 +1789,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1780,6 +1831,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1826,6 +1878,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1872,6 +1925,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1918,6 +1972,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1954,6 +2009,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -1995,6 +2051,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -2026,6 +2083,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -2057,6 +2115,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -2093,6 +2152,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -2129,6 +2189,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); @@ -2160,6 +2221,7 @@ describe('bundle files method - 2.0', function() { bundleFormat: 'JSON' }; const res = await Converter.bundle(input); + expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); diff --git a/test/unit/jsonPointer.test.js b/test/unit/jsonPointer.test.js index 2742680..829e89f 100644 --- a/test/unit/jsonPointer.test.js +++ b/test/unit/jsonPointer.test.js @@ -1,53 +1,94 @@ const expect = require('chai').expect, - { jsonPointerEncodeAndReplace, + { getJsonPointerRelationToRoot, concatJsonPointer, - getKeyInComponents } = require('./../../lib/jsonPointer'); + getKeyInComponents + } = require('./../../lib/jsonPointer'); describe('getKeyInComponents function', function () { it('should return [] when is pointing to an element in components', function () { const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '', '3.0'); + expect(result).to.be.an('array').with.length(0); }); it('should return [] when is pointing to a local ref in components', function () { const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world', '3.0'); + expect(result).to.be.an('array').with.length(0); }); - it('should return ["schemas", "folder/pet.yaml"] when there is an scaped slash', function () { - const result = getKeyInComponents(['path', 'schemas'], 'folder~1pet.yaml', '', '3.0'); + it('should return ["schemas", "_folder_pet.yaml"] when the filename scaped slash', function () { + const result = getKeyInComponents(['path', 'schemas'], '~1folder~1pet.yaml'); + expect(result).to.be.an('array').with.length(2); expect(result[0]).to.equal('schemas'); + expect(result[1]).to.equal('_folder_pet.yaml'); + }); + + it('should return ["schemas", "_folder_pet.yaml"] when the filename contains any slash', function () { + const result = getKeyInComponents(['path', 'schemas'], '/folder/pet.yaml'); + + expect(result).to.be.an('array').with.length(2); + expect(result[0]).to.equal('schemas'); + expect(result[1]).to.equal('_folder_pet.yaml'); + }); + + it('should return ["schemas", "_folder_pet.yaml-_Name"] when the filename contains any sacped slash' + + 'and a local part using a scaped sharp', function () { + const result = getKeyInComponents(['path', 'schemas'], '~1folder~1pet.yaml%23~1Name'); + + expect(result).to.be.an('array').with.length(2); + expect(result[0]).to.equal('schemas'); + expect(result[1]).to.equal('_folder_pet.yaml-_Name'); + }); + + it('should return ["schemas", "_folder_pet.yaml-_Name"] when the filename contains any slash' + + 'and a local part using a sharp', function () { + const result = getKeyInComponents(['path', 'schemas'], '/folder/pet.yaml#/Name'); + + expect(result).to.be.an('array').with.length(2); + expect(result[0]).to.equal('schemas'); + expect(result[1]).to.equal('_folder_pet.yaml-_Name'); + }); + + it('should return ["schemas", "_foldertest_pet.yaml-_Name"] when the filename contains any slash' + + 'and a local part using a sharp and another not valid character', function () { + const result = getKeyInComponents(['path', 'schemas'], '/folder@test/pet@.yaml#/Name'); + + expect(result).to.be.an('array').with.length(2); + expect(result[0]).to.equal('schemas'); + expect(result[1]).to.equal('_foldertest_pet.yaml-_Name'); }); }); - describe('getJsonPointerRelationToRoot function', function () { it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () { let res = getJsonPointerRelationToRoot( - jsonPointerEncodeAndReplace, 'Pets.yaml', ['schemas', 'Pets.yaml'] ); + expect(res).to.equal('#/components/schemas/Pets.yaml'); }); + it('should return "#/components/schemas/hello.yaml" no local path and schema', function () { let res = getJsonPointerRelationToRoot( - jsonPointerEncodeAndReplace, 'hello.yaml#/definitions/world', ['schemas', 'hello.yaml'] ); + expect(res).to.equal('#/components/schemas/hello.yaml'); }); + it('should return "#/components/schemas/Error" no file path', function () { let res = getJsonPointerRelationToRoot( - jsonPointerEncodeAndReplace, '#/components/schemas/Error', ['components', 'schemas', 'Error'] ); + expect(res).to.equal('#/components/schemas/Error'); }); }); @@ -55,45 +96,47 @@ describe('getJsonPointerRelationToRoot function', function () { describe('concatJsonPointer function ', function () { it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () { let res = concatJsonPointer( - jsonPointerEncodeAndReplace, ['schemas', 'Pets.yaml'], '/components' ); + expect(res).to.equal('#/components/schemas/Pets.yaml'); }); - it('should return "#/components/schemas/other~1Pets.yaml" no local path and schema folder in filename', function () { + it('should return "#/components/schemas/other_Pets.yaml" no local path and schema folder in filename', function () { let res = concatJsonPointer( - jsonPointerEncodeAndReplace, - ['schemas', 'other/Pets.yaml'], + ['schemas', 'other_Pets.yaml'], '/components' ); - expect(res).to.equal('#/components/schemas/other~1Pets.yaml'); + + expect(res).to.equal('#/components/schemas/other_Pets.yaml'); }); - it('should return "#/components/schemas/some~1Pet" no local path and schema folder in filename', function () { + + it('should return "#/components/schemas/some_Pet" no local path and schema folder in filename', function () { let res = concatJsonPointer( - jsonPointerEncodeAndReplace, - ['schemas', 'some/Pet.yaml'], + ['schemas', 'some_Pet.yaml'], '/components' ); - expect(res).to.equal('#/components/schemas/some~1Pet.yaml'); + + expect(res).to.equal('#/components/schemas/some_Pet.yaml'); }); + it('should return "#/components/schemas/hello.yaml" no local path and schema', function () { let res = concatJsonPointer( - jsonPointerEncodeAndReplace, ['schemas', 'hello.yaml'], '/components' ); + expect(res).to.equal('#/components/schemas/hello.yaml'); }); - it('should return "#/components/schemas/~1Pets.yaml" no local path and schema', function () { + it('should return "#/components/schemas/_Pets.yaml" no local path and schema', function () { let res = concatJsonPointer( - jsonPointerEncodeAndReplace, - ['schemas', '/Pets.yaml'], + ['schemas', '_Pets.yaml'], '/components' ); - expect(res).to.equal('#/components/schemas/~1Pets.yaml'); + + expect(res).to.equal('#/components/schemas/_Pets.yaml'); }); }); From 361c5d8298673bef84f2a47a9a85e23773a88ff7 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Mon, 13 Jun 2022 16:40:05 -0500 Subject: [PATCH 19/56] Delete unnecesary test Delete unnecesary test --- .../swagger20/referenced_info/expected.json | 78 ------------------- .../swagger20/referenced_info/info/info.yaml | 11 --- .../swagger20/referenced_info/root.yaml | 42 ---------- test/unit/bundle.test.js | 33 -------- 4 files changed, 164 deletions(-) delete mode 100644 test/data/toBundleExamples/swagger20/referenced_info/expected.json delete mode 100644 test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml delete mode 100644 test/data/toBundleExamples/swagger20/referenced_info/root.yaml diff --git a/test/data/toBundleExamples/swagger20/referenced_info/expected.json b/test/data/toBundleExamples/swagger20/referenced_info/expected.json deleted file mode 100644 index ce26f83..0000000 --- a/test/data/toBundleExamples/swagger20/referenced_info/expected.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "version": "1.0.0", - "title": "Swagger Petstore", - "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", - "termsOfService": "http://swagger.io/terms/", - "contact": { - "name": "Swagger API Team", - "email": "apiteam@swagger.io", - "url": "http://swagger.io" - }, - "license": { - "name": "Apache 2.0", - "url": "https://www.apache.org/licenses/LICENSE-2.0.html" - } - }, - "paths": { - "/pets": { - "get": { - "description": "Returns all pets alesuada ac...", - "operationId": "findPets", - "responses": { - "200": { - "description": "pet response", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Pet" - } - } - }, - "default": { - "description": "unexpected error", - "schema": { - "$ref": "#/definitions/Error" - } - } - } - } - } - }, - "definitions": { - "Pet": { - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "Error": { - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml b/test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml deleted file mode 100644 index 938fb64..0000000 --- a/test/data/toBundleExamples/swagger20/referenced_info/info/info.yaml +++ /dev/null @@ -1,11 +0,0 @@ -version: 1.0.0 -title: Swagger Petstore -description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification -termsOfService: http://swagger.io/terms/ -contact: - name: Swagger API Team - email: apiteam@swagger.io - url: http://swagger.io -license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html diff --git a/test/data/toBundleExamples/swagger20/referenced_info/root.yaml b/test/data/toBundleExamples/swagger20/referenced_info/root.yaml deleted file mode 100644 index 97a29cc..0000000 --- a/test/data/toBundleExamples/swagger20/referenced_info/root.yaml +++ /dev/null @@ -1,42 +0,0 @@ -swagger: '2.0' -info: - "$ref": "./info/info.yaml" -paths: - /pets: - get: - description: Returns all pets alesuada ac... - operationId: findPets - responses: - '200': - description: pet response - schema: - type: array - items: - $ref: '#/definitions/Pet' - default: - description: unexpected error - schema: - $ref: '#/definitions/Error' -definitions: - Pet: - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Error: - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 7308b2e..d3be6cf 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -24,7 +24,6 @@ let expect = require('chai').expect, refPaths = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths'), SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), - refInfo20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_info'), refTags20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_tags'), basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'), @@ -2289,38 +2288,6 @@ describe('bundle files method - 2.0', function() { expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); - it('Should return bundled file with referenced info from root', async function () { - let contentRootFile = fs.readFileSync(refInfo20 + '/root.yaml', 'utf8'), - info = fs.readFileSync(refInfo20 + '/info/info.yaml', 'utf8'), - expected = fs.readFileSync(refInfo20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/info/info.yaml', - content: info - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - it('Should return bundled file with referenced tags from root', async function () { let contentRootFile = fs.readFileSync(refTags20 + '/root.yaml', 'utf8'), tags = fs.readFileSync(refTags20 + '/tags/tags.yaml', 'utf8'), From 7d8dc86462a169d1a921a22c735478e51d442136 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Mon, 13 Jun 2022 18:24:10 -0500 Subject: [PATCH 20/56] Adding tests - AdditionalProperties 3.0 and 2.0 - allOf 2.0 (it is included in petstore separated tests) --- .../expectedBundle.json | 223 ++++++++++++++++++ .../additionalProperties.yaml | 9 + .../additionalProperties/expected.json | 71 ++++++ .../additionalProperties/pet.yaml | 14 ++ .../additionalProperties/root.yaml | 20 ++ .../additionalProperties.yaml | 9 + .../additionalProperties/expected.json | 69 ++++++ .../swagger20/additionalProperties/pet.yaml | 14 ++ .../swagger20/additionalProperties/root.yaml | 20 ++ test/unit/bundle.test.js | 123 +++++++++- 10 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 test/data/swaggerMultifile/petstore-separate-yaml/expectedBundle.json create mode 100644 test/data/toBundleExamples/additionalProperties/additionalProperties.yaml create mode 100644 test/data/toBundleExamples/additionalProperties/expected.json create mode 100644 test/data/toBundleExamples/additionalProperties/pet.yaml create mode 100644 test/data/toBundleExamples/additionalProperties/root.yaml create mode 100644 test/data/toBundleExamples/swagger20/additionalProperties/additionalProperties.yaml create mode 100644 test/data/toBundleExamples/swagger20/additionalProperties/expected.json create mode 100644 test/data/toBundleExamples/swagger20/additionalProperties/pet.yaml create mode 100644 test/data/toBundleExamples/swagger20/additionalProperties/root.yaml diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/expectedBundle.json b/test/data/swaggerMultifile/petstore-separate-yaml/expectedBundle.json new file mode 100644 index 0000000..fe4fccb --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/expectedBundle.json @@ -0,0 +1,223 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/api", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to\nNam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.\n\nSed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.\n", + "operationId": "findPets", + "parameters": [ + { + "$ref": "#/parameters/_spec_parameters.yaml-_tagsParam" + }, + { + "$ref": "#/parameters/_spec_parameters.yaml-_limitsParam" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/_spec_Pet.yaml" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/_common_Error.yaml" + } + } + } + }, + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "parameters": [ + { + "name": "pet", + "in": "body", + "description": "Pet to add to the store", + "required": true, + "schema": { + "$ref": "#/definitions/_spec_NewPet.yaml" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/_spec_Pet.yaml" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/_common_Error.yaml" + } + } + } + } + }, + "/pets/{id}": { + "get": { + "description": "Returns a user based on a single ID, if the user does not have access to the pet", + "operationId": "find pet by id", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to fetch", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/_spec_Pet.yaml" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/_common_Error.yaml" + } + } + } + }, + "delete": { + "description": "deletes a single pet based on the ID supplied", + "operationId": "deletePet", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "204": { + "description": "pet deleted" + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/_common_Error.yaml" + } + } + } + } + } + }, + "parameters": { + "_spec_parameters.yaml-_tagsParam": { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "type": "array", + "collectionFormat": "csv", + "items": { + "type": "string" + } + }, + "_spec_parameters.yaml-_limitsParam": { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "type": "integer", + "format": "int32" + } + }, + "definitions": { + "_spec_Pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "_common_Error.yaml": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + }, + "_spec_NewPet.yaml": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/_spec_Pet.yaml" + }, + { + "required": [ + "name" + ], + "properties": { + "description": { + "type": "integer", + "format": "int64" + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/additionalProperties/additionalProperties.yaml b/test/data/toBundleExamples/additionalProperties/additionalProperties.yaml new file mode 100644 index 0000000..f80e915 --- /dev/null +++ b/test/data/toBundleExamples/additionalProperties/additionalProperties.yaml @@ -0,0 +1,9 @@ +type: + object +properties: + color: + type: string + weight: + type: integer + height: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/additionalProperties/expected.json b/test/data/toBundleExamples/additionalProperties/expected.json new file mode 100644 index 0000000..e5c95c8 --- /dev/null +++ b/test/data/toBundleExamples/additionalProperties/expected.json @@ -0,0 +1,71 @@ +{ + "openapi": "3.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/components/schemas/_pet.yaml" + } + } + } + } + } + }, + "components": { + "schemas": { + "_pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/components/schemas/_additionalProperties.yaml" + } + } + }, + "_additionalProperties.yaml": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "weight": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/additionalProperties/pet.yaml b/test/data/toBundleExamples/additionalProperties/pet.yaml new file mode 100644 index 0000000..4d5a473 --- /dev/null +++ b/test/data/toBundleExamples/additionalProperties/pet.yaml @@ -0,0 +1,14 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + additionalProperties: + $ref: "./additionalProperties.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/additionalProperties/root.yaml b/test/data/toBundleExamples/additionalProperties/root.yaml new file mode 100644 index 0000000..83661b5 --- /dev/null +++ b/test/data/toBundleExamples/additionalProperties/root.yaml @@ -0,0 +1,20 @@ +openapi: "3.0" +info: + title: Sample API + description: API description in Markdown. + version: 1.0.0 +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "./pet.yaml" diff --git a/test/data/toBundleExamples/swagger20/additionalProperties/additionalProperties.yaml b/test/data/toBundleExamples/swagger20/additionalProperties/additionalProperties.yaml new file mode 100644 index 0000000..f80e915 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/additionalProperties/additionalProperties.yaml @@ -0,0 +1,9 @@ +type: + object +properties: + color: + type: string + weight: + type: integer + height: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/additionalProperties/expected.json b/test/data/toBundleExamples/swagger20/additionalProperties/expected.json new file mode 100644 index 0000000..2eff55e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/additionalProperties/expected.json @@ -0,0 +1,69 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to,", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.,", + "schema": { + "$ref": "#/definitions/_pet.yaml" + } + } + } + } + } + }, + "definitions": { + "_pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/definitions/_additionalProperties.yaml" + } + } + }, + "_additionalProperties.yaml": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "weight": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/additionalProperties/pet.yaml b/test/data/toBundleExamples/swagger20/additionalProperties/pet.yaml new file mode 100644 index 0000000..4d5a473 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/additionalProperties/pet.yaml @@ -0,0 +1,14 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + additionalProperties: + $ref: "./additionalProperties.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/additionalProperties/root.yaml b/test/data/toBundleExamples/swagger20/additionalProperties/root.yaml new file mode 100644 index 0000000..e435b0e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/additionalProperties/root.yaml @@ -0,0 +1,20 @@ +swagger: "2.0" +info: + title: Sample API + description: API description in Markdown. + version: 1.0.0 +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /pets: + get: + description: Returns all pets from the system that the user has access to, + produces: + - application/json + responses: + 200: + description: A list of pets., + schema: + $ref: "./pet.yaml" diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d3be6cf..d57c08e 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -51,7 +51,10 @@ let expect = require('chai').expect, referencedHeader = path.join(__dirname, BUNDLES_FOLDER + '/referenced_header'), referencedLink = path.join(__dirname, BUNDLES_FOLDER + '/referenced_link'), referencedCallback = path.join(__dirname, BUNDLES_FOLDER + '/referenced_callback'), - referencedSecuritySchemes = path.join(__dirname, BUNDLES_FOLDER + '/referenced_security_schemes'); + referencedSecuritySchemes = path.join(__dirname, BUNDLES_FOLDER + '/referenced_security_schemes'), + SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), + additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), + additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -1768,6 +1771,42 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should bundle file from - additionalProperties 3.0', async function() { + let contentRootFile = fs.readFileSync(additionalProperties + '/root.yaml', 'utf8'), + pet = fs.readFileSync(additionalProperties + '/pet.yaml', 'utf8'), + additionalProps = fs.readFileSync(additionalProperties + '/additionalProperties.yaml', 'utf8'), + expected = fs.readFileSync(additionalProperties + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/additionalProperties.yaml', + content: additionalProps + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('bundle files method - 2.0', function() { @@ -2425,6 +2464,88 @@ describe('bundle files method - 2.0', function() { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should return bundled file from - petstore-separated-yaml (contains allOf)', async function() { + let contentRootFile = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/swagger.yaml', 'utf8'), + parameters = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/parameters.yaml', 'utf8'), + pet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/Pet.yaml', 'utf8'), + newPet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/NewPet.yaml', 'utf8'), + error = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/common/Error.yaml', 'utf8'), + expected = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/expectedBundle.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/spec/swagger.yaml' + } + ], + data: [ + { + path: '/spec/swagger.yaml', + content: contentRootFile + }, + { + path: '/spec/parameters.yaml', + content: parameters + }, + { + path: '/spec/Pet.yaml', + content: pet + }, + { + path: '/spec/NewPet.yaml', + content: newPet + }, + { + path: '/common/Error.yaml', + content: error + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle file from - additionalProperties 2.0', async function() { + let contentRootFile = fs.readFileSync(additionalProperties20 + '/root.yaml', 'utf8'), + pet = fs.readFileSync(additionalProperties20 + '/pet.yaml', 'utf8'), + additionalProps = fs.readFileSync(additionalProperties20 + '/additionalProperties.yaml', 'utf8'), + expected = fs.readFileSync(additionalProperties20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/additionalProperties.yaml', + content: additionalProps + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 835dcb27b2bb762e4c31c65b58105bbe82aaaa2d Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:38:03 -0500 Subject: [PATCH 21/56] Add test for security schemes v 2 --- .../referenced_security_schemes/expected.json | 91 +++++++++++++++++++ .../referenced_security_schemes/root.yaml | 55 +++++++++++ .../referenced_security_schemes/sschemes.yaml | 6 ++ test/unit/bundle.test.js | 35 ++++++- 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/swagger20/referenced_security_schemes/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_security_schemes/root.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_security_schemes/sschemes.yaml diff --git a/test/data/toBundleExamples/swagger20/referenced_security_schemes/expected.json b/test/data/toBundleExamples/swagger20/referenced_security_schemes/expected.json new file mode 100644 index 0000000..f003055 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_security_schemes/expected.json @@ -0,0 +1,91 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_security_schemes/root.yaml b/test/data/toBundleExamples/swagger20/referenced_security_schemes/root.yaml new file mode 100644 index 0000000..cfef5e9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_security_schemes/root.yaml @@ -0,0 +1,55 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + description: pet response + schema: + type: array + items: + $ref: '#/definitions/Pet' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string +securityDefinitions: + petstore_auth: + $ref: "./sschemes.yaml" diff --git a/test/data/toBundleExamples/swagger20/referenced_security_schemes/sschemes.yaml b/test/data/toBundleExamples/swagger20/referenced_security_schemes/sschemes.yaml new file mode 100644 index 0000000..68a4da3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_security_schemes/sschemes.yaml @@ -0,0 +1,6 @@ +type: oauth2 +authorizationUrl: http://swagger.io/api/oauth/dialog +flow: implicit +scopes: + write:pets: modify pets in your account + read:pets: read your pets diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d57c08e..dfe653e 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -54,7 +54,8 @@ let expect = require('chai').expect, referencedSecuritySchemes = path.join(__dirname, BUNDLES_FOLDER + '/referenced_security_schemes'), SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), - additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'); + additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), + referencedSecuritySchemes20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_security_schemes'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2546,6 +2547,38 @@ describe('bundle files method - 2.0', function() { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should return bundled file - referenced Security Schemes', async function () { + let contentRoot = fs.readFileSync(referencedSecuritySchemes20 + '/root.yaml', 'utf8'), + contentRef = fs.readFileSync(referencedSecuritySchemes20 + '/sschemes.yaml', 'utf8'), + expected = fs.readFileSync(referencedSecuritySchemes20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + }, + { + path: '/sschemes.yaml', + content: contentRef + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 9a555ca003b051243b465fe013dd12ac16b28745 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Tue, 14 Jun 2022 13:29:51 -0500 Subject: [PATCH 22/56] Add test 3.1 add test for webhooks and path item in components --- lib/31XUtils/componentsParentMatcher.js | 109 ++++++++++++++++++ lib/bundle.js | 6 +- lib/common/versionUtils.js | 4 +- lib/jsonPointer.js | 10 +- .../referenced_paths_31/expected.json | 108 +++++++++++++++++ .../referenced_paths_31/path.yaml | 29 +++++ .../referenced_paths_31/root.yaml | 41 +++++++ .../referenced_webhook_31/expected.json | 103 +++++++++++++++++ .../referenced_webhook_31/root.yaml | 41 +++++++ .../referenced_webhook_31/webhook.yaml | 29 +++++ test/unit/bundle31.test.js | 76 ++++++++++++ 11 files changed, 551 insertions(+), 5 deletions(-) create mode 100644 lib/31XUtils/componentsParentMatcher.js create mode 100644 test/data/toBundleExamples/referenced_paths_31/expected.json create mode 100644 test/data/toBundleExamples/referenced_paths_31/path.yaml create mode 100644 test/data/toBundleExamples/referenced_paths_31/root.yaml create mode 100644 test/data/toBundleExamples/referenced_webhook_31/expected.json create mode 100644 test/data/toBundleExamples/referenced_webhook_31/root.yaml create mode 100644 test/data/toBundleExamples/referenced_webhook_31/webhook.yaml create mode 100644 test/unit/bundle31.test.js diff --git a/lib/31XUtils/componentsParentMatcher.js b/lib/31XUtils/componentsParentMatcher.js new file mode 100644 index 0000000..1dc097a --- /dev/null +++ b/lib/31XUtils/componentsParentMatcher.js @@ -0,0 +1,109 @@ +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 +}; diff --git a/lib/bundle.js b/lib/bundle.js index c980333..cbe7391 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -12,6 +12,7 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), traverseUtility = require('traverse'), parse = require('./parse.js'), { COMPONENTS_KEYS_20 } = require('./swaggerUtils/componentParentMatcher'), + { COMPONENTS_KEYS_31 } = require('./31XUtils/componentsParentMatcher'), { ParseError } = require('./common/ParseError'); let path = require('path'), @@ -19,7 +20,7 @@ let path = require('path'), BROWSER = 'browser', { DFS } = require('./dfs'), deref = require('./deref.js'), - { SWAGGER_VERSION } = require('./common/versionUtils'); + { SWAGGER_VERSION, VERSION_3_1 } = require('./common/versionUtils'); /** @@ -157,7 +158,8 @@ function getContentFromTrace(content, partial) { * @returns {null} It modifies components global context */ function setValueInComponents(keyInComponents, components, value, version) { - const COMPONENTS_KEYS = version === SWAGGER_VERSION ? COMPONENTS_KEYS_20 : COMPONENTS_KEYS_30; + const COMPONENTS_KEYS = version === SWAGGER_VERSION ? COMPONENTS_KEYS_20 : + version === VERSION_3_1 ? COMPONENTS_KEYS_31 : COMPONENTS_KEYS_30; let currentPlace = components, target = keyInComponents[keyInComponents.length - 2], key = keyInComponents.length === 2 && COMPONENTS_KEYS.includes(keyInComponents[0]) ? diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 0e465bb..5c48d27 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -5,6 +5,7 @@ const VERSION_30 = { key: 'openapi', version: '3.0' }, GENERIC_VERSION3 = { key: 'openapi', version: '3.' }, DEFAULT_SPEC_VERSION = VERSION_30.version, SWAGGER_VERSION = VERSION_20.version, + VERSION_3_1 = VERSION_31.version, fs = require('fs'); /** @@ -259,5 +260,6 @@ module.exports = { isSwagger, compareVersion, getVersionRegexBySpecificationVersion, - SWAGGER_VERSION + SWAGGER_VERSION, + VERSION_3_1 }; diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 98fe488..364d8e1 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -8,7 +8,9 @@ const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher' jsonPointerLevelSeparator = '/', escapedTildeString = '~0', { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'), - { SWAGGER_VERSION } = require('./common/versionUtils'); + { getKeyInComponents31 } = require('./31XUtils/componentsParentMatcher'), + { SWAGGER_VERSION } = require('./common/versionUtils'), + { VERSION_3_1 } = require('./common/versionUtils'); /** @@ -52,12 +54,16 @@ function jsonPointerDecodeAndReplace(filePathName) { */ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { const localPart = localPath ? `${localPointer}${localPath}` : '', - is20 = version === SWAGGER_VERSION; + is20 = version === SWAGGER_VERSION, + is31 = version === VERSION_3_1; let result; if (is20) { result = getKeyInComponents20(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } + else if (is31) { + result = getKeyInComponents31(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); + } else { result = getKeyInComponents30(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } diff --git a/test/data/toBundleExamples/referenced_paths_31/expected.json b/test/data/toBundleExamples/referenced_paths_31/expected.json new file mode 100644 index 0000000..189b022 --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_31/expected.json @@ -0,0 +1,108 @@ +{ + "openapi": "3.1.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "$ref": "#/components/pathItems/_path.yaml" + } + }, + "components": { + "schemas": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "pathItems": { + "_path.yaml": { + "get": { + "description": "Returns pets based on ID", + "summary": "Find pets by ID", + "operationId": "getPetsById", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to use", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "style": "simple" + } + ] + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_paths_31/path.yaml b/test/data/toBundleExamples/referenced_paths_31/path.yaml new file mode 100644 index 0000000..0f9fd29 --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_31/path.yaml @@ -0,0 +1,29 @@ +get: + description: Returns pets based on ID + summary: Find pets by ID + operationId: getPetsById + responses: + '200': + description: pet response + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string +parameters: +- name: id + in: path + description: ID of pet to use + required: true + schema: + type: array + items: + type: string + style: simple diff --git a/test/data/toBundleExamples/referenced_paths_31/root.yaml b/test/data/toBundleExamples/referenced_paths_31/root.yaml new file mode 100644 index 0000000..777a24e --- /dev/null +++ b/test/data/toBundleExamples/referenced_paths_31/root.yaml @@ -0,0 +1,41 @@ + +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + "$ref": "./path.yaml" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/referenced_webhook_31/expected.json b/test/data/toBundleExamples/referenced_webhook_31/expected.json new file mode 100644 index 0000000..7cd9c5f --- /dev/null +++ b/test/data/toBundleExamples/referenced_webhook_31/expected.json @@ -0,0 +1,103 @@ +{ + "openapi": "3.1.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "webhooks": { + "/pets": { + "get": { + "description": "Returns pets based on ID", + "summary": "Find pets by ID", + "operationId": "getPetsById", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to use", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "style": "simple" + } + ] + } + }, + "components": { + "schemas": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_webhook_31/root.yaml b/test/data/toBundleExamples/referenced_webhook_31/root.yaml new file mode 100644 index 0000000..61e5135 --- /dev/null +++ b/test/data/toBundleExamples/referenced_webhook_31/root.yaml @@ -0,0 +1,41 @@ + +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +webhooks: + /pets: + "$ref": "./webhook.yaml" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/referenced_webhook_31/webhook.yaml b/test/data/toBundleExamples/referenced_webhook_31/webhook.yaml new file mode 100644 index 0000000..0f9fd29 --- /dev/null +++ b/test/data/toBundleExamples/referenced_webhook_31/webhook.yaml @@ -0,0 +1,29 @@ +get: + description: Returns pets based on ID + summary: Find pets by ID + operationId: getPetsById + responses: + '200': + description: pet response + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string +parameters: +- name: id + in: path + description: ID of pet to use + required: true + schema: + type: array + items: + type: string + style: simple diff --git a/test/unit/bundle31.test.js b/test/unit/bundle31.test.js new file mode 100644 index 0000000..f4d6894 --- /dev/null +++ b/test/unit/bundle31.test.js @@ -0,0 +1,76 @@ +const expect = require('chai').expect, + Converter = require('../../index.js'), + fs = require('fs'), + path = require('path'), + BUNDLES_FOLDER = '../data/toBundleExamples', + pathItem31 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_31'), + webhookItem31 = path.join(__dirname, BUNDLES_FOLDER + '/referenced_webhook_31'); + + +describe('bundle files method - 3.1', function () { + it('Should return bundled file as json - Path item object', async function () { + let contentRootFile = fs.readFileSync(pathItem31 + '/root.yaml', 'utf8'), + user = fs.readFileSync(pathItem31 + '/path.yaml', 'utf8'), + expected = fs.readFileSync(pathItem31 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.1', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/path.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.1'); + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file as json - webhook object', async function () { + let contentRootFile = fs.readFileSync(webhookItem31 + '/root.yaml', 'utf8'), + user = fs.readFileSync(webhookItem31 + '/webhook.yaml', 'utf8'), + expected = fs.readFileSync(webhookItem31 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.1', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/webhook.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.1'); + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); +}); From f06aca545aff0b944480af71abd849834aaacd6f Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Tue, 14 Jun 2022 16:20:24 -0500 Subject: [PATCH 23/56] Add support for reusable response Add support for reusable response version swagger 2.0 --- lib/swaggerUtils/componentParentMatcher.js | 6 ++ .../referenced_response/expected.json | 74 +++++++++++++++++++ .../referenced_response/response.yaml | 3 + .../swagger20/referenced_response/root.yaml | 44 +++++++++++ test/unit/bundle.test.js | 34 ++++++++- 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/swagger20/referenced_response/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_response/response.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_response/root.yaml diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js index 9aaa383..a57537e 100644 --- a/lib/swaggerUtils/componentParentMatcher.js +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -15,6 +15,9 @@ const COMPONENTS_KEYS_20 = [ ], PROPERTY_DEFINITION = [ 'properties' + ], + RESPONSE_DEFINITION = [ + 'responses' ]; module.exports = { @@ -46,6 +49,9 @@ module.exports = { trace[index + 1] = 'definitions'; traceModified = true; } + if (RESPONSE_DEFINITION.includes(trace[index + 2])) { + trace[index + 1] = 'responses'; + } if (INLINE.includes(item)) { matchFound = false; break; diff --git a/test/data/toBundleExamples/swagger20/referenced_response/expected.json b/test/data/toBundleExamples/swagger20/referenced_response/expected.json new file mode 100644 index 0000000..06175a3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_response/expected.json @@ -0,0 +1,74 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "$ref": "#/responses/_response.yaml" + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "responses": { + "_response.yaml": { + "description": "A simple string response", + "schema": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_response/response.yaml b/test/data/toBundleExamples/swagger20/referenced_response/response.yaml new file mode 100644 index 0000000..30e4662 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_response/response.yaml @@ -0,0 +1,3 @@ +description: A simple string response +schema: + type: string diff --git a/test/data/toBundleExamples/swagger20/referenced_response/root.yaml b/test/data/toBundleExamples/swagger20/referenced_response/root.yaml new file mode 100644 index 0000000..dbcc120 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_response/root.yaml @@ -0,0 +1,44 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + $ref: "./response.yaml" +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d57c08e..65422e0 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -54,7 +54,8 @@ let expect = require('chai').expect, referencedSecuritySchemes = path.join(__dirname, BUNDLES_FOLDER + '/referenced_security_schemes'), SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), - additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'); + additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), + referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2546,6 +2547,37 @@ describe('bundle files method - 2.0', function() { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should bundle file from - referenced response 2.0', async function() { + let contentRootFile = fs.readFileSync(referencedResponse20 + '/root.yaml', 'utf8'), + referenced = fs.readFileSync(referencedResponse20 + '/response.yaml', 'utf8'), + expected = fs.readFileSync(referencedResponse20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/response.yaml', + content: referenced + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From ce6c486a9494427aa2e9a9330cf4cfb36d35c1aa Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Tue, 14 Jun 2022 16:29:35 -0500 Subject: [PATCH 24/56] Update bundle.test.js --- test/unit/bundle.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index dbe5e2b..412d957 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -2550,7 +2550,7 @@ describe('bundle files method - 2.0', function() { }); it('Should return bundled file - referenced Security Schemes', async function () { - let contentRootFile = fs.readFileSync(referencedSecuritySchemes20 + '/root.yaml', 'utf8'), + let contentRoot = fs.readFileSync(referencedSecuritySchemes20 + '/root.yaml', 'utf8'), contentRef = fs.readFileSync(referencedSecuritySchemes20 + '/sschemes.yaml', 'utf8'), expected = fs.readFileSync(referencedSecuritySchemes20 + '/expected.json', 'utf8'), input = { @@ -2564,7 +2564,7 @@ describe('bundle files method - 2.0', function() { data: [ { path: '/root.yaml', - content: contentRootFile + content: contentRoot }, { path: '/sschemes.yaml', From 2722b2d26e4c2eb5d29fcf821bb0419c37edcd78 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 15 Jun 2022 13:06:31 -0500 Subject: [PATCH 25/56] Adding composite tests 3.0 - anyOf - oneOf - not Those objects are not supported in 2.0 version --- .../composite_anyOf/expected.json | 81 +++++++++++++ .../composite_anyOf/root.yaml | 31 +++++ .../composite_anyOf/schemas/pet.yaml | 6 + .../composite_anyOf/schemas/user.yaml | 6 + .../composite_not/expected.json | 63 ++++++++++ .../toBundleExamples/composite_not/root.yaml | 30 +++++ .../composite_not/schemas/user.yaml | 6 + .../composite_oneOf/expected.json | 81 +++++++++++++ .../composite_oneOf/root.yaml | 31 +++++ .../composite_oneOf/schemas/pet.yaml | 6 + .../composite_oneOf/schemas/user.yaml | 6 + test/unit/bundle.test.js | 111 +++++++++++++++++- 12 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/composite_anyOf/expected.json create mode 100644 test/data/toBundleExamples/composite_anyOf/root.yaml create mode 100644 test/data/toBundleExamples/composite_anyOf/schemas/pet.yaml create mode 100644 test/data/toBundleExamples/composite_anyOf/schemas/user.yaml create mode 100644 test/data/toBundleExamples/composite_not/expected.json create mode 100644 test/data/toBundleExamples/composite_not/root.yaml create mode 100644 test/data/toBundleExamples/composite_not/schemas/user.yaml create mode 100644 test/data/toBundleExamples/composite_oneOf/expected.json create mode 100644 test/data/toBundleExamples/composite_oneOf/root.yaml create mode 100644 test/data/toBundleExamples/composite_oneOf/schemas/pet.yaml create mode 100644 test/data/toBundleExamples/composite_oneOf/schemas/user.yaml diff --git a/test/data/toBundleExamples/composite_anyOf/expected.json b/test/data/toBundleExamples/composite_anyOf/expected.json new file mode 100644 index 0000000..a1302ed --- /dev/null +++ b/test/data/toBundleExamples/composite_anyOf/expected.json @@ -0,0 +1,81 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/_schemas_pet.yaml" + }, + { + "type": "object", + "properties": { + "testString": { + "type": "string" + }, + "testNumber": { + "type": "integer" + } + } + }, + { + "$ref": "#/components/schemas/_schemas_user.yaml" + } + ] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_pet.yaml": { + "type": "object", + "properties": { + "petId": { + "type": "integer" + }, + "petName": { + "type": "string" + } + } + }, + "_schemas_user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_anyOf/root.yaml b/test/data/toBundleExamples/composite_anyOf/root.yaml new file mode 100644 index 0000000..7c3be77 --- /dev/null +++ b/test/data/toBundleExamples/composite_anyOf/root.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + anyOf: + - $ref: "./schemas/pet.yaml" + - type: object + properties: + testString: + type: string + testNumber: + type: integer + - $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_anyOf/schemas/pet.yaml b/test/data/toBundleExamples/composite_anyOf/schemas/pet.yaml new file mode 100644 index 0000000..e1fdd7e --- /dev/null +++ b/test/data/toBundleExamples/composite_anyOf/schemas/pet.yaml @@ -0,0 +1,6 @@ +type: object +properties: + petId: + type: integer + petName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_anyOf/schemas/user.yaml b/test/data/toBundleExamples/composite_anyOf/schemas/user.yaml new file mode 100644 index 0000000..76872ae --- /dev/null +++ b/test/data/toBundleExamples/composite_anyOf/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_not/expected.json b/test/data/toBundleExamples/composite_not/expected.json new file mode 100644 index 0000000..fd53249 --- /dev/null +++ b/test/data/toBundleExamples/composite_not/expected.json @@ -0,0 +1,63 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "client": { + "type": "object", + "not": { + "$ref": "#/components/schemas/_schemas_user.yaml" + } + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_not/root.yaml b/test/data/toBundleExamples/composite_not/root.yaml new file mode 100644 index 0000000..5b781c8 --- /dev/null +++ b/test/data/toBundleExamples/composite_not/root.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + type: object + properties: + id: + type: integer + client: + type: object + not: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_not/schemas/user.yaml b/test/data/toBundleExamples/composite_not/schemas/user.yaml new file mode 100644 index 0000000..76872ae --- /dev/null +++ b/test/data/toBundleExamples/composite_not/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_oneOf/expected.json b/test/data/toBundleExamples/composite_oneOf/expected.json new file mode 100644 index 0000000..c34d007 --- /dev/null +++ b/test/data/toBundleExamples/composite_oneOf/expected.json @@ -0,0 +1,81 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/_schemas_pet.yaml" + }, + { + "type": "object", + "properties": { + "testString": { + "type": "string" + }, + "testNumber": { + "type": "integer" + } + } + }, + { + "$ref": "#/components/schemas/_schemas_user.yaml" + } + ] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_pet.yaml": { + "type": "object", + "properties": { + "petId": { + "type": "integer" + }, + "petName": { + "type": "string" + } + } + }, + "_schemas_user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_oneOf/root.yaml b/test/data/toBundleExamples/composite_oneOf/root.yaml new file mode 100644 index 0000000..7b2e50c --- /dev/null +++ b/test/data/toBundleExamples/composite_oneOf/root.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + oneOf: + - $ref: "./schemas/pet.yaml" + - type: object + properties: + testString: + type: string + testNumber: + type: integer + - $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_oneOf/schemas/pet.yaml b/test/data/toBundleExamples/composite_oneOf/schemas/pet.yaml new file mode 100644 index 0000000..e1fdd7e --- /dev/null +++ b/test/data/toBundleExamples/composite_oneOf/schemas/pet.yaml @@ -0,0 +1,6 @@ +type: object +properties: + petId: + type: integer + petName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_oneOf/schemas/user.yaml b/test/data/toBundleExamples/composite_oneOf/schemas/user.yaml new file mode 100644 index 0000000..76872ae --- /dev/null +++ b/test/data/toBundleExamples/composite_oneOf/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 412d957..9aab28f 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -56,7 +56,10 @@ let expect = require('chai').expect, additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), referencedSecuritySchemes20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_security_schemes'), - referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'); + referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'), + compositeOneOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_oneOf'), + compositeNot = path.join(__dirname, BUNDLES_FOLDER + '/composite_not'), + compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -1809,6 +1812,112 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should bundle composite file with oneOf - composite_oneOf', async function() { + let contentRoot = fs.readFileSync(compositeOneOf + '/root.yaml', 'utf8'), + pet = fs.readFileSync(compositeOneOf + '/schemas/pet.yaml', 'utf8'), + user = fs.readFileSync(compositeOneOf + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(compositeOneOf + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle composite file with anyOf - composite_anyOf', async function() { + let contentRoot = fs.readFileSync(compositeAnyOf + '/root.yaml', 'utf8'), + pet = fs.readFileSync(compositeAnyOf + '/schemas/pet.yaml', 'utf8'), + user = fs.readFileSync(compositeAnyOf + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(compositeAnyOf + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle composite file with not - composite_not', async function() { + let contentRoot = fs.readFileSync(compositeNot + '/root.yaml', 'utf8'), + user = fs.readFileSync(compositeNot + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(compositeNot + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); describe('bundle files method - 2.0', function() { From 2ea998509cb715acc65538a0d0c1bc17f73f4c02 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 15 Jun 2022 13:16:44 -0500 Subject: [PATCH 26/56] removing spaces at the end in test files --- test/data/toBundleExamples/composite_anyOf/expected.json | 2 +- test/data/toBundleExamples/composite_not/expected.json | 2 +- test/data/toBundleExamples/composite_oneOf/expected.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/data/toBundleExamples/composite_anyOf/expected.json b/test/data/toBundleExamples/composite_anyOf/expected.json index a1302ed..0ac8fc8 100644 --- a/test/data/toBundleExamples/composite_anyOf/expected.json +++ b/test/data/toBundleExamples/composite_anyOf/expected.json @@ -78,4 +78,4 @@ } } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_not/expected.json b/test/data/toBundleExamples/composite_not/expected.json index fd53249..71cd00e 100644 --- a/test/data/toBundleExamples/composite_not/expected.json +++ b/test/data/toBundleExamples/composite_not/expected.json @@ -60,4 +60,4 @@ } } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/test/data/toBundleExamples/composite_oneOf/expected.json b/test/data/toBundleExamples/composite_oneOf/expected.json index c34d007..47b54d6 100644 --- a/test/data/toBundleExamples/composite_oneOf/expected.json +++ b/test/data/toBundleExamples/composite_oneOf/expected.json @@ -78,4 +78,4 @@ } } } -} \ No newline at end of file +} \ No newline at end of file From d47666992deb10727f7aca1f7ed63dcb75660f03 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:32:48 -0500 Subject: [PATCH 27/56] Update bundle.test.js --- test/unit/bundle.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 9aab28f..9dfd9e1 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -1820,7 +1820,7 @@ describe('bundle files method - 3.0', function () { expected = fs.readFileSync(compositeOneOf + '/expected.json', 'utf8'), input = { type: 'multiFile', - specificationVersion: '3.0', + specificationVersion: '3.0.0', rootFiles: [ { path: '/root.yaml' From 6e192043d3dd14b81261e4bda5cc6c4c698f6d3d Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 15 Jun 2022 16:41:43 -0500 Subject: [PATCH 28/56] Refactoring refactoring --- lib/bundle.js | 32 +++-- lib/common/versionUtils.js | 22 +-- lib/jsonPointer.js | 15 +- lib/relatedEntities.js | 116 ---------------- lib/relatedFiles.js | 43 +++++- lib/schemaUtils.js | 8 +- test/unit/relatedEntities.test.js | 220 ------------------------------ 7 files changed, 86 insertions(+), 370 deletions(-) delete mode 100644 lib/relatedEntities.js delete mode 100644 test/unit/relatedEntities.test.js diff --git a/lib/bundle.js b/lib/bundle.js index cbe7391..96abd34 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -20,7 +20,7 @@ let path = require('path'), BROWSER = 'browser', { DFS } = require('./dfs'), deref = require('./deref.js'), - { SWAGGER_VERSION, VERSION_3_1 } = require('./common/versionUtils'); + { isSwagger, isOAS31 } = require('./common/versionUtils'); /** @@ -154,18 +154,16 @@ function getContentFromTrace(content, partial) { * @param {array} keyInComponents - The trace to the key in components * @param {object} components - A global components object * @param {object} value - The value from node matched with data - * @param {string} version - The current version + * @param {array} componentsKeys - The keys of the reusable component * @returns {null} It modifies components global context */ -function setValueInComponents(keyInComponents, components, value, version) { - const COMPONENTS_KEYS = version === SWAGGER_VERSION ? COMPONENTS_KEYS_20 : - version === VERSION_3_1 ? COMPONENTS_KEYS_31 : COMPONENTS_KEYS_30; +function setValueInComponents(keyInComponents, components, value, componentsKeys) { let currentPlace = components, target = keyInComponents[keyInComponents.length - 2], - key = keyInComponents.length === 2 && COMPONENTS_KEYS.includes(keyInComponents[0]) ? + key = keyInComponents.length === 2 && componentsKeys.includes(keyInComponents[0]) ? keyInComponents[1] : null; - if (COMPONENTS_KEYS.includes(keyInComponents[0])) { + if (componentsKeys.includes(keyInComponents[0])) { if (keyInComponents[0] === 'schema') { keyInComponents[0] = 'schemas'; } @@ -324,6 +322,17 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { return { graphAdj, missingNodes, nodeContent, nodeReferenceDirectory, nodeName: currentNode.fileName }; } +/** + * Returns the keys of the reusable components in the Spec + * according to the version + * @param {string} version - The current version + * @returns {array} string array with the keys + */ +function getReusableComponentsKeysAccordingToVersion(version) { + return isSwagger(version) ? COMPONENTS_KEYS_20 : + isOAS31(version) ? COMPONENTS_KEYS_31 : COMPONENTS_KEYS_30; +} + /** * Generates the components object from the documentContext data * @param {object} documentContext The document context from root @@ -337,13 +346,14 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver let notInLine = Object.entries(documentContext.globalReferences).filter(([, value]) => { return value.keyInComponents.length !== 0; }); + const COMPONENTS_KEYS = getReusableComponentsKeysAccordingToVersion(version); notInLine.forEach(([key, value]) => { - let [, partial] = key.split('#'); + let [, partial] = key.split(localPointer); setValueInComponents( value.keyInComponents, components, getContentFromTrace(documentContext.nodeContents[key], partial), - version + COMPONENTS_KEYS ); }); [rootContent, components].forEach((contentData) => { @@ -392,7 +402,7 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver refData.keyInComponents, components, refData.nodeContent, - version + COMPONENTS_KEYS ); } } @@ -410,7 +420,7 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver function generateComponentsWrapper(parsedOasObject, version) { let components = {}; - if (version === SWAGGER_VERSION) { + if (isSwagger(version)) { COMPONENTS_KEYS_20.forEach((property) => { if (parsedOasObject.hasOwnProperty(property)) { components[property] = parsedOasObject[property]; diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 5c48d27..415e86b 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -211,10 +211,8 @@ function getSpecVersion({ type, data, specificationVersion }) { */ function getConcreteSchemaUtils({ type, data, specificationVersion }) { const specVersion = getSpecVersion({ type, data, specificationVersion }); - if (!specVersion) { - return; - } let concreteUtils = {}; + if (specVersion === DEFAULT_SPEC_VERSION) { concreteUtils = require('../30XUtils/schemaUtils30X'); } @@ -246,11 +244,16 @@ function filterOptionsByVersion(options, version) { * @returns {boolean} True if the current spec is using swagger 2.0 spec */ function isSwagger(version) { - let isSwagger = false; - if (version === VERSION_20.version) { - isSwagger = true; - } - return isSwagger; + return compareVersion(version, VERSION_20.version); +} + +/** + * Calculates if thew current input is using OAS 3.1 spec + * @param {string} version The current spec version + * @returns {boolean} True if the current spec is using OAS 3.1 spec + */ +function isOAS31(version) { + return compareVersion(version, VERSION_31.version); } module.exports = { @@ -261,5 +264,6 @@ module.exports = { compareVersion, getVersionRegexBySpecificationVersion, SWAGGER_VERSION, - VERSION_3_1 + VERSION_3_1, + isOAS31 }; diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 364d8e1..7a7ca29 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -9,8 +9,7 @@ const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher' escapedTildeString = '~0', { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'), { getKeyInComponents31 } = require('./31XUtils/componentsParentMatcher'), - { SWAGGER_VERSION } = require('./common/versionUtils'), - { VERSION_3_1 } = require('./common/versionUtils'); + { isOAS31, isSwagger } = require('./common/versionUtils'); /** @@ -53,15 +52,13 @@ function jsonPointerDecodeAndReplace(filePathName) { * @returns {Array} - the calculated keys in an array representing each nesting property name */ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { - const localPart = localPath ? `${localPointer}${localPath}` : '', - is20 = version === SWAGGER_VERSION, - is31 = version === VERSION_3_1; + const localPart = localPath ? `${localPointer}${localPath}` : ''; let result; - if (is20) { + if (isSwagger(version)) { result = getKeyInComponents20(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } - else if (is31) { + else if (isOAS31(version)) { result = getKeyInComponents31(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); } else { @@ -78,7 +75,7 @@ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { * @returns {string} - the concatenated json pointer */ function concatJsonPointer(traceFromParent, targetInRoot) { - const traceFromParentAsString = traceFromParent.join('/'); + const traceFromParentAsString = traceFromParent.join(jsonPointerLevelSeparator); return localPointer + targetInRoot + jsonPointerLevelSeparator + traceFromParentAsString; } @@ -91,7 +88,7 @@ function concatJsonPointer(traceFromParent, targetInRoot) { * @returns {string} - the concatenated json pointer */ function getJsonPointerRelationToRoot(refValue, traceFromKey, version) { - let targetInRoot = version === SWAGGER_VERSION ? '' : '/components'; + let targetInRoot = isSwagger(version) ? '' : '/components'; if (refValue.startsWith(localPointer)) { return refValue; } diff --git a/lib/relatedEntities.js b/lib/relatedEntities.js deleted file mode 100644 index c6f0e70..0000000 --- a/lib/relatedEntities.js +++ /dev/null @@ -1,116 +0,0 @@ -const traverseUtility = require('traverse'), - { DFS } = require('./dfs'), - { jsonPointerDecodeAndReplace, isLocalRef, getEntityName } = require('./jsonPointer'), - deref = require('./deref.js'); - -/** - * verifies if the path has been added to the result - * @param {string} path - path to find - * @param {Array} referencesInNode - Array with the already added paths - * @returns {boolean} - wheter a node with the same path has been added - */ -function added(path, referencesInNode) { - return referencesInNode.find((reference) => { return reference.path === path; }) !== undefined; -} - -/** - * Gets the path from a referenced entity - * @param {object} property - current node in process - * @returns {string} - $ref value - */ -function pathSolver(property) { - return property.$ref; -} - -/** - * Gets all the $refs from an object - * @param {object} currentNode - current node in process - * @param {Function} refTypeResolver - function to resolve the ref according to type (local, external, web etc) - * @param {Function} pathSolver - function to resolve the Path - * @returns {object} - {path : $ref value} - */ -function getReferences (currentNode, refTypeResolver, pathSolver) { - let referencesInNode = []; - traverseUtility(currentNode).forEach((property) => { - if (property) { - let hasReferenceTypeKey; - hasReferenceTypeKey = Object.keys(property) - .find( - (key) => { - return refTypeResolver(property, key); - } - ); - if (hasReferenceTypeKey) { - if (!added(property.$ref, referencesInNode)) { - referencesInNode.push({ path: pathSolver(property) }); - } - } - } - }); - return referencesInNode; -} - -/** - * Locates a referenced node from the data input by path - * @param {string} referencePath - value from the $ref property - * @param {object} spec - parsed spec - * @returns {object} - Detect root files result object - */ -function findNodeFromPath(referencePath, spec) { - let found, - splitRef = referencePath.split('/'); - splitRef = splitRef.slice(1).map((elem) => { - return jsonPointerDecodeAndReplace(elem); - }); - found = deref._getEscaped(spec, splitRef); - return found; -} - -/** - * Maps the output from get root files to detect root files - * @param {object} currentNode - current { path, content} object - * @param {object} spec - the whole parsed spec - * @returns {object} - Detect root files result object - */ -function getAdjacentAndMissing(currentNode, spec) { - let currentNodeReferences = getReferences(currentNode, isLocalRef, pathSolver), - graphAdj = [], - missingNodes = []; - - currentNodeReferences.forEach((reference) => { - let referencePath = reference.path, - adjacentNode = findNodeFromPath(referencePath, spec); - if (adjacentNode) { - adjacentNode.$info = { $ref: referencePath, name: getEntityName(referencePath) }; - graphAdj.push(adjacentNode); - } - else { - missingNodes.push({ $ref: referencePath }); - } - - }); - return { graphAdj, missingNodes }; -} - -module.exports = { - - /** - * Maps the output from get root files to detect root files - * @param {object} entityRoot - root file information - * @param {Array} spec - array of { path, content} objects - * @returns {object} - Detect root files result object - */ - getRelatedEntities: function (entityRoot, spec) { - let algorithm = new DFS(), - { traverseOrder, missing } = - algorithm.traverse(entityRoot, (currentNode) => { - return getAdjacentAndMissing(currentNode, spec); - }); - return { relatedEntities: traverseOrder, missingRelatedEntities: missing }; - }, - getReferences, - getAdjacentAndMissing, - isLocalRef, - findNodeFromPath, - pathSolver -}; diff --git a/lib/relatedFiles.js b/lib/relatedFiles.js index 0790714..0856373 100644 --- a/lib/relatedFiles.js +++ b/lib/relatedFiles.js @@ -1,7 +1,7 @@ const parse = require('./parse.js'), + traverseUtility = require('traverse'), BROWSER = 'browser', { DFS } = require('./dfs'), - { getReferences } = require('./relatedEntities'), { isExtRef, removeLocalReferenceFromPath } = require('./jsonPointer'); let path = require('path'), pathBrowserify = require('path-browserify'); @@ -23,6 +23,9 @@ function comparePaths(path1, path2) { * @returns {object} - Detect root files result object */ function calculatePath(parentFileName, referencePath) { + if (path.isAbsolute(referencePath)) { + return referencePath; + } let currentDirName = path.dirname(parentFileName), refDirName = path.join(currentDirName, referencePath); return refDirName; @@ -61,6 +64,44 @@ function findNodeFromPath(referencePath, allData) { }); } +/** + * verifies if the path has been added to the result + * @param {string} path - path to find + * @param {Array} referencesInNode - Array with the already added paths + * @returns {boolean} - wheter a node with the same path has been added + */ +function added(path, referencesInNode) { + return referencesInNode.find((reference) => { return reference.path === path; }) !== undefined; +} + +/** + * Gets all the $refs from an object + * @param {object} currentNode - current node in process + * @param {Function} refTypeResolver - function to resolve the ref according to type (local, external, web etc) + * @param {Function} pathSolver - function to resolve the Path + * @returns {object} - {path : $ref value} + */ +function getReferences (currentNode, refTypeResolver, pathSolver) { + let referencesInNode = []; + traverseUtility(currentNode).forEach((property) => { + if (property) { + let hasReferenceTypeKey; + hasReferenceTypeKey = Object.keys(property) + .find( + (key) => { + return refTypeResolver(property, key); + } + ); + if (hasReferenceTypeKey) { + if (!added(property.$ref, referencesInNode)) { + referencesInNode.push({ path: pathSolver(property) }); + } + } + } + }); + return referencesInNode; +} + /** * Maps the output from get root files to detect root files * @param {object} currentNode - current { path, content} object diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 7314728..0bd7a53 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -6,7 +6,7 @@ const { ParseError } = require('./common/ParseError.js'); const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/schemaUtilsCommon.js'), - { getConcreteSchemaUtils, SWAGGER_VERSION } = require('./common/versionUtils.js'), + { getConcreteSchemaUtils, isSwagger } = require('./common/versionUtils.js'), async = require('async'), sdk = require('postman-collection'), schemaFaker = require('../assets/json-schema-faker.js'), @@ -4849,7 +4849,7 @@ module.exports = { mapBundleOutput(format, parsedRootFiles, version) { return (contentAndComponents) => { let bundledFile = contentAndComponents.fileContent; - if (version === SWAGGER_VERSION) { + if (isSwagger(version)) { Object.entries(contentAndComponents.components).forEach(([key, value]) => { bundledFile[key] = value; }); @@ -4912,7 +4912,7 @@ module.exports = { let parsedContent = parseFileOrThrow(rootFile.content); return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent }; }).filter((rootWithParsedContent) => { - let fileVersion = version === SWAGGER_VERSION ? + let fileVersion = isSwagger(version) ? rootWithParsedContent.parsed.oasObject.swagger : rootWithParsedContent.parsed.oasObject.openapi; return compareVersion(version, fileVersion); @@ -4920,8 +4920,8 @@ module.exports = { data = toBundle ? this.getBundledFileData(parsedRootFiles, inputData, origin, bundleFormat, version) : this.getRelatedFilesData(parsedRootFiles, inputData, origin); - return data; + return data; }, /** diff --git a/test/unit/relatedEntities.test.js b/test/unit/relatedEntities.test.js deleted file mode 100644 index da3a290..0000000 --- a/test/unit/relatedEntities.test.js +++ /dev/null @@ -1,220 +0,0 @@ -const { getReferences, - isLocalRef, - getAdjacentAndMissing, - findNodeFromPath, - getRelatedEntities, - pathSolver } = require('./../../lib/relatedEntities'), - { isExtRef, - removeLocalReferenceFromPath } = require('./../../lib/jsonPointer'), - path = require('path'), - expect = require('chai').expect, - PET_STORE_SEPARATED = '../data/petstore separate yaml/spec', - newPet = path.join(__dirname, PET_STORE_SEPARATED + '/NewPet.yaml'), - fs = require('fs'), - parse = require('./../../lib/parse.js'), - swaggerRoot = path.join(__dirname, PET_STORE_SEPARATED + '/swagger.yaml'), - mockedInputPetstore = { - 'components': { - 'schemas': { - 'Pet': { - 'required': [ - 'id', - 'name' - ], - 'properties': { - 'id': { - 'type': 'integer', - 'format': 'int64' - }, - 'name': { - 'type': 'string' - }, - 'tag': { - 'type': 'string' - } - } - }, - 'Pets': { - 'type': 'array', - 'items': { - '$ref': '#/components/schemas/Pet' - } - }, - 'Error': { - 'required': [ - 'code', - 'message' - ], - 'properties': { - 'code': { - 'type': 'integer', - 'format': 'int32' - }, - 'message': { - 'type': 'string' - } - } - } - } - } - }; - - -describe('getReferences function', function () { - it('should return 1 local reference from input', function () { - const inputNode = { - type: 'array', - items: { - $ref: '#/components/schemas/Pet' - } - }, - result = getReferences(inputNode, isLocalRef, pathSolver); - expect(result.length).to.equal(1); - expect(result[0].path).to.equal('#/components/schemas/Pet'); - - }); - - it('should return 1 local reference from input, even when repeated', function () { - const inputNode = { - type: 'object', - properties: { - pet: { $ref: '#/components/schemas/Pet' }, - newPet: { $ref: '#/components/schemas/Pet' } - } - }, - result = getReferences(inputNode, isLocalRef, pathSolver); - expect(result.length).to.equal(1); - expect(result[0].path).to.equal('#/components/schemas/Pet'); - }); - - it('should not identify an external ref as local ref', function () { - const inputNode = { - type: 'object', - properties: { - pet: { $ref: 'Pet.yaml' }, - newPet: { $ref: 'Pet.yaml' } - } - }, - result = getReferences(inputNode, isLocalRef, pathSolver); - expect(result.length).to.equal(0); - }); - - - it('should return 1 reference from input', function () { - const parsedContentFile = parse.getOasObject(fs.readFileSync(newPet, 'utf8')), - result = getReferences(parsedContentFile, isExtRef, removeLocalReferenceFromPath); - expect(result.length).to.equal(1); - expect(result[0].path).to.equal('Pet.yaml'); - - }); - - it('should return unique references from input', function () { - const parsedContentFile = parse.getOasObject(fs.readFileSync(swaggerRoot, 'utf8')), - result = getReferences(parsedContentFile, isExtRef, removeLocalReferenceFromPath); - expect(result.length).to.equal(5); - expect(result[0].path).to.equal('parameters.yaml'); - expect(result[1].path).to.equal('parameters.yaml'); - expect(result[2].path).to.equal('Pet.yaml'); - expect(result[3].path).to.equal('../common/Error.yaml'); - expect(result[4].path).to.equal('NewPet.yaml'); - - }); - - -}); - -describe('findNodeFromPath method', function () { - it('should return the node by the json pointer', function () { - const res = findNodeFromPath('#/components/schemas/Pet', mockedInputPetstore); - expect(res).to.not.be.undefined; - }); -}); - -describe('getAdjacentAndMissing function', function () { - it('should return 1 adjacent reference from input', function () { - const inputNode = { - type: 'array', - items: { - $ref: '#/components/schemas/Pet' - } - }, - { graphAdj, missingNodes } = getAdjacentAndMissing(inputNode, mockedInputPetstore); - expect(graphAdj.length).to.equal(1); - expect(missingNodes.length).to.equal(0); - - }); - - it('should return 1 missing reference from input', function () { - const inputNode = { - type: 'array', - items: { - $ref: '#/components/schemas/Dog' - } - }, - { graphAdj, missingNodes } = getAdjacentAndMissing(inputNode, mockedInputPetstore); - expect(graphAdj.length).to.equal(0); - expect(missingNodes.length).to.equal(1); - expect(missingNodes[0].$ref).to.equal('#/components/schemas/Dog'); - }); -}); - -describe('getRelatedEntities function', function () { - it('should return 1 adjacent and the root', function () { - const inputNode = { - type: 'array', - items: { - $ref: '#/components/schemas/Pet' - } - }, - { relatedEntities, missingRelatedEntities } = getRelatedEntities(inputNode, mockedInputPetstore); - expect(relatedEntities.length).to.equal(2); - expect(relatedEntities[1].$info.$ref).to.equal('#/components/schemas/Pet'); - expect(relatedEntities[1].$info.name).to.equal('Pet'); - expect(missingRelatedEntities.length).to.equal(0); - - }); - - it('should return 1 adjacent and the root even with 2 refs', function () { - const inputNode = { - type: 'object', - properties: { - pet: { $ref: '#/components/schemas/Pet' }, - newPet: { $ref: '#/components/schemas/Pets' } - } - }, - { relatedEntities, missingRelatedEntities } = getRelatedEntities(inputNode, mockedInputPetstore); - expect(relatedEntities.length).to.equal(3); - expect(relatedEntities[1].$info.$ref).to.equal('#/components/schemas/Pets'); - expect(relatedEntities[1].$info.name).to.equal('Pets'); - expect(relatedEntities[2].$info.$ref).to.equal('#/components/schemas/Pet'); - expect(relatedEntities[2].$info.name).to.equal('Pet'); - expect(missingRelatedEntities.length).to.equal(0); - - }); - - it('should return 1 missing reference from input', function () { - const inputNode = { - type: 'array', - items: { - $ref: '#/components/schemas/Dog' - } - }, - { relatedEntities, missingRelatedEntities } = getRelatedEntities(inputNode, mockedInputPetstore); - expect(relatedEntities.length).to.equal(1); - expect(missingRelatedEntities.length).to.equal(1); - expect(missingRelatedEntities[0].$ref).to.equal('#/components/schemas/Dog'); - }); - - it('should ignore external references', function () { - const inputNode = { - type: 'array', - items: { - $ref: 'pet.yaml' - } - }, - { relatedEntities, missingRelatedEntities } = getRelatedEntities(inputNode, mockedInputPetstore); - expect(relatedEntities.length).to.equal(1); - expect(missingRelatedEntities.length).to.equal(0); - }); - -}); From 0b78cf18a58db06d9f3c9e75b8a5bc4a71a5f251 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 15 Jun 2022 18:08:34 -0500 Subject: [PATCH 29/56] Validate if the input version is supported Validate if the input version is supported --- lib/common/versionUtils.js | 18 ++++++++++++++++- lib/schemaUtils.js | 5 ++++- test/unit/bundle.test.js | 28 +++++++++++++++++++++++++++ test/unit/versionUtils.test.js | 35 +++++++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 415e86b..67b9c57 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -256,6 +256,21 @@ function isOAS31(version) { return compareVersion(version, VERSION_31.version); } +/** + * Validates if the input version is valid + * @param {string} version The current spec version + * @returns {boolean} True if the current version is supported + */ +function validateSupportedVersion(version) { + if (!version) { + return false; + } + let isValid = [DEFAULT_SPEC_VERSION, SWAGGER_VERSION, VERSION_3_1].find((supportedVersion) => { + return compareVersion(version, supportedVersion); + }); + return isValid !== undefined; +} + module.exports = { getSpecVersion, getConcreteSchemaUtils, @@ -265,5 +280,6 @@ module.exports = { getVersionRegexBySpecificationVersion, SWAGGER_VERSION, VERSION_3_1, - isOAS31 + isOAS31, + validateSupportedVersion }; diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 0bd7a53..f1f71cf 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -6,7 +6,7 @@ const { ParseError } = require('./common/ParseError.js'); const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/schemaUtilsCommon.js'), - { getConcreteSchemaUtils, isSwagger } = require('./common/versionUtils.js'), + { getConcreteSchemaUtils, isSwagger, validateSupportedVersion } = require('./common/versionUtils.js'), async = require('async'), sdk = require('postman-collection'), schemaFaker = require('../assets/json-schema-faker.js'), @@ -4994,6 +4994,9 @@ module.exports = { if (processInput.data[0].path === '') { throw new Error('"Path" of the data element should be provided'); } + if (processInput.specificationVersion && !validateSupportedVersion(processInput.specificationVersion)) { + throw new Error(`The provided version "${processInput.specificationVersion}" is not valid`); + } }, MULTI_FILE_API_TYPE_ALLOWED_VALUE }; diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 9dfd9e1..b2f8fd2 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -1918,6 +1918,34 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('Should throw error when version is not correct', async function () { + let contentRoot = fs.readFileSync(compositeNot + '/root.yaml', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: 'Anything', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + } + ], + options: {}, + bundleFormat: 'JSON' + }; + + try { + await Converter.bundle(input); + } + catch (error) { + expect(error.message).to.equal('The provided version "Anything" is not valid'); + } + }); }); describe('bundle files method - 2.0', function() { diff --git a/test/unit/versionUtils.test.js b/test/unit/versionUtils.test.js index 4876f3d..fc574ce 100644 --- a/test/unit/versionUtils.test.js +++ b/test/unit/versionUtils.test.js @@ -1,7 +1,8 @@ const { getSpecVersion, filterOptionsByVersion, compareVersion, - getVersionRegexBySpecificationVersion } = require('../../lib/common/versionUtils'), + getVersionRegexBySpecificationVersion, + validateSupportedVersion } = require('../../lib/common/versionUtils'), expect = require('chai').expect; describe('getSpecVersion', function() { @@ -329,3 +330,35 @@ describe('getVersionRegexBySpecificationVersion method', function () { }); }); +describe('validateSupportedVersion method', function () { + it('should return true with version 3.0', function () { + const result = validateSupportedVersion('3.0'); + expect(result).to.be.true; + }); + + it('should return true with version 2.0', function () { + const result = validateSupportedVersion('2.0'); + expect(result).to.be.true; + }); + + it('should return true with version 3.1', function () { + const result = validateSupportedVersion('3.1'); + expect(result).to.be.true; + }); + + it('should return false with version "any"', function () { + const result = validateSupportedVersion('any'); + expect(result).to.be.false; + }); + + it('should return false with version ""', function () { + const result = validateSupportedVersion(''); + expect(result).to.be.false; + }); + + it('should return false with version undefined', function () { + const result = validateSupportedVersion(); + expect(result).to.be.false; + }); +}); + From 9500ea6bfa9773ce153095c30b2eb6931f8652ed Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 15 Jun 2022 23:41:10 -0500 Subject: [PATCH 30/56] Refactoring bundle rules resolving 2.0, 3.0 and 3.1 - Moving data for each version to different files - Generalizing rule resolvers for all versions - Replacing each version getKeyInComponents to use the generalized one - Removing all componentsParentMatcher files - Updating new constant sources in bundle.js --- lib/30XUtils/componentsParentMatcher.js | 102 ------------------- lib/31XUtils/componentsParentMatcher.js | 109 --------------------- lib/bundle.js | 22 +---- lib/bundleRules/resolvers.js | 33 +++++++ lib/bundleRules/spec20.js | 33 +++++++ lib/bundleRules/spec30.js | 52 ++++++++++ lib/bundleRules/spec31.js | 54 ++++++++++ lib/common/versionUtils.js | 27 ++++- lib/jsonPointer.js | 56 ++++++++--- lib/swaggerUtils/componentParentMatcher.js | 72 -------------- 10 files changed, 243 insertions(+), 317 deletions(-) delete mode 100644 lib/30XUtils/componentsParentMatcher.js delete mode 100644 lib/31XUtils/componentsParentMatcher.js create mode 100644 lib/bundleRules/resolvers.js create mode 100644 lib/bundleRules/spec20.js create mode 100644 lib/bundleRules/spec30.js create mode 100644 lib/bundleRules/spec31.js delete mode 100644 lib/swaggerUtils/componentParentMatcher.js diff --git a/lib/30XUtils/componentsParentMatcher.js b/lib/30XUtils/componentsParentMatcher.js deleted file mode 100644 index 330e717..0000000 --- a/lib/30XUtils/componentsParentMatcher.js +++ /dev/null @@ -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 -}; diff --git a/lib/31XUtils/componentsParentMatcher.js b/lib/31XUtils/componentsParentMatcher.js deleted file mode 100644 index 1dc097a..0000000 --- a/lib/31XUtils/componentsParentMatcher.js +++ /dev/null @@ -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 -}; diff --git a/lib/bundle.js b/lib/bundle.js index 96abd34..8522f55 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -1,5 +1,4 @@ -const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), - { +const { isExtRef, getKeyInComponents, getJsonPointerRelationToRoot, @@ -11,8 +10,6 @@ const { COMPONENTS_KEYS_30 } = require('./30XUtils/componentsParentMatcher'), } = require('./jsonPointer'), traverseUtility = require('traverse'), parse = require('./parse.js'), - { COMPONENTS_KEYS_20 } = require('./swaggerUtils/componentParentMatcher'), - { COMPONENTS_KEYS_31 } = require('./31XUtils/componentsParentMatcher'), { ParseError } = require('./common/ParseError'); let path = require('path'), @@ -20,7 +17,7 @@ let path = require('path'), BROWSER = 'browser', { DFS } = require('./dfs'), deref = require('./deref.js'), - { isSwagger, isOAS31 } = require('./common/versionUtils'); + { isSwagger, getBundleRulesDataByVersion } = require('./common/versionUtils'); /** @@ -322,17 +319,6 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { return { graphAdj, missingNodes, nodeContent, nodeReferenceDirectory, nodeName: currentNode.fileName }; } -/** - * Returns the keys of the reusable components in the Spec - * according to the version - * @param {string} version - The current version - * @returns {array} string array with the keys - */ -function getReusableComponentsKeysAccordingToVersion(version) { - return isSwagger(version) ? COMPONENTS_KEYS_20 : - isOAS31(version) ? COMPONENTS_KEYS_31 : COMPONENTS_KEYS_30; -} - /** * Generates the components object from the documentContext data * @param {object} documentContext The document context from root @@ -346,7 +332,7 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver let notInLine = Object.entries(documentContext.globalReferences).filter(([, value]) => { return value.keyInComponents.length !== 0; }); - const COMPONENTS_KEYS = getReusableComponentsKeysAccordingToVersion(version); + const { COMPONENTS_KEYS } = getBundleRulesDataByVersion(version); notInLine.forEach(([key, value]) => { let [, partial] = key.split(localPointer); setValueInComponents( @@ -421,7 +407,7 @@ function generateComponentsWrapper(parsedOasObject, version) { let components = {}; if (isSwagger(version)) { - COMPONENTS_KEYS_20.forEach((property) => { + getBundleRulesDataByVersion(version).COMPONENTS_KEYS.forEach((property) => { if (parsedOasObject.hasOwnProperty(property)) { components[property] = parsedOasObject[property]; } diff --git a/lib/bundleRules/resolvers.js b/lib/bundleRules/resolvers.js new file mode 100644 index 0000000..8290e2e --- /dev/null +++ b/lib/bundleRules/resolvers.js @@ -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; + } +}; diff --git a/lib/bundleRules/spec20.js b/lib/bundleRules/spec20.js new file mode 100644 index 0000000..72b21da --- /dev/null +++ b/lib/bundleRules/spec20.js @@ -0,0 +1,33 @@ +const SCHEMA_CONTAINERS = [ + 'schema', + 'items', + 'allOf', + 'additionalProperties' + ], + CONTAINERS = { + definitions: SCHEMA_CONTAINERS + }, + DEFINITIONS = { + properties: 'definitions', + responses: 'responses' + }, + INLINE = [ + 'examples' + ], + COMPONENTS_KEYS = [ + 'definitions', + 'parameters', + 'responses', + 'securityDefinitions' + ], + ROOT_CONTAINERS_KEYS = COMPONENTS_KEYS; + +module.exports = { + RULES_20: { + CONTAINERS, + DEFINITIONS, + COMPONENTS_KEYS, + INLINE, + ROOT_CONTAINERS_KEYS + } +}; diff --git a/lib/bundleRules/spec30.js b/lib/bundleRules/spec30.js new file mode 100644 index 0000000..bdce520 --- /dev/null +++ b/lib/bundleRules/spec30.js @@ -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 + } +}; diff --git a/lib/bundleRules/spec31.js b/lib/bundleRules/spec31.js new file mode 100644 index 0000000..4026d29 --- /dev/null +++ b/lib/bundleRules/spec31.js @@ -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 + } +}; diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 67b9c57..a35674c 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -6,7 +6,10 @@ 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'), + { RULES_20 } = require('./../bundleRules/spec20'); /** * gets the version key and the version and generates a regular expression that @@ -271,6 +274,25 @@ 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), + is20 = compareVersion(version, VERSION_20.version); + if (is20) { + return RULES_20; + } + else if (is31) { + return RULES_31; + } + else { + return RULES_30; + } +} + module.exports = { getSpecVersion, getConcreteSchemaUtils, @@ -281,5 +303,6 @@ module.exports = { SWAGGER_VERSION, VERSION_3_1, isOAS31, - validateSupportedVersion + validateSupportedVersion, + getBundleRulesDataByVersion }; diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 7a7ca29..062591d 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -1,5 +1,4 @@ -const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher'), - slashes = /\//g, +const slashes = /\//g, tildes = /~/g, escapedSlash = /~1/g, escapedSlashString = '~1', @@ -7,9 +6,11 @@ const { getKeyInComponents20 } = require('./swaggerUtils/componentParentMatcher' escapedTilde = /~0/g, jsonPointerLevelSeparator = '/', escapedTildeString = '~0', - { getKeyInComponents30 } = require('./30XUtils/componentsParentMatcher'), - { getKeyInComponents31 } = require('./31XUtils/componentsParentMatcher'), - { isOAS31, isSwagger } = require('./common/versionUtils'); + { isSwagger, getBundleRulesDataByVersion } = require('./common/versionUtils'), + { + resolveFirstLevelChild, + resolveSecondLevelChild + } = require('./bundleRules/resolvers'); /** @@ -52,18 +53,45 @@ function jsonPointerDecodeAndReplace(filePathName) { * @returns {Array} - the calculated keys in an array representing each nesting property name */ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { - const localPart = localPath ? `${localPointer}${localPath}` : ''; - let result; + const localPart = localPath ? `${localPointer}${localPath}` : '', + { + 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 (isSwagger(version)) { - result = getKeyInComponents20(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); + if (isRootAndReusableItemsContainer) { + return []; } - else if (isOAS31(version)) { - result = getKeyInComponents31(traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace); - } - 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); } diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js deleted file mode 100644 index a57537e..0000000 --- a/lib/swaggerUtils/componentParentMatcher.js +++ /dev/null @@ -1,72 +0,0 @@ -const COMPONENTS_KEYS_20 = [ - 'definitions', - 'parameters', - 'responses', - 'securityDefinitions' - ], - SCHEMA_PARENTS = [ - 'schema', - 'items', - 'allOf', - 'additionalProperties' - ], - INLINE = [ - 'examples' - ], - PROPERTY_DEFINITION = [ - 'properties' - ], - RESPONSE_DEFINITION = [ - 'responses' - ]; - -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 -*/ - getKeyInComponents20: function (traceFromParent, filePathName, localPart, jsonPointerDecodeAndReplace) { - let res = traceFromParent, - trace = [], - traceToKey = [], - matchFound = false, - isRootKey = false, - traceModified = false; - - res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); - trace = [...res].reverse(); - - for (let [index, item] of trace.entries()) { - if (SCHEMA_PARENTS.includes(item)) { - item = 'definitions'; - traceModified = true; - } - if (PROPERTY_DEFINITION.includes(trace[index + 2])) { - trace[index + 1] = 'definitions'; - traceModified = true; - } - if (RESPONSE_DEFINITION.includes(trace[index + 2])) { - trace[index + 1] = 'responses'; - } - if (INLINE.includes(item)) { - matchFound = false; - break; - } - traceToKey.push(item); - if (COMPONENTS_KEYS_20.includes(item)) { - matchFound = true; - isRootKey = trace[index + 1] === undefined && - !traceModified; - break; - } - } - return matchFound && !isRootKey ? - traceToKey.reverse() : - []; - }, - COMPONENTS_KEYS_20 -}; From a53cbcdfcc1c6f56b8d08aa97a29cf3aed84ee91 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 16 Jun 2022 11:17:59 -0500 Subject: [PATCH 31/56] moving 2.0 tests to a different file --- test/unit/bundle.test.js | 825 ------------------------------------ test/unit/bundle20.test.js | 829 +++++++++++++++++++++++++++++++++++++ 2 files changed, 829 insertions(+), 825 deletions(-) create mode 100644 test/unit/bundle20.test.js diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index b2f8fd2..bf6f08a 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -22,26 +22,8 @@ let expect = require('chai').expect, refTags = path.join(__dirname, BUNDLES_FOLDER + '/referenced_tags'), refInfo = path.join(__dirname, BUNDLES_FOLDER + '/referenced_info'), refPaths = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths'), - SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', refPathsRefToLocalSchema = path.join(__dirname, BUNDLES_FOLDER + '/referenced_paths_local_schema'), - refTags20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_tags'), - basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), - refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'), - refPathsRefToLocalSchema20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths_local_schema'), refExample = path.join(__dirname, BUNDLES_FOLDER + '/referenced_examples'), - nestedRefs = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedRefs'), - nestedLocalRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedLocalRef'), - withParametersAndItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/withParametersAndItems'), - bringLocalFromExternal = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/bringLocalDependenciesFromExternal'), - bringLocalFromExternalWithItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + - '/bringLocalDependenciesFromExternalWithItems'), - bringLocalFromExternalMultiple = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + - '/bringLocalDependenciesFromExternalMultiple'), - multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), - sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), - nestedProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedProperties20'), - simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), - refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'), properties = path.join(__dirname, BUNDLES_FOLDER + '/properties'), sameSourceDifferentPlace = path.join(__dirname, BUNDLES_FOLDER + '/same_source_different_place'), nestedProperties = path.join(__dirname, BUNDLES_FOLDER + '/nestedProperties'), @@ -52,11 +34,7 @@ let expect = require('chai').expect, referencedLink = path.join(__dirname, BUNDLES_FOLDER + '/referenced_link'), referencedCallback = path.join(__dirname, BUNDLES_FOLDER + '/referenced_callback'), referencedSecuritySchemes = path.join(__dirname, BUNDLES_FOLDER + '/referenced_security_schemes'), - SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), - additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), - referencedSecuritySchemes20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_security_schemes'), - referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'), compositeOneOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_oneOf'), compositeNot = path.join(__dirname, BUNDLES_FOLDER + '/composite_not'), compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'); @@ -1948,809 +1926,6 @@ describe('bundle files method - 3.0', function () { }); }); -describe('bundle files method - 2.0', function() { - it('Should return bundled result from - nestedProperties20', async function() { - let contentRootFile = fs.readFileSync(nestedProperties20 + '/index.yaml', 'utf8'), - info = fs.readFileSync(nestedProperties20 + '/info.yaml', 'utf8'), - paths = fs.readFileSync(nestedProperties20 + '/paths.yaml', 'utf8'), - age = fs.readFileSync(nestedProperties20 + '/schemas/age.yaml', 'utf8'), - hobbies = fs.readFileSync(nestedProperties20 + '/schemas/hobbies.yaml', 'utf8'), - hobby = fs.readFileSync(nestedProperties20 + '/schemas/hobby.yaml', 'utf8'), - user = fs.readFileSync(nestedProperties20 + '/schemas/user.yaml', 'utf8'), - expected = fs.readFileSync(nestedProperties20 + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/user.yaml', - content: user - }, - { - path: '/schemas/age.yaml', - content: age - }, - { - path: '/schemas/hobbies.yaml', - content: hobbies - }, - { - path: '/schemas/hobby.yaml', - content: hobby - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - sameRefDifferentSource', async function() { - let contentRootFile = fs.readFileSync(sameRefDifferentSource + '/index.yaml', 'utf8'), - info = fs.readFileSync(sameRefDifferentSource + '/info.yaml', 'utf8'), - paths = fs.readFileSync(sameRefDifferentSource + '/paths.yaml', 'utf8'), - user = fs.readFileSync(sameRefDifferentSource + '/schemas/user.yaml', 'utf8'), - userDetail = fs.readFileSync(sameRefDifferentSource + '/schemas/detail.yaml', 'utf8'), - client = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/client.yaml', 'utf8'), - clientDetail = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/detail.yaml', 'utf8'), - expected = fs.readFileSync(sameRefDifferentSource + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/user.yaml', - content: user - }, - { - path: '/schemas/detail.yaml', - content: userDetail - }, - { - path: '/otherSchemas/client.yaml', - content: client - }, - { - path: '/otherSchemas/detail.yaml', - content: clientDetail - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - multipleRefFromRootComponents', async function() { - let contentRootFile = fs.readFileSync(multipleRefFromRootComponents + '/index.yaml', 'utf8'), - info = fs.readFileSync(multipleRefFromRootComponents + '/info.yaml', 'utf8'), - paths = fs.readFileSync(multipleRefFromRootComponents + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(multipleRefFromRootComponents + '/pet.yaml', 'utf8'), - parameters = fs.readFileSync(multipleRefFromRootComponents + '/parameters/parameters.yaml', 'utf8'), - expected = fs.readFileSync(multipleRefFromRootComponents + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - }, - { - path: '/parameters/parameters.yaml', - content: parameters - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - bringLocalDependenciesFromExternalMultiple', async function() { - let contentRootFile = fs.readFileSync(bringLocalFromExternalMultiple + '/index.yaml', 'utf8'), - info = fs.readFileSync(bringLocalFromExternalMultiple + '/info.yaml', 'utf8'), - paths = fs.readFileSync(bringLocalFromExternalMultiple + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(bringLocalFromExternalMultiple + '/pet.yaml', 'utf8'), - food = fs.readFileSync(bringLocalFromExternalMultiple + '/food.yaml', 'utf8'), - parameters = fs.readFileSync(bringLocalFromExternalMultiple + '/parameters/parameters.yaml', 'utf8'), - expected = fs.readFileSync(bringLocalFromExternalMultiple + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - }, - { - path: '/food.yaml', - content: food - }, - { - path: '/parameters/parameters.yaml', - content: parameters - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - bringLocalDependenciesFromExternalWithItems', async function() { - let contentRootFile = fs.readFileSync(bringLocalFromExternalWithItems + '/index.yaml', 'utf8'), - info = fs.readFileSync(bringLocalFromExternalWithItems + '/info.yaml', 'utf8'), - paths = fs.readFileSync(bringLocalFromExternalWithItems + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(bringLocalFromExternalWithItems + '/pet.yaml', 'utf8'), - expected = fs.readFileSync(bringLocalFromExternalWithItems + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - bringLocalDependenciesFromExternal', async function() { - let contentRootFile = fs.readFileSync(bringLocalFromExternal + '/index.yaml', 'utf8'), - info = fs.readFileSync(bringLocalFromExternal + '/info.yaml', 'utf8'), - paths = fs.readFileSync(bringLocalFromExternal + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(bringLocalFromExternal + '/pet.yaml', 'utf8'), - expected = fs.readFileSync(bringLocalFromExternal + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - withParametersAndItems', async function() { - let contentRootFile = fs.readFileSync(withParametersAndItems + '/index.yaml', 'utf8'), - info = fs.readFileSync(withParametersAndItems + '/info.yaml', 'utf8'), - paths = fs.readFileSync(withParametersAndItems + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(withParametersAndItems + '/pet.yaml', 'utf8'), - parameters = fs.readFileSync(withParametersAndItems + '/parameters/parameters.yaml', 'utf8'), - expected = fs.readFileSync(withParametersAndItems + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - }, - { - path: '/parameters/parameters.yaml', - content: parameters - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - nestedLocalRef', async function() { - let contentRootFile = fs.readFileSync(nestedLocalRef + '/index.yaml', 'utf8'), - info = fs.readFileSync(nestedLocalRef + '/info.yaml', 'utf8'), - paths = fs.readFileSync(nestedLocalRef + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(nestedLocalRef + '/schemas/pet.yaml', 'utf8'), - favoriteFood = fs.readFileSync(nestedLocalRef + '/schemas/favorite_food.yaml', 'utf8'), - expected = fs.readFileSync(nestedLocalRef + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/pet.yaml', - content: pet - }, - { - path: '/schemas/favorite_food.yaml', - content: favoriteFood - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - nestedRefs', async function() { - let contentRootFile = fs.readFileSync(nestedRefs + '/index.yaml', 'utf8'), - info = fs.readFileSync(nestedRefs + '/info.yaml', 'utf8'), - paths = fs.readFileSync(nestedRefs + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(nestedRefs + '/schemas/pet.yaml', 'utf8'), - favoriteFood = fs.readFileSync(nestedRefs + '/schemas/favorite_food.yaml', 'utf8'), - expected = fs.readFileSync(nestedRefs + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/schemas/pet.yaml', - content: pet - }, - { - path: '/schemas/favorite_food.yaml', - content: favoriteFood - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - basicExample', async function() { - let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), - info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), - paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), - expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled result from - simpleRef', async function() { - let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), - info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), - paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), - pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), - expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/index.yaml' - } - ], - data: [ - { - path: '/index.yaml', - content: contentRootFile - }, - { - path: '/info.yaml', - content: info - }, - { - path: '/paths.yaml', - content: paths - }, - { - path: '/pet.yaml', - content: pet - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file with referenced tags from root', async function () { - let contentRootFile = fs.readFileSync(refTags20 + '/root.yaml', 'utf8'), - tags = fs.readFileSync(refTags20 + '/tags/tags.yaml', 'utf8'), - expected = fs.readFileSync(refTags20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/tags/tags.yaml', - content: tags - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file with referenced paths from root', async function () { - let contentRootFile = fs.readFileSync(refPaths20 + '/root.yaml', 'utf8'), - paths = fs.readFileSync(refPaths20 + '/paths/paths.yaml', 'utf8'), - path = fs.readFileSync(refPaths20 + '/paths/path.yaml', 'utf8'), - expected = fs.readFileSync(refPaths20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/paths/paths.yaml', - content: paths - }, - { - path: '/paths/path.yaml', - content: path - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file with referenced paths from root - path references local schema', async function () { - let contentRootFile = fs.readFileSync(refPathsRefToLocalSchema20 + '/root.yaml', 'utf8'), - paths = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/paths.yaml', 'utf8'), - path = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/path.yaml', 'utf8'), - expected = fs.readFileSync(refPathsRefToLocalSchema20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/paths/paths.yaml', - content: paths - }, - { - path: '/paths/path.yaml', - content: path - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file with referenced example', async function () { - let contentRootFile = fs.readFileSync(refExample20 + '/root.yaml', 'utf8'), - examples = fs.readFileSync(refExample20 + '/examples.yaml', 'utf8'), - expected = fs.readFileSync(refExample20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/examples.yaml', - content: examples - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file from - petstore-separated-yaml (contains allOf)', async function() { - let contentRootFile = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/swagger.yaml', 'utf8'), - parameters = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/parameters.yaml', 'utf8'), - pet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/Pet.yaml', 'utf8'), - newPet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/NewPet.yaml', 'utf8'), - error = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/common/Error.yaml', 'utf8'), - expected = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/expectedBundle.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/spec/swagger.yaml' - } - ], - data: [ - { - path: '/spec/swagger.yaml', - content: contentRootFile - }, - { - path: '/spec/parameters.yaml', - content: parameters - }, - { - path: '/spec/Pet.yaml', - content: pet - }, - { - path: '/spec/NewPet.yaml', - content: newPet - }, - { - path: '/common/Error.yaml', - content: error - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should bundle file from - additionalProperties 2.0', async function() { - let contentRootFile = fs.readFileSync(additionalProperties20 + '/root.yaml', 'utf8'), - pet = fs.readFileSync(additionalProperties20 + '/pet.yaml', 'utf8'), - additionalProps = fs.readFileSync(additionalProperties20 + '/additionalProperties.yaml', 'utf8'), - expected = fs.readFileSync(additionalProperties20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/pet.yaml', - content: pet - }, - { - path: '/additionalProperties.yaml', - content: additionalProps - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should return bundled file - referenced Security Schemes', async function () { - let contentRoot = fs.readFileSync(referencedSecuritySchemes20 + '/root.yaml', 'utf8'), - contentRef = fs.readFileSync(referencedSecuritySchemes20 + '/sschemes.yaml', 'utf8'), - expected = fs.readFileSync(referencedSecuritySchemes20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRoot - }, - { - path: '/sschemes.yaml', - content: contentRef - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); - - it('Should bundle file from - referenced response 2.0', async function() { - let contentRootFile = fs.readFileSync(referencedResponse20 + '/root.yaml', 'utf8'), - referenced = fs.readFileSync(referencedResponse20 + '/response.yaml', 'utf8'), - expected = fs.readFileSync(referencedResponse20 + '/expected.json', 'utf8'), - input = { - type: 'multiFile', - specificationVersion: '2.0', - rootFiles: [ - { - path: '/root.yaml' - } - ], - data: [ - { - path: '/root.yaml', - content: contentRootFile - }, - { - path: '/response.yaml', - content: referenced - } - ], - options: {}, - bundleFormat: 'JSON' - }; - const res = await Converter.bundle(input); - - expect(res).to.not.be.empty; - expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - }); -}); - describe('getReferences method when node does not have any reference', function() { it('Should return reference data empty if there are not any reference', function() { const userData = 'type: object\n' + diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js new file mode 100644 index 0000000..483c028 --- /dev/null +++ b/test/unit/bundle20.test.js @@ -0,0 +1,829 @@ +let expect = require('chai').expect, + Converter = require('../../index.js'), + fs = require('fs'), + path = require('path'), + SWAGGER_MULTIFILE_FOLDER = '../data/toBundleExamples/swagger20', + refTags20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_tags'), + basicExample = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/basicExample'), + refPaths20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths'), + refPathsRefToLocalSchema20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_paths_local_schema'), + nestedRefs = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedRefs'), + nestedLocalRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedLocalRef'), + withParametersAndItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/withParametersAndItems'), + bringLocalFromExternal = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/bringLocalDependenciesFromExternal'), + bringLocalFromExternalWithItems = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/bringLocalDependenciesFromExternalWithItems'), + bringLocalFromExternalMultiple = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/bringLocalDependenciesFromExternalMultiple'), + multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), + sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), + nestedProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedProperties20'), + simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), + refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'), + SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), + additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), + referencedSecuritySchemes20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_security_schemes'), + referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'); + +describe('bundle files method - 2.0', function() { + it('Should return bundled result from - nestedProperties20', async function() { + let contentRootFile = fs.readFileSync(nestedProperties20 + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedProperties20 + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedProperties20 + '/paths.yaml', 'utf8'), + age = fs.readFileSync(nestedProperties20 + '/schemas/age.yaml', 'utf8'), + hobbies = fs.readFileSync(nestedProperties20 + '/schemas/hobbies.yaml', 'utf8'), + hobby = fs.readFileSync(nestedProperties20 + '/schemas/hobby.yaml', 'utf8'), + user = fs.readFileSync(nestedProperties20 + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(nestedProperties20 + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/age.yaml', + content: age + }, + { + path: '/schemas/hobbies.yaml', + content: hobbies + }, + { + path: '/schemas/hobby.yaml', + content: hobby + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - sameRefDifferentSource', async function() { + let contentRootFile = fs.readFileSync(sameRefDifferentSource + '/index.yaml', 'utf8'), + info = fs.readFileSync(sameRefDifferentSource + '/info.yaml', 'utf8'), + paths = fs.readFileSync(sameRefDifferentSource + '/paths.yaml', 'utf8'), + user = fs.readFileSync(sameRefDifferentSource + '/schemas/user.yaml', 'utf8'), + userDetail = fs.readFileSync(sameRefDifferentSource + '/schemas/detail.yaml', 'utf8'), + client = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/client.yaml', 'utf8'), + clientDetail = fs.readFileSync(sameRefDifferentSource + '/otherSchemas/detail.yaml', 'utf8'), + expected = fs.readFileSync(sameRefDifferentSource + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/detail.yaml', + content: userDetail + }, + { + path: '/otherSchemas/client.yaml', + content: client + }, + { + path: '/otherSchemas/detail.yaml', + content: clientDetail + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - multipleRefFromRootComponents', async function() { + let contentRootFile = fs.readFileSync(multipleRefFromRootComponents + '/index.yaml', 'utf8'), + info = fs.readFileSync(multipleRefFromRootComponents + '/info.yaml', 'utf8'), + paths = fs.readFileSync(multipleRefFromRootComponents + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(multipleRefFromRootComponents + '/pet.yaml', 'utf8'), + parameters = fs.readFileSync(multipleRefFromRootComponents + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(multipleRefFromRootComponents + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternalMultiple', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternalMultiple + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternalMultiple + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternalMultiple + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternalMultiple + '/pet.yaml', 'utf8'), + food = fs.readFileSync(bringLocalFromExternalMultiple + '/food.yaml', 'utf8'), + parameters = fs.readFileSync(bringLocalFromExternalMultiple + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternalMultiple + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/food.yaml', + content: food + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternalWithItems', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternalWithItems + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternalWithItems + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternalWithItems + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternalWithItems + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternalWithItems + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - bringLocalDependenciesFromExternal', async function() { + let contentRootFile = fs.readFileSync(bringLocalFromExternal + '/index.yaml', 'utf8'), + info = fs.readFileSync(bringLocalFromExternal + '/info.yaml', 'utf8'), + paths = fs.readFileSync(bringLocalFromExternal + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(bringLocalFromExternal + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(bringLocalFromExternal + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - withParametersAndItems', async function() { + let contentRootFile = fs.readFileSync(withParametersAndItems + '/index.yaml', 'utf8'), + info = fs.readFileSync(withParametersAndItems + '/info.yaml', 'utf8'), + paths = fs.readFileSync(withParametersAndItems + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(withParametersAndItems + '/pet.yaml', 'utf8'), + parameters = fs.readFileSync(withParametersAndItems + '/parameters/parameters.yaml', 'utf8'), + expected = fs.readFileSync(withParametersAndItems + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/parameters/parameters.yaml', + content: parameters + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedLocalRef', async function() { + let contentRootFile = fs.readFileSync(nestedLocalRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedLocalRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedLocalRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedLocalRef + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedLocalRef + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedLocalRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - nestedRefs', async function() { + let contentRootFile = fs.readFileSync(nestedRefs + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedRefs + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedRefs + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(nestedRefs + '/schemas/pet.yaml', 'utf8'), + favoriteFood = fs.readFileSync(nestedRefs + '/schemas/favorite_food.yaml', 'utf8'), + expected = fs.readFileSync(nestedRefs + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/pet.yaml', + content: pet + }, + { + path: '/schemas/favorite_food.yaml', + content: favoriteFood + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - basicExample', async function() { + let contentRootFile = fs.readFileSync(basicExample + '/index.yaml', 'utf8'), + info = fs.readFileSync(basicExample + '/info.yaml', 'utf8'), + paths = fs.readFileSync(basicExample + '/paths.yaml', 'utf8'), + expected = fs.readFileSync(basicExample + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled result from - simpleRef', async function() { + let contentRootFile = fs.readFileSync(simpleRef + '/index.yaml', 'utf8'), + info = fs.readFileSync(simpleRef + '/info.yaml', 'utf8'), + paths = fs.readFileSync(simpleRef + '/paths.yaml', 'utf8'), + pet = fs.readFileSync(simpleRef + '/pet.yaml', 'utf8'), + expected = fs.readFileSync(simpleRef + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/pet.yaml', + content: pet + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file with referenced tags from root', async function () { + let contentRootFile = fs.readFileSync(refTags20 + '/root.yaml', 'utf8'), + tags = fs.readFileSync(refTags20 + '/tags/tags.yaml', 'utf8'), + expected = fs.readFileSync(refTags20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/tags/tags.yaml', + content: tags + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file with referenced paths from root', async function () { + let contentRootFile = fs.readFileSync(refPaths20 + '/root.yaml', 'utf8'), + paths = fs.readFileSync(refPaths20 + '/paths/paths.yaml', 'utf8'), + path = fs.readFileSync(refPaths20 + '/paths/path.yaml', 'utf8'), + expected = fs.readFileSync(refPaths20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/paths/paths.yaml', + content: paths + }, + { + path: '/paths/path.yaml', + content: path + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file with referenced paths from root - path references local schema', async function () { + let contentRootFile = fs.readFileSync(refPathsRefToLocalSchema20 + '/root.yaml', 'utf8'), + paths = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/paths.yaml', 'utf8'), + path = fs.readFileSync(refPathsRefToLocalSchema20 + '/paths/path.yaml', 'utf8'), + expected = fs.readFileSync(refPathsRefToLocalSchema20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/paths/paths.yaml', + content: paths + }, + { + path: '/paths/path.yaml', + content: path + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file with referenced example', async function () { + let contentRootFile = fs.readFileSync(refExample20 + '/root.yaml', 'utf8'), + examples = fs.readFileSync(refExample20 + '/examples.yaml', 'utf8'), + expected = fs.readFileSync(refExample20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/examples.yaml', + content: examples + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file from - petstore-separated-yaml (contains allOf)', async function() { + let contentRootFile = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/swagger.yaml', 'utf8'), + parameters = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/parameters.yaml', 'utf8'), + pet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/Pet.yaml', 'utf8'), + newPet = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/spec/NewPet.yaml', 'utf8'), + error = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/common/Error.yaml', 'utf8'), + expected = fs.readFileSync(SWAGGER_PETSTORE_FOLDER + '/expectedBundle.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/spec/swagger.yaml' + } + ], + data: [ + { + path: '/spec/swagger.yaml', + content: contentRootFile + }, + { + path: '/spec/parameters.yaml', + content: parameters + }, + { + path: '/spec/Pet.yaml', + content: pet + }, + { + path: '/spec/NewPet.yaml', + content: newPet + }, + { + path: '/common/Error.yaml', + content: error + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle file from - additionalProperties 2.0', async function() { + let contentRootFile = fs.readFileSync(additionalProperties20 + '/root.yaml', 'utf8'), + pet = fs.readFileSync(additionalProperties20 + '/pet.yaml', 'utf8'), + additionalProps = fs.readFileSync(additionalProperties20 + '/additionalProperties.yaml', 'utf8'), + expected = fs.readFileSync(additionalProperties20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/pet.yaml', + content: pet + }, + { + path: '/additionalProperties.yaml', + content: additionalProps + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file - referenced Security Schemes', async function () { + let contentRoot = fs.readFileSync(referencedSecuritySchemes20 + '/root.yaml', 'utf8'), + contentRef = fs.readFileSync(referencedSecuritySchemes20 + '/sschemes.yaml', 'utf8'), + expected = fs.readFileSync(referencedSecuritySchemes20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRoot + }, + { + path: '/sschemes.yaml', + content: contentRef + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle file from - referenced response 2.0', async function() { + let contentRootFile = fs.readFileSync(referencedResponse20 + '/root.yaml', 'utf8'), + referenced = fs.readFileSync(referencedResponse20 + '/response.yaml', 'utf8'), + expected = fs.readFileSync(referencedResponse20 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/response.yaml', + content: referenced + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); +}); From ac6c73209df8bdf11e31d499ee4e4a5c943c9817 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:33:34 -0500 Subject: [PATCH 32/56] change json output format --- lib/schemaUtils.js | 6 +++ test/unit/bundle.test.js | 76 +++++++++++++++++++------------------- test/unit/bundle20.test.js | 38 +++++++++---------- test/unit/bundle31.test.js | 4 +- 4 files changed, 65 insertions(+), 59 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index f1f71cf..87f0565 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4864,10 +4864,16 @@ module.exports = { if (rootFormat.toLowerCase() === parse.YAML_FORMAT) { bundledFile = parse.toYAML(bundledFile); } + else if (rootFormat.toLowerCase() === parse.JSON_FORMAT) { + bundledFile = parse.toJSON(bundledFile, null); + } } else if (format.toLowerCase() === parse.YAML_FORMAT) { bundledFile = parse.toYAML(bundledFile); } + else if (format.toLowerCase() === parse.JSON_FORMAT) { + bundledFile = parse.toJSON(bundledFile, null); + } return { rootFile: { path: contentAndComponents.fileName }, bundledContent: bundledFile }; }; }, diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d5bea4b..3a9c834 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -70,7 +70,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.0'); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file as yaml - schema_from_response', async function () { @@ -190,7 +190,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); @@ -238,7 +238,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - petstore separated example', async function () { @@ -382,7 +382,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - with_ref_in_items', async function () { @@ -419,7 +419,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return error data - with_ref_in_items - wrong root', async function () { @@ -510,7 +510,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - multiple_references_from_root_components', async function () { @@ -582,7 +582,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file as json - bring_local_dependencies_from_external', async function () { @@ -614,7 +614,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file as json - bring_local_dependencies_from_external_multiple_local', async function () { @@ -651,7 +651,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return a "/missing/node/path": NotProvided' + @@ -694,7 +694,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced tags from root', async function () { @@ -726,7 +726,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced info from root', async function () { @@ -758,7 +758,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced paths from root', async function () { @@ -795,7 +795,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced paths from root - path references local schema', async function () { @@ -832,7 +832,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should throw error when root files is undefined and in data there is no root file', async function () { @@ -902,7 +902,7 @@ describe('bundle files method - 3.0', function () { const res = await Converter.bundle(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should throw error when root files is empty array and in data there is no root file', async function () { @@ -987,8 +987,8 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); - expect(JSON.stringify(res.output.data[1].bundledContent, null, 2)).to.be.equal(expected2); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[1].bundledContent), null, 2)).to.be.equal(expected2); expect(res.output.data[0].rootFile.path).to.equal('/root.yaml'); expect(res.output.data[1].rootFile.path).to.equal('/root2.yaml'); }); @@ -1073,7 +1073,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); expect(res.output.data.length).to.equal(1); }); @@ -1116,7 +1116,7 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.1'); expect(res.output.data.length).to.equal(1); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle only the files with the specified version 3.0', async function () { @@ -1158,7 +1158,7 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.0'); expect(res.output.data.length).to.equal(1); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('should bundle files to 3.0 when specificationVersion is not provided', async function () { @@ -1199,7 +1199,7 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.0'); expect(res.output.data.length).to.equal(1); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced example', async function () { @@ -1231,7 +1231,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); expect(res.output.data.length).to.equal(1); }); @@ -1341,7 +1341,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); expect(res.output.data.length).to.equal(1); }); @@ -1399,7 +1399,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); expect(res.output.data.length).to.equal(1); }); @@ -1437,7 +1437,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); expect(res.output.data.length).to.equal(1); }); @@ -1480,7 +1480,7 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.0'); expect(res.output.data.length).to.equal(2); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expectedJSON); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expectedJSON); expect(res.output.data[1].bundledContent).to.be.equal(expectedYAML); }); @@ -1528,7 +1528,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced response', async function () { @@ -1560,7 +1560,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Parameter', async function () { @@ -1592,7 +1592,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Request Body', async function () { @@ -1624,7 +1624,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Header', async function () { @@ -1656,7 +1656,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Link', async function () { @@ -1688,7 +1688,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Callback', async function () { @@ -1720,7 +1720,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Security Schemes', async function () { @@ -1752,7 +1752,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle file from - additionalProperties', async function() { @@ -1788,7 +1788,7 @@ describe('bundle files method - 3.0', function () { const res = await Converter.bundle(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle composite file with oneOf - composite_oneOf', async function() { @@ -1825,7 +1825,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle composite file with anyOf - composite_anyOf', async function() { @@ -1862,7 +1862,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle composite file with not - composite_not', async function() { @@ -1894,7 +1894,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should throw error when version is not correct', async function () { diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js index 483c028..476a712 100644 --- a/test/unit/bundle20.test.js +++ b/test/unit/bundle20.test.js @@ -80,7 +80,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - sameRefDifferentSource', async function() { @@ -137,7 +137,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - multipleRefFromRootComponents', async function() { @@ -184,7 +184,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - bringLocalDependenciesFromExternalMultiple', async function() { @@ -236,7 +236,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - bringLocalDependenciesFromExternalWithItems', async function() { @@ -278,7 +278,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - bringLocalDependenciesFromExternal', async function() { @@ -320,7 +320,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - withParametersAndItems', async function() { @@ -367,7 +367,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - nestedLocalRef', async function() { @@ -414,7 +414,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - nestedRefs', async function() { @@ -461,7 +461,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - basicExample', async function() { @@ -498,7 +498,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled result from - simpleRef', async function() { @@ -540,7 +540,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced tags from root', async function () { @@ -572,7 +572,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced paths from root', async function () { @@ -609,7 +609,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced paths from root - path references local schema', async function () { @@ -646,7 +646,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file with referenced example', async function () { @@ -678,7 +678,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file from - petstore-separated-yaml (contains allOf)', async function() { @@ -724,7 +724,7 @@ describe('bundle files method - 2.0', function() { const res = await Converter.bundle(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle file from - additionalProperties 2.0', async function() { @@ -760,7 +760,7 @@ describe('bundle files method - 2.0', function() { const res = await Converter.bundle(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file - referenced Security Schemes', async function () { @@ -792,7 +792,7 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should bundle file from - referenced response 2.0', async function() { @@ -824,6 +824,6 @@ describe('bundle files method - 2.0', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); }); diff --git a/test/unit/bundle31.test.js b/test/unit/bundle31.test.js index f4d6894..6fe0e4b 100644 --- a/test/unit/bundle31.test.js +++ b/test/unit/bundle31.test.js @@ -38,7 +38,7 @@ describe('bundle files method - 3.1', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.1'); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); it('Should return bundled file as json - webhook object', async function () { @@ -71,6 +71,6 @@ describe('bundle files method - 3.1', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(res.output.specification.version).to.equal('3.1'); - expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); }); From ad1baee51c7c554b1b31a8d37681f6db22cad817 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:47:41 -0500 Subject: [PATCH 33/56] fix long paths fix long paths --- lib/bundle.js | 25 +++-- lib/jsonPointer.js | 6 +- lib/utils.js | 31 ++++++ .../toBundleExamples/longPath/client.json | 14 +++ .../toBundleExamples/longPath/expected.json | 94 +++++++++++++++++++ .../data/toBundleExamples/longPath/magic.yaml | 6 ++ test/data/toBundleExamples/longPath/root.yaml | 33 +++++++ .../toBundleExamples/longPath/special.yaml | 6 ++ test/data/toBundleExamples/longPath/user.yaml | 8 ++ .../longPath/userSpecial.yaml | 4 + test/unit/bundle.test.js | 60 +++++++++++- test/unit/util.test.js | 28 ++++++ 12 files changed, 304 insertions(+), 11 deletions(-) create mode 100644 test/data/toBundleExamples/longPath/client.json create mode 100644 test/data/toBundleExamples/longPath/expected.json create mode 100644 test/data/toBundleExamples/longPath/magic.yaml create mode 100644 test/data/toBundleExamples/longPath/root.yaml create mode 100644 test/data/toBundleExamples/longPath/special.yaml create mode 100644 test/data/toBundleExamples/longPath/user.yaml create mode 100644 test/data/toBundleExamples/longPath/userSpecial.yaml diff --git a/lib/bundle.js b/lib/bundle.js index 8522f55..29baf99 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -10,7 +10,8 @@ const { } = require('./jsonPointer'), traverseUtility = require('traverse'), parse = require('./parse.js'), - { ParseError } = require('./common/ParseError'); + { ParseError } = require('./common/ParseError'), + Utils = require('./utils'); let path = require('path'), pathBrowserify = require('path-browserify'), @@ -187,9 +188,10 @@ function setValueInComponents(keyInComponents, components, value, componentsKeys * @param {object} nodeContext - The current node we are processing * @param {object} property - The current property that contains the $ref * @param {string} version - The current version of the spec + * @param {string} commonPathFromData - The common path in the file's paths * @returns {array} The trace to the place where the $ref appears */ -function getTraceFromParentKeyInComponents(nodeContext, property, version) { +function getTraceFromParentKeyInComponents(nodeContext, property, version, commonPathFromData) { const parents = [...nodeContext.parents].reverse(), isArrayKeyRegexp = new RegExp('^\\d$', 'g'), key = nodeContext.key, @@ -202,7 +204,7 @@ function getTraceFromParentKeyInComponents(nodeContext, property, version) { [key, ...parentKeys], nodeTrace = getRootFileTrace(nodeParentsKey), [file, local] = property.split(localPointer), - keyTraceInComponents = getKeyInComponents(nodeTrace, file, local, version); + keyTraceInComponents = getKeyInComponents(nodeTrace, file, local, version, commonPathFromData); return keyTraceInComponents; } @@ -213,9 +215,10 @@ function getTraceFromParentKeyInComponents(nodeContext, property, version) { * @param {Function} pathSolver - function to resolve the Path * @param {string} parentFilename - The parent's filename * @param {object} version - The version of the spec we are bundling + * @param {string} commonPathFromData - The common path in the file's paths * @returns {object} - The references in current node and the new content from the node */ -function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version) { +function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, commonPathFromData) { let referencesInNode = [], nodeReferenceDirectory = {}; traverseUtility(currentNode).forEach(function (property) { @@ -232,7 +235,7 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve ); if (hasReferenceTypeKey) { const tempRef = calculatePath(parentFilename, property.$ref), - nodeTrace = getTraceFromParentKeyInComponents(this, tempRef, version), + nodeTrace = getTraceFromParentKeyInComponents(this, tempRef, version, commonPathFromData), referenceInDocument = getJsonPointerRelationToRoot( tempRef, nodeTrace, @@ -276,9 +279,10 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve * @param {Array} allData - array of { path, content} objects * @param {object} specRoot - root file information * @param {string} version - The current version + * @param {string} commonPathFromData - The common path in the file's paths * @returns {object} - Detect root files result object */ -function getNodeContentAndReferences (currentNode, allData, specRoot, version) { +function getNodeContentAndReferences (currentNode, allData, specRoot, version, commonPathFromData) { let graphAdj = [], missingNodes = [], nodeContent; @@ -295,7 +299,8 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { currentNode.fileName !== specRoot.fileName, removeLocalReferenceFromPath, currentNode.fileName, - version + version, + commonPathFromData ); referencesInNode.forEach((reference) => { @@ -437,9 +442,13 @@ module.exports = { } let algorithm = new DFS(), components = {}, + commonPathFromData = '', rootContextData; + commonPathFromData = Utils.findCommonSubpath(allData.map((fileData) => { + return fileData.fileName; + })); rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { - return getNodeContentAndReferences(currentNode, allData, specRoot, version); + return getNodeContentAndReferences(currentNode, allData, specRoot, version, commonPathFromData); }); components = generateComponentsWrapper(specRoot.parsed.oasObject, version); generateComponentsObject( diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index dd19507..af5d6d4 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -49,9 +49,10 @@ function jsonPointerDecodeAndReplace(filePathName) { * @param {string} filePathName the filePathName of the file * @param {string} localPath the local path that the pointer will reach * @param {string} version - The current spec version +* @param {string} commonPathFromData - The common path in the file's paths * @returns {Array} - the calculated keys in an array representing each nesting property name */ -function getKeyInComponents(traceFromParent, filePathName, localPath, version) { +function getKeyInComponents(traceFromParent, filePathName, localPath, version, commonPathFromData) { const localPart = localPath ? `${localPointer}${localPath}` : '', { CONTAINERS, @@ -61,9 +62,10 @@ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { ROOT_CONTAINERS_KEYS } = getBundleRulesDataByVersion(version); let result, + newFPN = filePathName.replace(commonPathFromData, ''), trace = [ ...traceFromParent, - jsonPointerDecodeAndReplace(`${filePathName}${localPart}`) + jsonPointerDecodeAndReplace(`${newFPN}${localPart}`) ].reverse(), traceToKey = [], matchFound = false, diff --git a/lib/utils.js b/lib/utils.js index be975fc..1d1bcc9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -111,5 +111,36 @@ module.exports = { return reqName.substring(0, 255); } return reqName; + }, + + /** + * Finds the common subpath from an array of strings starting from the + * strings starts + * @param {Array} stringArrays - pointer to get the name from + * @returns {string} - string: the common substring + */ + findCommonSubpath(stringArrays) { + if (!stringArrays || stringArrays.length === 0) { + return ''; + } + let cleanStringArrays = [], + res = []; + stringArrays.forEach((cString) => { + if (cString) { + cleanStringArrays.push(cString.split('/')); + } + }); + const asc = cleanStringArrays.sort((a, b) => { return a.length - b.length; }); + for (let segmentIndex = 0; segmentIndex < asc[0].length; segmentIndex++) { + const segment = asc[0][segmentIndex]; + let nonCompliant = asc.find((cString) => { + return cString[segmentIndex] !== segment; + }); + if (nonCompliant) { + break; + } + res.push(segment); + } + return res.join('/'); } }; diff --git a/test/data/toBundleExamples/longPath/client.json b/test/data/toBundleExamples/longPath/client.json new file mode 100644 index 0000000..181ffbd --- /dev/null +++ b/test/data/toBundleExamples/longPath/client.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "properties": { + "idClient": { + "type": "integer" + }, + "clientName": { + "type": "string" + }, + "special": { + "$ref": "../user/special.yaml" + } + } +} diff --git a/test/data/toBundleExamples/longPath/expected.json b/test/data/toBundleExamples/longPath/expected.json new file mode 100644 index 0000000..e895b2f --- /dev/null +++ b/test/data/toBundleExamples/longPath/expected.json @@ -0,0 +1,94 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_user_user.yaml" + } + } + } + } + } + } + }, + "/clients": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_client_client.json" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_user_user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "special": { + "$ref": "#/components/schemas/_schemas_user_special.yaml" + } + } + }, + "_schemas_client_client.json": { + "type": "object", + "properties": { + "idClient": { + "type": "integer" + }, + "clientName": { + "type": "string" + }, + "special": { + "$ref": "#/components/schemas/_schemas_user_special.yaml" + } + } + }, + "_schemas_user_special.yaml": { + "type": "object", + "properties": { + "specialUserId": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/longPath/magic.yaml b/test/data/toBundleExamples/longPath/magic.yaml new file mode 100644 index 0000000..6e8fc86 --- /dev/null +++ b/test/data/toBundleExamples/longPath/magic.yaml @@ -0,0 +1,6 @@ +type: object +properties: + magicNumber: + type: integer + magicString: + type: string diff --git a/test/data/toBundleExamples/longPath/root.yaml b/test/data/toBundleExamples/longPath/root.yaml new file mode 100644 index 0000000..37594eb --- /dev/null +++ b/test/data/toBundleExamples/longPath/root.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user/user.yaml" + /clients: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/client/client.json" diff --git a/test/data/toBundleExamples/longPath/special.yaml b/test/data/toBundleExamples/longPath/special.yaml new file mode 100644 index 0000000..91e0cc5 --- /dev/null +++ b/test/data/toBundleExamples/longPath/special.yaml @@ -0,0 +1,6 @@ +type: object +properties: + specialClientId: + type: string + magic: + $ref: ./magic.yaml diff --git a/test/data/toBundleExamples/longPath/user.yaml b/test/data/toBundleExamples/longPath/user.yaml new file mode 100644 index 0000000..4704746 --- /dev/null +++ b/test/data/toBundleExamples/longPath/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + id: + type: integer + userName: + type: string + special: + $ref: ./special.yaml diff --git a/test/data/toBundleExamples/longPath/userSpecial.yaml b/test/data/toBundleExamples/longPath/userSpecial.yaml new file mode 100644 index 0000000..85f8831 --- /dev/null +++ b/test/data/toBundleExamples/longPath/userSpecial.yaml @@ -0,0 +1,4 @@ +type: object +properties: + specialUserId: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 3a9c834..73c1ff6 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -37,7 +37,8 @@ let expect = require('chai').expect, additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), compositeOneOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_oneOf'), compositeNot = path.join(__dirname, BUNDLES_FOLDER + '/composite_not'), - compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'); + compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'), + longPath = path.join(__dirname, BUNDLES_FOLDER + '/longPath'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -1924,6 +1925,63 @@ describe('bundle files method - 3.0', function () { expect(error.message).to.equal('The provided version "Anything" is not valid'); } }); + + it('Should bundle long paths into shorter ones', async function () { + let contentRootFile = fs.readFileSync(longPath + '/root.yaml', 'utf8'), + client = fs.readFileSync(longPath + '/client.json', 'utf8'), + magic = fs.readFileSync(longPath + '/magic.yaml', 'utf8'), + special = fs.readFileSync(longPath + '/special.yaml', 'utf8'), + userSpecial = fs.readFileSync(longPath + '/userSpecial.yaml', 'utf8'), + user = fs.readFileSync(longPath + '/user.yaml', 'utf8'), + expected = fs.readFileSync(longPath + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/root.yaml' + } + ], + data: [ + { + 'content': contentRootFile, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/root.yaml' + }, + { + 'content': client, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/schemas' + + '/client/client.json' + }, + { + 'content': magic, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/schemas' + + '/client/magic.yaml' + }, + { + 'content': special, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/schemas' + + '/client/special.yaml' + }, + { + 'content': userSpecial, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/schemas' + + '/user/special.yaml' + }, + { + 'content': user, + 'path': '/pm/openapi-to-postman/test/data/toBundleExamples/same_ref_different_source/schemas/user/user.yaml' + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { diff --git a/test/unit/util.test.js b/test/unit/util.test.js index d1a3948..9e9ef55 100644 --- a/test/unit/util.test.js +++ b/test/unit/util.test.js @@ -2927,3 +2927,31 @@ describe('getPostmanUrlSchemaMatchScore function', function() { expect(endpointMatchScore.pathVars[0]).to.eql({ key: 'spaceId', value: ':spaceId' }); }); }); + +describe('findCommonSubpath method', function () { + it('should return aabb with input ["aa/bb/cc/dd", "aa/bb"]', function () { + const result = Utils.findCommonSubpath(['aa/bb/cc/dd', 'aa/bb']); + expect(result).to.equal('aa/bb'); + }); + + it('should return empty string with undefined input', function () { + const result = Utils.findCommonSubpath(); + expect(result).to.equal(''); + }); + + it('should return empty string with empty array input', function () { + const result = Utils.findCommonSubpath([]); + expect(result).to.equal(''); + }); + + it('should return aabb with input ["aa/bb/cc/dd", "aa/bb", undefined]', function () { + const result = Utils.findCommonSubpath(['aa/bb/cc/dd', 'aa/bb', undefined]); + expect(result).to.equal('aa/bb'); + }); + + it('should return "" with input ["aabbccdd", "aabb", "ccddee"]', function () { + const result = Utils.findCommonSubpath(['aa/bb/cc/dd', 'aa/bb', 'ccddee']); + expect(result).to.equal(''); + }); + +}); From 9441394461bc440e38cc617ef887e1cfb20d9323 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Tue, 21 Jun 2022 20:23:58 -0500 Subject: [PATCH 34/56] Handling collisions - Handle external collisions generated from the naming strategy - Handle local collisions generated by a same named component in local components objects and a new key generated from bundle --- lib/bundle.js | 110 +++++++++++++++-- lib/jsonPointer.js | 50 ++++---- .../expected.json | 111 ++++++++++++++++++ .../schema_collision_from_responses/root.yaml | 43 +++++++ .../schemas/__user.yaml | 8 ++ .../schemas_/_user.yaml | 6 + .../schemas__/user.yaml | 6 + .../expected.json | 80 +++++++++++++ .../root.yaml | 40 +++++++ .../schemas/user.yaml | 8 ++ test/unit/bundle.test.js | 86 +++++++++++++- 11 files changed, 508 insertions(+), 40 deletions(-) create mode 100644 test/data/toBundleExamples/schema_collision_from_responses/expected.json create mode 100644 test/data/toBundleExamples/schema_collision_from_responses/root.yaml create mode 100644 test/data/toBundleExamples/schema_collision_from_responses/schemas/__user.yaml create mode 100644 test/data/toBundleExamples/schema_collision_from_responses/schemas_/_user.yaml create mode 100644 test/data/toBundleExamples/schema_collision_from_responses/schemas__/user.yaml create mode 100644 test/data/toBundleExamples/schema_collision_w_root_components/expected.json create mode 100644 test/data/toBundleExamples/schema_collision_w_root_components/root.yaml create mode 100644 test/data/toBundleExamples/schema_collision_w_root_components/schemas/user.yaml diff --git a/lib/bundle.js b/lib/bundle.js index 8522f55..b35b9e6 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -6,11 +6,13 @@ const { localPointer, jsonPointerLevelSeparator, isLocalRef, - jsonPointerDecodeAndReplace + jsonPointerDecodeAndReplace, + generateObjectName } = require('./jsonPointer'), traverseUtility = require('traverse'), parse = require('./parse.js'), - { ParseError } = require('./common/ParseError'); + { ParseError } = require('./common/ParseError'), + crypto = require('crypto'); let path = require('path'), pathBrowserify = require('path-browserify'), @@ -182,14 +184,44 @@ function setValueInComponents(keyInComponents, components, value, componentsKeys } } +/** + * Calculates the has of a provided key and join its first 4 numbers at the end of the current key by a _ + * @param {string} currentKey The key we will hash + * @returns {string} A key with a hashed part joined by _ + */ +function createComponentHashedKey(currentKey) { + let hashPart = crypto.createHash('sha1') + .update(currentKey) + .digest('base64') + .substring(0, 4), + newKey = generateObjectName(currentKey, hashPart); + return newKey; +} + +/** + * Generates a not repeated mainKey for the component + * @param {string} tempRef - The reference to the node we are processing + * @param {array} mainKeys - A list of previous generated mainKeys + * @returns {string} A generated mainKey from the provided tempRef + */ +function createComponentMainKey(tempRef, mainKeys) { + let newKey = generateObjectName(tempRef), + mainKey = mainKeys[newKey]; + if (mainKey && mainKey !== tempRef) { + newKey = createComponentHashedKey(tempRef); + } + return newKey; +} + /** * Return a trace from the current node's root to the place where we find a $ref * @param {object} nodeContext - The current node we are processing - * @param {object} property - The current property that contains the $ref + * @param {object} tempRef - The tempRef from the $ref + * @param {object} mainKeys - The dictionary of the previous keys generated * @param {string} version - The current version of the spec * @returns {array} The trace to the place where the $ref appears */ -function getTraceFromParentKeyInComponents(nodeContext, property, version) { +function getTraceFromParentKeyInComponents(nodeContext, tempRef, mainKeys, version) { const parents = [...nodeContext.parents].reverse(), isArrayKeyRegexp = new RegExp('^\\d$', 'g'), key = nodeContext.key, @@ -201,11 +233,31 @@ function getTraceFromParentKeyInComponents(nodeContext, property, version) { parentKeys : [key, ...parentKeys], nodeTrace = getRootFileTrace(nodeParentsKey), - [file, local] = property.split(localPointer), - keyTraceInComponents = getKeyInComponents(nodeTrace, file, local, version); + componentKey = createComponentMainKey(tempRef, mainKeys), + keyTraceInComponents = getKeyInComponents(nodeTrace, componentKey, version); return keyTraceInComponents; } +/** + * Modifies the generated trace if there is a collision with a key that exists in root components object + * @param {array} trace The generated trace + * @param {object} initialMainKeys the main keys in local components object if it exists + * @returns {array} A modified trace if there is any collision with local reusable objects + */ +function handleLocalCollisions(trace, initialMainKeys) { + if (trace.length === 0) { + return trace; + } + const componentType = trace[trace.length - 2], + initialKeysOfType = initialMainKeys[componentType], + generatedKeyIndex = trace.length - 1; + + if (initialKeysOfType && initialKeysOfType.includes(trace[generatedKeyIndex])) { + trace[generatedKeyIndex] = createComponentHashedKey(trace[generatedKeyIndex]); + } + return trace; +} + /** * Gets all the $refs from an object * @param {object} currentNode - current node in process @@ -213,11 +265,13 @@ function getTraceFromParentKeyInComponents(nodeContext, property, version) { * @param {Function} pathSolver - function to resolve the Path * @param {string} parentFilename - The parent's filename * @param {object} version - The version of the spec we are bundling + * @param {object} rootMainKeys - A dictionary with the component keys in local components object and its mainKeys * @returns {object} - The references in current node and the new content from the node */ -function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version) { +function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, rootMainKeys) { let referencesInNode = [], - nodeReferenceDirectory = {}; + nodeReferenceDirectory = {}, + mainKeys = {}; traverseUtility(currentNode).forEach(function (property) { if (property) { let hasReferenceTypeKey; @@ -232,7 +286,11 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve ); if (hasReferenceTypeKey) { const tempRef = calculatePath(parentFilename, property.$ref), - nodeTrace = getTraceFromParentKeyInComponents(this, tempRef, version), + nodeTrace = handleLocalCollisions( + getTraceFromParentKeyInComponents(this, tempRef, mainKeys, version), + rootMainKeys + ), + componentKey = nodeTrace[nodeTrace.length - 1], referenceInDocument = getJsonPointerRelationToRoot( tempRef, nodeTrace, @@ -257,9 +315,12 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve node: newValue, reference: referenceInDocument, traceToParent, - parentNodeKey: parentFilename + parentNodeKey: parentFilename, + mainKeyInTrace: nodeTrace[nodeTrace.length - 1] }; + mainKeys[componentKey] = tempRef; + if (!added(property.$ref, referencesInNode)) { referencesInNode.push({ path: pathSolver(property), keyInComponents: nodeTrace, newValue: this.node }); } @@ -276,9 +337,10 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve * @param {Array} allData - array of { path, content} objects * @param {object} specRoot - root file information * @param {string} version - The current version + * @param {object} rootMainKeys - A dictionary with the local reusable components keys and its mainKeys * @returns {object} - Detect root files result object */ -function getNodeContentAndReferences (currentNode, allData, specRoot, version) { +function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys) { let graphAdj = [], missingNodes = [], nodeContent; @@ -295,7 +357,8 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version) { currentNode.fileName !== specRoot.fileName, removeLocalReferenceFromPath, currentNode.fileName, - version + version, + rootMainKeys ); referencesInNode.forEach((reference) => { @@ -420,6 +483,25 @@ function generateComponentsWrapper(parsedOasObject, version) { return components; } +/** + * Returns a dictionary with the resusable component keys and their mainKeys in document before the bundle + * @param {object} components - The wrapper with the root reusable components before the bundle + * @param {string} version - The spec version + * @returns {object} - A directory with the local components keys related with their mainKeys + */ +function getMainKeysFromComponents(components, version) { + const { + COMPONENTS_KEYS + } = getBundleRulesDataByVersion(version); + let componentsDictionary = {}; + COMPONENTS_KEYS.forEach((key) => { + if (components[key]) { + componentsDictionary[key] = Object.keys(components[key]); + } + }); + return componentsDictionary; +} + module.exports = { /** * Takes in an spec root file and an array of data files @@ -435,11 +517,13 @@ module.exports = { if (origin === BROWSER) { path = pathBrowserify; } + const initialComponents = generateComponentsWrapper(specRoot.parsed.oasObject, version), + initialMainKeys = getMainKeysFromComponents(initialComponents, version); let algorithm = new DFS(), components = {}, rootContextData; rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { - return getNodeContentAndReferences(currentNode, allData, specRoot, version); + return getNodeContentAndReferences(currentNode, allData, specRoot, version, initialMainKeys); }); components = generateComponentsWrapper(specRoot.parsed.oasObject, version); generateComponentsObject( diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index dd19507..8b621e2 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -22,17 +22,6 @@ function jsonPointerEncodeAndReplace(filePathName) { return encodeURIComponent(filePathName.replace(tildes, escapedTildeString).replace(slashes, escapedSlashString)); } -/** - * Get a path and return a valid key name in openapi spec - * @param {string} filePathName - The filePath from the ref called - * @returns {string} A valid in openapi object name - */ -function generateObjectName(filePathName) { - let validName = filePathName.replace(/\//g, '_').replace(/#/g, '-'); - validName = validName.replace(/[^a-zA-Z0-9\.\-_]/g, ''); - return validName; -} - /** * Decodes a json pointer * replaces ~0 and ~1 for tildes and slashes @@ -43,27 +32,40 @@ function jsonPointerDecodeAndReplace(filePathName) { return decodeURIComponent(filePathName.replace(escapedSlash, jsonPointerLevelSeparator).replace(escapedTilde, '~')); } +/** + * Get a path and return a valid key name in openapi spec + * @param {string} filePathName - The filePath from the ref called + * @param {string} hash - A calculated hash to join with the resultant generatedName + * @returns {string} A valid in openapi object name + */ +function generateObjectName(filePathName, hash = '') { + let decodedRef = jsonPointerDecodeAndReplace(filePathName), + validName = decodedRef.replace(/\//g, '_').replace(/#/g, '-'), + hashPart = hash ? `_${hash}` : ''; + validName = `${validName.replace(/[^a-zA-Z0-9\.\-_]/g, '')}${hashPart}`; + return validName; +} + /** * returns the key that the object in components will have could be nested * @param {string} traceFromParent the node trace from root. -* @param {string} filePathName the filePathName of the file -* @param {string} localPath the local path that the pointer will reach +* @param {string} mainKey - The generated mainKey for the components * @param {string} version - The current spec version * @returns {Array} - the calculated keys in an array representing each nesting property name */ -function getKeyInComponents(traceFromParent, filePathName, localPath, version) { - const localPart = localPath ? `${localPointer}${localPath}` : '', - { - CONTAINERS, - DEFINITIONS, - COMPONENTS_KEYS, - INLINE, - ROOT_CONTAINERS_KEYS - } = getBundleRulesDataByVersion(version); +function getKeyInComponents(traceFromParent, mainKey, version) { + // const localPart = localPath ? `${localPointer}${localPath}` : '', + const { + CONTAINERS, + DEFINITIONS, + COMPONENTS_KEYS, + INLINE, + ROOT_CONTAINERS_KEYS + } = getBundleRulesDataByVersion(version); let result, trace = [ ...traceFromParent, - jsonPointerDecodeAndReplace(`${filePathName}${localPart}`) + jsonPointerDecodeAndReplace(mainKey) ].reverse(), traceToKey = [], matchFound = false, @@ -91,7 +93,7 @@ function getKeyInComponents(traceFromParent, filePathName, localPath, version) { result = matchFound ? traceToKey.reverse() : []; - return result.map(generateObjectName); + return result; } /** diff --git a/test/data/toBundleExamples/schema_collision_from_responses/expected.json b/test/data/toBundleExamples/schema_collision_from_responses/expected.json new file mode 100644 index 0000000..98bdf09 --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_from_responses/expected.json @@ -0,0 +1,111 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas___user.yaml" + } + } + } + } + } + } + }, + "/users/weird": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas___user.yaml_RQm8" + } + } + } + } + } + } + }, + "/users/other": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas___user.yaml_mvS0" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas___user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + }, + "_schemas___user.yaml_RQm8": { + "type": "object", + "properties": { + "otherCollisionedId": { + "type": "integer" + }, + "someData": { + "type": "string" + } + } + }, + "_schemas___user.yaml_mvS0": { + "type": "object", + "properties": { + "aCollisionedItem": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "randomString": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_from_responses/root.yaml b/test/data/toBundleExamples/schema_collision_from_responses/root.yaml new file mode 100644 index 0000000..82943c2 --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_from_responses/root.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas__/user.yaml" + /users/weird: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas_/_user.yaml" + /users/other: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/__user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_from_responses/schemas/__user.yaml b/test/data/toBundleExamples/schema_collision_from_responses/schemas/__user.yaml new file mode 100644 index 0000000..46d4750 --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_from_responses/schemas/__user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + aCollisionedItem: + type: integer + userName: + type: string + randomString: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_from_responses/schemas_/_user.yaml b/test/data/toBundleExamples/schema_collision_from_responses/schemas_/_user.yaml new file mode 100644 index 0000000..b63536e --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_from_responses/schemas_/_user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + otherCollisionedId: + type: integer + someData: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_from_responses/schemas__/user.yaml b/test/data/toBundleExamples/schema_collision_from_responses/schemas__/user.yaml new file mode 100644 index 0000000..81370de --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_from_responses/schemas__/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_w_root_components/expected.json b/test/data/toBundleExamples/schema_collision_w_root_components/expected.json new file mode 100644 index 0000000..e875d4f --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_w_root_components/expected.json @@ -0,0 +1,80 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_user.yaml_zfNS" + } + } + } + } + } + } + }, + "/users/other": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_user.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_user.yaml": { + "type": "object", + "properties": { + "componentInRootId": { + "type": "string" + } + } + }, + "_schemas_user.yaml_zfNS": { + "type": "object", + "properties": { + "aCollisionedItem": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "randomString": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_w_root_components/root.yaml b/test/data/toBundleExamples/schema_collision_w_root_components/root.yaml new file mode 100644 index 0000000..00e6b64 --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_w_root_components/root.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user.yaml" + /users/other: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "#/components/schemas/_schemas_user.yaml" +components: + schemas: + "_schemas_user.yaml": + type: object + properties: + componentInRootId: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_collision_w_root_components/schemas/user.yaml b/test/data/toBundleExamples/schema_collision_w_root_components/schemas/user.yaml new file mode 100644 index 0000000..46d4750 --- /dev/null +++ b/test/data/toBundleExamples/schema_collision_w_root_components/schemas/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + aCollisionedItem: + type: integer + userName: + type: string + randomString: + type: string \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 3a9c834..6d658bd 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -37,7 +37,9 @@ let expect = require('chai').expect, additionalProperties = path.join(__dirname, BUNDLES_FOLDER + '/additionalProperties'), compositeOneOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_oneOf'), compositeNot = path.join(__dirname, BUNDLES_FOLDER + '/composite_not'), - compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'); + compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'), + schemaCollision = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_from_responses'), + schemaCollisionWRootComponent = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_w_root_components'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -1924,6 +1926,82 @@ describe('bundle files method - 3.0', function () { expect(error.message).to.equal('The provided version "Anything" is not valid'); } }); + + it('Should return bundled file as json - schema_collision_from_responses', async function () { + let contentRootFile = fs.readFileSync(schemaCollision + '/root.yaml', 'utf8'), + user = fs.readFileSync(schemaCollision + '/schemas_/_user.yaml', 'utf8'), + user1 = fs.readFileSync(schemaCollision + '/schemas/__user.yaml', 'utf8'), + user2 = fs.readFileSync(schemaCollision + '/schemas__/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaCollision + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas__/user.yaml', + content: user2 + }, + { + path: '/schemas_/_user.yaml', + content: user + }, + { + path: '/schemas/__user.yaml', + content: user1 + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file as json - schema_collision_w_root_components', async function () { + let contentRootFile = fs.readFileSync(schemaCollisionWRootComponent + '/root.yaml', 'utf8'), + user = fs.readFileSync(schemaCollisionWRootComponent + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaCollisionWRootComponent + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { @@ -1940,7 +2018,8 @@ describe('getReferences method when node does not have any reference', function( userNode.oasObject, nodeIsRoot, removeLocalReferenceFromPath, - 'the/parent/filename' + 'the/parent/filename', + {} ); expect(result.referencesInNode).to.be.an('array').with.length(0); @@ -1971,7 +2050,8 @@ describe('getReferences method when node does not have any reference', function( userNode.oasObject, nodeIsRoot, removeLocalReferenceFromPath, - 'the/parent/filename' + 'the/parent/filename', + {} ); expect(result.nodeReferenceDirectory).to.be.an('object'); expect(Object.keys(result.nodeReferenceDirectory).length).to.equal(1); From 904c55715a47a1dbc84c6047ae4b17101220cf93 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Tue, 21 Jun 2022 20:44:15 -0500 Subject: [PATCH 35/56] Adding tests to 2.0 version --- .../expected.json | 109 ++++++++++++++++++ .../schema_collision_from_responses/root.yaml | 43 +++++++ .../schemas/__user.yaml | 8 ++ .../schemas_/_user.yaml | 6 + .../schemas__/user.yaml | 6 + .../expected.json | 78 +++++++++++++ .../root.yaml | 39 +++++++ .../schemas/user.yaml | 8 ++ test/unit/bundle20.test.js | 82 ++++++++++++- 9 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_from_responses/expected.json create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_from_responses/root.yaml create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas/__user.yaml create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas_/_user.yaml create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas__/user.yaml create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_w_root_components/expected.json create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_w_root_components/root.yaml create mode 100644 test/data/toBundleExamples/swagger20/schema_collision_w_root_components/schemas/user.yaml diff --git a/test/data/toBundleExamples/swagger20/schema_collision_from_responses/expected.json b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/expected.json new file mode 100644 index 0000000..1c30fa8 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/expected.json @@ -0,0 +1,109 @@ +{ + "swagger": 2, + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/_schemas___user.yaml" + } + } + } + } + } + } + }, + "/users/weird": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/_schemas___user.yaml_RQm8" + } + } + } + } + } + } + }, + "/users/other": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/_schemas___user.yaml_mvS0" + } + } + } + } + } + } + } + }, + "definitions": { + "_schemas___user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + }, + "_schemas___user.yaml_RQm8": { + "type": "object", + "properties": { + "otherCollisionedId": { + "type": "integer" + }, + "someData": { + "type": "string" + } + } + }, + "_schemas___user.yaml_mvS0": { + "type": "object", + "properties": { + "aCollisionedItem": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "randomString": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_from_responses/root.yaml b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/root.yaml new file mode 100644 index 0000000..60ebad2 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/root.yaml @@ -0,0 +1,43 @@ +swagger: 2.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas__/user.yaml" + /users/weird: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas_/_user.yaml" + /users/other: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/__user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas/__user.yaml b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas/__user.yaml new file mode 100644 index 0000000..46d4750 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas/__user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + aCollisionedItem: + type: integer + userName: + type: string + randomString: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas_/_user.yaml b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas_/_user.yaml new file mode 100644 index 0000000..b63536e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas_/_user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + otherCollisionedId: + type: integer + someData: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas__/user.yaml b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas__/user.yaml new file mode 100644 index 0000000..81370de --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_from_responses/schemas__/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/expected.json b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/expected.json new file mode 100644 index 0000000..141900f --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/expected.json @@ -0,0 +1,78 @@ +{ + "swagger": 2, + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/_schemas_user.yaml_zfNS" + } + } + } + } + } + } + }, + "/users/other": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/definitions/_schemas_user.yaml" + } + } + } + } + } + } + } + }, + "definitions": { + "_schemas_user.yaml": { + "type": "object", + "properties": { + "componentInRootId": { + "type": "string" + } + } + }, + "_schemas_user.yaml_zfNS": { + "type": "object", + "properties": { + "aCollisionedItem": { + "type": "integer" + }, + "userName": { + "type": "string" + }, + "randomString": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/root.yaml b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/root.yaml new file mode 100644 index 0000000..b9ca19a --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/root.yaml @@ -0,0 +1,39 @@ +swagger: 2.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "./schemas/user.yaml" + /users/other: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + content: + application/json: + schema: + $ref: "#/definitions/_schemas_user.yaml" +definitions: + "_schemas_user.yaml": + type: object + properties: + componentInRootId: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/schemas/user.yaml b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/schemas/user.yaml new file mode 100644 index 0000000..46d4750 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/schema_collision_w_root_components/schemas/user.yaml @@ -0,0 +1,8 @@ +type: object +properties: + aCollisionedItem: + type: integer + userName: + type: string + randomString: + type: string \ No newline at end of file diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js index 476a712..99034dd 100644 --- a/test/unit/bundle20.test.js +++ b/test/unit/bundle20.test.js @@ -23,7 +23,11 @@ let expect = require('chai').expect, SWAGGER_PETSTORE_FOLDER = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), additionalProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/additionalProperties'), referencedSecuritySchemes20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_security_schemes'), - referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'); + referencedResponse20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_response'), + schemaCollision = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/schema_collision_from_responses'), + schemaCollisionWRootComponent = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/schema_collision_w_root_components'); describe('bundle files method - 2.0', function() { it('Should return bundled result from - nestedProperties20', async function() { @@ -826,4 +830,80 @@ describe('bundle files method - 2.0', function() { expect(res.result).to.be.true; expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should return bundled file as json - schema_collision_from_responses', async function () { + let contentRootFile = fs.readFileSync(schemaCollision + '/root.yaml', 'utf8'), + user = fs.readFileSync(schemaCollision + '/schemas_/_user.yaml', 'utf8'), + user1 = fs.readFileSync(schemaCollision + '/schemas/__user.yaml', 'utf8'), + user2 = fs.readFileSync(schemaCollision + '/schemas__/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaCollision + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas__/user.yaml', + content: user2 + }, + { + path: '/schemas_/_user.yaml', + content: user + }, + { + path: '/schemas/__user.yaml', + content: user1 + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('2.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); + + it('Should return bundled file as json - schema_collision_w_root_components', async function () { + let contentRootFile = fs.readFileSync(schemaCollisionWRootComponent + '/root.yaml', 'utf8'), + user = fs.readFileSync(schemaCollisionWRootComponent + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaCollisionWRootComponent + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('2.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); From a8ba60547328a2aa8d4dcb84f457f8f08260967e Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Tue, 21 Jun 2022 21:09:06 -0500 Subject: [PATCH 36/56] Fix and removing outdated getKeyInComponents tests The getKeyInComponents method has changed and now it receives the component key after rename it using the current naming strategy. --- test/unit/jsonPointer.test.js | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/test/unit/jsonPointer.test.js b/test/unit/jsonPointer.test.js index d30e151..89d2843 100644 --- a/test/unit/jsonPointer.test.js +++ b/test/unit/jsonPointer.test.js @@ -20,43 +20,12 @@ describe('getKeyInComponents function', function () { expect(result).to.be.an('array').with.length(0); }); - it('should return ["schemas", "_folder_pet.yaml"] when the filename scaped slash', function () { - const result = getKeyInComponents(['path', 'schemas'], '~1folder~1pet.yaml'); + it('should return ["schemas", "_folder_pet.yaml"] when the filename _folder_pet.yaml', function () { + const result = getKeyInComponents(['path', 'schemas'], '_folder_pet.yaml'); expect(result).to.be.an('array').with.length(2); expect(result[0]).to.equal('schemas'); expect(result[1]).to.equal('_folder_pet.yaml'); }); - - it('should return ["schemas", "_folder_pet.yaml"] when the filename contains any slash', function () { - const result = getKeyInComponents(['path', 'schemas'], '/folder/pet.yaml'); - expect(result).to.be.an('array').with.length(2); - expect(result[0]).to.equal('schemas'); - expect(result[1]).to.equal('_folder_pet.yaml'); - }); - - it('should return ["schemas", "_folder_pet.yaml-_Name"] when the filename contains any sacped slash' + - 'and a local part using a scaped sharp', function () { - const result = getKeyInComponents(['path', 'schemas'], '~1folder~1pet.yaml%23~1Name'); - expect(result).to.be.an('array').with.length(2); - expect(result[0]).to.equal('schemas'); - expect(result[1]).to.equal('_folder_pet.yaml-_Name'); - }); - - it('should return ["schemas", "_folder_pet.yaml-_Name"] when the filename contains any slash' + - 'and a local part using a sharp', function () { - const result = getKeyInComponents(['path', 'schemas'], '/folder/pet.yaml#/Name'); - expect(result).to.be.an('array').with.length(2); - expect(result[0]).to.equal('schemas'); - expect(result[1]).to.equal('_folder_pet.yaml-_Name'); - }); - - it('should return ["schemas", "_foldertest_pet.yaml-_Name"] when the filename contains any slash' + - 'and a local part using a sharp and another not valid character', function () { - const result = getKeyInComponents(['path', 'schemas'], '/folder@test/pet@.yaml#/Name'); - expect(result).to.be.an('array').with.length(2); - expect(result[0]).to.equal('schemas'); - expect(result[1]).to.equal('_foldertest_pet.yaml-_Name'); - }); }); describe('getJsonPointerRelationToRoot function', function () { From 1754339889196223bfab0436ddae3161a3720cb0 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 22 Jun 2022 12:21:34 -0500 Subject: [PATCH 37/56] Update jsonPointer.test.js Add required parameter --- test/unit/jsonPointer.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/jsonPointer.test.js b/test/unit/jsonPointer.test.js index 89d2843..63c9cfd 100644 --- a/test/unit/jsonPointer.test.js +++ b/test/unit/jsonPointer.test.js @@ -21,7 +21,7 @@ describe('getKeyInComponents function', function () { }); it('should return ["schemas", "_folder_pet.yaml"] when the filename _folder_pet.yaml', function () { - const result = getKeyInComponents(['path', 'schemas'], '_folder_pet.yaml'); + const result = getKeyInComponents(['path', 'schemas'], '_folder_pet.yaml', '3.0', ''); expect(result).to.be.an('array').with.length(2); expect(result[0]).to.equal('schemas'); expect(result[1]).to.equal('_folder_pet.yaml'); From 920ea81dccee260eb12cc6b782dc993df4330c9b Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Thu, 23 Jun 2022 11:26:03 -0500 Subject: [PATCH 38/56] homologate from multifile --- lib/bundle.js | 80 ++++++--- lib/parse.js | 6 +- lib/schemaUtils.js | 2 +- lib/schemapack.js | 10 +- .../expectedNodeNotProvided.json | 4 +- test/unit/bundle.test.js | 157 +++++++++++++++++- 6 files changed, 222 insertions(+), 37 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index c0b7b83..0c9d130 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -46,6 +46,16 @@ function parseFileOrThrow(fileContent) { return result; } +/** + * Parses a node content or throw ParseError if there's any error + * @param {string} fileContent The content from the current node + * @returns {object} The parsed content + */ +function parseFile(fileContent) { + const result = parse.getOasObject(fileContent); + return result; +} + /** * Calculates the path relative to parent * @param {string} parentFileName - parent file name of the current node @@ -269,13 +279,15 @@ function handleLocalCollisions(trace, initialMainKeys) { * @param {object} version - The version of the spec we are bundling * @param {object} rootMainKeys - A dictionary with the component keys in local components object and its mainKeys * @param {string} commonPathFromData - The common path in the file's paths + * @param {Array} allData - array of { path, content} objects * @returns {object} - The references in current node and the new content from the node */ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, rootMainKeys, - commonPathFromData) { + commonPathFromData, allData) { let referencesInNode = [], nodeReferenceDirectory = {}, mainKeys = {}; + traverseUtility(currentNode).forEach(function (property) { if (property) { let hasReferenceTypeKey; @@ -306,10 +318,21 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve return item !== undefined; }), this.key]; let newValue, - [, local] = tempRef.split(localPointer); + [, local] = tempRef.split(localPointer), + nodeFromData, + refHasContent = false, + parseResult; newValue = Object.assign({}, this.node); - newValue.$ref = referenceInDocument; + nodeFromData = findNodeFromPath(tempRef, allData); + if (nodeFromData && nodeFromData.content) { + parseResult = parseFile(nodeFromData.content); + if (parseResult.result) { + newValue.$ref = referenceInDocument; + refHasContent = true; + nodeFromData.parsed = parseResult; + } + } this.update({ $ref: tempRef }); @@ -320,7 +343,8 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve reference: referenceInDocument, traceToParent, parentNodeKey: parentFilename, - mainKeyInTrace: nodeTrace[nodeTrace.length - 1] + mainKeyInTrace: nodeTrace[nodeTrace.length - 1], + refHasContent }; mainKeys[componentKey] = tempRef; @@ -348,13 +372,18 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys, commonPathFromData) { let graphAdj = [], missingNodes = [], - nodeContent; + nodeContent, + parseResult; if (currentNode.parsed) { nodeContent = currentNode.parsed.oasObject; } else { - nodeContent = parseFileOrThrow(currentNode.content).oasObject; + parseResult = parseFile(currentNode.content); + if (parseResult.result === false) { + return { graphAdj, missingNodes, undefined, nodeReferenceDirectory: {}, nodeName: currentNode.fileName }; + } + nodeContent = parseResult.oasObject; } const { referencesInNode, nodeReferenceDirectory } = getReferences( @@ -364,7 +393,8 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version, r currentNode.fileName, version, rootMainKeys, - commonPathFromData + commonPathFromData, + allData ); referencesInNode.forEach((reference) => { @@ -404,12 +434,14 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver const { COMPONENTS_KEYS } = getBundleRulesDataByVersion(version); notInLine.forEach(([key, value]) => { let [, partial] = key.split(localPointer); - setValueInComponents( - value.keyInComponents, - components, - getContentFromTrace(documentContext.nodeContents[key], partial), - COMPONENTS_KEYS - ); + if (documentContext.globalReferences[key].refHasContent) { + setValueInComponents( + value.keyInComponents, + components, + getContentFromTrace(documentContext.nodeContents[key], partial), + COMPONENTS_KEYS + ); + } }); [rootContent, components].forEach((contentData) => { traverseUtility(contentData).forEach(function (property) { @@ -429,12 +461,12 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver return missingNode.path === nodeRef; }); if (isMissingNode) { - refData.nodeContent = { - [tempRef]: 'This related node was not found in provided data' - }; - refData.inline = true; + refData.nodeContent = refData.node; refData.local = false; } + else if (!refData) { + return; + } else { refData.nodeContent = documentContext.nodeContents[nodeRef]; refData.inline = refData.keyInComponents.length === 0; @@ -453,12 +485,14 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver } this.update(refData.node); if (!refData.inline) { - setValueInComponents( - refData.keyInComponents, - components, - refData.nodeContent, - COMPONENTS_KEYS - ); + if (documentContext.globalReferences[tempRef].refHasContent) { + setValueInComponents( + refData.keyInComponents, + components, + refData.nodeContent, + version + ); + } } } } diff --git a/lib/parse.js b/lib/parse.js index 80ff638..5923d34 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -96,9 +96,11 @@ module.exports = { * @param {Object} options computed process options * @param {Object} files Files map * @param {string} specificationVersion the string of the desired version + * @param {boolean} allowReadingFS wheter to allow reading content from file system * @return {String} rootFile */ - getRootFiles: function (input, inputValidation, options, files = {}, specificationVersion) { + getRootFiles: function (input, inputValidation, options, files = {}, specificationVersion, + allowReadingFS = true) { let rootFilesArray = [], filesPathArray = input.data, origin = input.origin || ''; @@ -114,7 +116,7 @@ module.exports = { if (!_.isEmpty(files)) { file = files[path.resolve(filePath.fileName)]; } - else { + else if (allowReadingFS) { file = fs.readFileSync(filePath.fileName, 'utf8'); } diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 87f0565..ae75338 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -4854,7 +4854,7 @@ module.exports = { bundledFile[key] = value; }); } - else { + else if (!_.isEmpty(contentAndComponents.components)) { bundledFile.components = contentAndComponents.components; } if (!format) { diff --git a/lib/schemapack.js b/lib/schemapack.js index a2212e5..6b72436 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -643,15 +643,13 @@ class SchemaPack { path = pathBrowserify; OasResolverOptions.browser = true; } - if ('content' in input.data[0]) { - input.data.forEach((file) => { - files[path.resolve(file.fileName)] = file.content ? file.content : ''; - }); - } + input.data.forEach((file) => { + files[path.resolve(file.fileName)] = file.content ? file.content : ''; + }); adaptedInput = schemaUtils.mapDetectRootFilesInputToGetRootFilesInput(input); adaptedInput.origin = input.origin; rootFiles = parse.getRootFiles(adaptedInput, concreteUtils.inputValidation, this.computedOptions, files, - input.specificationVersion); + input.specificationVersion, false); res = schemaUtils.mapGetRootFilesOutputToDetectRootFilesOutput(rootFiles, input.specificationVersion); return res; } diff --git a/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json b/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json index 9a8dd9c..ed726ee 100644 --- a/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json +++ b/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json @@ -60,7 +60,7 @@ }, "components": { "responses": { - "/responses.yaml": "This related node was not found in provided data" + "$ref": "./responses.yaml" }, "schemas": { "Monster": { @@ -98,4 +98,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index bbd738c..f160d48 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -657,8 +657,8 @@ describe('bundle files method - 3.0', function () { expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); - it('Should return a "/missing/node/path": NotProvided' + - ' in the place of a not providen node - local_references', async function () { + it('Should return the not handled reference ($ref: ./responses.yaml) ' + + 'in the place of a not provided node - local_references', async function () { let contentRootFile = fs.readFileSync(localRefFolder + '/root.yaml', 'utf8'), schemasIndex = fs.readFileSync(localRefFolder + '/schemas/index.yaml', 'utf8'), schemasClient = fs.readFileSync(localRefFolder + '/schemas/client.yaml', 'utf8'), @@ -2060,6 +2060,156 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + it('should ignore reference when is empty content and no root is sent', async function () { + let input = + { + type: 'multiFile', + specificationVersion: '3.0', + bundleFormat: 'YAML', + data: [ + { + path: 'hello.yaml', + content: '' + }, + { + path: 'openapi.yaml', + content: 'openapi: 3.0.0\n' + + 'info:\n' + + ' title: hello world\n' + + ' version: 0.1.1\n' + + 'paths:\n' + + ' /hello:\n' + + ' get:\n' + + ' summary: get the hello\n' + + ' responses:\n' + + ' \'200\':\n' + + ' description: sample des\n' + + ' content:\n' + + ' application/json:\n' + + ' schema:\n' + + ' $ref: ./hello.yaml\n' + } + ] + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(res.output.data[0].bundledContent).to.be.equal(input.data[1].content); + }); + + it('should ignore reference when is empty content', async function () { + let input = + { + type: 'multiFile', + specificationVersion: '3.0', + bundleFormat: 'YAML', + rootFiles: [{ path: 'openapi.yaml' }], + data: [ + { + path: 'hello.yaml', + content: '' + }, + { + path: 'openapi.yaml', + content: 'openapi: 3.0.0\n' + + 'info:\n' + + ' title: hello world\n' + + ' version: 0.1.1\n' + + 'paths:\n' + + ' /hello:\n' + + ' get:\n' + + ' summary: get the hello\n' + + ' responses:\n' + + ' \'200\':\n' + + ' description: sample des\n' + + ' content:\n' + + ' application/json:\n' + + ' schema:\n' + + ' $ref: ./hello.yaml\n' + } + ] + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(res.output.data[0].bundledContent).to.be.equal(input.data[1].content); + }); + + it('should ignore reference when is invalid content', async function () { + let input = + { + type: 'multiFile', + specificationVersion: '3.0', + bundleFormat: 'YAML', + rootFiles: [{ path: 'openapi.yaml' }], + data: [ + { + path: 'hello.yaml', + content: 'asd' + }, + { + path: 'openapi.yaml', + content: 'openapi: 3.0.0\n' + + 'info:\n' + + ' title: hello world\n' + + ' version: 0.1.1\n' + + 'paths:\n' + + ' /hello:\n' + + ' get:\n' + + ' summary: get the hello\n' + + ' responses:\n' + + ' \'200\':\n' + + ' description: sample des\n' + + ' content:\n' + + ' application/json:\n' + + ' schema:\n' + + ' $ref: ./hello.yaml\n' + } + ] + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(res.output.data[0].bundledContent).to.be.equal(input.data[1].content); + }); + + it('should ignore reference when is invalid', async function () { + let input = + { + type: 'multiFile', + specificationVersion: '3.0', + bundleFormat: 'YAML', + rootFiles: [{ path: 'openapi.yaml' }], + data: [ + { + path: 'openapi.yaml', + content: 'openapi: 3.0.0\n' + + 'info:\n' + + ' title: hello world\n' + + ' version: 0.1.1\n' + + 'paths:\n' + + ' /hello:\n' + + ' get:\n' + + ' summary: get the hello\n' + + ' responses:\n' + + ' \'200\':\n' + + ' description: sample des\n' + + ' content:\n' + + ' application/json:\n' + + ' schema:\n' + + ' $ref: ./hello.yaml\n' + } + ] + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(res.output.data[0].bundledContent).to.be.equal(input.data[0].content); + }); }); describe('getReferences method when node does not have any reference', function() { @@ -2111,7 +2261,8 @@ describe('getReferences method when node does not have any reference', function( 'the/parent/filename', '3.0', {}, - '' + '', + [] ); expect(result.nodeReferenceDirectory).to.be.an('object'); expect(Object.keys(result.nodeReferenceDirectory).length).to.equal(1); From 272db64377e7a24fd3e64a1fe5031cd5b85dc51d Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 23 Jun 2022 13:03:56 -0500 Subject: [PATCH 39/56] Fix components keys injection in setValueInComponents method --- lib/bundle.js | 2 +- .../local_references/expectedNodeNotProvided.json | 2 +- test/unit/detectRoot.test.js | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index 0c9d130..2be4e3e 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -490,7 +490,7 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver refData.keyInComponents, components, refData.nodeContent, - version + COMPONENTS_KEYS ); } } diff --git a/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json b/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json index ed726ee..5f1c8d1 100644 --- a/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json +++ b/test/data/toBundleExamples/local_references/expectedNodeNotProvided.json @@ -98,4 +98,4 @@ } } } -} +} \ No newline at end of file diff --git a/test/unit/detectRoot.test.js b/test/unit/detectRoot.test.js index 34b07f9..fda8cad 100644 --- a/test/unit/detectRoot.test.js +++ b/test/unit/detectRoot.test.js @@ -246,7 +246,7 @@ describe('detectRoot method', function() { expect(res.output.data[0].path).to.equal('/swagger.json'); }); - it('should read content when is not present 3.0 and no specific version', async function () { + it('should not read content from FS when is not present', async function () { let input = { type: 'multiFile', specificationVersion: '3.1.0', @@ -262,8 +262,7 @@ describe('detectRoot method', function() { const res = await Converter.detectRootFiles(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; - expect(res.output.data[0].path).to.equal(validHopService31x); - + expect(res.output.data.length).to.equal(0); }); it('should return error when "type" parameter is not sent', async function () { From 9db4efb61a725e06b86f2869f65fc974eb869cdc Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:01:39 -0500 Subject: [PATCH 40/56] add computed opions in bundle with root resolving --- lib/schemapack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/schemapack.js b/lib/schemapack.js index 8f6a8d4..9b24b86 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -716,7 +716,7 @@ class SchemaPack { } }); input.rootFiles = inputContent; - return schemaUtils.processRelatedFiles(input, true); + return schemaUtils.processRelatedFiles(input, true, this.computedOptions); } } let adaptedRootFiles = input.rootFiles.map((rootFile) => { From 92a6ad79c2352a2b1b595301899400af7726be3e Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Thu, 30 Jun 2022 16:12:03 -0500 Subject: [PATCH 41/56] inline properties schema properties resolved inline --- lib/bundleRules/spec30.js | 2 +- lib/bundleRules/spec31.js | 2 +- .../referenced_properties/attributes.yaml | 10 ++++ .../referenced_properties/expected.json | 47 +++++++++++++++++++ .../referenced_properties/operation.yaml | 22 +++++++++ .../referenced_properties/root.yaml | 10 ++++ test/unit/bundle.test.js | 41 +++++++++++++++- 7 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 test/data/toBundleExamples/referenced_properties/attributes.yaml create mode 100644 test/data/toBundleExamples/referenced_properties/expected.json create mode 100644 test/data/toBundleExamples/referenced_properties/operation.yaml create mode 100644 test/data/toBundleExamples/referenced_properties/root.yaml diff --git a/lib/bundleRules/spec30.js b/lib/bundleRules/spec30.js index bdce520..85a4aff 100644 --- a/lib/bundleRules/spec30.js +++ b/lib/bundleRules/spec30.js @@ -25,7 +25,7 @@ const SCHEMA_CONTAINERS = [ properties: 'schemas', links: 'links' }, - INLINE = [], + INLINE = ['properties'], ROOT_CONTAINERS_KEYS = [ 'components' ], diff --git a/lib/bundleRules/spec31.js b/lib/bundleRules/spec31.js index 4026d29..aeddf76 100644 --- a/lib/bundleRules/spec31.js +++ b/lib/bundleRules/spec31.js @@ -26,7 +26,7 @@ const SCHEMA_CONTAINERS = [ links: 'links', paths: 'pathItems' }, - INLINE = [], + INLINE = ['properties'], ROOT_CONTAINERS_KEYS = [ 'components' ], diff --git a/test/data/toBundleExamples/referenced_properties/attributes.yaml b/test/data/toBundleExamples/referenced_properties/attributes.yaml new file mode 100644 index 0000000..4d90787 --- /dev/null +++ b/test/data/toBundleExamples/referenced_properties/attributes.yaml @@ -0,0 +1,10 @@ +load_balancer_droplet_ids: + droplet_ids: + type: array + items: + type: integer + example: + - 3164444 + - 3164445 + description: An array containing the IDs of the Droplets assigned to the + load balancer. diff --git a/test/data/toBundleExamples/referenced_properties/expected.json b/test/data/toBundleExamples/referenced_properties/expected.json new file mode 100644 index 0000000..8e72d9f --- /dev/null +++ b/test/data/toBundleExamples/referenced_properties/expected.json @@ -0,0 +1,47 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "paths": { + "/users/{userId}": { + "get": { + "operationId": "loadBalancers_remove_droplets", + "summary": "Remove Droplets from a Load Balancer", + "description": "To remove a..\n", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "droplet_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "example": [ + 3164444, + 3164445 + ], + "description": "An array containing the IDs of the Droplets assigned to the load balancer." + } + }, + "required": [ + "droplet_ids" + ] + } + } + } + }, + "responses": { + "default": { + "description": "ok" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_properties/operation.yaml b/test/data/toBundleExamples/referenced_properties/operation.yaml new file mode 100644 index 0000000..0f151a7 --- /dev/null +++ b/test/data/toBundleExamples/referenced_properties/operation.yaml @@ -0,0 +1,22 @@ +operationId: loadBalancers_remove_droplets + +summary: Remove Droplets from a Load Balancer + +description: | + To remove a.. + +requestBody: + required: true + + content: + application/json: + schema: + properties: + $ref: '/attributes.yaml#/load_balancer_droplet_ids' + required: + - droplet_ids + +responses: + default: + description: ok + diff --git a/test/data/toBundleExamples/referenced_properties/root.yaml b/test/data/toBundleExamples/referenced_properties/root.yaml new file mode 100644 index 0000000..8d105f0 --- /dev/null +++ b/test/data/toBundleExamples/referenced_properties/root.yaml @@ -0,0 +1,10 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +paths: + /users/{userId}: + get: + $ref: 'operation.yaml' diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index bb92ae5..a80a380 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -40,7 +40,8 @@ let expect = require('chai').expect, compositeAnyOf = path.join(__dirname, BUNDLES_FOLDER + '/composite_anyOf'), longPath = path.join(__dirname, BUNDLES_FOLDER + '/longPath'), schemaCollision = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_from_responses'), - schemaCollisionWRootComponent = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_w_root_components'); + schemaCollisionWRootComponent = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_w_root_components'), + referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2211,6 +2212,44 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(res.output.data[0].bundledContent).to.be.equal(input.data[0].content); }); + + it('Should return bundled file as with referenced properties', async function () { + let contentRootFile = fs.readFileSync(referencedProperties + '/root.yaml', 'utf8'), + operation = fs.readFileSync(referencedProperties + '/operation.yaml', 'utf8'), + attributes = fs.readFileSync(referencedProperties + '/attributes.yaml', 'utf8'), + expected = fs.readFileSync(referencedProperties + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/operation.yaml', + content: operation + }, + { + path: '/attributes.yaml', + content: attributes + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 9cfb88c36c18b83d5151c1361029e6a884f11ba4 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 30 Jun 2022 19:49:30 -0500 Subject: [PATCH 42/56] Adding fix to issue 14 - Adding support to values as a nested example - Merging the root components with the referenced ones instead of replace --- lib/bundle.js | 6 +- lib/bundleRules/spec30.js | 3 +- lib/bundleRules/spec31.js | 3 +- .../nested_examples_as_value/expected.json | 304 ++++++++++++++++++ .../nested_examples_as_value/index.yml | 42 +++ .../parameters/_index.yml | 158 +++++++++ .../parameters/arrival_time.yml | 20 ++ .../nested_examples_as_value/paths/_index.yml | 82 +++++ .../paths/geolocate.yml | 80 +++++ ...ps_http_geolocation_celltowers_request.yml | 29 ++ .../maps_http_geolocation_wifi_request.yml | 31 ++ .../maps_http_geolocation_wifi_response.yml | 23 ++ .../schemas/Bounds.yml | 23 ++ .../schemas/CellTower.yml | 57 ++++ .../schemas/GeolocationRequest.yml | 45 +++ .../schemas/GeolocationResponse.yml | 27 ++ .../schemas/LatLngLiteral.yml | 27 ++ .../schemas/_index.yml | 169 ++++++++++ .../nested_examples_as_value/schemas/test.yml | 6 + test/unit/bundle.test.js | 102 +++++- 20 files changed, 1233 insertions(+), 4 deletions(-) create mode 100644 test/data/toBundleExamples/nested_examples_as_value/expected.json create mode 100644 test/data/toBundleExamples/nested_examples_as_value/index.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/parameters/_index.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/parameters/arrival_time.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/paths/_index.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_celltowers_request.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_wifi_request.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/responses/maps_http_geolocation_wifi_response.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/Bounds.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/CellTower.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationRequest.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationResponse.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/LatLngLiteral.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/_index.yml create mode 100644 test/data/toBundleExamples/nested_examples_as_value/schemas/test.yml diff --git a/lib/bundle.js b/lib/bundle.js index ad25c27..aefec15 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -481,7 +481,11 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver } } if (refData.inline) { - refData.node = refData.nodeContent; + let referenceSiblings = _.omit(property, ['$ref']), + hasSiblings = !_.isEmpty(referenceSiblings); + refData.node = hasSiblings ? + _.merge(referenceSiblings, refData.nodeContent) : + refData.nodeContent; } this.update(refData.node); if (!refData.inline) { diff --git a/lib/bundleRules/spec30.js b/lib/bundleRules/spec30.js index 85a4aff..1e489cd 100644 --- a/lib/bundleRules/spec30.js +++ b/lib/bundleRules/spec30.js @@ -8,7 +8,8 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example' + 'example', + 'value' ], REQUEST_BODY_CONTAINER = [ 'requestBody' diff --git a/lib/bundleRules/spec31.js b/lib/bundleRules/spec31.js index aeddf76..b8de95c 100644 --- a/lib/bundleRules/spec31.js +++ b/lib/bundleRules/spec31.js @@ -8,7 +8,8 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example' + 'example', + 'value' ], REQUEST_BODY_CONTAINER = [ 'requestBody' diff --git a/test/data/toBundleExamples/nested_examples_as_value/expected.json b/test/data/toBundleExamples/nested_examples_as_value/expected.json new file mode 100644 index 0000000..c358317 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/expected.json @@ -0,0 +1,304 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Google Maps Platform", + "description": "API Specification for Google Maps Platform", + "version": "1.17.16" + }, + "servers": [ + { + "url": "https://www.googleapis.com" + } + ], + "paths": { + "/geolocation/v1/geolocate": { + "post": { + "servers": [ + { + "url": "https://www.googleapis.com" + } + ], + "tags": [ + "Geolocation API" + ], + "description": "Geolocation API returns a location and accuracy radius based on information about cell towers and WiFi nodes that the mobile client can detect. This document describes the protocol used to send this data to the server and to return a response to the client.\n\nCommunication is done over HTTPS using POST. Both request and response are formatted as JSON, and the content type of both is `application/json`.\n\nYou must specify a key in your request, included as the value of a`key` parameter. A `key` is your application's API key. This key identifies your application for purposes of quota management. Learn how to [get a key](https://developers.google.com/maps/documentation/geolocation/get-api-key).", + "responses": { + "200": { + "description": "200 OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_GeolocationResponse.yml" + }, + "examples": { + "WIFI": { + "value": { + "$ref": "#/components/examples/_responses_maps_http_geolocation_wifi_response.yml" + } + } + } + } + } + }, + "400": { + "description": "400 BAD REQUEST", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/_schemas_test.yml" + }, + "other": { + "type": "string" + } + } + } + } + } + } + }, + "requestBody": { + "description": "The request body must be formatted as JSON.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemas_GeolocationRequest.yml" + }, + "examples": { + "WIFI": { + "value": { + "$ref": "#/components/examples/_requests_maps_http_geolocation_wifi_request.yml" + } + } + } + } + }, + "required": false + } + } + } + }, + "components": { + "parameters": { + "arrival_time": { + "name": "arrival_time", + "description": "Specifies the desired time of arrival for transit directions, in seconds since midnight, January 1, 1970 UTC. You can specify either `departure_time` or `arrival_time`, but not both. Note that `arrival_time` must be specified as an integer.\n", + "in": "query", + "schema": { + "type": "number" + } + } + }, + "schemas": { + "_schemas_LatLngLiteral.yml": { + "type": "object", + "title": "LatLngLiteral", + "description": "An object describing a specific location with Latitude and Longitude in decimal degrees.", + "required": [ + "lat", + "lng" + ], + "properties": { + "lat": { + "type": "number", + "description": "Latitude in decimal degrees" + }, + "lng": { + "type": "number", + "description": "Longitude in decimal degrees" + } + } + }, + "_schemas_GeolocationResponse.yml": { + "type": "object", + "title": "GeolocationResponse", + "description": "A successful geolocation request will return a JSON-formatted response defining a location and radius.", + "required": [ + "location", + "accuracy" + ], + "properties": { + "location": { + "description": "The user’s estimated latitude and longitude, in degrees.", + "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" + }, + "accuracy": { + "description": "The accuracy of the estimated location, in meters. This represents the radius of a circle around the given `location`. If your Geolocation response shows a very high value in the `accuracy` field, the service may be geolocating based on the request IP, instead of WiFi points or cell towers. This can happen if no cell towers or access points are valid or recognized. To confirm that this is the issue, set `considerIp` to `false` in your request. If the response is a `404`, you've confirmed that your `wifiAccessPoints` and `cellTowers` objects could not be geolocated.", + "type": "number" + } + }, + "example": { + "$ref": "#/components/examples/_responses_maps_http_geolocation_wifi_response.yml" + } + }, + "_schemas_test.yml": { + "type": "object", + "properties": { + "first": { + "type": "string" + }, + "second": { + "type": "integer" + } + } + }, + "_schemas_GeolocationRequest.yml": { + "type": "object", + "title": "GeolocationRequest", + "description": "The request body must be formatted as JSON. The following fields are supported, and all fields are optional.", + "properties": { + "homeMobileCountryCode": { + "type": "integer", + "description": "The cell tower's Mobile Country Code (MCC)." + }, + "homeMobileNetworkCode": { + "type": "integer", + "description": "The cell tower's Mobile Network Code. This is the MNC for GSM and WCDMA; CDMA uses the System ID (SID)." + }, + "radioType": { + "type": "string", + "description": "The mobile radio type. Supported values are lte, gsm, cdma, and wcdma. While this field is optional, it should be included if a value is available, for more accurate results." + }, + "carrier": { + "type": "string", + "description": "The carrier name." + }, + "considerIp": { + "type": "string", + "description": "Specifies whether to fall back to IP geolocation if wifi and cell tower signals are not available. Defaults to true. Set considerIp to false to disable fall back." + }, + "cellTowers": { + "type": "array", + "description": "The request body's cellTowers array contains zero or more cell tower objects.", + "items": { + "$ref": "#/components/schemas/_schemas_CellTower.yml" + } + } + } + }, + "_schemas_CellTower.yml": { + "type": "object", + "title": "CellTower", + "description": "Attributes used to describe a cell tower. The following optional fields are not currently used, but may be included if values are available: `age`, `signalStrength`, `timingAdvance`.", + "required": [ + "cellId", + "locationAreaCode", + "mobileCountryCode", + "mobileNetworkCode" + ], + "properties": { + "cellId": { + "description": "Unique identifier of the cell. On GSM, this is the Cell ID (CID); CDMA networks use the Base Station ID (BID). WCDMA networks use the UTRAN/GERAN Cell Identity (UC-Id), which is a 32-bit value concatenating the Radio Network Controller (RNC) and Cell ID. Specifying only the 16-bit Cell ID value in WCDMA networks may return inaccurate results.", + "type": "integer" + }, + "locationAreaCode": { + "description": "The Location Area Code (LAC) for GSM and WCDMA networks. The Network ID (NID) for CDMA networks.", + "type": "integer" + }, + "mobileCountryCode": { + "description": "The cell tower's Mobile Country Code (MCC).", + "type": "integer" + }, + "mobileNetworkCode": { + "description": "The cell tower's Mobile Network Code. This is the MNC for GSM and WCDMA; CDMA uses the System ID (SID).", + "type": "integer" + }, + "age": { + "description": "The number of milliseconds since this cell was primary. If age is 0, the cellId represents a current measurement.", + "type": "integer" + }, + "signalStrength": { + "description": "Radio signal strength measured in dBm.", + "type": "number" + }, + "timingAdvance": { + "description": "The timing advance value.", + "type": "number" + } + }, + "example": { + "$ref": "#/components/examples/_requests_maps_http_geolocation_celltowers_request.yml" + } + }, + "Bounds": { + "title": "Bounds", + "type": "object", + "description": "A rectangle in geographical coordinates from points at the southwest and northeast corners.", + "required": [ + "northeast", + "southwest" + ], + "properties": { + "northeast": { + "description": "The user’s estimated latitude and longitude, in degrees.", + "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" + }, + "southwest": { + "description": "The user’s estimated latitude and longitude, in degrees.", + "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" + } + } + } + }, + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "query", + "name": "key" + } + }, + "examples": { + "_responses_maps_http_geolocation_wifi_response.yml": { + "location": { + "lat": 37.421925, + "lng": -122.0841293 + }, + "accuracy": 30 + }, + "_requests_maps_http_geolocation_wifi_request.yml": { + "considerIp": "false", + "wifiAccessPoints": [ + { + "macAddress": "84:d4:7e:09:a5:f1", + "signalStrength": -43, + "signalToNoiseRatio": 0 + }, + { + "macAddress": "44:48:c1:a6:f3:d0", + "signalStrength": -55, + "signalToNoiseRatio": 0 + } + ] + }, + "_requests_maps_http_geolocation_celltowers_request.yml": { + "cellTowers": [ + { + "cellId": 170402199, + "locationAreaCode": 35632, + "mobileCountryCode": 310, + "mobileNetworkCode": 410, + "age": 0, + "signalStrength": -60, + "timingAdvance": 15 + } + ] + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "tags": [ + { + "name": "Directions", + "description": "The Directions API is a web service that uses an HTTP request to return JSON or XML-formatted directions between locations. You can receive directions for several modes of transportation, such as transit, driving, walking, or cycling.", + "externalDocs": { + "url": "https://developers.google.com/maps/documentation/directions/overview" + } + } + ] +} \ No newline at end of file diff --git a/test/data/toBundleExamples/nested_examples_as_value/index.yml b/test/data/toBundleExamples/nested_examples_as_value/index.yml new file mode 100644 index 0000000..1227ada --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/index.yml @@ -0,0 +1,42 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: 3.0.3 +info: + title: Google Maps Platform + description: API Specification for Google Maps Platform + version: 1.17.16 +servers: + - url: "https://www.googleapis.com" +paths: + $ref: "./paths/_index.yml" +components: + parameters: + $ref: "./parameters/_index.yml" + schemas: + $ref: "./schemas/_index.yml" + securitySchemes: + ApiKeyAuth: + type: apiKey + in: query + name: key +security: + - ApiKeyAuth: [] +tags: + - name: Directions + description: |- + The Directions API is a web service that uses an HTTP request to return JSON or XML-formatted directions between locations. You can receive directions for several modes of transportation, such as transit, driving, walking, or cycling. + externalDocs: + url: https://developers.google.com/maps/documentation/directions/overview + \ No newline at end of file diff --git a/test/data/toBundleExamples/nested_examples_as_value/parameters/_index.yml b/test/data/toBundleExamples/nested_examples_as_value/parameters/_index.yml new file mode 100644 index 0000000..44ba7cd --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/parameters/_index.yml @@ -0,0 +1,158 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# WARNING: This file is automatically updated as part of `npm run build`. + +arrival_time: + $ref: "./arrival_time.yml" +# departure_time: +# $ref: "./departure_time.yml" +# directions_alternatives: +# $ref: "./directions/alternatives.yml" +# directions_avoid: +# $ref: "./directions/avoid.yml" +# directions_departure_time: +# $ref: "./directions/departure_time.yml" +# directions_destination: +# $ref: "./directions/destination.yml" +# directions_origin: +# $ref: "./directions/origin.yml" +# directions_units: +# $ref: "./directions/units.yml" +# directions_waypoints: +# $ref: "./directions/waypoints.yml" +# distancematrix_avoid: +# $ref: "./distanceMatrix/avoid.yml" +# distancematrix_destinations: +# $ref: "./distanceMatrix/destinations.yml" +# distancematrix_origins: +# $ref: "./distanceMatrix/origins.yml" +# distancematrix_units: +# $ref: "./distanceMatrix/units.yml" +# elevation_locations: +# $ref: "./elevation/locations.yml" +# elevation_path: +# $ref: "./elevation/path.yml" +# elevation_samples: +# $ref: "./elevation/samples.yml" +# geocode_address: +# $ref: "./geocode/address.yml" +# geocode_bounds: +# $ref: "./geocode/bounds.yml" +# geocode_components: +# $ref: "./geocode/components.yml" +# geocode_latlng: +# $ref: "./geocode/latlng.yml" +# geocode_location_type: +# $ref: "./geocode/location_type.yml" +# geocode_place_id: +# $ref: "./geocode/place_id.yml" +# geocode_result_type: +# $ref: "./geocode/result_type.yml" +# language: +# $ref: "./language.yml" +# mode: +# $ref: "./mode.yml" +# nearestroads_points: +# $ref: "./nearestRoads/points.yml" +# places_components: +# $ref: "./places/components.yml" +# places_fields: +# $ref: "./places/fields.yml" +# places_inputtype: +# $ref: "./places/inputtype.yml" +# places_keyword: +# $ref: "./places/keyword.yml" +# places_location-required: +# $ref: "./places/location-required.yml" +# places_location-weighted: +# $ref: "./places/location-weighted.yml" +# places_location: +# $ref: "./places/location.yml" +# places_locationbias: +# $ref: "./places/locationbias.yml" +# places_maxheight: +# $ref: "./places/maxheight.yml" +# places_maxprice: +# $ref: "./places/maxprice.yml" +# places_maxwidth: +# $ref: "./places/maxwidth.yml" +# places_minprice: +# $ref: "./places/minprice.yml" +# places_name: +# $ref: "./places/name.yml" +# places_offset: +# $ref: "./places/offset.yml" +# places_opennow: +# $ref: "./places/opennow.yml" +# places_origin: +# $ref: "./places/origin.yml" +# places_pagetoken: +# $ref: "./places/pagetoken.yml" +# places_photo_reference: +# $ref: "./places/photo_reference.yml" +# places_place_id: +# $ref: "./places/place_id.yml" +# places_query: +# $ref: "./places/query.yml" +# places_radius: +# $ref: "./places/radius.yml" +# places_rankby: +# $ref: "./places/rankby.yml" +# places_sessiontoken: +# $ref: "./places/sessiontoken.yml" +# places_strictbounds: +# $ref: "./places/strictbounds.yml" +# places_type: +# $ref: "./places/type.yml" +# places_types: +# $ref: "./places/types.yml" +# region: +# $ref: "./region.yml" +# snaptoroads_interpolate: +# $ref: "./snapToRoads/interpolate.yml" +# snaptoroads_path: +# $ref: "./snapToRoads/path.yml" +# streetview_fov: +# $ref: "./streetview/fov.yml" +# streetview_heading: +# $ref: "./streetview/heading.yml" +# streetview_location: +# $ref: "./streetview/location.yml" +# streetview_pano: +# $ref: "./streetview/pano.yml" +# streetview_pitch: +# $ref: "./streetview/pitch.yml" +# streetview_radius: +# $ref: "./streetview/radius.yml" +# streetview_return_error_code: +# $ref: "./streetview/return_error_code.yml" +# streetview_signature: +# $ref: "./streetview/signature.yml" +# streetview_size_optional: +# $ref: "./streetview/size_optional.yml" +# streetview_size: +# $ref: "./streetview/size.yml" +# streetview_source: +# $ref: "./streetview/source.yml" +# timezone_location: +# $ref: "./timezone/location.yml" +# timezone_timestamp: +# $ref: "./timezone/timestamp.yml" +# traffic_model: +# $ref: "./traffic_model.yml" +# transit_mode: +# $ref: "./transit_mode.yml" +# transit_routing_preference: +# $ref: "./transit_routing_preference.yml" \ No newline at end of file diff --git a/test/data/toBundleExamples/nested_examples_as_value/parameters/arrival_time.yml b/test/data/toBundleExamples/nested_examples_as_value/parameters/arrival_time.yml new file mode 100644 index 0000000..8acd6af --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/parameters/arrival_time.yml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: arrival_time +description: | + Specifies the desired time of arrival for transit directions, in seconds since midnight, January 1, 1970 UTC. You can specify either `departure_time` or `arrival_time`, but not both. Note that `arrival_time` must be specified as an integer. +in: query +schema: + type: number diff --git a/test/data/toBundleExamples/nested_examples_as_value/paths/_index.yml b/test/data/toBundleExamples/nested_examples_as_value/paths/_index.yml new file mode 100644 index 0000000..e0b578e --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/paths/_index.yml @@ -0,0 +1,82 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +/geolocation/v1/geolocate: + post: + operationId: geolocate + $ref: "./geolocate.yml" +# /maps/api/directions/json: +# get: +# operationId: directions +# $ref: "./directions.yml" +# /maps/api/elevation/json: +# get: +# operationId: elevation +# $ref: "./elevation.yml" +# /maps/api/geocode/json: +# get: +# operationId: geocode +# $ref: "./geocode.yml" +# /maps/api/timezone/json: +# get: +# operationId: timezone +# $ref: "./timezone.yml" +# /v1/snaptoroads: +# get: +# operationId: snapToRoads +# $ref: "./snapToRoads.yml" +# /v1/nearestRoads: +# get: +# operationId: nearestRoads +# $ref: "./nearestRoads.yml" +# /maps/api/distancematrix/json: +# get: +# operationId: distanceMatrix +# $ref: "./distanceMatrix.yml" +# /maps/api/place/details/json: +# get: +# operationId: placeDetails +# $ref: "./places/details.yml" +# /maps/api/place/findplacefromtext/json: +# get: +# operationId: findPlaceFromText +# $ref: "./places/findplacefromtext.yml" +# /maps/api/place/nearbysearch/json: +# get: +# operationId: nearbySearch +# $ref: "./places/nearbysearch.yml" +# /maps/api/place/textsearch/json: +# get: +# operationId: textSearch +# $ref: "./places/textsearch.yml" +# /maps/api/place/photo: +# get: +# operationId: placePhoto +# $ref: "./places/photo.yml" +# /maps/api/place/queryautocomplete/json: +# get: +# operationId: queryAutocomplete +# $ref: "./places/queryautocomplete.yml" +# /maps/api/place/autocomplete/json: +# get: +# operationId: autocomplete +# $ref: "./places/autocomplete.yml" +# /maps/api/streetview: +# get: +# operationId: streetView +# $ref: "./streetview/static.yml" +# /maps/api/streetview/metadata: +# get: +# operationId: streetViewMetadata +# $ref: "./streetview/metadata.yml" diff --git a/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml b/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml new file mode 100644 index 0000000..a30af35 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml @@ -0,0 +1,80 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +servers: + - url: https://www.googleapis.com +tags: + - Geolocation API +description: |- + Geolocation API returns a location and accuracy radius based on information about cell towers and WiFi nodes that the mobile client can detect. This document describes the protocol used to send this data to the server and to return a response to the client. + + Communication is done over HTTPS using POST. Both request and response are formatted as JSON, and the content type of both is `application/json`. + + You must specify a key in your request, included as the value of a`key` parameter. A `key` is your application's API key. This key identifies your application for purposes of quota management. Learn how to [get a key](https://developers.google.com/maps/documentation/geolocation/get-api-key). +responses: + "200": + description: 200 OK + content: + application/json: + schema: + $ref: "../schemas/GeolocationResponse.yml" + examples: + WIFI: + value: + $ref: ../responses/maps_http_geolocation_wifi_response.yml + # Cell Towers: + # value: + # $ref: ../responses/maps_http_geolocation_celltowers_response.yml + # IP Only: + # value: + # $ref: ../responses/maps_http_geolocation_ip_response.yml + "400": + description: 400 BAD REQUEST + content: + application/json: + schema: + type: object + properties: + value: + $ref: "../schemas/test.yml" + other: + type: string + + # "404": + # description: 404 NOT FOUND + # content: + # application/json: + # schema: + # $ref: "../schemas/errors/ErrorResponse.yml" + # examples: + # Invalid: + # value: + # $ref: ../responses/maps_http_geolocation_error_404_response.yml +requestBody: + description: The request body must be formatted as JSON. + content: + "application/json": + schema: + $ref: "../schemas/GeolocationRequest.yml" + examples: + WIFI: + value: + $ref: ../requests/maps_http_geolocation_wifi_request.yml + # Cell Towers: + # value: + # $ref: ../requests/maps_http_geolocation_celltowers_request.yml + # IP Only: + # value: + # $ref: ../requests/maps_http_geolocation_ip_request.yml + required: false diff --git a/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_celltowers_request.yml b/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_celltowers_request.yml new file mode 100644 index 0000000..361b56e --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_celltowers_request.yml @@ -0,0 +1,29 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START maps_http_geolocation_celltowers_request] +{ + "cellTowers": [ + { + "cellId": 170402199, + "locationAreaCode": 35632, + "mobileCountryCode": 310, + "mobileNetworkCode": 410, + "age": 0, + "signalStrength": -60, + "timingAdvance": 15 + } + ] +} +# [END maps_http_geolocation_celltowers_request] diff --git a/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_wifi_request.yml b/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_wifi_request.yml new file mode 100644 index 0000000..f7352da --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/requests/maps_http_geolocation_wifi_request.yml @@ -0,0 +1,31 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START maps_http_geolocation_wifi_request] +{ + "considerIp": "false", + "wifiAccessPoints": [ + { + "macAddress": "84:d4:7e:09:a5:f1", + "signalStrength": -43, + "signalToNoiseRatio": 0 + }, + { + "macAddress": "44:48:c1:a6:f3:d0", + "signalStrength": -55, + "signalToNoiseRatio": 0 + } + ] +} +# [END maps_http_geolocation_wifi_request] diff --git a/test/data/toBundleExamples/nested_examples_as_value/responses/maps_http_geolocation_wifi_response.yml b/test/data/toBundleExamples/nested_examples_as_value/responses/maps_http_geolocation_wifi_response.yml new file mode 100644 index 0000000..5a0d636 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/responses/maps_http_geolocation_wifi_response.yml @@ -0,0 +1,23 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START maps_http_geolocation_wifi_response] +{ + "location": { + "lat": 37.421925, + "lng": -122.0841293 + }, + "accuracy": 30 +} +# [END maps_http_geolocation_wifi_response] diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/Bounds.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/Bounds.yml new file mode 100644 index 0000000..de97d31 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/Bounds.yml @@ -0,0 +1,23 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +title: Bounds +type: object +description: A rectangle in geographical coordinates from points at the southwest and northeast corners. +required: [northeast, southwest] +properties: + northeast: + $ref: './LatLngLiteral.yml' + southwest: + $ref: './LatLngLiteral.yml' diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/CellTower.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/CellTower.yml new file mode 100644 index 0000000..ce93b17 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/CellTower.yml @@ -0,0 +1,57 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: CellTower +description: "Attributes used to describe a cell tower. The following optional fields are not currently used, but may be included if values are available: `age`, `signalStrength`, `timingAdvance`." +required: + - cellId + - locationAreaCode + - mobileCountryCode + - mobileNetworkCode +properties: + cellId: + description: + Unique identifier of the cell. On GSM, this is the Cell ID (CID); + CDMA networks use the Base Station ID (BID). WCDMA networks use the + UTRAN/GERAN Cell Identity (UC-Id), which is a 32-bit value + concatenating the Radio Network Controller (RNC) and Cell ID. + Specifying only the 16-bit Cell ID value in WCDMA networks may + return inaccurate results. + type: integer + locationAreaCode: + description: The Location Area Code (LAC) for GSM and WCDMA networks. The + Network ID (NID) for CDMA networks. + type: integer + mobileCountryCode: + description: The cell tower's Mobile Country Code (MCC). + type: integer + mobileNetworkCode: + description: + The cell tower's Mobile Network Code. This is the MNC for GSM and + WCDMA; CDMA uses the System ID (SID). + type: integer + age: + description: + The number of milliseconds since this cell was primary. If age is + 0, the cellId represents a current measurement. + type: integer + signalStrength: + description: Radio signal strength measured in dBm. + type: number + timingAdvance: + description: The timing advance value. + type: number +example: + $ref: ../requests/maps_http_geolocation_celltowers_request.yml diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationRequest.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationRequest.yml new file mode 100644 index 0000000..fce7f89 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationRequest.yml @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: GeolocationRequest +description: The request body must be formatted as JSON. The following fields are supported, and all fields are optional. +properties: + homeMobileCountryCode: + type: integer + description: The cell tower's Mobile Country Code (MCC). + homeMobileNetworkCode: + type: integer + description: + The cell tower's Mobile Network Code. This is the MNC for GSM and + WCDMA; CDMA uses the System ID (SID). + radioType: + type: string + description: The mobile radio type. Supported values are lte, gsm, cdma, and wcdma. While this field is optional, it should be included if a value is available, for more accurate results. + carrier: + type: string + description: The carrier name. + considerIp: + type: string + description: Specifies whether to fall back to IP geolocation if wifi and cell tower signals are not available. Defaults to true. Set considerIp to false to disable fall back. + cellTowers: + type: array + description: The request body's cellTowers array contains zero or more cell tower objects. + items: + $ref: "./CellTower.yml" + # wifiAccessPoints: + # type: array + # description: An array of two or more WiFi access point objects. + # items: + # $ref: "./WiFiAccessPoint.yml" diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationResponse.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationResponse.yml new file mode 100644 index 0000000..1b79416 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/GeolocationResponse.yml @@ -0,0 +1,27 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: GeolocationResponse +description: A successful geolocation request will return a JSON-formatted response defining a location and radius. +required: [location, accuracy] +properties: + location: + description: The user’s estimated latitude and longitude, in degrees. + $ref: "./LatLngLiteral.yml" + accuracy: + description: The accuracy of the estimated location, in meters. This represents the radius of a circle around the given `location`. If your Geolocation response shows a very high value in the `accuracy` field, the service may be geolocating based on the request IP, instead of WiFi points or cell towers. This can happen if no cell towers or access points are valid or recognized. To confirm that this is the issue, set `considerIp` to `false` in your request. If the response is a `404`, you've confirmed that your `wifiAccessPoints` and `cellTowers` objects could not be geolocated. + type: number +example: + $ref: ../responses/maps_http_geolocation_wifi_response.yml diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/LatLngLiteral.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/LatLngLiteral.yml new file mode 100644 index 0000000..d0da9e4 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/LatLngLiteral.yml @@ -0,0 +1,27 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: LatLngLiteral +description: An object describing a specific location with Latitude and Longitude in decimal degrees. +required: + - lat + - lng +properties: + lat: + type: number + description: Latitude in decimal degrees + lng: + type: number + description: Longitude in decimal degrees diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/_index.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/_index.yml new file mode 100644 index 0000000..0377e29 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/_index.yml @@ -0,0 +1,169 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Bounds: + $ref: "./Bounds.yml" +# LatLngArrayString: +# $ref: "./LatLngArrayString.yml" +# LatLngLiteral: +# $ref: "./LatLngLiteral.yml" +# LatitudeLongitudeLiteral: +# $ref: "./LatitudeLongitudeLiteral.yml" +# AddressComponent: +# $ref: "./AddressComponent.yml" +# Fare: +# $ref: "./Fare.yml" +# Geometry: +# $ref: "./Geometry.yml" +# PlusCode: +# $ref: "./PlusCode.yml" +# TravelMode: +# $ref: "./TravelMode.yml" +# TextValueObject: +# $ref: "./TextValueObject.yml" +# TimeZoneTextValueObject: +# $ref: "./TimeZoneTextValueObject.yml" +# # Distance Matrix +# DistanceMatrixElementStatus: +# $ref: "./DistanceMatrixElementStatus.yml" +# DistanceMatrixResponse: +# $ref: "./DistanceMatrixResponse.yml" +# DistanceMatrixRow: +# $ref: "./DistanceMatrixRow.yml" +# DistanceMatrixElement: +# $ref: "./DistanceMatrixElement.yml" +# DistanceMatrixStatus: +# $ref: "./DistanceMatrixStatus.yml" +# # Directions +# DirectionsResponse: +# $ref: "./DirectionsResponse.yml" +# DirectionsStep: +# $ref: "./DirectionsStep.yml" +# DirectionsLeg: +# $ref: "./DirectionsLeg.yml" +# DirectionsRoute: +# $ref: "./DirectionsRoute.yml" +# DirectionsStatus: +# $ref: "./DirectionsStatus.yml" +# DirectionsGeocodedWaypoint: +# $ref: "./DirectionsGeocodedWaypoint.yml" +# DirectionsPolyline: +# $ref: "./DirectionsPolyline.yml" +# DirectionsTrafficSpeedEntry: +# $ref: "./DirectionsTrafficSpeedEntry.yml" +# DirectionsTransitAgency: +# $ref: "./DirectionsTransitAgency.yml" +# DirectionsTransitDetails: +# $ref: "./DirectionsTransitDetails.yml" +# DirectionsTransitLine: +# $ref: "./DirectionsTransitLine.yml" +# DirectionsTransitStop: +# $ref: "./DirectionsTransitStop.yml" +# DirectionsTransitVehicle: +# $ref: "./DirectionsTransitVehicle.yml" +# DirectionsViaWaypoint: +# $ref: "./DirectionsViaWaypoint.yml" +# # Elevation +# ElevationResult: +# $ref: "./ElevationResult.yml" +# ElevationResponse: +# $ref: "./ElevationResponse.yml" +# ElevationStatus: +# $ref: "./ElevationStatus.yml" +# # Geocoding +# GeocodingResult: +# $ref: "./GeocodingResult.yml" +# GeocodingResponse: +# $ref: "./GeocodingResponse.yml" +# GeocodingStatus: +# $ref: "./GeocodingStatus.yml" +# GeocodingGeometry: +# $ref: "./GeocodingGeometry.yml" +# # Geolocation +# GeolocationRequest: +# $ref: "./GeolocationRequest.yml" +# GeolocationResponse: +# $ref: "./GeolocationResponse.yml" +# CellTower: +# $ref: "./CellTower.yml" +# WiFiAccessPoint: +# $ref: "./WiFiAccessPoint.yml" +# # Roads +# NearestRoadsError: +# $ref: "./NearestRoadsError.yml" +# NearestRoadsErrorResponse: +# $ref: "./NearestRoadsErrorResponse.yml" +# NearestRoadsResponse: +# $ref: "./NearestRoadsResponse.yml" +# SnappedPoint: +# $ref: "./SnappedPoint.yml" +# SnapToRoadsResponse: +# $ref: "./SnapToRoadsResponse.yml" +# # Time Zone +# TimeZoneResponse: +# $ref: "./TimeZoneResponse.yml" +# TimeZoneStatus: +# $ref: "./TimeZoneStatus.yml" +# # Errors +# ErrorResponse: +# $ref: "./errors/ErrorResponse.yml" +# ErrorObject: +# $ref: "./errors/ErrorObject.yml" +# ErrorDetail: +# $ref: "./errors/ErrorDetail.yml" +# FieldViolation: +# $ref: "./errors/FieldViolation.yml" +# # Places +# Place: +# $ref: "./Place.yml" +# PlaceAutocompleteMatchedSubstring: +# $ref: "./PlaceAutocompleteMatchedSubstring.yml" +# PlaceAutocompletePrediction: +# $ref: "./PlaceAutocompletePrediction.yml" +# PlaceAutocompleteStructuredFormat: +# $ref: "./PlaceAutocompleteStructuredFormat.yml" +# PlaceAutocompleteTerm: +# $ref: "./PlaceAutocompleteTerm.yml" +# PlacePhoto: +# $ref: "./PlacePhoto.yml" +# PlaceOpeningHours: +# $ref: "./PlaceOpeningHours.yml" +# PlaceOpeningHoursPeriod: +# $ref: "./PlaceOpeningHoursPeriod.yml" +# PlaceOpeningHoursPeriodDetail: +# $ref: "./PlaceOpeningHoursPeriodDetail.yml" +# PlaceReview: +# $ref: "./PlaceReview.yml" +# PlacesAutocompleteResponse: +# $ref: "./PlacesAutocompleteResponse.yml" +# PlacesAutocompleteStatus: +# $ref: "./PlacesAutocompleteStatus.yml" +# PlacesDetailsResponse: +# $ref: "./PlacesDetailsResponse.yml" +# PlacesDetailsStatus: +# $ref: "./PlacesDetailsStatus.yml" +# PlacesFindPlaceFromTextResponse: +# $ref: "./PlacesFindPlaceFromTextResponse.yml" +# PlacesNearbySearchResponse: +# $ref: "./PlacesNearbySearchResponse.yml" +# PlacesQueryAutocompleteResponse: +# $ref: "./PlacesQueryAutocompleteResponse.yml" +# PlacesSearchStatus: +# $ref: "./PlacesSearchStatus.yml" +# PlacesTextSearchResponse: +# $ref: "./PlacesTextSearchResponse.yml" +# StreetViewStatus: +# $ref: "./StreetViewStatus.yml" +# StreetViewResponse: +# $ref: "./StreetViewResponse.yml" diff --git a/test/data/toBundleExamples/nested_examples_as_value/schemas/test.yml b/test/data/toBundleExamples/nested_examples_as_value/schemas/test.yml new file mode 100644 index 0000000..c295960 --- /dev/null +++ b/test/data/toBundleExamples/nested_examples_as_value/schemas/test.yml @@ -0,0 +1,6 @@ +type: object +properties: + first: + type: string + second: + type: integer \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index a80a380..1c39031 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -41,7 +41,8 @@ let expect = require('chai').expect, longPath = path.join(__dirname, BUNDLES_FOLDER + '/longPath'), schemaCollision = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_from_responses'), schemaCollisionWRootComponent = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_w_root_components'), - referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'); + referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'), + nestedExamplesAsValue = path.join(__dirname, BUNDLES_FOLDER + '/nested_examples_as_value'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2250,6 +2251,105 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should return bundled file - TestSpec_from_issue_14', async function () { + let contentRoot = fs.readFileSync(nestedExamplesAsValue + '/index.yml', 'utf8'), + parametersIndex = fs.readFileSync(nestedExamplesAsValue + '/parameters/_index.yml', 'utf8'), + arrivalTime = fs.readFileSync(nestedExamplesAsValue + '/parameters/arrival_time.yml', 'utf8'), + pathsIndex = fs.readFileSync(nestedExamplesAsValue + '/paths/_index.yml', 'utf8'), + geolocate = fs.readFileSync(nestedExamplesAsValue + '/paths/geolocate.yml', 'utf8'), + requests = + fs.readFileSync(nestedExamplesAsValue + '/requests/maps_http_geolocation_celltowers_request.yml', 'utf8'), + schemasIndex = fs.readFileSync(nestedExamplesAsValue + '/schemas/_index.yml', 'utf8'), + bounds = fs.readFileSync(nestedExamplesAsValue + '/schemas/Bounds.yml', 'utf8'), + cellTower = fs.readFileSync(nestedExamplesAsValue + '/schemas/CellTower.yml', 'utf8'), + geolocationRequest = fs.readFileSync(nestedExamplesAsValue + '/schemas/GeolocationRequest.yml', 'utf8'), + latLngLiteral = fs.readFileSync(nestedExamplesAsValue + '/schemas/LatLngLiteral.yml', 'utf8'), + wifiResponse = + fs.readFileSync(nestedExamplesAsValue + '/responses/maps_http_geolocation_wifi_response.yml', 'utf8'), + wifiRequest = fs.readFileSync(nestedExamplesAsValue + '/requests/maps_http_geolocation_wifi_request.yml', 'utf8'), + geolocationResponse = fs.readFileSync(nestedExamplesAsValue + '/schemas/GeolocationResponse.yml', 'utf8'), + testSchema = fs.readFileSync(nestedExamplesAsValue + '/schemas/test.yml', 'utf8'), + expected = fs.readFileSync(nestedExamplesAsValue + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/index.yml' + } + ], + data: [ + { + path: '/index.yml', + content: contentRoot + }, + { + path: '/parameters/_index.yml', + content: parametersIndex + }, + { + path: '/parameters/arrival_time.yml', + content: arrivalTime + }, + { + path: '/paths/_index.yml', + content: pathsIndex + }, + { + path: '/paths/geolocate.yml', + content: geolocate + }, + { + path: '/requests/maps_http_geolocation_celltowers_request.yml', + content: requests + }, + { + path: '/schemas/_index.yml', + content: schemasIndex + }, + { + path: '/schemas/Bounds.yml', + content: bounds + }, + { + path: '/schemas/CellTower.yml', + content: cellTower + }, + { + path: '/schemas/GeolocationRequest.yml', + content: geolocationRequest + }, + { + path: '/schemas/LatLngLiteral.yml', + content: latLngLiteral + }, + { + path: '/schemas/GeolocationResponse.yml', + content: geolocationResponse + }, + { + path: '/requests/maps_http_geolocation_wifi_request.yml', + content: wifiRequest + }, + { + path: '/responses/maps_http_geolocation_wifi_response.yml', + content: wifiResponse + }, + { + path: '/schemas/test.yml', + content: testSchema + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From 15158bb27fd184c66dc8f056770e6a933c2632c1 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:06:47 -0500 Subject: [PATCH 43/56] Read correctly the version in multifile apis Read correctly the version in multifile apis --- lib/bundle.js | 5 ++++- lib/common/versionUtils.js | 24 +++++++++++++++++++++++- test/unit/versionUtils.test.js | 4 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index aefec15..c33aa50 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -443,7 +443,10 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver ); } }); - [rootContent, components].forEach((contentData) => { + [rootContent, components].forEach((contentData, index) => { + if (index === 1 && contentData.hasOwnProperty('$ref')) { + delete contentData.$ref; + } traverseUtility(contentData).forEach(function (property) { if (property) { let hasReferenceTypeKey; diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index ab44417..b908750 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -162,6 +162,21 @@ function getFileWithVersion(data, specificationVersion) { return file; } +/** + * Gets the named version constant from an string input + * @param {string} version The spec version we are bundling + * @returns {string} The constant version + */ +function getVersionByStringInput(version) { + if (!version) { + return false; + } + let found = [DEFAULT_SPEC_VERSION, SWAGGER_VERSION, VERSION_3_1].find((supportedVersion) => { + return compareVersion(version, supportedVersion); + }); + return found; +} + /** * Return the version of the provided specification * @param {string} spec Data from input file @@ -172,7 +187,14 @@ function getSpecVersion({ type, data, specificationVersion }) { return DEFAULT_SPEC_VERSION; } - if (['folder', 'multiFile'].includes(type)) { + if (['multiFile'].includes(type)) { + if (!specificationVersion) { + return DEFAULT_SPEC_VERSION; + } + return getVersionByStringInput(specificationVersion); + } + + if (['folder'].includes(type)) { data = getFileWithVersion(data, specificationVersion); } else if (['file'].includes(type)) { diff --git a/test/unit/versionUtils.test.js b/test/unit/versionUtils.test.js index fc574ce..90b956c 100644 --- a/test/unit/versionUtils.test.js +++ b/test/unit/versionUtils.test.js @@ -305,6 +305,10 @@ describe('compareVersion method', function () { const result = compareVersion('invalid', 'invalid'); expect(result).to.be.false; }); + it('should return true when input is 3.0.2 and version is 3.0.0', function () { + const result = compareVersion('3.0.2', '3.0.0'); + expect(result).to.be.true; + }); }); describe('getVersionRegexBySpecificationVersion method', function () { From ec803ba0e192d04ce1c241e0b2c91b03e1559a10 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Mon, 4 Jul 2022 20:12:52 -0500 Subject: [PATCH 44/56] Support root components referenced from external files --- lib/bundle.js | 28 +++- lib/jsonPointer.js | 8 +- .../referenced_components/components.yaml | 4 + .../referenced_components/expected.json | 82 ++++++++++ .../referenced_components/responses.yaml | 25 ++++ .../referenced_components/root.yaml | 15 ++ .../referenced_components/schemaA.yaml | 6 + .../referenced_components/schemas.yaml | 2 + .../definitions.yaml | 22 +++ .../referenced_root_components/expected.json | 141 ++++++++++++++++++ .../referenced_root_components/info.yaml | 11 ++ .../paths/path.yaml | 31 ++++ .../paths/paths.yaml | 3 + .../referenced_root_components/responses.yaml | 8 + .../referenced_root_components/root.yaml | 13 ++ .../securitySchemes.yaml | 6 + .../referenced_root_components/tags.yaml | 6 + test/unit/bundle.test.js | 49 ++++++ test/unit/bundle20.test.js | 67 ++++++++- 19 files changed, 518 insertions(+), 9 deletions(-) create mode 100644 test/data/toBundleExamples/referenced_components/components.yaml create mode 100644 test/data/toBundleExamples/referenced_components/expected.json create mode 100644 test/data/toBundleExamples/referenced_components/responses.yaml create mode 100644 test/data/toBundleExamples/referenced_components/root.yaml create mode 100644 test/data/toBundleExamples/referenced_components/schemaA.yaml create mode 100644 test/data/toBundleExamples/referenced_components/schemas.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/definitions.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/info.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/paths/path.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/paths/paths.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/responses.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/root.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/securitySchemes.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_root_components/tags.yaml diff --git a/lib/bundle.js b/lib/bundle.js index 3f98348..a4210b1 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -247,7 +247,14 @@ function getTraceFromParentKeyInComponents(nodeContext, tempRef, mainKeys, versi [key, ...parentKeys], nodeTrace = getRootFileTrace(nodeParentsKey), componentKey = createComponentMainKey(tempRef, mainKeys), - keyTraceInComponents = getKeyInComponents(nodeTrace, componentKey, version, commonPathFromData); + parentNodeKey = nodeContext.parent.key, + keyTraceInComponents = getKeyInComponents( + nodeTrace, + componentKey, + version, + commonPathFromData, + parentNodeKey + ); return keyTraceInComponents; } @@ -518,10 +525,14 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver * Generates the components object wrapper * @param {object} parsedOasObject The parsed root * @param {string} version - The current version + * @param {object} nodesContent - The nodes content * @returns {object} The components object wrapper */ -function generateComponentsWrapper(parsedOasObject, version) { - let components = {}; +function generateComponentsWrapper(parsedOasObject, version, nodesContent = {}) { + let components = _.isNil(parsedOasObject.components) ? + {} : + parsedOasObject.components, + componentsAreReferenced = components.$ref !== undefined && !_.isEmpty(nodesContent); if (isSwagger(version)) { getBundleRulesDataByVersion(version).COMPONENTS_KEYS.forEach((property) => { @@ -531,7 +542,10 @@ function generateComponentsWrapper(parsedOasObject, version) { }); } else if (parsedOasObject.hasOwnProperty('components')) { - components = parsedOasObject.components; + if (componentsAreReferenced) { + components = _.merge(parsedOasObject.components, nodesContent[components.$ref]); + delete components.$ref; + } } return components; @@ -604,7 +618,11 @@ module.exports = { rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { return getNodeContentAndReferences(currentNode, allData, specRoot, version, initialMainKeys, commonPathFromData); }); - components = generateComponentsWrapper(specRoot.parsed.oasObject, version); + components = generateComponentsWrapper( + specRoot.parsed.oasObject, + version, + rootContextData.nodeContents + ); generateComponentsObject( rootContextData, rootContextData.nodeContents[specRoot.fileName], diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 1939883..86fc80c 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -54,7 +54,7 @@ function generateObjectName(filePathName, hash = '') { * @param {string} commonPathFromData - The common path in the file's paths * @returns {Array} - the calculated keys in an array representing each nesting property name */ -function getKeyInComponents(traceFromParent, mainKey, version, commonPathFromData) { +function getKeyInComponents(traceFromParent, mainKey, version, commonPathFromData, parentNodeKey = undefined) { const { CONTAINERS, DEFINITIONS, @@ -70,9 +70,11 @@ function getKeyInComponents(traceFromParent, mainKey, version, commonPathFromDat ].reverse(), traceToKey = [], matchFound = false, - isRootAndReusableItemsContainer = ROOT_CONTAINERS_KEYS.includes(traceFromParent[0]); + hasNotParent = parentNodeKey === undefined, + isRootAndReusableItemsContainer = ROOT_CONTAINERS_KEYS.includes(traceFromParent[0]), + isAComponentKeyReferenced = COMPONENTS_KEYS.includes(traceFromParent[0]) && hasNotParent; - if (isRootAndReusableItemsContainer) { + if (isRootAndReusableItemsContainer || isAComponentKeyReferenced) { return []; } diff --git a/test/data/toBundleExamples/referenced_components/components.yaml b/test/data/toBundleExamples/referenced_components/components.yaml new file mode 100644 index 0000000..c87ca43 --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/components.yaml @@ -0,0 +1,4 @@ +responses: + $ref: './responses.yaml' +schemas: + $ref: './schemas.yaml' \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_components/expected.json b/test/data/toBundleExamples/referenced_components/expected.json new file mode 100644 index 0000000..3e96ee9 --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/expected.json @@ -0,0 +1,82 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "paths": { + "/hello": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "$ref": "#/components/responses/responseA" + } + } + } + }, + "components": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "address": { + "type": "string" + } + } + } + } + } + }, + "responseA": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_schemaA.yaml" + } + } + } + }, + "responseB": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "fromB": { + "type": "string" + }, + "addressFromB": { + "type": "string" + } + } + } + } + } + } + }, + "schemas": { + "_schemaA.yaml": { + "type": "object", + "properties": { + "fromA": { + "type": "string" + }, + "addressFromA": { + "type": "string" + } + } + }, + "schemaB": { + "type": "integer" + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_components/responses.yaml b/test/data/toBundleExamples/referenced_components/responses.yaml new file mode 100644 index 0000000..4f9f8a0 --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/responses.yaml @@ -0,0 +1,25 @@ +"200": + content: + application/json: + schema: + type: object + properties: + name: + type: string + address: + type: string +"responseA": + content: + application/json: + schema: + $ref: './schemaA.yaml' +"responseB": + content: + application/json: + schema: + type: object + properties: + fromB: + type: string + addressFromB: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_components/root.yaml b/test/data/toBundleExamples/referenced_components/root.yaml new file mode 100644 index 0000000..cbb7d61 --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/root.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +paths: + /hello: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + $ref: '#/components/responses/responseA' + +components: + $ref: './components.yaml' \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_components/schemaA.yaml b/test/data/toBundleExamples/referenced_components/schemaA.yaml new file mode 100644 index 0000000..74c10ec --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/schemaA.yaml @@ -0,0 +1,6 @@ +type: object +properties: + fromA: + type: string + addressFromA: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_components/schemas.yaml b/test/data/toBundleExamples/referenced_components/schemas.yaml new file mode 100644 index 0000000..ea6be40 --- /dev/null +++ b/test/data/toBundleExamples/referenced_components/schemas.yaml @@ -0,0 +1,2 @@ +schemaB: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/definitions.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/definitions.yaml new file mode 100644 index 0000000..7ec0b9c --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/definitions.yaml @@ -0,0 +1,22 @@ +Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string +Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/expected.json b/test/data/toBundleExamples/swagger20/referenced_root_components/expected.json new file mode 100644 index 0000000..654b54d --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/expected.json @@ -0,0 +1,141 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "type": "oauth2", + "authorizationUrl": "http://swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "tags": [ + { + "name": "Authorization", + "x-bx-tag": "authorization", + "x-bx-priority": true + }, + { + "name": "Bx Sign", + "x-bx-tag": "sign_requests" + } + ], + "responses": { + "200": { + "description": "A simple string response", + "schema": { + "type": "string" + } + }, + "400": { + "description": "A simple string response from 400 code", + "schema": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/info.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/info.yaml new file mode 100644 index 0000000..2ddd567 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/info.yaml @@ -0,0 +1,11 @@ +version: 1.0.0 +title: Swagger Petstore +description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification +termsOfService: http://swagger.io/terms/ +contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io +license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/paths/path.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/paths/path.yaml new file mode 100644 index 0000000..dab6ca5 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/paths/path.yaml @@ -0,0 +1,31 @@ +description: Returns all pets alesuada ac... +operationId: findPets +responses: + "200": + description: pet response + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + default: + description: unexpected error + content: + application/json: + schema: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/paths/paths.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/paths/paths.yaml new file mode 100644 index 0000000..e826f5c --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/paths/paths.yaml @@ -0,0 +1,3 @@ +/pets: + get: + "$ref": "./path.yaml" diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/responses.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/responses.yaml new file mode 100644 index 0000000..0fa534b --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/responses.yaml @@ -0,0 +1,8 @@ +'200': + description: A simple string response + schema: + type: string +'400': + description: A simple string response from 400 code + schema: + type: string diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/root.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/root.yaml new file mode 100644 index 0000000..8cd48a6 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/root.yaml @@ -0,0 +1,13 @@ +swagger: '2.0' +info: + $ref: './info.yaml' +paths: + "$ref": "./paths/paths.yaml" +definitions: + $ref: './definitions.yaml' +securityDefinitions: + $ref: './securitySchemes.yaml' +tags: + $ref: './tags.yaml' +responses: + $ref: './responses.yaml' \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/securitySchemes.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/securitySchemes.yaml new file mode 100644 index 0000000..68a4da3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/securitySchemes.yaml @@ -0,0 +1,6 @@ +type: oauth2 +authorizationUrl: http://swagger.io/api/oauth/dialog +flow: implicit +scopes: + write:pets: modify pets in your account + read:pets: read your pets diff --git a/test/data/toBundleExamples/swagger20/referenced_root_components/tags.yaml b/test/data/toBundleExamples/swagger20/referenced_root_components/tags.yaml new file mode 100644 index 0000000..a123fd4 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_root_components/tags.yaml @@ -0,0 +1,6 @@ +- name: Authorization + x-bx-tag: authorization + x-bx-priority: true + +- name: Bx Sign + x-bx-tag: sign_requests diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 1f4f994..733b01c 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -43,6 +43,7 @@ let expect = require('chai').expect, schemaCollisionWRootComponent = path.join(__dirname, BUNDLES_FOLDER + '/schema_collision_w_root_components'), nestedExamplesAsValue = path.join(__dirname, BUNDLES_FOLDER + '/nested_examples_as_value'), referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'), + referencedComponents = path.join(__dirname, BUNDLES_FOLDER + '/referenced_components'), referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'); describe('bundle files method - 3.0', function () { @@ -2402,6 +2403,54 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.data[0].referenceMap).to.deep.equal(expected); }); + + it('Should return bundled file - referenced-components', async function () { + let contentRootFile = fs.readFileSync(referencedComponents + '/root.yaml', 'utf8'), + components = fs.readFileSync(referencedComponents + '/components.yaml', 'utf8'), + responses = fs.readFileSync(referencedComponents + '/responses.yaml', 'utf8'), + schemas = fs.readFileSync(referencedComponents + '/schemas.yaml', 'utf8'), + schemaA = fs.readFileSync(referencedComponents + '/schemaA.yaml', 'utf8'), + expected = fs.readFileSync(referencedComponents + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/components.yaml', + content: components + }, + { + path: '/responses.yaml', + content: responses + }, + { + path: '/schemas.yaml', + content: schemas + }, + { + path: '/schemaA.yaml', + content: schemaA + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js index 99034dd..39eec5e 100644 --- a/test/unit/bundle20.test.js +++ b/test/unit/bundle20.test.js @@ -27,7 +27,9 @@ let expect = require('chai').expect, schemaCollision = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/schema_collision_from_responses'), schemaCollisionWRootComponent = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + - '/schema_collision_w_root_components'); + '/schema_collision_w_root_components'), + referencedRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/referenced_root_components'); describe('bundle files method - 2.0', function() { it('Should return bundled result from - nestedProperties20', async function() { @@ -906,4 +908,67 @@ describe('bundle files method - 2.0', function() { expect(res.output.specification.version).to.equal('2.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should return bundled file as json - referenced_root_components', async function () { + let contentRootFile = fs.readFileSync(referencedRootComponents + '/root.yaml', 'utf8'), + definitions = fs.readFileSync(referencedRootComponents + '/definitions.yaml', 'utf8'), + info = fs.readFileSync(referencedRootComponents + '/info.yaml', 'utf8'), + responses = fs.readFileSync(referencedRootComponents + '/responses.yaml', 'utf8'), + securitySchemes = fs.readFileSync(referencedRootComponents + '/securitySchemes.yaml', 'utf8'), + tags = fs.readFileSync(referencedRootComponents + '/tags.yaml', 'utf8'), + paths = fs.readFileSync(referencedRootComponents + '/paths/paths.yaml', 'utf8'), + singlePath = fs.readFileSync(referencedRootComponents + '/paths/path.yaml', 'utf8'), + expected = fs.readFileSync(referencedRootComponents + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/definitions.yaml', + content: definitions + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/responses.yaml', + content: responses + }, + { + path: '/securitySchemes.yaml', + content: securitySchemes + }, + { + path: '/tags.yaml', + content: tags + }, + { + path: '/paths/paths.yaml', + content: paths + }, + { + path: '/paths/path.yaml', + content: singlePath + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('2.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); From 2c8b7a5b8dd85a43fb178a93c5182e6715cb9fc7 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Tue, 5 Jul 2022 10:35:36 -0500 Subject: [PATCH 45/56] fix jsDoc warning --- lib/jsonPointer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index 86fc80c..ea27064 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -52,6 +52,7 @@ function generateObjectName(filePathName, hash = '') { * @param {string} mainKey - The generated mainKey for the components * @param {string} version - The current spec version * @param {string} commonPathFromData - The common path in the file's paths +* @param {string} parentNodeKey - The key from the parent element of the trace * @returns {Array} - the calculated keys in an array representing each nesting property name */ function getKeyInComponents(traceFromParent, mainKey, version, commonPathFromData, parentNodeKey = undefined) { From 9189cc8ae52d585e8e1dda105e664495e7440a28 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 6 Jul 2022 12:14:02 -0500 Subject: [PATCH 46/56] Fix references map --- lib/bundle.js | 27 ++++- .../paths_schema/ErrorResponse.yml | 20 ++++ .../paths_schema/GeolocationRequest.yml | 26 +++++ .../paths_schema/GeolocationResponse.yml | 8 ++ .../paths_schema/geolocate.yml | 33 +++++++ .../toBundleExamples/paths_schema/index.yml | 9 ++ .../toBundleExamples/paths_schema/paths.yml | 4 + .../referenced_path/expected.json | 98 ++++++++++++++++++ .../referenced_path/path.yaml | 16 +-- test/unit/bundle.test.js | 99 ++++++++++++++++++- 10 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 test/data/toBundleExamples/paths_schema/ErrorResponse.yml create mode 100644 test/data/toBundleExamples/paths_schema/GeolocationRequest.yml create mode 100644 test/data/toBundleExamples/paths_schema/GeolocationResponse.yml create mode 100644 test/data/toBundleExamples/paths_schema/geolocate.yml create mode 100644 test/data/toBundleExamples/paths_schema/index.yml create mode 100644 test/data/toBundleExamples/paths_schema/paths.yml create mode 100644 test/data/toBundleExamples/referenced_path/expected.json diff --git a/lib/bundle.js b/lib/bundle.js index a4210b1..093d258 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -347,7 +347,6 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve if (nodeTrace.length === 0) { inline = true; - newRefInDoc = localPointer + jsonPointerLevelSeparator + traceToParent.join(jsonPointerLevelSeparator); } nodeReferenceDirectory[tempRef] = { @@ -433,6 +432,30 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version, r return { graphAdj, missingNodes, nodeContent, nodeReferenceDirectory, nodeName: currentNode.fileName }; } + +/** + * Maps the output from get root files to detect root files + * @param {array} parents - current { path, content} object + * @param {string} key - array of { path, content} objects + * @returns {object} - Detect root files result object + */ +function resolveJsonPointerInlineNodes(parents, key) { + let pointer = parents.filter((parent) => { + return parent !== undefined; + }).map((parent) => { + return parent.key; + }).join(jsonPointerLevelSeparator); + if (pointer) { + pointer = localPointer + pointer; + } + else { + pointer += localPointer; + } + pointer += jsonPointerLevelSeparator + key; + + return pointer; +} + /** * Generates the components object from the documentContext data * @param {object} documentContext The document context from root @@ -503,6 +526,8 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver refData.node = hasSiblings ? _.merge(referenceSiblings, refData.nodeContent) : refData.nodeContent; + documentContext.globalReferences[property.$ref].reference = + resolveJsonPointerInlineNodes(this.parents, this.key); } this.update(refData.node); if (!refData.inline) { diff --git a/test/data/toBundleExamples/paths_schema/ErrorResponse.yml b/test/data/toBundleExamples/paths_schema/ErrorResponse.yml new file mode 100644 index 0000000..014d1d5 --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/ErrorResponse.yml @@ -0,0 +1,20 @@ +type: object +title: ErrorResponse +description: In the case of an error, a standard format error r +required: [error] +properties: + error: + description: An error return by the server. + type: object + title: ErrorObject + required: [code, message, errors] + properties: + code: + description: This is the same as the HTTP status of the response. + type: number + message: + description: A short description of the error. + type: string + status: + description: A status code that indicates the error type. + type: string diff --git a/test/data/toBundleExamples/paths_schema/GeolocationRequest.yml b/test/data/toBundleExamples/paths_schema/GeolocationRequest.yml new file mode 100644 index 0000000..08f211e --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/GeolocationRequest.yml @@ -0,0 +1,26 @@ + +type: object +title: GeolocationRequest +description: The request body must be formatted as JSON. The following fields are supported, and all fields are optional. +properties: + homeMobileCountryCode: + type: integer + description: The cell tower's Mobile Country Code (MCC). + homeMobileNetworkCode: + type: integer + description: + The cell + radioType: + type: string + description: The mobile radio type. Supported values are lte, gsm, cdma, and wcdma. While this field is optional, it should be included if a value is available, for more accurate results. + carrier: + type: string + description: The carrier name. + considerIp: + type: string + description: Specifies whether to fall back to IP geolocation if wifi and cell tower signals are not available. Defaults to true. Set considerIp to false to disable fall back. + cellTowers: + type: array + description: The request body's cellTowers array contains zero or more cell tower objects. + items: + $ref: "./CellTower.yml" diff --git a/test/data/toBundleExamples/paths_schema/GeolocationResponse.yml b/test/data/toBundleExamples/paths_schema/GeolocationResponse.yml new file mode 100644 index 0000000..b2e8264 --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/GeolocationResponse.yml @@ -0,0 +1,8 @@ +type: object +title: GeolocationResponse +description: A successful geolocation request will return a JSON-formatted response defining a location and radius. +required: [location, accuracy] +properties: + accuracy: + description: The accuracy of the estimated location, in meters. This represents the radius of a circle around the given `location`. If your Geolocation response shows a very high value in the `accuracy` field, the service may be geolocating based on the request IP, instead of WiFi points or cell towers. This can happen if no cell towers or access points are valid or recognized. To confirm that this is the issue, set `considerIp` to `false` in your request. If the response is a `404`, you've confirmed that your `wifiAccessPoints` and `cellTowers` objects could not be geolocated. + type: number diff --git a/test/data/toBundleExamples/paths_schema/geolocate.yml b/test/data/toBundleExamples/paths_schema/geolocate.yml new file mode 100644 index 0000000..5826988 --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/geolocate.yml @@ -0,0 +1,33 @@ + +servers: + - url: https://www.server.com +tags: + - Geolocation API +description: |- + Geolocation API returns a location and accuracy +requestBody: + description: The request body must be formatted as JSON. + content: + "application/json": + schema: + $ref: "./GeolocationRequest.yml" + required: false +responses: + "200": + description: 200 OK + content: + application/json: + schema: + $ref: "./GeolocationResponse.yml" + "400": + description: 400 BAD REQUEST + content: + application/json: + schema: + $ref: "./ErrorResponse.yml" + "404": + description: 404 NOT FOUND + content: + application/json: + schema: + $ref: "./ErrorResponse.yml" diff --git a/test/data/toBundleExamples/paths_schema/index.yml b/test/data/toBundleExamples/paths_schema/index.yml new file mode 100644 index 0000000..a41cb38 --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/index.yml @@ -0,0 +1,9 @@ +openapi: 3.0.3 +info: + title: Maps Platform + description: API Specification for Maps Platform + version: 1.17.16 +servers: + - url: "https://www.server.com" +paths: + $ref: "./paths.yml" diff --git a/test/data/toBundleExamples/paths_schema/paths.yml b/test/data/toBundleExamples/paths_schema/paths.yml new file mode 100644 index 0000000..c08ea5c --- /dev/null +++ b/test/data/toBundleExamples/paths_schema/paths.yml @@ -0,0 +1,4 @@ +/geolocation/v1/geolocate: + post: + operationId: geolocate + $ref: "./geolocate.yml" diff --git a/test/data/toBundleExamples/referenced_path/expected.json b/test/data/toBundleExamples/referenced_path/expected.json new file mode 100644 index 0000000..0c29046 --- /dev/null +++ b/test/data/toBundleExamples/referenced_path/expected.json @@ -0,0 +1,98 @@ +{ + "openapi": "3.0.2", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_pet.yaml" + } + } + } + } + } + } + }, + "/cat": { + "get": { + "description": "Returns one cat", + "operationId": "findcat", + "responses": { + "200": { + "description": "cat response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/_cat.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "_cat.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "favFood": { + "type": "string" + } + } + }, + "_pet.yaml": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_path/path.yaml b/test/data/toBundleExamples/referenced_path/path.yaml index ee70013..579a0fe 100644 --- a/test/data/toBundleExamples/referenced_path/path.yaml +++ b/test/data/toBundleExamples/referenced_path/path.yaml @@ -1,9 +1,9 @@ description: Returns all pets alesuada ac... - operationId: findPets - responses: - "200": - description: pet response - content: - application/json: - schema: - $ref: "./pet.yaml" +operationId: findPets +responses: + "200": + description: pet response + content: + application/json: + schema: + $ref: "./pet.yaml" diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 733b01c..9875935 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -44,7 +44,8 @@ let expect = require('chai').expect, nestedExamplesAsValue = path.join(__dirname, BUNDLES_FOLDER + '/nested_examples_as_value'), referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'), referencedComponents = path.join(__dirname, BUNDLES_FOLDER + '/referenced_components'), - referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'); + referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'), + referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -774,6 +775,16 @@ describe('bundle files method - 3.0', function () { paths = fs.readFileSync(refPaths + '/paths/paths.yaml', 'utf8'), path = fs.readFileSync(refPaths + '/paths/path.yaml', 'utf8'), expected = fs.readFileSync(refPaths + '/expected.json', 'utf8'), + expectedMap = { + '#/paths': { + path: '/paths/paths.yaml', + type: 'inline' + }, + '#/paths//pets/get': { + path: '/paths/path.yaml', + type: 'inline' + } + }, input = { type: 'multiFile', specificationVersion: '3.0', @@ -796,13 +807,14 @@ describe('bundle files method - 3.0', function () { content: path } ], - options: {}, + options: { includeReferenceMap: true }, bundleFormat: 'JSON' }; const res = await Converter.bundle(input); expect(res).to.not.be.empty; expect(res.result).to.be.true; + expect(res.output.data[0].referenceMap).to.deep.equal(expectedMap); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); @@ -2358,6 +2370,7 @@ describe('bundle files method - 3.0', function () { path = fs.readFileSync(referencedPath + '/path.yaml', 'utf8'), pet = fs.readFileSync(referencedPath + '/pet.yaml', 'utf8'), cat = fs.readFileSync(referencedPath + '/cat.yaml', 'utf8'), + expectedBundled = fs.readFileSync(referencedPath + '/expected.json', 'utf8'), expected = { '#/paths//pets/get': { path: '/path.yaml', @@ -2366,6 +2379,10 @@ describe('bundle files method - 3.0', function () { '#/components/schemas/_cat.yaml': { path: '/cat.yaml', type: 'component' + }, + '#/components/schemas/_pet.yaml': { + path: '/pet.yaml', + type: 'component' } }, input = { @@ -2402,6 +2419,7 @@ describe('bundle files method - 3.0', function () { expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(res.output.data[0].referenceMap).to.deep.equal(expected); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expectedBundled); }); it('Should return bundled file - referenced-components', async function () { @@ -2451,6 +2469,83 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should return bundled file with referenced paths from roots', async function () { + let contentRootFile = fs.readFileSync(referencedPathSchema + '/index.yml', 'utf8'), + paths = fs.readFileSync(referencedPathSchema + '/paths.yml', 'utf8'), + errorResponse = fs.readFileSync(referencedPathSchema + '/ErrorResponse.yml', 'utf8'), + geolocationResponse = fs.readFileSync(referencedPathSchema + '/GeolocationResponse.yml', 'utf8'), + geolocationRequest = fs.readFileSync(referencedPathSchema + '/GeolocationRequest.yml', 'utf8'), + geolocate = fs.readFileSync(referencedPathSchema + '/geolocate.yml', 'utf8'), + expectedMap = { + '#/paths': { + path: '/paths.yml', + type: 'inline' + }, + '#/paths//geolocation/v1/geolocate/post': { + path: '/geolocate.yml', + type: 'inline' + }, + '#/components/schemas/_GeolocationRequest.yml': { + path: '/GeolocationRequest.yml', + type: 'component' + }, + '#/components/schemas/_GeolocationResponse.yml': { + path: '/GeolocationResponse.yml', + type: 'component' + }, + '#/components/schemas/_ErrorResponse.yml': { + path: '/ErrorResponse.yml', + type: 'component' + }, + '#/components/schemas/_CellTower.yml': { + path: '/CellTower.yml', + type: 'component' + } + }, + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/index.yml' + } + ], + data: [ + { + path: '/index.yml', + content: contentRootFile + }, + { + path: '/paths.yml', + content: paths + }, + { + path: '/ErrorResponse.yml', + content: errorResponse + }, + { + path: '/GeolocationResponse.yml', + content: geolocationResponse + }, + { + path: '/GeolocationRequest.yml', + content: geolocationRequest + }, + { + path: '/geolocate.yml', + content: geolocate + } + ], + options: { includeReferenceMap: true }, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data[0].referenceMap).to.deep.equal(expectedMap); + }); }); describe('getReferences method when node does not have any reference', function() { From 4bf664e80a9e4660635f34f77ccc5c779a204add Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 6 Jul 2022 15:24:29 -0500 Subject: [PATCH 47/56] Fix value resolution. - When value is not below the properties key, it will be resolved inline - Avoiding to overwrite the already created references when they are called again from a different node --- lib/bundle.js | 46 +++++---- lib/bundleRules/spec20.js | 3 +- lib/bundleRules/spec30.js | 8 +- lib/bundleRules/spec31.js | 8 +- lib/dfs.js | 2 +- .../nested_examples_as_value/expected.json | 93 ++++++++++--------- .../paths/geolocate.yml | 6 +- test/unit/bundle.test.js | 3 +- 8 files changed, 97 insertions(+), 72 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index 093d258..38878f0 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -288,10 +288,11 @@ function handleLocalCollisions(trace, initialMainKeys) { * @param {object} rootMainKeys - A dictionary with the component keys in local components object and its mainKeys * @param {string} commonPathFromData - The common path in the file's paths * @param {Array} allData - array of { path, content} objects + * @param {object} globalReferences - The accumulated global references from all nodes * @returns {object} - The references in current node and the new content from the node */ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, rootMainKeys, - commonPathFromData, allData) { + commonPathFromData, allData, globalReferences) { let referencesInNode = [], nodeReferenceDirectory = {}, mainKeys = {}; @@ -349,17 +350,19 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve inline = true; } - nodeReferenceDirectory[tempRef] = { - local, - keyInComponents: nodeTrace, - node: newValue, - reference: inline ? newRefInDoc : referenceInDocument, - traceToParent, - parentNodeKey: parentFilename, - mainKeyInTrace: nodeTrace[nodeTrace.length - 1], - refHasContent, - inline - }; + if (_.isNil(globalReferences[tempRef])) { + nodeReferenceDirectory[tempRef] = { + local, + keyInComponents: nodeTrace, + node: newValue, + reference: inline ? newRefInDoc : referenceInDocument, + traceToParent, + parentNodeKey: parentFilename, + mainKeyInTrace: nodeTrace[nodeTrace.length - 1], + refHasContent, + inline + }; + } mainKeys[componentKey] = tempRef; @@ -381,9 +384,11 @@ function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, ve * @param {string} version - The current version * @param {object} rootMainKeys - A dictionary with the component keys in local components object and its mainKeys * @param {string} commonPathFromData - The common path in the file's paths + * @param {object} globalReferences - The accumulated global refernces from all nodes * @returns {object} - Detect root files result object */ -function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys, commonPathFromData) { +function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys, + commonPathFromData, globalReferences) { let graphAdj = [], missingNodes = [], nodeContent, @@ -408,7 +413,8 @@ function getNodeContentAndReferences (currentNode, allData, specRoot, version, r version, rootMainKeys, commonPathFromData, - allData + allData, + globalReferences ); referencesInNode.forEach((reference) => { @@ -640,8 +646,16 @@ module.exports = { commonPathFromData = Utils.findCommonSubpath(allData.map((fileData) => { return fileData.fileName; })); - rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode) => { - return getNodeContentAndReferences(currentNode, allData, specRoot, version, initialMainKeys, commonPathFromData); + rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode, globalReferences) => { + return getNodeContentAndReferences( + currentNode, + allData, + specRoot, + version, + initialMainKeys, + commonPathFromData, + globalReferences + ); }); components = generateComponentsWrapper( specRoot.parsed.oasObject, diff --git a/lib/bundleRules/spec20.js b/lib/bundleRules/spec20.js index 72b21da..eab3074 100644 --- a/lib/bundleRules/spec20.js +++ b/lib/bundleRules/spec20.js @@ -12,7 +12,8 @@ const SCHEMA_CONTAINERS = [ responses: 'responses' }, INLINE = [ - 'examples' + 'examples', + 'value' ], COMPONENTS_KEYS = [ 'definitions', diff --git a/lib/bundleRules/spec30.js b/lib/bundleRules/spec30.js index 1e489cd..0014006 100644 --- a/lib/bundleRules/spec30.js +++ b/lib/bundleRules/spec30.js @@ -8,8 +8,7 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example', - 'value' + 'example' ], REQUEST_BODY_CONTAINER = [ 'requestBody' @@ -26,7 +25,10 @@ const SCHEMA_CONTAINERS = [ properties: 'schemas', links: 'links' }, - INLINE = ['properties'], + INLINE = [ + 'properties', + 'value' + ], ROOT_CONTAINERS_KEYS = [ 'components' ], diff --git a/lib/bundleRules/spec31.js b/lib/bundleRules/spec31.js index b8de95c..1e2bcab 100644 --- a/lib/bundleRules/spec31.js +++ b/lib/bundleRules/spec31.js @@ -8,8 +8,7 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example', - 'value' + 'example' ], REQUEST_BODY_CONTAINER = [ 'requestBody' @@ -27,7 +26,10 @@ const SCHEMA_CONTAINERS = [ links: 'links', paths: 'pathItems' }, - INLINE = ['properties'], + INLINE = [ + 'properties', + 'value' + ], ROOT_CONTAINERS_KEYS = [ 'components' ], diff --git a/lib/dfs.js b/lib/dfs.js index 2fa3736..f0bd82f 100644 --- a/lib/dfs.js +++ b/lib/dfs.js @@ -48,7 +48,7 @@ class DFS { nodeContent, nodeReferenceDirectory, nodeName - } = getAdjacentAndBundle(node); + } = getAdjacentAndBundle(node, globalReferences); nodeContents[nodeName] = nodeContent; Object.entries(nodeReferenceDirectory).forEach(([key, data]) => { globalReferences[key] = data; diff --git a/test/data/toBundleExamples/nested_examples_as_value/expected.json b/test/data/toBundleExamples/nested_examples_as_value/expected.json index c358317..3dd1f20 100644 --- a/test/data/toBundleExamples/nested_examples_as_value/expected.json +++ b/test/data/toBundleExamples/nested_examples_as_value/expected.json @@ -33,7 +33,11 @@ "examples": { "WIFI": { "value": { - "$ref": "#/components/examples/_responses_maps_http_geolocation_wifi_response.yml" + "location": { + "lat": 37.421925, + "lng": -122.0841293 + }, + "accuracy": 30 } } } @@ -69,7 +73,34 @@ "examples": { "WIFI": { "value": { - "$ref": "#/components/examples/_requests_maps_http_geolocation_wifi_request.yml" + "considerIp": "false", + "wifiAccessPoints": [ + { + "macAddress": "84:d4:7e:09:a5:f1", + "signalStrength": -43, + "signalToNoiseRatio": 0 + }, + { + "macAddress": "44:48:c1:a6:f3:d0", + "signalStrength": -55, + "signalToNoiseRatio": 0 + } + ] + } + }, + "Cell Towers": { + "value": { + "cellTowers": [ + { + "cellId": 170402199, + "locationAreaCode": 35632, + "mobileCountryCode": 310, + "mobileNetworkCode": 410, + "age": 0, + "signalStrength": -60, + "timingAdvance": 15 + } + ] } } } @@ -121,7 +152,6 @@ ], "properties": { "location": { - "description": "The user’s estimated latitude and longitude, in degrees.", "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" }, "accuracy": { @@ -130,7 +160,11 @@ } }, "example": { - "$ref": "#/components/examples/_responses_maps_http_geolocation_wifi_response.yml" + "location": { + "lat": 37.421925, + "lng": -122.0841293 + }, + "accuracy": 30 } }, "_schemas_test.yml": { @@ -219,7 +253,17 @@ } }, "example": { - "$ref": "#/components/examples/_requests_maps_http_geolocation_celltowers_request.yml" + "cellTowers": [ + { + "cellId": 170402199, + "locationAreaCode": 35632, + "mobileCountryCode": 310, + "mobileNetworkCode": 410, + "age": 0, + "signalStrength": -60, + "timingAdvance": 15 + } + ] } }, "Bounds": { @@ -232,11 +276,9 @@ ], "properties": { "northeast": { - "description": "The user’s estimated latitude and longitude, in degrees.", "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" }, "southwest": { - "description": "The user’s estimated latitude and longitude, in degrees.", "$ref": "#/components/schemas/_schemas_LatLngLiteral.yml" } } @@ -248,43 +290,6 @@ "in": "query", "name": "key" } - }, - "examples": { - "_responses_maps_http_geolocation_wifi_response.yml": { - "location": { - "lat": 37.421925, - "lng": -122.0841293 - }, - "accuracy": 30 - }, - "_requests_maps_http_geolocation_wifi_request.yml": { - "considerIp": "false", - "wifiAccessPoints": [ - { - "macAddress": "84:d4:7e:09:a5:f1", - "signalStrength": -43, - "signalToNoiseRatio": 0 - }, - { - "macAddress": "44:48:c1:a6:f3:d0", - "signalStrength": -55, - "signalToNoiseRatio": 0 - } - ] - }, - "_requests_maps_http_geolocation_celltowers_request.yml": { - "cellTowers": [ - { - "cellId": 170402199, - "locationAreaCode": 35632, - "mobileCountryCode": 310, - "mobileNetworkCode": 410, - "age": 0, - "signalStrength": -60, - "timingAdvance": 15 - } - ] - } } }, "security": [ diff --git a/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml b/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml index a30af35..841739f 100644 --- a/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml +++ b/test/data/toBundleExamples/nested_examples_as_value/paths/geolocate.yml @@ -71,9 +71,9 @@ requestBody: WIFI: value: $ref: ../requests/maps_http_geolocation_wifi_request.yml - # Cell Towers: - # value: - # $ref: ../requests/maps_http_geolocation_celltowers_request.yml + Cell Towers: + value: + $ref: ../requests/maps_http_geolocation_celltowers_request.yml # IP Only: # value: # $ref: ../requests/maps_http_geolocation_ip_request.yml diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 9875935..d117ae2 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -2600,7 +2600,8 @@ describe('getReferences method when node does not have any reference', function( '3.0', {}, '', - [] + [], + {} ); expect(result.nodeReferenceDirectory).to.be.an('object'); expect(Object.keys(result.nodeReferenceDirectory).length).to.equal(1); From 9005d5e6fb31ad67d3d4549a1d3de2da64d193ee Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 6 Jul 2022 15:34:16 -0500 Subject: [PATCH 48/56] Adding a test case --- .../example_value/example.yml | 15 + .../example_value/expected.json | 466 ++++++++++++++++++ .../toBundleExamples/example_value/root.yml | 286 +++++++++++ test/unit/bundle.test.js | 36 +- 4 files changed, 802 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/example_value/example.yml create mode 100644 test/data/toBundleExamples/example_value/expected.json create mode 100644 test/data/toBundleExamples/example_value/root.yml diff --git a/test/data/toBundleExamples/example_value/example.yml b/test/data/toBundleExamples/example_value/example.yml new file mode 100644 index 0000000..98ce373 --- /dev/null +++ b/test/data/toBundleExamples/example_value/example.yml @@ -0,0 +1,15 @@ +{ + "considerIp": "false", + "wifiAccessPoints": [ + { + "macAddress": "84:d4:7e:09:a5:f1", + "signalStrength": -43, + "signalToNoiseRatio": 0 + }, + { + "macAddress": "44:48:c1:a6:f3:d0", + "signalStrength": -55, + "signalToNoiseRatio": 0 + } + ] +} diff --git a/test/data/toBundleExamples/example_value/expected.json b/test/data/toBundleExamples/example_value/expected.json new file mode 100644 index 0000000..c454973 --- /dev/null +++ b/test/data/toBundleExamples/example_value/expected.json @@ -0,0 +1,466 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.10", + "title": "Petstore 224", + "description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Postman" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + }, + "x-postman-projectname": "PostmanPetstore" + }, + "paths": { + "/pets": { + "parameters": [ + { + "name": "GlobalParam", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "GlobalCookie", + "in": "cookie", + "schema": { + "type": "string" + } + } + ], + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "requestBody": { + "description": "Pet to add to the store", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "201": { + "description": "Pet was created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + }, + "examples": { + "WIFI": { + "value": { + "considerIp": "false", + "wifiAccessPoints": [ + { + "macAddress": "84:d4:7e:09:a5:f1", + "signalStrength": -43, + "signalToNoiseRatio": 0 + }, + { + "macAddress": "44:48:c1:a6:f3:d0", + "signalStrength": -55, + "signalToNoiseRatio": 0 + } + ] + } + } + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{id}": { + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to work with in operations", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "get": { + "description": "Returns a user based on a single ID, if the user does not have access to the pet", + "operationId": "find pet by id", + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "put": { + "description": "Replaces Pet with contents", + "operationId": "replacePet", + "requestBody": { + "description": "Pet to replace with", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "Pet updated correctly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "patch": { + "description": "Updates Pet with contents", + "operationId": "UPDATEPET", + "requestBody": { + "description": "Pet to update with", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "Pet updated correctly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/stores": { + "get": { + "description": "Retrieves contents of stores", + "operationId": "GetAllStores", + "responses": { + "200": { + "description": "Returns list of Store items", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "typeOf": { + "type": "string", + "enum": [ + "Cat", + "Dog", + "Bird", + "Reptile" + ] + }, + "tag": { + "type": "string" + }, + "value": { + "type": "number", + "format": "float", + "description": "price" + }, + "saleValue": { + "type": "number", + "format": "float", + "description": "price if on sale" + }, + "onSale": { + "type": "boolean", + "default": false, + "description": "True if item is on sale, false if not." + } + } + } + } + } + } + }, + "400": { + "description": "No items found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Something went wrong on server", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/stores/{id}": { + "get": { + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of store to fetch", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreItem" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/stores/{id}/pets": { + "get": { + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of store to fetch", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "description": "Retrieves contents of stores", + "operationId": "GetAllPetsForStoreById", + "responses": { + "200": { + "description": "Returns list of Store items", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "400": { + "description": "No items found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Something went wrong on server", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "description": "A pet", + "required": [ + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "typeOf": { + "type": "string", + "enum": [ + "Cat", + "Dog", + "Bird", + "Reptile" + ] + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "StoreItem": { + "description": "Single store item", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "typeOf": { + "type": "string", + "enum": [ + "Cat", + "Dog", + "Bird", + "Reptile" + ] + }, + "tag": { + "type": "string" + }, + "value": { + "type": "number", + "format": "float", + "description": "price" + }, + "saleValue": { + "type": "number", + "format": "float", + "description": "price if on sale" + }, + "onSale": { + "type": "boolean", + "default": false, + "description": "True if item is on sale, false if not." + } + } + }, + "Error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/example_value/root.yml b/test/data/toBundleExamples/example_value/root.yml new file mode 100644 index 0000000..a11e4f3 --- /dev/null +++ b/test/data/toBundleExamples/example_value/root.yml @@ -0,0 +1,286 @@ +openapi: "3.0.0" +info: + version: 1.0.10 + title: Petstore 224 + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Postman + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + x-postman-projectname: PostmanPetstore + +paths: + /pets: + parameters: + - name: GlobalParam + in: query + schema: + type: string + - name: GlobalCookie + in: cookie + schema: + type: string + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + requestBody: + description: Pet to add to the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + 201: + description: Pet was created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + examples: + WIFI: + value: + $ref: "./example.yml" + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pets/{id}: + parameters: + - name: id + in: path + description: ID of pet to work with in operations + required: true + schema: + type: integer + format: int64 + get: + description: Returns a user based on a single ID, if the user does not have access to the pet + operationId: find pet by id + responses: + '200': + description: pet response + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + description: Replaces Pet with contents + operationId: replacePet + requestBody: + description: Pet to replace with + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + 200: + description: Pet updated correctly + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + description: Updates Pet with contents + operationId: UPDATEPET + requestBody: + description: Pet to update with + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + '200': + description: Pet updated correctly + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /stores: + get: + description: Retrieves contents of stores + operationId: GetAllStores + responses: + 200: + description: Returns list of Store items + content: + application/json: + schema: + type: array + items: + properties: + id: + type: integer + format: int64 + name: + type: string + typeOf: + type: string + enum: [Cat, Dog, Bird, Reptile] + tag: + type: string + value: + type: number + format: float + description: price + saleValue: + type: number + format: float + description: price if on sale + onSale: + type: boolean + default: false + description: True if item is on sale, false if not. + 400: + description: No items found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + 500: + description: Something went wrong on server + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /stores/{id}: + get: + parameters: + - name: id + in: path + description: ID of store to fetch + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: pet response + content: + application/json: + schema: + $ref: '#/components/schemas/StoreItem' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /stores/{id}/pets: + get: + parameters: + - name: id + in: path + description: ID of store to fetch + required: true + schema: + type: integer + format: int64 + description: Retrieves contents of stores + operationId: GetAllPetsForStoreById + responses: + 200: + description: Returns list of Store items + content: + application/json: + schema: + $ref: '#/components/schemas/Pets' + + 400: + description: No items found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + 500: + description: Something went wrong on server + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + Pet: + type: object + description: A pet + required: + - name + properties: + id: + type: integer + format: int64 + name: + type: string + typeOf: + type: string + enum: [Cat, Dog, Bird, Reptile] + tag: + type: string + + Pets: + type: object + properties: + id: + type: integer + + StoreItem: + description: Single store item + properties: + id: + type: integer + format: int64 + name: + type: string + typeOf: + type: string + enum: [ Cat, Dog, Bird, Reptile ] + tag: + type: string + value: + type: number + format: float + description: price + saleValue: + type: number + format: float + description: price if on sale + onSale: + type: boolean + default: false + description: True if item is on sale, false if not. + + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d117ae2..79eddc6 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -45,7 +45,8 @@ let expect = require('chai').expect, referencedProperties = path.join(__dirname, BUNDLES_FOLDER + '/referenced_properties'), referencedComponents = path.join(__dirname, BUNDLES_FOLDER + '/referenced_components'), referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'), - referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'); + referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'), + exampleValue = path.join(__dirname, BUNDLES_FOLDER + '/example_value'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2546,6 +2547,39 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(res.output.data[0].referenceMap).to.deep.equal(expectedMap); }); + + it('Should return bundled file - example_schema', async function () { + let contentRootFile = fs.readFileSync(exampleValue + '/root.yml', 'utf8'), + example = fs.readFileSync(exampleValue + '/example.yml', 'utf8'), + expected = fs.readFileSync(exampleValue + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yml' + } + ], + data: [ + { + path: '/root.yml', + content: contentRootFile + }, + { + path: '/example.yml', + content: example + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From a10e67862cb39f2aa9b9fc7ad091801805af5c62 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Wed, 6 Jul 2022 16:19:29 -0500 Subject: [PATCH 49/56] Modifying a file name --- .../example_value/{example.yml => example_value.yml} | 0 test/data/toBundleExamples/example_value/root.yml | 2 +- test/unit/bundle.test.js | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename test/data/toBundleExamples/example_value/{example.yml => example_value.yml} (100%) diff --git a/test/data/toBundleExamples/example_value/example.yml b/test/data/toBundleExamples/example_value/example_value.yml similarity index 100% rename from test/data/toBundleExamples/example_value/example.yml rename to test/data/toBundleExamples/example_value/example_value.yml diff --git a/test/data/toBundleExamples/example_value/root.yml b/test/data/toBundleExamples/example_value/root.yml index a11e4f3..5ecd0a2 100644 --- a/test/data/toBundleExamples/example_value/root.yml +++ b/test/data/toBundleExamples/example_value/root.yml @@ -42,7 +42,7 @@ paths: examples: WIFI: value: - $ref: "./example.yml" + $ref: "./example_value.yml" default: description: unexpected error content: diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 79eddc6..f281fae 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -2550,7 +2550,7 @@ describe('bundle files method - 3.0', function () { it('Should return bundled file - example_schema', async function () { let contentRootFile = fs.readFileSync(exampleValue + '/root.yml', 'utf8'), - example = fs.readFileSync(exampleValue + '/example.yml', 'utf8'), + example = fs.readFileSync(exampleValue + '/example_value.yml', 'utf8'), expected = fs.readFileSync(exampleValue + '/expected.json', 'utf8'), input = { type: 'multiFile', @@ -2566,7 +2566,7 @@ describe('bundle files method - 3.0', function () { content: contentRootFile }, { - path: '/example.yml', + path: '/example_value.yml', content: example } ], From dd7d1426c11327a650ec16f0f83a7312e394f836 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:26:12 -0500 Subject: [PATCH 50/56] Force example to be inline --- lib/bundleRules/spec20.js | 3 +- lib/bundleRules/spec30.js | 7 +- lib/bundleRules/spec31.js | 7 +- lib/jsonPointer.js | 2 +- .../toBundleExamples/example2/another.yml | 106 ++++++++++ .../toBundleExamples/example2/example2.yaml | 17 ++ .../toBundleExamples/example2/expected.json | 187 ++++++++++++++++++ .../referenced_examples/expected.json | 15 +- test/unit/bundle.test.js | 34 +++- 9 files changed, 359 insertions(+), 19 deletions(-) create mode 100644 test/data/toBundleExamples/example2/another.yml create mode 100644 test/data/toBundleExamples/example2/example2.yaml create mode 100644 test/data/toBundleExamples/example2/expected.json diff --git a/lib/bundleRules/spec20.js b/lib/bundleRules/spec20.js index eab3074..79154c2 100644 --- a/lib/bundleRules/spec20.js +++ b/lib/bundleRules/spec20.js @@ -13,7 +13,8 @@ const SCHEMA_CONTAINERS = [ }, INLINE = [ 'examples', - 'value' + 'value', + 'example' ], COMPONENTS_KEYS = [ 'definitions', diff --git a/lib/bundleRules/spec30.js b/lib/bundleRules/spec30.js index 0014006..2093736 100644 --- a/lib/bundleRules/spec30.js +++ b/lib/bundleRules/spec30.js @@ -8,7 +8,6 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example' ], REQUEST_BODY_CONTAINER = [ 'requestBody' @@ -23,11 +22,13 @@ const SCHEMA_CONTAINERS = [ responses: 'responses', callbacks: 'callbacks', properties: 'schemas', - links: 'links' + links: 'links', + examples: 'examples' }, INLINE = [ 'properties', - 'value' + 'value', + 'example' ], ROOT_CONTAINERS_KEYS = [ 'components' diff --git a/lib/bundleRules/spec31.js b/lib/bundleRules/spec31.js index 1e2bcab..48e81e8 100644 --- a/lib/bundleRules/spec31.js +++ b/lib/bundleRules/spec31.js @@ -8,7 +8,6 @@ const SCHEMA_CONTAINERS = [ 'schema' ], EXAMPLE_CONTAINERS = [ - 'example' ], REQUEST_BODY_CONTAINER = [ 'requestBody' @@ -24,11 +23,13 @@ const SCHEMA_CONTAINERS = [ callbacks: 'callbacks', properties: 'schemas', links: 'links', - paths: 'pathItems' + paths: 'pathItems', + examples: 'examples' }, INLINE = [ 'properties', - 'value' + 'value', + 'example' ], ROOT_CONTAINERS_KEYS = [ 'components' diff --git a/lib/jsonPointer.js b/lib/jsonPointer.js index ea27064..c38a0d2 100644 --- a/lib/jsonPointer.js +++ b/lib/jsonPointer.js @@ -88,7 +88,7 @@ function getKeyInComponents(traceFromParent, mainKey, version, commonPathFromDat } item = resolveFirstLevelChild(item, CONTAINERS); resolveSecondLevelChild(trace, index, DEFINITIONS); - traceToKey.push(item); + traceToKey.push(item.replace(/\s/g, '')); if (COMPONENTS_KEYS.includes(item)) { matchFound = true; break; diff --git a/test/data/toBundleExamples/example2/another.yml b/test/data/toBundleExamples/example2/another.yml new file mode 100644 index 0000000..c5d65e6 --- /dev/null +++ b/test/data/toBundleExamples/example2/another.yml @@ -0,0 +1,106 @@ +openapi: "3.0.0" +info: + version: 1.0.10 + title: Petstore 224 + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Postman + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + x-postman-projectname: PostmanPetstore + +paths: + /pets: + parameters: + - name: GlobalParam + in: query + schema: + type: string + - name: GlobalCookie + in: cookie + schema: + type: string + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + requestBody: + description: Pet to add to the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + examples: + Single Droplet Create Request: + $ref: "./example2.yaml#/droplet_create_request" +components: + schemas: + Pet: + type: object + description: A pet + required: + - name + properties: + id: + type: integer + format: int64 + name: + type: string + typeOf: + type: string + enum: [Cat, Dog, Bird, Reptile] + tag: + type: string + + Pets: + type: object + properties: + id: + type: integer + + StoreItem: + description: Single store item + properties: + id: + type: integer + format: int64 + name: + type: string + typeOf: + type: string + enum: [ Cat, Dog, Bird, Reptile ] + tag: + type: string + value: + type: number + format: float + description: price + saleValue: + type: number + format: float + description: price if on sale + onSale: + type: boolean + default: false + description: True if item is on sale, false if not. + + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/data/toBundleExamples/example2/example2.yaml b/test/data/toBundleExamples/example2/example2.yaml new file mode 100644 index 0000000..6b5e5ee --- /dev/null +++ b/test/data/toBundleExamples/example2/example2.yaml @@ -0,0 +1,17 @@ +droplet_create_request: + value: + name: example.com + region: nyc3 + size: s-1vcpu-1gb + image: ubuntu-20-04-x64 + ssh_keys: + - 289794 + - 3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45 + backups: true + ipv6: true + monitoring: true + tags: + - env:prod + - web + user_data: "#cloud-config\nruncmd:\n - touch /test.txt\n" + vpc_uuid: 760e09ef-dc84-11e8-981e-3cfdfeaae000 diff --git a/test/data/toBundleExamples/example2/expected.json b/test/data/toBundleExamples/example2/expected.json new file mode 100644 index 0000000..383f346 --- /dev/null +++ b/test/data/toBundleExamples/example2/expected.json @@ -0,0 +1,187 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.10", + "title": "Petstore 224", + "description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Postman" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + }, + "x-postman-projectname": "PostmanPetstore" + }, + "paths": { + "/pets": { + "parameters": [ + { + "name": "GlobalParam", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "GlobalCookie", + "in": "cookie", + "schema": { + "type": "string" + } + } + ], + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "requestBody": { + "description": "Pet to add to the store", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Single Droplet Create Request": { + "$ref": "#/components/examples/_example2.yaml-_droplet_create_request" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "description": "A pet", + "required": [ + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "typeOf": { + "type": "string", + "enum": [ + "Cat", + "Dog", + "Bird", + "Reptile" + ] + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "StoreItem": { + "description": "Single store item", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "typeOf": { + "type": "string", + "enum": [ + "Cat", + "Dog", + "Bird", + "Reptile" + ] + }, + "tag": { + "type": "string" + }, + "value": { + "type": "number", + "format": "float", + "description": "price" + }, + "saleValue": { + "type": "number", + "format": "float", + "description": "price if on sale" + }, + "onSale": { + "type": "boolean", + "default": false, + "description": "True if item is on sale, false if not." + } + } + }, + "Error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "examples": { + "_example2.yaml-_droplet_create_request": { + "value": { + "name": "example.com", + "region": "nyc3", + "size": "s-1vcpu-1gb", + "image": "ubuntu-20-04-x64", + "ssh_keys": [ + 289794, + "3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45" + ], + "backups": true, + "ipv6": true, + "monitoring": true, + "tags": [ + "env:prod", + "web" + ], + "user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n", + "vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/referenced_examples/expected.json b/test/data/toBundleExamples/referenced_examples/expected.json index 9ace3da..c723309 100644 --- a/test/data/toBundleExamples/referenced_examples/expected.json +++ b/test/data/toBundleExamples/referenced_examples/expected.json @@ -32,7 +32,11 @@ } }, "example": { - "$ref": "#/components/examples/_examples.yaml-_foo" + "summary": "sum", + "value": { + "code": 1, + "message": "test error message" + } } } } @@ -86,15 +90,6 @@ } } } - }, - "examples": { - "_examples.yaml-_foo": { - "summary": "sum", - "value": { - "code": 1, - "message": "test error message" - } - } } } } \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index f281fae..315f72b 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -46,7 +46,8 @@ let expect = require('chai').expect, referencedComponents = path.join(__dirname, BUNDLES_FOLDER + '/referenced_components'), referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'), referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'), - exampleValue = path.join(__dirname, BUNDLES_FOLDER + '/example_value'); + exampleValue = path.join(__dirname, BUNDLES_FOLDER + '/example_value'), + example2 = path.join(__dirname, BUNDLES_FOLDER + '/example2'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2580,6 +2581,37 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + it('Should resolve examples correctly', async function () { + let contentRootFile = fs.readFileSync(example2 + '/another.yml', 'utf8'), + example = fs.readFileSync(example2 + '/example2.yaml', 'utf8'), + expected = fs.readFileSync(example2 + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/another.yml' + } + ], + data: [ + { + path: '/another.yml', + content: contentRootFile + }, + { + path: '/example2.yaml', + content: example + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From b9f7c00627d62c4a46ca62453cfc43563dfcbd89 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 7 Jul 2022 16:25:49 -0500 Subject: [PATCH 51/56] adding test to validate example key resolution inline --- .../referenced_example_key/example.yaml | 3 + .../referenced_example_key/expected.json | 83 +++++++++++++++++++ .../referenced_example_key/root.yaml | 54 ++++++++++++ test/unit/bundle20.test.js | 37 ++++++++- 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 test/data/toBundleExamples/swagger20/referenced_example_key/example.yaml create mode 100644 test/data/toBundleExamples/swagger20/referenced_example_key/expected.json create mode 100644 test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml diff --git a/test/data/toBundleExamples/swagger20/referenced_example_key/example.yaml b/test/data/toBundleExamples/swagger20/referenced_example_key/example.yaml new file mode 100644 index 0000000..871af42 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example_key/example.yaml @@ -0,0 +1,3 @@ +id: 1 +name: Puma +tag: Test diff --git a/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json b/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json new file mode 100644 index 0000000..afb8725 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json @@ -0,0 +1,83 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + }, + "example": { + "id": 1, + "name": "Puma", + "tag": "Test" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml b/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml new file mode 100644 index 0000000..9759860 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml @@ -0,0 +1,54 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + '200': + description: pet response + schema: + type: array + items: + $ref: '#/definitions/Pet' + example: + $ref: "example.yaml" + default: + description: unexpected error + schema: + $ref: '#/definitions/Error' +definitions: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/test/unit/bundle20.test.js b/test/unit/bundle20.test.js index 39eec5e..451b8ae 100644 --- a/test/unit/bundle20.test.js +++ b/test/unit/bundle20.test.js @@ -29,7 +29,9 @@ let expect = require('chai').expect, schemaCollisionWRootComponent = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/schema_collision_w_root_components'), referencedRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + - '/referenced_root_components'); + '/referenced_root_components'), + referencedExampleKey = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + + '/referenced_example_key'); describe('bundle files method - 2.0', function() { it('Should return bundled result from - nestedProperties20', async function() { @@ -971,4 +973,37 @@ describe('bundle files method - 2.0', function() { expect(res.output.specification.version).to.equal('2.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should return bundled file as json - referenced_example_key', async function () { + let contentRootFile = fs.readFileSync(referencedExampleKey + '/root.yaml', 'utf8'), + example = fs.readFileSync(referencedExampleKey + '/example.yaml', 'utf8'), + expected = fs.readFileSync(referencedExampleKey + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/example.yaml', + content: example + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('2.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); From 68ad0c4f88822618dd8f8b5994843ec6e1445cef Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Thu, 7 Jul 2022 18:22:58 -0500 Subject: [PATCH 52/56] Fix test root file --- .../swagger20/referenced_example_key/expected.json | 10 +++++----- .../swagger20/referenced_example_key/root.yaml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json b/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json index afb8725..6d41686 100644 --- a/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json +++ b/test/data/toBundleExamples/swagger20/referenced_example_key/expected.json @@ -27,12 +27,12 @@ "type": "array", "items": { "$ref": "#/definitions/Pet" + }, + "example": { + "id": 1, + "name": "Puma", + "tag": "Test" } - }, - "example": { - "id": 1, - "name": "Puma", - "tag": "Test" } }, "default": { diff --git a/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml b/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml index 9759860..7f40d15 100644 --- a/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml +++ b/test/data/toBundleExamples/swagger20/referenced_example_key/root.yaml @@ -23,8 +23,8 @@ paths: type: array items: $ref: '#/definitions/Pet' - example: - $ref: "example.yaml" + example: + $ref: "example.yaml" default: description: unexpected error schema: From 4b32d1bf8c44715bfb39b9563484f316beb6124b Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 8 Jul 2022 11:04:03 -0500 Subject: [PATCH 53/56] fix reading version when swagger --- lib/common/versionUtils.js | 19 +++++++++++++++++-- lib/parse.js | 4 ++-- lib/swaggerUtils/inputValidationSwagger.js | 9 ++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index cb06c2b..ded8069 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -1,4 +1,5 @@ -const VERSION_30 = { key: 'openapi', version: '3.0' }, +const _ = require('lodash'), + VERSION_30 = { key: 'openapi', version: '3.0' }, VERSION_31 = { key: 'openapi', version: '3.1' }, VERSION_20 = { key: 'swagger', version: '2.0' }, GENERIC_VERSION2 = { key: 'swagger', version: '2.' }, @@ -305,6 +306,19 @@ function getBundleRulesDataByVersion(version) { } } +/** + * Gets the version of a parsed Spec + * @param {object} spec The parsed openapi spec + * @returns {object} The bundling rules related with the spec + */ +function getVersionFromSpec(spec) { + if (!_.isNil(spec) && _.has(spec, 'swagger')) { + return spec.swagger; + } + if (!_.isNil(spec) && _.has(spec, 'openapi')) { + return spec.openapi; + } +} module.exports = { getSpecVersion, @@ -316,5 +330,6 @@ module.exports = { SWAGGER_VERSION, VERSION_3_1, validateSupportedVersion, - getBundleRulesDataByVersion + getBundleRulesDataByVersion, + getVersionFromSpec }; diff --git a/lib/parse.js b/lib/parse.js index 5923d34..44b97f0 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -6,7 +6,7 @@ var yaml = require('js-yaml'), pathBrowserify = require('path-browserify'), resolver = require('oas-resolver-browser'), yamlParse = require('yaml'), - { compareVersion } = require('./common/versionUtils.js'); + { compareVersion, getVersionFromSpec } = require('./common/versionUtils.js'); const BROWSER = 'browser', YAML_FORMAT = 'yaml', JSON_FORMAT = 'json', @@ -131,7 +131,7 @@ module.exports = { } if (inputValidation.validateSpec(oasObject, options).result) { if (specificationVersion) { - if (compareVersion(specificationVersion, oasObject.openapi)) { + if (compareVersion(specificationVersion, getVersionFromSpec(oasObject))) { rootFilesArray.push(filePath.fileName); } } diff --git a/lib/swaggerUtils/inputValidationSwagger.js b/lib/swaggerUtils/inputValidationSwagger.js index 753cbbe..aaa8c34 100644 --- a/lib/swaggerUtils/inputValidationSwagger.js +++ b/lib/swaggerUtils/inputValidationSwagger.js @@ -1,4 +1,5 @@ +const _ = require('lodash'); module.exports = { /** @@ -8,6 +9,12 @@ module.exports = { * @return {Object} Validation result */ validateSpec: function (spec, options) { + if (_.isNil(spec)) { + return { + result: false, + reason: 'The Specification is null or undefined' + }; + } if (spec.swagger !== '2.0') { return { result: false, @@ -20,7 +27,7 @@ module.exports = { reason: 'The Swagger specification must have an "info" field' }; } - if (!(spec.info.title && spec.info.version) && !options.isFolder) { + if (!(_.get(spec, 'info.title') && _.get(spec, 'info.version')) && !options.isFolder) { return { result: false, reason: 'Title, and version fields are required for the Info Object' From 8e3293717dc5c404ce1e912cebc39657d86a5fd2 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Fri, 8 Jul 2022 17:37:16 -0500 Subject: [PATCH 54/56] Fix circular references fix circular references --- lib/bundle.js | 19 ++++- .../circular_reference/expected.json | 76 +++++++++++++++++++ .../circular_reference/root.yaml | 33 ++++++++ .../circular_reference/schemas/schemas.yaml | 24 ++++++ test/unit/bundle.test.js | 35 ++++++++- 5 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 test/data/toBundleExamples/circular_reference/expected.json create mode 100644 test/data/toBundleExamples/circular_reference/root.yaml create mode 100644 test/data/toBundleExamples/circular_reference/schemas/schemas.yaml diff --git a/lib/bundle.js b/lib/bundle.js index 38878f0..90db6ac 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -550,6 +550,18 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver } }); }); + return { + resRoot: traverseUtility(rootContent).map(function () { + if (this.circular) { + this.update({ [this.key]: '- Circular' }); + } + }), + newComponents: traverseUtility(components).map(function () { + if (this.circular) { + this.update({ [this.key]: '- Circular' }); + } + }) + }; } /** @@ -642,6 +654,7 @@ module.exports = { let algorithm = new DFS(), components = {}, commonPathFromData = '', + finalElements = {}, rootContextData; commonPathFromData = Utils.findCommonSubpath(allData.map((fileData) => { return fileData.fileName; @@ -662,7 +675,7 @@ module.exports = { version, rootContextData.nodeContents ); - generateComponentsObject( + finalElements = generateComponentsObject( rootContextData, rootContextData.nodeContents[specRoot.fileName], isExtRef, @@ -670,8 +683,8 @@ module.exports = { version ); return { - fileContent: rootContextData.nodeContents[specRoot.fileName], - components, + fileContent: finalElements.resRoot, + components: finalElements.newComponents, fileName: specRoot.fileName, referenceMap: getReferenceMap(rootContextData.globalReferences) }; diff --git a/test/data/toBundleExamples/circular_reference/expected.json b/test/data/toBundleExamples/circular_reference/expected.json new file mode 100644 index 0000000..b5d5cde --- /dev/null +++ b/test/data/toBundleExamples/circular_reference/expected.json @@ -0,0 +1,76 @@ +{ + "openapi": "3.0.2", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/pets": { + "get": { + "description": "Returns all pets alesuada ac...", + "operationId": "findPets", + "responses": { + "200": { + "description": "pet response", + "schema": { + "type": "array", + "items": { + "$ref\"": "./schemas/schemas.yaml" + } + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/components/schemas/_schemas_schemas.yaml-components_schemas_ErrorDetail" + } + } + } + } + } + }, + "components": { + "schemas": { + "_schemas_schemas.yaml-components_schemas_ErrorDetail": { + "type": "object", + "description": "The error detail.", + "properties": { + "code": { + "readOnly": true, + "type": "string", + "description": "The error code." + }, + "message": { + "readOnly": true, + "type": "string", + "description": "The error message." + }, + "target": { + "readOnly": true, + "type": "string", + "description": "The error target." + }, + "details": { + "readOnly": true, + "type": "array", + "items": { + "items": "- Circular" + }, + "description": "The error details." + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/circular_reference/root.yaml b/test/data/toBundleExamples/circular_reference/root.yaml new file mode 100644 index 0000000..329fa61 --- /dev/null +++ b/test/data/toBundleExamples/circular_reference/root.yaml @@ -0,0 +1,33 @@ + +openapi: "3.0.2" +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + + +paths: + /pets: + get: + description: Returns all pets alesuada ac... + operationId: findPets + responses: + "200": + description: pet response + schema: + type: array + items: + $ref": "./schemas/schemas.yaml" + default: + description: unexpected error + schema: + $ref: "./schemas/schemas.yaml#components/schemas/ErrorDetail" + diff --git a/test/data/toBundleExamples/circular_reference/schemas/schemas.yaml b/test/data/toBundleExamples/circular_reference/schemas/schemas.yaml new file mode 100644 index 0000000..cfa540a --- /dev/null +++ b/test/data/toBundleExamples/circular_reference/schemas/schemas.yaml @@ -0,0 +1,24 @@ +components: + schemas: + ErrorDetail: + type: object + description: The error detail. + properties: + code: + readOnly: true + type: string + description: The error code. + message: + readOnly: true + type: string + description: The error message. + target: + readOnly: true + type: string + description: The error target. + details: + readOnly: true + type: array + items: + $ref: "#/components/schemas/ErrorDetail" + description: The error details. diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 315f72b..ecfc587 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -47,7 +47,8 @@ let expect = require('chai').expect, referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'), referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'), exampleValue = path.join(__dirname, BUNDLES_FOLDER + '/example_value'), - example2 = path.join(__dirname, BUNDLES_FOLDER + '/example2'); + example2 = path.join(__dirname, BUNDLES_FOLDER + '/example2'), + schemaCircularRef = path.join(__dirname, BUNDLES_FOLDER + '/circular_reference'); describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { @@ -2612,6 +2613,38 @@ describe('bundle files method - 3.0', function () { expect(res.output.specification.version).to.equal('3.0'); expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); }); + + it('Should resolve circular reference in schema correctly', async function () { + let contentRootFile = fs.readFileSync(schemaCircularRef + '/root.yaml', 'utf8'), + schema = fs.readFileSync(schemaCircularRef + '/schemas/schemas.yaml', 'utf8'), + expected = fs.readFileSync(schemaCircularRef + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas/schemas.yaml', + content: schema + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.specification.version).to.equal('3.0'); + expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected); + }); }); describe('getReferences method when node does not have any reference', function() { From f1ae42f46b1ccc7de478feec88f815e585cf2d46 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Mon, 11 Jul 2022 12:20:00 -0500 Subject: [PATCH 55/56] Add ref and property for circular ref Add ref and property for circular ref --- lib/bundle.js | 29 +++++++++++++++++-- .../circular_reference/expected.json | 23 +++++++-------- .../circular_reference/root.yaml | 21 +++++--------- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index 90db6ac..5a8a3e1 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -21,7 +21,8 @@ let path = require('path'), BROWSER = 'browser', { DFS } = require('./dfs'), deref = require('./deref.js'), - { isSwagger, getBundleRulesDataByVersion } = require('./common/versionUtils'); + { isSwagger, getBundleRulesDataByVersion } = require('./common/versionUtils'), + CIRCULAR_REF_EXT_PROP = 'x-circularRef'; /** @@ -462,6 +463,24 @@ function resolveJsonPointerInlineNodes(parents, key) { return pointer; } +/** + * Finds the reference in the document context + * @param {object} documentContext The document context from root + * @param {object} mainKeyInTrace - The key to find + * @returns {string} The reference value + */ +function findReferenceByMainKeyInTraceFromContext(documentContext, mainKeyInTrace) { + let relatedRef = '', + globalRef; + globalRef = Object.values(documentContext.globalReferences).find((item) => { + return item.mainKeyInTrace === mainKeyInTrace; + }); + if (globalRef) { + relatedRef = globalRef.reference; + } + return relatedRef; +} + /** * Generates the components object from the documentContext data * @param {object} documentContext The document context from root @@ -552,13 +571,17 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver }); return { resRoot: traverseUtility(rootContent).map(function () { + let relatedRef = ''; if (this.circular) { - this.update({ [this.key]: '- Circular' }); + relatedRef = findReferenceByMainKeyInTraceFromContext(documentContext, this.circular.key); + this.update({ $ref: relatedRef, [CIRCULAR_REF_EXT_PROP]: true }); } }), newComponents: traverseUtility(components).map(function () { + let relatedRef = ''; if (this.circular) { - this.update({ [this.key]: '- Circular' }); + relatedRef = findReferenceByMainKeyInTraceFromContext(documentContext, this.circular.key); + this.update({ $ref: relatedRef, [CIRCULAR_REF_EXT_PROP]: true }); } }) }; diff --git a/test/data/toBundleExamples/circular_reference/expected.json b/test/data/toBundleExamples/circular_reference/expected.json index b5d5cde..6cc400f 100644 --- a/test/data/toBundleExamples/circular_reference/expected.json +++ b/test/data/toBundleExamples/circular_reference/expected.json @@ -22,19 +22,17 @@ "operationId": "findPets", "responses": { "200": { - "description": "pet response", - "schema": { - "type": "array", - "items": { - "$ref\"": "./schemas/schemas.yaml" + "description": "An paged array of pets", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/_schemas_schemas.yaml-components_schemas_ErrorDetail" + } + } } } - }, - "default": { - "description": "unexpected error", - "schema": { - "$ref": "#/components/schemas/_schemas_schemas.yaml-components_schemas_ErrorDetail" - } } } } @@ -65,7 +63,8 @@ "readOnly": true, "type": "array", "items": { - "items": "- Circular" + "$ref": "#/components/schemas/_schemas_schemas.yaml-components_schemas_ErrorDetail", + "x-circularRef": true }, "description": "The error details." } diff --git a/test/data/toBundleExamples/circular_reference/root.yaml b/test/data/toBundleExamples/circular_reference/root.yaml index 329fa61..79fe8e2 100644 --- a/test/data/toBundleExamples/circular_reference/root.yaml +++ b/test/data/toBundleExamples/circular_reference/root.yaml @@ -12,22 +12,17 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - - paths: /pets: get: description: Returns all pets alesuada ac... operationId: findPets responses: - "200": - description: pet response - schema: - type: array - items: - $ref": "./schemas/schemas.yaml" - default: - description: unexpected error - schema: - $ref: "./schemas/schemas.yaml#components/schemas/ErrorDetail" - + '200': + description: An paged array of pets + content: + application/json: + schema: + type: array + items: + $ref: "./schemas/schemas.yaml#components/schemas/ErrorDetail" From bc22ae21e68f7b0e5ab3521a8d8cc0cf7539c9f0 Mon Sep 17 00:00:00 2001 From: Erik Mendoza Date: Mon, 11 Jul 2022 14:26:22 -0500 Subject: [PATCH 56/56] Fix validateSupportedVersion test to support swagger 2.0 --- test/unit/versionUtils.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/versionUtils.test.js b/test/unit/versionUtils.test.js index 050f3e6..90b956c 100644 --- a/test/unit/versionUtils.test.js +++ b/test/unit/versionUtils.test.js @@ -340,9 +340,9 @@ describe('validateSupportedVersion method', function () { expect(result).to.be.true; }); - it('should return false with version 2.0', function () { + it('should return true with version 2.0', function () { const result = validateSupportedVersion('2.0'); - expect(result).to.be.false; + expect(result).to.be.true; }); it('should return true with version 3.1', function () {