Adding bundle files proces

This commit is contained in:
Erik Mendoza
2022-05-20 16:50:07 -05:00
parent 6223594703
commit 4aa298b399
16 changed files with 472 additions and 188 deletions

View File

@@ -119,13 +119,112 @@ function getRootFileTrace(nodeParents) {
} }
/** /**
* Generates a trace from the root to the current item * Get partial content from file content
* @param {array} nodeTrace - The trace from the current file to the current element * @param {object} content - The content in related node
* @param {*} connector - The trace from the root's document context to the current file context * @param {string} partial - The partial part from reference
* @returns {array} The merged trace from the current item to the root's context * @returns {object} The related content to the trace
*/ */
function getTraceFromParent(nodeTrace, connector) { function getContentFromTrace(content, partial) {
return connector.concat(nodeTrace); partial = partial[0] === '/' ? partial.substring(1) : partial;
const trace = partial.split('/');
let currentValue = content;
for (let place of trace) {
currentValue = currentValue[place];
}
return currentValue;
}
/**
* Set a value in the global components object following the provided trace
* @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
* @returns {null} It modifies components global context
*/
function setValueInComponents(keyInComponents, components, value) {
let currentPlace = components,
target = keyInComponents[keyInComponents.length - 2],
referencedPart = keyInComponents[keyInComponents.length - 1],
[, local] = referencedPart.split('#'),
key = keyInComponents.length === 2 && keyInComponents[0] === 'schema' ?
keyInComponents[1] :
null;
if (keyInComponents[0] === 'schema') {
keyInComponents[0] = 'schemas';
target = key;
}
for (let place of keyInComponents) {
if (place === target) {
if (local) {
value = getContentFromTrace(value, local);
}
currentPlace[place] = value;
break;
}
else if (currentPlace[place]) {
currentPlace = currentPlace[place];
}
else {
currentPlace[place] = {};
currentPlace = currentPlace[place];
}
}
}
/**
* 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} parentFilename - The parent's filename
* @returns {array} The trace to the place where the $ref appears
*/
function getTraceFromParent(nodeContext, property, parentFilename) {
const parents = [...nodeContext.parents].reverse(),
key = nodeContext.key,
nodeParentsKey = [key, ...parents.map((parent) => {
return parent.key;
})],
nodeTrace = getRootFileTrace(nodeParentsKey),
cleanFileName = (filename) => {
const [file, local] = filename.split('#');
return [calculatePath(parentFilename, file), local];
},
[file, local] = cleanFileName(property.$ref),
keyInComponents = getKeyInComponents(nodeTrace, file, local);
return keyInComponents;
}
/**
* Returns the key trace if this node will be included in components else returns an empty array
* @param {array} nodeTrace - The trace from file to the $ref
* @returns {array} An arrat with the trace to the key in components
*/
function getKeyParent(nodeTrace) {
const componentsKeys = [
'schemas',
'schema',
'responses',
'parameters',
'examples',
'requestBodies',
'headers',
'securitySchemes',
'links',
'callbacks'
],
trace = [...nodeTrace].reverse();
let traceToKey = [];
for (let item of trace) {
traceToKey.push(item);
if (componentsKeys.includes(item)) {
break;
}
}
return traceToKey.length === trace.length ?
[] :
traceToKey.reverse();
} }
/** /**
@@ -135,11 +234,11 @@ function getTraceFromParent(nodeTrace, connector) {
* @param {Function} pathSolver - function to resolve the Path * @param {Function} pathSolver - function to resolve the Path
* @param {string} parentFilename - The parent's filename * @param {string} parentFilename - The parent's filename
* @param {object} globalComponentsContext - The global context from root file * @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} * @returns {object} - {path : $ref value}
*/ */
function getReferences (currentNode, refTypeResolver, pathSolver, parentFilename, globalComponentsContext) { function getReferences (currentNode, refTypeResolver, pathSolver, parentFilename, globalComponentsContext, allData) {
let referencesInNode = []; let referencesInNode = [];
traverseUtility(currentNode).forEach(function (property) { traverseUtility(currentNode).forEach(function (property) {
if (property) { if (property) {
let hasReferenceTypeKey; let hasReferenceTypeKey;
@@ -150,66 +249,39 @@ function getReferences (currentNode, refTypeResolver, pathSolver, parentFilename
} }
); );
if (hasReferenceTypeKey) { if (hasReferenceTypeKey) {
const parents = [...this.parents].reverse(), const nodeTrace = getTraceFromParent(this, property, parentFilename),
key = this.key, keyParent = getKeyParent(nodeTrace),
nodeParentsKey = [key, ...parents.map((parent) => {
return parent.key;
})],
nodeTrace = getRootFileTrace(nodeParentsKey),
connectorFromParent = globalComponentsContext[parentFilename] ?
globalComponentsContext[parentFilename].connector :
[],
traceFromParent = getTraceFromParent(nodeTrace, connectorFromParent),
cleanFileName = (filename) => {
const [file, local] = filename.split('#');
return [calculatePath(parentFilename, file), local];
},
[file, local] = cleanFileName(property.$ref),
newValue = Object.assign({}, this.node),
keyInComponents = getKeyInComponents(traceFromParent, file, local, connectorFromParent),
referenceInDocument = getJsonPointerRelationToRoot( referenceInDocument = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
file,
property.$ref, property.$ref,
traceFromParent keyParent
); );
let newValue,
nodeData = findNodeFromPath(calculatePath(parentFilename, property.$ref), allData).content,
nodeContent = nodeData ?
parse.getOasObject(nodeData).oasObject :
{ $missedReference: `property ${property.$ref} was not provided in data` };
newValue.$ref = referenceInDocument; if (keyParent.length === 0) {
this.update(newValue); newValue = nodeContent;
if (globalComponentsContext[file]) {
globalComponentsContext[file].isFull =
globalComponentsContext[file].isFull && !local;
if (local) {
globalComponentsContext[file].partialCalled.push(local);
}
} }
else { else {
globalComponentsContext[file] = { newValue = Object.assign({}, this.node);
calledFrom: parentFilename, newValue.$ref = referenceInDocument;
connector: keyInComponents,
isFull: !local,
partialsCalled: local ? [local] : [],
referenceInDocument,
content: this.node
};
} }
globalComponentsContext[property.$ref] = {
newValue: newValue,
keyInComponents: keyParent,
nodeContent
};
if (!added(property.$ref, referencesInNode)) { if (!added(property.$ref, referencesInNode)) {
referencesInNode.push({ path: pathSolver(property), keyInComponents }); referencesInNode.push({ path: pathSolver(property), keyInComponents: keyParent, newValue: this.node });
} }
} }
} }
}); });
if (globalComponentsContext[parentFilename]) {
globalComponentsContext[parentFilename].content = currentNode.oasObject;
}
else {
globalComponentsContext[parentFilename] = {
isRoot: true,
filename: parentFilename,
content: currentNode.oasObject
};
}
return referencesInNode; return referencesInNode;
} }
@@ -223,17 +295,16 @@ function getReferences (currentNode, refTypeResolver, pathSolver, parentFilename
*/ */
function getAdjacentAndMissingToBundle (currentNode, allData, specRoot, globalComponentsContext) { function getAdjacentAndMissingToBundle (currentNode, allData, specRoot, globalComponentsContext) {
let currentNodeReferences, let currentNodeReferences,
currentContent = currentNode.content,
graphAdj = [], graphAdj = [],
missingNodes = [], missingNodes = [],
bundleDataInAdjacent = [], bundleDataInAdjacent = [],
OASObject; OASObject;
if (currentContent.parsed) { if (currentNode.parsed) {
OASObject = currentNode.parsed; OASObject = currentNode.parsed.oasObject;
} }
else { else {
OASObject = parse.getOasObject(currentContent); OASObject = parse.getOasObject(currentNode.content).oasObject;
} }
currentNodeReferences = getReferences( currentNodeReferences = getReferences(
@@ -241,14 +312,15 @@ function getAdjacentAndMissingToBundle (currentNode, allData, specRoot, globalCo
isExtRef, isExtRef,
removeLocalReferenceFromPath, removeLocalReferenceFromPath,
currentNode.fileName, currentNode.fileName,
globalComponentsContext globalComponentsContext,
allData
); );
currentNodeReferences.forEach((reference) => { currentNodeReferences.forEach((reference) => {
let referencePath = reference.path, let referencePath = reference.path,
adjacentNode = findNodeFromPath(calculatePath(currentNode.fileName, referencePath), allData); adjacentNode = findNodeFromPath(calculatePath(currentNode.fileName, referencePath), allData);
if (adjacentNode) { if (adjacentNode) {
bundleDataInAdjacent.push({ reference, adjacentNode, currentNode });
graphAdj.push(adjacentNode); graphAdj.push(adjacentNode);
} }
else if (!comparePaths(referencePath, specRoot.fileName)) { else if (!comparePaths(referencePath, specRoot.fileName)) {
@@ -267,56 +339,39 @@ function getAdjacentAndMissingToBundle (currentNode, allData, specRoot, globalCo
return { graphAdj, missingNodes, bundleDataInAdjacent, currentNode }; return { graphAdj, missingNodes, bundleDataInAdjacent, currentNode };
} }
// function fillExistentComponents(components, componentsObject) {
// Object.keys(components).forEach((key) => {
// componentsObject[key] = components[key];
// });
// return componentsObject;
// }
/**
* Convert the current key data in document context to an item in components object
* @param {array} namesArray - The conector from root related with the current item
* @param {object} target - The components global object where the result will be added
* @param {string} dataKey - The current key in the document context
* @param {object} documentContext - The document context data necesary to generate the component's items
* @returns {object} The object related with the current key in document context
*/
function convert(namesArray, target, dataKey, documentContext) {
let result = target,
nestedObj = result;
for (let [index, name] of namesArray.entries()) {
let nextName = namesArray[index + 1];
if (documentContext[name]) {
continue;
}
else if (documentContext[nextName]) {
nestedObj[name] = documentContext[nextName].content;
}
else if (!nestedObj[name]) {
nestedObj[name] = {};
}
nestedObj = nestedObj[name];
}
return result;
}
/** /**
* Generates the components object from the documentContext data * Generates the components object from the documentContext data
* @param {object} documentContext The document context from root * @param {object} documentContext The document context from root
* @param {string} rootFilename - The root's filename * @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
* @returns {object} The components object related to the file * @returns {object} The components object related to the file
*/ */
function generateComponentsObject (documentContext, rootFilename) { function generateComponentsObject (documentContext, rootContent, refTypeResolver, components) {
let components = {}; [rootContent, components].forEach((contentData) => {
Object.keys(documentContext).forEach((dataKey) => { traverseUtility(contentData).forEach(function (property) {
if (dataKey === rootFilename) { if (property) {
return; let hasReferenceTypeKey;
} hasReferenceTypeKey = Object.keys(property)
convert(documentContext[dataKey].connector, components, dataKey, documentContext); .find(
(key) => {
return refTypeResolver(property, key);
}
);
if (hasReferenceTypeKey) {
let refData = documentContext[property.$ref];
this.update(refData.newValue);
if (refData.keyInComponents.length > 0) {
setValueInComponents(
refData.keyInComponents,
components,
refData.nodeContent
);
}
}
}
});
}); });
return components;
} }
module.exports = { module.exports = {
@@ -332,13 +387,18 @@ module.exports = {
path = pathBrowserify; path = pathBrowserify;
} }
let algorithm = new DFS(), let algorithm = new DFS(),
globalComponentsContext = {}; globalComponentsContext = {},
components = {};
algorithm.traverseAndBundle(specRoot, (currentNode) => { algorithm.traverseAndBundle(specRoot, (currentNode) => {
return getAdjacentAndMissingToBundle(currentNode, allData, specRoot, globalComponentsContext); return getAdjacentAndMissingToBundle(currentNode, allData, specRoot, globalComponentsContext);
}); });
return generateComponentsObject(globalComponentsContext, specRoot.fileName); generateComponentsObject(globalComponentsContext, specRoot.parsed.oasObject, isExtRef, components);
return {
fileContent: specRoot.parsed.oasObject,
components
};
}, },
bundleFiles: function(data) { bundleFiles: function(data) {
@@ -351,7 +411,7 @@ module.exports = {
Object.keys(bundleData).forEach((key) => { Object.keys(bundleData).forEach((key) => {
if (bundleData[key].hasOwnProperty('components')) { if (bundleData[key].hasOwnProperty('components')) {
if (componentsFromFile) { if (componentsFromFile) {
throw new Error('Muyltiple components definition through your files'); throw new Error('Multiple components definition through your files');
} }
components = fillExistentComponents(bundleData.key.components, components); components = fillExistentComponents(bundleData.key.components, components);
componentsFromFile = true; componentsFromFile = true;

View File

@@ -30,7 +30,6 @@ class DFS {
} }
traverseAndBundle(node, getAdjacentAndBundle) { traverseAndBundle(node, getAdjacentAndBundle) {
const mainNode = node;
let traverseOrder = [], let traverseOrder = [],
stack = [], stack = [],
missing = [], missing = [],
@@ -42,9 +41,9 @@ class DFS {
if (!visited.has(node)) { if (!visited.has(node)) {
traverseOrder.push(node); traverseOrder.push(node);
visited.add(node); visited.add(node);
let { graphAdj, missingNodes } = getAdjacentAndBundle(node); let { graphAdj, missingNodes, bundleDataInAdjacent } = getAdjacentAndBundle(node);
missing.push(...missingNodes); missing.push(...missingNodes);
bundleData.push(bundleData); bundleData.push(...bundleDataInAdjacent);
for (let j = 0; j < graphAdj.length; j++) { for (let j = 0; j < graphAdj.length; j++) {
stack.push(graphAdj[j]); stack.push(graphAdj[j]);
} }
@@ -59,7 +58,7 @@ class DFS {
].map((str) => { ].map((str) => {
return JSON.parse(str); return JSON.parse(str);
}); });
return { traverseOrder, missing, bundleData, mainNode }; return { traverseOrder, missing, bundleData };
} }
} }

View File

@@ -34,20 +34,13 @@ function jsonPointerDecodeAndReplace(filePathName) {
* @param {string} localPath the local path that the pointer will reach * @param {string} localPath the local path that the pointer will reach
* @returns {Array} - the calculated keys in an array representing each nesting property name * @returns {Array} - the calculated keys in an array representing each nesting property name
*/ */
function getKeyInComponents(traceFromParent, filePathName) { function getKeyInComponents(traceFromParent, filePathName, localPath) {
const localPart = localPath ? `#${localPath}` : '';
let res = traceFromParent; let res = traceFromParent;
res.push(jsonPointerDecodeAndReplace(filePathName)); // .map((key) => {
// TODOE: Add local support // return key.split('#')[0];
// if (localPath) { // });
// if (localPath.startsWith(jsonPointerLevelSeparator)) { res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`));
// localPathToCheck = localPath.substring(1);
// }
// pointer = localPathToCheck.split(jsonPointerLevelSeparator);
// for (let i = 0; i < pointer.length; i++) {
// pointer[i] = jsonPointerDecodeAndReplace(pointer[i]);
// }
// res.push(...pointer);
// }
return res; return res;
} }
@@ -72,18 +65,11 @@ function getLocalPath(jsonPointer) {
* @param {string} localPath the local path that the pointer will reach * @param {string} localPath the local path that the pointer will reach
* @returns {string} - the concatenated json pointer * @returns {string} - the concatenated json pointer
*/ */
function concatJsonPointer(encodeFunction, filePathName, traceFromParent, localPath) { function concatJsonPointer(encodeFunction, traceFromParent) {
let local = '', const traceFromParentAsString = traceFromParent.map((trace) => {
// base = '', return encodeFunction(trace);
traceFromParentAsString = traceFromParent.map((trace) => { }).join('/');
return encodeFunction(trace); return localPointer + '/components' + jsonPointerLevelSeparator + traceFromParentAsString;
}).join('/');
// TODOE: local support
// base = jsonPointerLevelSeparator + encodeFunction(filePathName);
if (localPath) {
local = `${localPath}`;
}
return localPointer + jsonPointerLevelSeparator + traceFromParentAsString + local;
} }
/** /**
@@ -95,12 +81,12 @@ function concatJsonPointer(encodeFunction, filePathName, traceFromParent, localP
* @param {string} traceFromParent the trace from the parent node. * @param {string} traceFromParent the trace from the parent node.
* @returns {string} - the concatenated json pointer * @returns {string} - the concatenated json pointer
*/ */
function getJsonPointerRelationToRoot(encodeFunction, filePathName, refValue, traceFromParent) { function getJsonPointerRelationToRoot(encodeFunction, refValue, traceFromKey) {
if (refValue.startsWith(localPointer)) { if (refValue.startsWith(localPointer)) {
return refValue; return refValue;
} }
const localPath = getLocalPath(refValue); const localPath = getLocalPath(refValue);
return concatJsonPointer(encodeFunction, filePathName, traceFromParent, localPath); return concatJsonPointer(encodeFunction, traceFromKey, localPath);
} }
/** /**

View File

@@ -4826,13 +4826,15 @@ module.exports = {
return data; return data;
}, },
getComponentsObject(parsedRootFiles, inputData, origin, version) { getBundledFileData(parsedRootFiles, inputData, origin, version) {
const data = parsedRootFiles.map((root) => { const data = parsedRootFiles.map((root) => {
let calledAs = [], let calledAs = [],
componentsData = getRelatedFilesAndBundleData(root, inputData, origin, version, calledAs); bundleData = getRelatedFilesAndBundleData(root, inputData, origin, version, calledAs);
return componentsData; return bundleData;
}); });
return data; let bundledFile = data[0].fileContent;
bundledFile.components = data[0].components;
return { rootFile: { path: parsedRootFiles[0].fileName }, bundledContent: bundledFile };
}, },
/** /**
@@ -4855,9 +4857,10 @@ module.exports = {
return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi); return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi);
}), }),
data = toBundle ? data = toBundle ?
this.getComponentsObject(parsedRootFiles, inputData, origin, version) : this.getBundledFileData(parsedRootFiles, inputData, origin, version) :
this.getRelatedFilesData(parsedRootFiles, inputData, origin); this.getRelatedFilesData(parsedRootFiles, inputData, origin);
return data; return data;
}, },
/** /**
@@ -4874,7 +4877,7 @@ module.exports = {
res = { res = {
result: true, result: true,
output: { output: {
type: toBundle ? 'bundle' : 'relatedFiles', type: toBundle ? 'bundledContent' : 'relatedFiles',
specification: { specification: {
type: 'OpenAPI', type: 'OpenAPI',
version: version version: version

View File

@@ -0,0 +1,6 @@
type: object
properties:
id:
type: integer
toyName:
type: string

View File

@@ -0,0 +1,8 @@
NotFound:
content:
application/json:
schema:
type: object
properties:
message:
type: string

View File

@@ -0,0 +1,50 @@
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
parameters: ...
responses:
200:
description: A single user.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
404:
$ref: "#/components/responses/NotFound"
/users:
get:
summary: Get all users
responses:
200:
description: A list of users.
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
components:
responses:
$ref: "./responses.yaml"
schemas:
Monster:
$ref: "./schemas/index.yaml#/Monster"
Dog:
$ref: "./schemas/index.yaml#/Dog"
Toy:
$ref: "./otherSchemas/toy.yaml"

View File

@@ -0,0 +1,7 @@
Client:
type: object
properties:
id:
type: integer
clientName:
type: string

View File

@@ -0,0 +1,21 @@
User:
type: object
properties:
id:
type: integer
userName:
type: string
Monster:
type: object
properties:
id:
type: integer
clientName:
type: string
Dog:
type: object
properties:
id:
type: integer
clientName:
type: string

View File

@@ -2,6 +2,13 @@ User:
$ref: "./user.yaml" $ref: "./user.yaml"
Monster: Monster:
type: object
properties:
id:
type: integer
clientName:
type: string
Dog:
type: object type: object
properties: properties:
id: id:

View File

@@ -42,6 +42,7 @@ components:
$ref: "./responses.yaml" $ref: "./responses.yaml"
schemas: schemas:
$ref: "./schemas/index.yaml" User:
$ref: "./schemas/index.yaml"
Toy: Toy:
$ref: "./otherSchemas/toy.yaml" $ref: "./otherSchemas/toy.yaml"

View File

@@ -0,0 +1,50 @@
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
parameters: ...
responses:
200:
description: A single user.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
404:
$ref: "#/components/responses/NotFound"
/users:
get:
summary: Get all users
responses:
200:
description: A list of users.
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
components:
responses:
$ref: "./responses.yaml"
schemas:
Monster:
$ref: "./schemas/index.yaml#/Monster"
Dog:
$ref: "./schemas/index.yaml#/Dog"
Toy:
$ref: "./otherSchemas/toy.yaml"

View File

@@ -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"

View File

@@ -0,0 +1,6 @@
type: object
properties:
id:
type: integer
userName:
type: string

View File

@@ -3,18 +3,48 @@ let expect = require('chai').expect,
fs = require('fs'), fs = require('fs'),
path = require('path'), path = require('path'),
BUNDLES_FOLDER = '../data/toBundleExamples', BUNDLES_FOLDER = '../data/toBundleExamples',
swaggerToBundleFolder = path.join(__dirname, BUNDLES_FOLDER + '/swagger-multi-file'); swaggerMultifileFolder = path.join(__dirname, BUNDLES_FOLDER + '/swagger-multi-file'),
localRefFolder = path.join(__dirname, BUNDLES_FOLDER + '/local_ref'),
easyFolder = path.join(__dirname, BUNDLES_FOLDER + '/swagger-multi-file_easy');
describe('detectRelatedFiles method', function () { describe('bundle files method', function () {
it('Should return bundled file with an schema called from a response', async function () {
let contentRootFile = fs.readFileSync(easyFolder + '/root.yaml', 'utf8'),
user = fs.readFileSync(easyFolder + '/schemas/user.yaml', 'utf8'),
input = {
type: 'folder',
specificationVersion: '3.0',
rootFiles: [
{
path: '/root.yaml',
content: contentRootFile
}
],
data: [
{
path: '/schemas/user.yaml',
content: user
}
]
};
const res = await Converter.bundle(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
expect(res.output.data.bundledContent.paths['/users/{userId}'].get.responses['200']
.content['application/json'].schema.$ref)
.to.be.equal('#/components/schema/~1schemas~1user.yaml');
expect(Object.keys(res.output.data.bundledContent.components.schemas['/schemas/user.yaml']))
.to.have.members(['type', 'properties']);
});
it('Should return bundled file', async function () { it('Should return bundled file from root with components with', async function () {
let contentRootFile = fs.readFileSync(swaggerToBundleFolder + '/v1.yaml', 'utf8'), let contentRootFile = fs.readFileSync(swaggerMultifileFolder + '/v1.yaml', 'utf8'),
responses = fs.readFileSync(swaggerToBundleFolder + '/responses.yaml', 'utf8'), responses = fs.readFileSync(swaggerMultifileFolder + '/responses.yaml', 'utf8'),
schemasIndex = fs.readFileSync(swaggerToBundleFolder + '/schemas/index.yaml', 'utf8'), schemasIndex = fs.readFileSync(swaggerMultifileFolder + '/schemas/index.yaml', 'utf8'),
schemasUser = fs.readFileSync(swaggerToBundleFolder + '/schemas/user.yaml', 'utf8'), schemasUser = fs.readFileSync(swaggerMultifileFolder + '/schemas/user.yaml', 'utf8'),
schemasClient = fs.readFileSync(swaggerToBundleFolder + '/schemas/client.yaml', 'utf8'), schemasClient = fs.readFileSync(swaggerMultifileFolder + '/schemas/client.yaml', 'utf8'),
toySchema = fs.readFileSync(swaggerToBundleFolder + '/otherSchemas/toy.yaml', 'utf8'), toySchema = fs.readFileSync(swaggerMultifileFolder + '/otherSchemas/toy.yaml', 'utf8'),
userProps = fs.readFileSync(swaggerToBundleFolder + '/userProps.yaml', 'utf8'), userProps = fs.readFileSync(swaggerMultifileFolder + '/userProps.yaml', 'utf8'),
input = { input = {
type: 'folder', type: 'folder',
specificationVersion: '3.0', specificationVersion: '3.0',
@@ -55,4 +85,43 @@ describe('detectRelatedFiles method', function () {
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
expect(res.result).to.be.true; expect(res.result).to.be.true;
}); });
it('Should return bundled file from a file with local references', async function () {
let contentRootFile = fs.readFileSync(localRefFolder + '/root.yaml', 'utf8'),
responses = fs.readFileSync(localRefFolder + '/responses.yaml', 'utf8'),
schemasIndex = fs.readFileSync(localRefFolder + '/schemas/index.yaml', 'utf8'),
schemasClient = fs.readFileSync(localRefFolder + '/schemas/client.yaml', 'utf8'),
toySchema = fs.readFileSync(localRefFolder + '/otherSchemas/toy.yaml', 'utf8'),
input = {
type: 'folder',
specificationVersion: '3.0',
rootFiles: [
{
path: '/root.yaml',
content: contentRootFile
}
],
data: [
{
path: '/responses.yaml',
content: responses
},
{
path: '/schemas/index.yaml',
content: schemasIndex
},
{
path: '/schemas/client.yaml',
content: schemasClient
},
{
path: '/otherSchemas/toy.yaml',
content: toySchema
}
]
};
const res = await Converter.bundle(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
});
}); });

View File

@@ -14,17 +14,14 @@ describe('getKeyInComponents function', function () {
expect(result[2]).to.equal('pet.yaml'); expect(result[2]).to.equal('pet.yaml');
}); });
// TODOE: support this test it('should return ["components", "schemas", "pet.yaml", "definitions", "world"] when is pointing to a local ref',
// it('should return ["components", "schemas", "pet.yaml", "definitions", "world"] when is pointing to a local ref', function () {
// function () { const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world');
// const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world'); expect(result.length).to.equal(3);
// expect(result.length).to.equal(5); expect(result[0]).to.equal('components');
// expect(result[0]).to.equal('components'); expect(result[1]).to.equal('schemas');
// expect(result[1]).to.equal('schemas'); expect(result[2]).to.equal('pet.yaml#/definitions/world');
// expect(result[2]).to.equal('pet.yaml'); });
// expect(result[3]).to.equal('definitions');
// expect(result[4]).to.equal('world');
// });
it('should return ["components", "schemas", "folder/pet.yaml"] when there is an scaped slash', function () { it('should return ["components", "schemas", "folder/pet.yaml"] when there is an scaped slash', function () {
const result = getKeyInComponents(['components', 'schemas'], 'folder~1pet.yaml'); const result = getKeyInComponents(['components', 'schemas'], 'folder~1pet.yaml');
@@ -41,26 +38,23 @@ describe('getJsonPointerRelationToRoot function', function () {
let res = getJsonPointerRelationToRoot( let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'Pets.yaml', 'Pets.yaml',
'Pets.yaml', ['schemas', 'Pets.yaml']
['components', 'schemas', 'Pets.yaml']
); );
expect(res).to.equal('#/components/schemas/Pets.yaml'); expect(res).to.equal('#/components/schemas/Pets.yaml');
}); });
it('should return "#/components/schemas/hello.yaml/definitions/world" no local path and schema', function () { it('should return "#/components/schemas/hello.yaml" no local path and schema', function () {
let res = getJsonPointerRelationToRoot( let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'hello.yaml',
'hello.yaml#/definitions/world', 'hello.yaml#/definitions/world',
['components', 'schemas', 'hello.yaml'] ['schemas', 'hello.yaml']
); );
expect(res).to.equal('#/components/schemas/hello.yaml/definitions/world'); expect(res).to.equal('#/components/schemas/hello.yaml');
}); });
it('should return "#/components/schemas/Error" no file path', function () { it('should return "#/components/schemas/Error" no file path', function () {
let res = getJsonPointerRelationToRoot( let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'',
'#/components/schemas/Error', '#/components/schemas/Error',
['components', 'schemas'] ['components', 'schemas', 'Error']
); );
expect(res).to.equal('#/components/schemas/Error'); expect(res).to.equal('#/components/schemas/Error');
}); });
@@ -70,8 +64,7 @@ describe('concatJsonPointer function ', function () {
it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () { it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () {
let res = concatJsonPointer( let res = concatJsonPointer(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'Pets.yaml', ['schemas', 'Pets.yaml']
['components', 'schemas', 'Pets.yaml']
); );
expect(res).to.equal('#/components/schemas/Pets.yaml'); expect(res).to.equal('#/components/schemas/Pets.yaml');
}); });
@@ -79,34 +72,29 @@ describe('concatJsonPointer function ', function () {
it('should return "#/components/schemas/other~1Pets.yaml" no local path and schema folder in filename', function () { it('should return "#/components/schemas/other~1Pets.yaml" no local path and schema folder in filename', function () {
let res = concatJsonPointer( let res = concatJsonPointer(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'other/Pets.yaml', ['schemas', 'other/Pets.yaml']
['components', 'schemas', 'other/Pets.yaml']
); );
expect(res).to.equal('#/components/schemas/other~1Pets.yaml'); 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 () { it('should return "#/components/schemas/some~1Pet" no local path and schema folder in filename', function () {
let res = concatJsonPointer( let res = concatJsonPointer(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'some/Pet.yaml', ['schemas', 'some/Pet.yaml']
['components', 'schemas', 'some/Pet.yaml']
); );
expect(res).to.equal('#/components/schemas/some~1Pet.yaml'); expect(res).to.equal('#/components/schemas/some~1Pet.yaml');
}); });
it('should return "#/components/schemas/hello.yaml/definitions/world" no local path and schema', function () { it('should return "#/components/schemas/hello.yaml" no local path and schema', function () {
let res = concatJsonPointer( let res = concatJsonPointer(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'hello.yaml', ['schemas', 'hello.yaml']
['components', 'schemas', 'hello.yaml'],
'/definitions/world'
); );
expect(res).to.equal('#/components/schemas/hello.yaml/definitions/world'); 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/~1Pets.yaml" no local path and schema', function () {
let res = concatJsonPointer( let res = concatJsonPointer(
jsonPointerEncodeAndReplace, jsonPointerEncodeAndReplace,
'/Pets.yaml', ['schemas', '/Pets.yaml']
['components', 'schemas', '/Pets.yaml']
); );
expect(res).to.equal('#/components/schemas/~1Pets.yaml'); expect(res).to.equal('#/components/schemas/~1Pets.yaml');
}); });