Merge pull request #563 from postmanlabs/feature/bundleCollisionHandling3020

Feature/bundle collision handling3020
This commit is contained in:
Carlos-Veloz
2022-06-22 10:41:36 -05:00
committed by GitHub
21 changed files with 888 additions and 74 deletions

View File

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

View File

@@ -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;
}
/**

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
type: object
properties:
aCollisionedItem:
type: integer
userName:
type: string
randomString:
type: string

View File

@@ -0,0 +1,6 @@
type: object
properties:
otherCollisionedId:
type: integer
someData:
type: string

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
type: object
properties:
aCollisionedItem:
type: integer
userName:
type: string
randomString:
type: string

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
type: object
properties:
aCollisionedItem:
type: integer
userName:
type: string
randomString:
type: string

View File

@@ -0,0 +1,6 @@
type: object
properties:
otherCollisionedId:
type: integer
someData:
type: string

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
type: object
properties:
aCollisionedItem:
type: integer
userName:
type: string
randomString:
type: string

View File

@@ -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);

View File

@@ -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);
});
});

View File

@@ -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 () {