Adding components generation

This commit is contained in:
Erik Mendoza
2022-05-17 13:26:57 -05:00
parent d7cfba0b40
commit 6223594703
17 changed files with 709 additions and 66 deletions

View File

@@ -42,6 +42,11 @@ module.exports = {
return schema.detectRelatedFiles();
},
bundle: async function(input) {
var schema = new SchemaPack(input);
return schema.bundle();
},
// new API
SchemaPack
};

View File

@@ -9,11 +9,12 @@ module.exports = {
/**
* Parses an OAS string/object as a YAML or JSON
* @param {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON
* @param {object} options - The parsing options
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test
*/
parseSpec: function (openApiSpec) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation30X);
parseSpec: function (openApiSpec, options) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation30X, options);
},
/**

366
lib/bundle.js Normal file
View File

@@ -0,0 +1,366 @@
const {
isExtRef,
getKeyInComponents,
getJsonPointerRelationToRoot,
jsonPointerEncodeAndReplace
} = require('./jsonPointer'),
traverseUtility = require('traverse'),
parse = require('./parse.js');
let path = require('path'),
pathBrowserify = require('path-browserify'),
BROWSER = 'browser',
{ DFS } = require('./dfs');
/**
* Locates a referenced node from the data input by path
* @param {string} path1 - path1 to compare
* @param {string} path2 - path2 to compare
* @returns {boolean} - wheter is the same path
*/
function comparePaths(path1, path2) {
return path1 === path2;
}
/**
* Removes the local pointer inside a path
* aab.yaml#component returns aab.yaml
* @param {string} refValue - value of the $ref property
* @returns {string} - the calculated path only
*/
function removeLocalReferenceFromPath(refValue) {
if (refValue.$ref.includes('#')) {
return refValue.$ref.split('#')[0];
}
return refValue.$ref;
}
/**
* Calculates the path relative to parent
* @param {string} parentFileName - parent file name of the current node
* @param {string} referencePath - value of the $ref property
* @returns {object} - Detect root files result object
*/
function calculatePath(parentFileName, referencePath) {
let currentDirName = path.dirname(parentFileName),
refDirName = path.join(currentDirName, referencePath);
return refDirName;
}
/**
* Locates a referenced node from the data input by path
* @param {string} referencePath - value from the $ref property
* @param {Array} allData - array of { path, content} objects
* @returns {object} - Detect root files result object
*/
function findNodeFromPath(referencePath, allData) {
const partialComponents = referencePath.split('#');
let isPartial = partialComponents.length > 1,
node = allData.find((node) => {
if (isPartial) {
referencePath = partialComponents[0];
}
return comparePaths(node.fileName, referencePath);
});
if (node) {
node.isPartial = isPartial;
node.partialCalled = partialComponents[1];
}
return node;
}
/**
* Calculates the path relative to parent
* @param {string} parentFileName - parent file name of the current node
* @param {string} referencePath - value of the $ref property
* @returns {object} - Detect root files result object
*/
function calculatePathMissing(parentFileName, referencePath) {
let currentDirName = path.dirname(parentFileName),
refDirName = path.join(currentDirName, referencePath);
if (refDirName.startsWith('..' + path.sep)) {
return { path: undefined, $ref: referencePath };
}
else if (path.isAbsolute(parentFileName) && !path.isAbsolute(referencePath)) {
let relativeToRoot = path.join(currentDirName.replace(path.sep, ''), referencePath);
if (relativeToRoot.startsWith('..' + path.sep)) {
return { path: undefined, $ref: referencePath };
}
}
return { path: refDirName, $ref: undefined };
}
/**
* 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;
}
/**
* Return a trace from the first parent node name attachable in components object
* @param {array} nodeParents - The parent node's name from the current node
* @returns {array} A trace from the first node name attachable in components object
*/
function getRootFileTrace(nodeParents) {
let trace = [];
for (let parentKey of nodeParents) {
if ([undefined, 'oasObject'].includes(parentKey)) {
break;
}
trace.push(parentKey);
}
return trace.reverse();
}
/**
* Generates a trace from the root to the current item
* @param {array} nodeTrace - The trace from the current file to the current element
* @param {*} connector - The trace from the root's document context to the current file context
* @returns {array} The merged trace from the current item to the root's context
*/
function getTraceFromParent(nodeTrace, connector) {
return connector.concat(nodeTrace);
}
/**
* 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
* @param {string} parentFilename - The parent's filename
* @param {object} globalComponentsContext - The global context from root file
* @returns {object} - {path : $ref value}
*/
function getReferences (currentNode, refTypeResolver, pathSolver, parentFilename, globalComponentsContext) {
let referencesInNode = [];
traverseUtility(currentNode).forEach(function (property) {
if (property) {
let hasReferenceTypeKey;
hasReferenceTypeKey = Object.keys(property)
.find(
(key) => {
return refTypeResolver(property, key);
}
);
if (hasReferenceTypeKey) {
const parents = [...this.parents].reverse(),
key = this.key,
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(
jsonPointerEncodeAndReplace,
file,
property.$ref,
traceFromParent
);
newValue.$ref = referenceInDocument;
this.update(newValue);
if (globalComponentsContext[file]) {
globalComponentsContext[file].isFull =
globalComponentsContext[file].isFull && !local;
if (local) {
globalComponentsContext[file].partialCalled.push(local);
}
}
else {
globalComponentsContext[file] = {
calledFrom: parentFilename,
connector: keyInComponents,
isFull: !local,
partialsCalled: local ? [local] : [],
referenceInDocument,
content: this.node
};
}
if (!added(property.$ref, referencesInNode)) {
referencesInNode.push({ path: pathSolver(property), keyInComponents });
}
}
}
});
if (globalComponentsContext[parentFilename]) {
globalComponentsContext[parentFilename].content = currentNode.oasObject;
}
else {
globalComponentsContext[parentFilename] = {
isRoot: true,
filename: parentFilename,
content: currentNode.oasObject
};
}
return referencesInNode;
}
/**
* Maps the output from get root files to detect root files
* @param {object} currentNode - current { path, content} object
* @param {Array} allData - array of { path, content} objects
* @param {object} specRoot - root file information
* @param {string} globalComponentsContext - the context from the global level
* @returns {object} - Detect root files result object
*/
function getAdjacentAndMissingToBundle (currentNode, allData, specRoot, globalComponentsContext) {
let currentNodeReferences,
currentContent = currentNode.content,
graphAdj = [],
missingNodes = [],
bundleDataInAdjacent = [],
OASObject;
if (currentContent.parsed) {
OASObject = currentNode.parsed;
}
else {
OASObject = parse.getOasObject(currentContent);
}
currentNodeReferences = getReferences(
OASObject,
isExtRef,
removeLocalReferenceFromPath,
currentNode.fileName,
globalComponentsContext
);
currentNodeReferences.forEach((reference) => {
let referencePath = reference.path,
adjacentNode = findNodeFromPath(calculatePath(currentNode.fileName, referencePath), allData);
if (adjacentNode) {
bundleDataInAdjacent.push({ reference, adjacentNode, currentNode });
graphAdj.push(adjacentNode);
}
else if (!comparePaths(referencePath, specRoot.fileName)) {
let calculatedPathForMissing = calculatePathMissing(currentNode.fileName, referencePath);
if (!calculatedPathForMissing.$ref) {
missingNodes.push({ path: calculatedPathForMissing.path });
}
else {
missingNodes.push({ $ref: calculatedPathForMissing.$ref, path: null });
}
}
});
if (missingNodes.length > 0) {
throw new Error('Some files are missing, run detectRelatedFiles to get more detail');
}
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
* @param {object} documentContext The document context from root
* @param {string} rootFilename - The root's filename
* @returns {object} The components object related to the file
*/
function generateComponentsObject (documentContext, rootFilename) {
let components = {};
Object.keys(documentContext).forEach((dataKey) => {
if (dataKey === rootFilename) {
return;
}
convert(documentContext[dataKey].connector, components, dataKey, documentContext);
});
return components;
}
module.exports = {
/**
* Maps the output from get root files to detect root files
* @param {object} specRoot - root file information
* @param {Array} allData - array of { path, content} objects
* @param {Array} origin - process origin (BROWSER or node)
* @returns {object} - Detect root files result object
*/
getRelatedFilesAndBundleData: function (specRoot, allData, origin) {
if (origin === BROWSER) {
path = pathBrowserify;
}
let algorithm = new DFS(),
globalComponentsContext = {};
algorithm.traverseAndBundle(specRoot, (currentNode) => {
return getAdjacentAndMissingToBundle(currentNode, allData, specRoot, globalComponentsContext);
});
return generateComponentsObject(globalComponentsContext, specRoot.fileName);
},
bundleFiles: function(data) {
let { bundleData, missingRelatedFiles } = data[0],
components = {},
componentsFromFile = false;
if (missingRelatedFiles.length > 0) {
throw new Error(`There are ${missingRelatedFiles.length} missing files in yopur spec`);
}
Object.keys(bundleData).forEach((key) => {
if (bundleData[key].hasOwnProperty('components')) {
if (componentsFromFile) {
throw new Error('Muyltiple components definition through your files');
}
components = fillExistentComponents(bundleData.key.components, components);
componentsFromFile = true;
}
else {
components[key] = bundleData[key].content;
}
});
return components;
}
};

View File

@@ -229,6 +229,9 @@ module.exports = {
return obj;
}
}
if (options && options.partial) {
return obj;
}
// spec is a valid JSON object at this point

View File

@@ -28,6 +28,39 @@ class DFS {
});
return { traverseOrder, missing };
}
traverseAndBundle(node, getAdjacentAndBundle) {
const mainNode = node;
let traverseOrder = [],
stack = [],
missing = [],
visited = new Set(),
bundleData = [];
stack.push(node);
while (stack.length > 0) {
node = stack.pop();
if (!visited.has(node)) {
traverseOrder.push(node);
visited.add(node);
let { graphAdj, missingNodes } = getAdjacentAndBundle(node);
missing.push(...missingNodes);
bundleData.push(bundleData);
for (let j = 0; j < graphAdj.length; j++) {
stack.push(graphAdj[j]);
}
}
}
missing = [
...new Set(
missing.map((obj) => {
return JSON.stringify(obj);
})
)
].map((str) => {
return JSON.parse(str);
});
return { traverseOrder, missing, bundleData, mainNode };
}
}
module.exports = {

View File

@@ -2,7 +2,6 @@ const slashes = /\//g,
tildes = /~/g,
escapedSlash = /~1/g,
escapedSlashString = '~1',
componentsKey = 'components',
localPointer = '#',
escapedTilde = /~0/g,
jsonPointerLevelSeparator = '/',
@@ -30,27 +29,25 @@ function jsonPointerDecodeAndReplace(filePathName) {
/**
* returns the key that the object in components will have could be nested
* @param {string} componentName the type of component e.g. schemas, parameters, etc.
* @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
* @returns {Array} - the calculated keys in an array representing each nesting property name
*/
function getKeyInComponents(componentName, filePathName, localPath) {
let res = [componentsKey],
pointer,
localPathToCheck = localPath;
res.push(componentName);
function getKeyInComponents(traceFromParent, filePathName) {
let res = traceFromParent;
res.push(jsonPointerDecodeAndReplace(filePathName));
if (localPath) {
if (localPath.startsWith(jsonPointerLevelSeparator)) {
localPathToCheck = localPath.substring(1);
}
pointer = localPathToCheck.split(jsonPointerLevelSeparator);
for (let i = 0; i < pointer.length; i++) {
pointer[i] = jsonPointerDecodeAndReplace(pointer[i]);
}
res.push(...pointer);
}
// TODOE: Add local support
// if (localPath) {
// if (localPath.startsWith(jsonPointerLevelSeparator)) {
// 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;
}
@@ -71,19 +68,22 @@ function getLocalPath(jsonPointer) {
* @constructor
* @param {Function} encodeFunction function to encode url
* @param {string} filePathName the filePathName of the file
* @param {string} componentName the type of component e.g. schemas, parameters, etc.
* @param {string} traceFromParent the trace from parent.
* @param {string} localPath the local path that the pointer will reach
* @returns {string} - the concatenated json pointer
*/
function concatJsonPointer(encodeFunction, filePathName, componentName, localPath) {
let base = '',
local = '';
base = jsonPointerLevelSeparator + encodeFunction(filePathName);
function concatJsonPointer(encodeFunction, filePathName, traceFromParent, localPath) {
let local = '',
// base = '',
traceFromParentAsString = traceFromParent.map((trace) => {
return encodeFunction(trace);
}).join('/');
// TODOE: local support
// base = jsonPointerLevelSeparator + encodeFunction(filePathName);
if (localPath) {
local = localPath;
local = `${localPath}`;
}
return localPointer + jsonPointerLevelSeparator + componentsKey +
jsonPointerLevelSeparator + componentName + base + local;
return localPointer + jsonPointerLevelSeparator + traceFromParentAsString + local;
}
/**
@@ -92,15 +92,15 @@ function concatJsonPointer(encodeFunction, filePathName, componentName, localPat
* @param {Function} encodeFunction function to encode url
* @param {string} filePathName the filePathName of the file
* @param {string} refValue the type of component e.g. schemas, parameters, etc.
* @param {string} componentName the type of component e.g. schemas, parameters, etc.
* @param {string} traceFromParent the trace from the parent node.
* @returns {string} - the concatenated json pointer
*/
function getJsonPointerRelationToRoot(encodeFunction, filePathName, refValue, componentName) {
function getJsonPointerRelationToRoot(encodeFunction, filePathName, refValue, traceFromParent) {
if (refValue.startsWith(localPointer)) {
return refValue;
}
const localPath = getLocalPath(refValue);
return concatJsonPointer(encodeFunction, filePathName, componentName, localPath);
return concatJsonPointer(encodeFunction, filePathName, traceFromParent, localPath);
}
/**

View File

@@ -89,7 +89,8 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X'),
{ getRelatedFiles } = require('./relatedFiles'),
{ compareVersion } = require('./common/versionUtils.js'),
parse = require('./parse');
parse = require('./parse'),
{ getRelatedFilesAndBundleData } = require('./bundle.js');
/* eslint-enable */
// See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options
@@ -4812,6 +4813,28 @@ module.exports = {
};
},
getRelatedFilesData(parsedRootFiles, inputData, origin) {
const data = parsedRootFiles.map((root) => {
let relatedData = getRelatedFiles(root, inputData, origin),
result = {
rootFile: { path: root.fileName },
relatedFiles: relatedData.relatedFiles,
missingRelatedFiles: relatedData.missingRelatedFiles
};
return result;
});
return data;
},
getComponentsObject(parsedRootFiles, inputData, origin, version) {
const data = parsedRootFiles.map((root) => {
let calledAs = [],
componentsData = getRelatedFilesAndBundleData(root, inputData, origin, version, calledAs);
return componentsData;
});
return data;
},
/**
*
* @description Takes in root files, input data and origin process every root
@@ -4820,24 +4843,20 @@ module.exports = {
* @param {array} inputData - [{path:string}]}
* @param {string} origin - process origin
* @param {string} version - process specification version
* @param {boolean} toBundle - if it will be used in bundle
*
* @returns {object} root files information and data input
*/
mapProcessRelatedFiles(rootFiles, inputData, origin, version) {
mapProcessRelatedFiles(rootFiles, inputData, origin, version, toBundle = false) {
let parsedRootFiles = rootFiles.map((rootFile) => {
let parsedContent = parse.getOasObject(rootFile.content);
return { fileName: rootFile.fileName, content: rootFile.content, parsed: parsedContent };
}).filter((rootWithParsedContent) => {
return compareVersion(version, rootWithParsedContent.parsed.oasObject.openapi);
}),
data = parsedRootFiles.map((root) => {
let { relatedFiles, missingRelatedFiles } = getRelatedFiles(root, inputData, origin);
return {
rootFile: { path: root.fileName },
relatedFiles,
missingRelatedFiles
};
});
data = toBundle ?
this.getComponentsObject(parsedRootFiles, inputData, origin, version) :
this.getRelatedFilesData(parsedRootFiles, inputData, origin);
return data;
},
@@ -4846,15 +4865,16 @@ module.exports = {
* @description Takes in a folder and identifies the related files from the
* root file perspective (using $ref property)
* @param {string} inputRelatedFiles - {rootFile:{path:string}, data: [{path:string}]}
* @param {boolean} toBundle - if true it will return the bundle data
*
* @returns {object} root files information and data input
*/
processRelatedFiles(inputRelatedFiles) {
processRelatedFiles(inputRelatedFiles, toBundle = false) {
let version = inputRelatedFiles.specificationVersion ? inputRelatedFiles.specificationVersion : '3.0.0',
res = {
result: true,
output: {
type: 'relatedFiles',
type: toBundle ? 'bundle' : 'relatedFiles',
specification: {
type: 'OpenAPI',
version: version
@@ -4865,9 +4885,12 @@ module.exports = {
};
if (inputRelatedFiles.rootFiles && inputRelatedFiles.rootFiles.length > 0) {
res.output.data = this.mapProcessRelatedFiles(inputRelatedFiles.rootFiles, inputRelatedFiles.data,
inputRelatedFiles.origin, version);
inputRelatedFiles.origin, version, toBundle);
return res;
}
else {
return res;
}
return res;
}
};

View File

@@ -1,8 +1,10 @@
'use strict';
// const { traverseToBundle } = require('./bundle.js');
// This is the default collection name if one can't be inferred from the OpenAPI spec
const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
{ getConcreteSchemaUtils } = require('./common/versionUtils.js'),
{ getConcreteSchemaUtils/* , getConcreteUtilsFromVersion*/ } = require('./common/versionUtils.js'),
{ convertSwaggerToOpenapi } = require('./swaggerUtils/swaggerToOpenapi.js'),
BROWSER = 'browser',
Ajv = require('ajv'),
@@ -30,6 +32,7 @@ const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
let path = require('path'),
concreteUtils,
pathBrowserify = require('path-browserify');
// traverseUtility = require('traverse');
class SchemaPack {
constructor (input, options = {}) {
@@ -682,6 +685,36 @@ class SchemaPack {
input.rootFiles = adaptedRootFiles;
return schemaUtils.processRelatedFiles(input);
}
/**
*
* @description Takes in a folder and identifies the related files from the
* root file perspective (using $ref property)
*
* @returns {object} root files information and data input
*/
async bundle() {
const input = this.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);
}
}
let adaptedRootFiles = input.rootFiles.map((rootFile) => {
return { fileName: rootFile.path, content: rootFile.content };
});
input.rootFiles = adaptedRootFiles;
return schemaUtils.processRelatedFiles(input, true);
}
}
module.exports = {

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,7 @@
Client:
type: object
properties:
id:
type: integer
clientName:
type: string

View File

@@ -0,0 +1,10 @@
User:
$ref: "./user.yaml"
Monster:
type: object
properties:
id:
type: integer
clientName:
type: string

View File

@@ -0,0 +1,3 @@
type: object
properties:
$ref: '../userProps.yaml'

View File

@@ -0,0 +1,4 @@
id:
type: integer
userName:
type: string

View File

@@ -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
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:
$ref: "./schemas/index.yaml"
Toy:
$ref: "./otherSchemas/toy.yaml"

58
test/unit/bundle.test.js Normal file
View File

@@ -0,0 +1,58 @@
let expect = require('chai').expect,
Converter = require('../../index.js'),
fs = require('fs'),
path = require('path'),
BUNDLES_FOLDER = '../data/toBundleExamples',
swaggerToBundleFolder = path.join(__dirname, BUNDLES_FOLDER + '/swagger-multi-file');
describe('detectRelatedFiles method', function () {
it('Should return bundled file', async function () {
let contentRootFile = fs.readFileSync(swaggerToBundleFolder + '/v1.yaml', 'utf8'),
responses = fs.readFileSync(swaggerToBundleFolder + '/responses.yaml', 'utf8'),
schemasIndex = fs.readFileSync(swaggerToBundleFolder + '/schemas/index.yaml', 'utf8'),
schemasUser = fs.readFileSync(swaggerToBundleFolder + '/schemas/user.yaml', 'utf8'),
schemasClient = fs.readFileSync(swaggerToBundleFolder + '/schemas/client.yaml', 'utf8'),
toySchema = fs.readFileSync(swaggerToBundleFolder + '/otherSchemas/toy.yaml', 'utf8'),
userProps = fs.readFileSync(swaggerToBundleFolder + '/userProps.yaml', 'utf8'),
input = {
type: 'folder',
specificationVersion: '3.0',
rootFiles: [
{
path: '/v1.yaml',
content: contentRootFile
}
],
data: [
{
path: '/responses.yaml',
content: responses
},
{
path: '/schemas/index.yaml',
content: schemasIndex
},
{
path: '/schemas/client.yaml',
content: schemasClient
},
{
path: '/schemas/user.yaml',
content: schemasUser
},
{
path: '/otherSchemas/toy.yaml',
content: toySchema
},
{
path: '/userProps.yaml',
content: userProps
}
]
};
const res = await Converter.bundle(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
});
});

View File

@@ -7,26 +7,27 @@ const expect = require('chai').expect,
describe('getKeyInComponents function', function () {
it('should return ["components", "schemas", "pet.yaml"] when is pointing the entire file', function () {
const result = getKeyInComponents('schemas', 'pet.yaml');
const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml');
expect(result.length).to.equal(3);
expect(result[0]).to.equal('components');
expect(result[1]).to.equal('schemas');
expect(result[2]).to.equal('pet.yaml');
});
it('should return ["components", "schemas", "pet.yaml", "definitions", "word"] when is pointing to a local ref',
function () {
const result = getKeyInComponents('schemas', 'pet.yaml', '/definitions/world');
expect(result.length).to.equal(5);
expect(result[0]).to.equal('components');
expect(result[1]).to.equal('schemas');
expect(result[2]).to.equal('pet.yaml');
expect(result[3]).to.equal('definitions');
expect(result[4]).to.equal('world');
});
// TODOE: support this test
// it('should return ["components", "schemas", "pet.yaml", "definitions", "world"] when is pointing to a local ref',
// function () {
// const result = getKeyInComponents(['components', 'schemas'], 'pet.yaml', '/definitions/world');
// expect(result.length).to.equal(5);
// expect(result[0]).to.equal('components');
// expect(result[1]).to.equal('schemas');
// 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 () {
const result = getKeyInComponents('schemas', 'folder~1pet.yaml');
const result = getKeyInComponents(['components', 'schemas'], 'folder~1pet.yaml');
expect(result.length).to.equal(3);
expect(result[0]).to.equal('components');
expect(result[1]).to.equal('schemas');
@@ -37,41 +38,76 @@ describe('getKeyInComponents function', function () {
describe('getJsonPointerRelationToRoot function', function () {
it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () {
let res = getJsonPointerRelationToRoot(jsonPointerEncodeAndReplace, 'Pets.yaml', 'Pets.yaml', 'schemas');
let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace,
'Pets.yaml',
'Pets.yaml',
['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 () {
let res = getJsonPointerRelationToRoot(jsonPointerEncodeAndReplace, 'hello.yaml', 'hello.yaml#/definitions/world',
'schemas');
let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace,
'hello.yaml',
'hello.yaml#/definitions/world',
['components', 'schemas', 'hello.yaml']
);
expect(res).to.equal('#/components/schemas/hello.yaml/definitions/world');
});
it('should return "#/components/schemas/Error" no file path', function () {
let res = getJsonPointerRelationToRoot(jsonPointerEncodeAndReplace, '', '#/components/schemas/Error', 'schemas');
let res = getJsonPointerRelationToRoot(
jsonPointerEncodeAndReplace,
'',
'#/components/schemas/Error',
['components', 'schemas']
);
expect(res).to.equal('#/components/schemas/Error');
});
});
describe('concatJsonPointer function ', function () {
it('should return "#/components/schemas/Pets.yaml" no local path and schema', function () {
let res = concatJsonPointer(jsonPointerEncodeAndReplace, 'Pets.yaml', 'schemas');
let res = concatJsonPointer(
jsonPointerEncodeAndReplace,
'Pets.yaml',
['components', 'schemas', 'Pets.yaml']
);
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 () {
let res = concatJsonPointer(jsonPointerEncodeAndReplace, 'other/Pets.yaml', 'schemas');
let res = concatJsonPointer(
jsonPointerEncodeAndReplace,
'other/Pets.yaml',
['components', 'schemas', 'other/Pets.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 () {
let res = concatJsonPointer(jsonPointerEncodeAndReplace, 'some/Pet.yaml', 'schemas');
let res = concatJsonPointer(
jsonPointerEncodeAndReplace,
'some/Pet.yaml',
['components', 'schemas', 'some/Pet.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 () {
let res = concatJsonPointer(jsonPointerEncodeAndReplace, 'hello.yaml', 'schemas', '/definitions/world');
let res = concatJsonPointer(
jsonPointerEncodeAndReplace,
'hello.yaml',
['components', 'schemas', 'hello.yaml'],
'/definitions/world'
);
expect(res).to.equal('#/components/schemas/hello.yaml/definitions/world');
});
it('should return "#/components/schemas/~1Pets.yaml" no local path and schema', function () {
let res = concatJsonPointer(jsonPointerEncodeAndReplace, '/Pets.yaml', 'schemas');
let res = concatJsonPointer(
jsonPointerEncodeAndReplace,
'/Pets.yaml',
['components', 'schemas', '/Pets.yaml']
);
expect(res).to.equal('#/components/schemas/~1Pets.yaml');
});