Merge pull request #536 from postmanlabs/fix/bundleRootWithContent

bundle only found content
This commit is contained in:
Erik Mendoza
2022-06-08 17:20:10 -05:00
committed by GitHub
7 changed files with 340 additions and 13 deletions

View File

@@ -92,7 +92,8 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc
{ getRelatedFiles } = require('./relatedFiles'), { getRelatedFiles } = require('./relatedFiles'),
{ compareVersion } = require('./common/versionUtils.js'), { compareVersion } = require('./common/versionUtils.js'),
parse = require('./parse'), parse = require('./parse'),
{ getBundleContentAndComponents } = require('./bundle.js'); { getBundleContentAndComponents } = require('./bundle.js'),
MULTI_FILE_API_TYPE_ALLOWED_VALUE = 'multiFile';
/* eslint-enable */ /* eslint-enable */
// See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options // See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options
@@ -4797,7 +4798,7 @@ module.exports = {
*/ */
mapGetRootFilesOutputToDetectRootFilesOutput(output, version) { mapGetRootFilesOutputToDetectRootFilesOutput(output, version) {
if (!version) { if (!version) {
version = '3.0.0'; version = '3.0';
} }
let adaptedData = output.map((file) => { let adaptedData = output.map((file) => {
return { path: file }; return { path: file };
@@ -4906,7 +4907,7 @@ module.exports = {
* @returns {object} root files information and data input * @returns {object} root files information and data input
*/ */
processRelatedFiles(inputRelatedFiles, toBundle = false) { processRelatedFiles(inputRelatedFiles, toBundle = false) {
let version = inputRelatedFiles.specificationVersion ? inputRelatedFiles.specificationVersion : '3.0.0', let version = inputRelatedFiles.specificationVersion ? inputRelatedFiles.specificationVersion : '3.0',
res = { res = {
result: true, result: true,
output: { output: {
@@ -4957,6 +4958,9 @@ module.exports = {
if (!processInput.type) { if (!processInput.type) {
throw new Error('"Type" parameter should be provided'); throw new Error('"Type" parameter should be provided');
} }
if (processInput.type !== MULTI_FILE_API_TYPE_ALLOWED_VALUE) {
throw new Error('"Type" parameter value allowed is ' + MULTI_FILE_API_TYPE_ALLOWED_VALUE);
}
if (!processInput.data || processInput.data.length === 0) { if (!processInput.data || processInput.data.length === 0) {
throw new Error('"Data" parameter should be provided'); throw new Error('"Data" parameter should be provided');
} }
@@ -4971,6 +4975,6 @@ module.exports = {
throw new ParseError(result.reason); throw new ParseError(result.reason);
} }
return result; return result;
} },
MULTI_FILE_API_TYPE_ALLOWED_VALUE
}; };

View File

@@ -34,7 +34,8 @@ let path = require('path'),
class SchemaPack { class SchemaPack {
constructor (input, options = {}) { constructor (input, options = {}) {
if (input.type === 'multiFile' && input.data && input.data[0] && input.data[0].path) { if (input.type === schemaUtils.MULTI_FILE_API_TYPE_ALLOWED_VALUE &&
input.data && input.data[0] && input.data[0].path) {
input = schemaUtils.mapDetectRootFilesInputToFolderInput(input); input = schemaUtils.mapDetectRootFilesInputToFolderInput(input);
} }
this.input = input; this.input = input;
@@ -674,16 +675,19 @@ class SchemaPack {
let adaptedRootFiles = input.rootFiles.map((rootFile) => { let adaptedRootFiles = input.rootFiles.map((rootFile) => {
let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; }); let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; });
if (!foundInData) { if (!foundInData) {
throw new Error('Root file content not found in data array'); return undefined;
} }
return { fileName: rootFile.path, content: foundInData.content }; return { fileName: rootFile.path, content: foundInData.content };
}); }).filter((rootFile) => { return rootFile !== undefined; });
if (adaptedRootFiles.length === 0) {
throw new Error('Root file content not found in data array');
}
input.rootFiles = adaptedRootFiles; input.rootFiles = adaptedRootFiles;
return schemaUtils.processRelatedFiles(input); return schemaUtils.processRelatedFiles(input);
} }
/** /**
* *
* @description Takes in a folder and identifies the related files from the * @description Takes in a folder and identifies the related files from the
@@ -700,11 +704,13 @@ class SchemaPack {
let adaptedRootFiles = input.rootFiles.map((rootFile) => { let adaptedRootFiles = input.rootFiles.map((rootFile) => {
let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; }); let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; });
if (!foundInData) { if (!foundInData) {
throw new Error('Root file content not found in data array'); return undefined;
} }
return { fileName: rootFile.path, content: foundInData.content }; return { fileName: rootFile.path, content: foundInData.content };
}); }).filter((rootFile) => { return rootFile !== undefined; });
if (adaptedRootFiles.length === 0) {
throw new Error('Root file content not found in data array');
}
input.rootFiles = adaptedRootFiles; input.rootFiles = adaptedRootFiles;
return schemaUtils.processRelatedFiles(input, true); return schemaUtils.processRelatedFiles(input, true);
} }

View File

@@ -0,0 +1,52 @@
{
"openapi": "3.1.0",
"info": {
"title": "Sample API",
"description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.",
"version": "0.1.9"
},
"servers": [
{
"url": "http://api.example.com/v1",
"description": "Optional server description, e.g. Main (production) server"
},
{
"url": "http://staging-api.example.com",
"description": "Optional server description, e.g. Internal staging server for testing"
}
],
"paths": {
"/users/{userId}": {
"get": {
"summary": "Get a user by ID",
"responses": {
"200": {
"description": "A single user.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/~1schemas~1user.yaml"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"/schemas/user.yaml": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"userName": {
"type": "string"
}
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
openapi: 3.1.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

@@ -54,6 +54,7 @@ describe('bundle files method - 3.0', function () {
const res = await Converter.bundle(input); const res = await Converter.bundle(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
expect(res.result).to.be.true; expect(res.result).to.be.true;
expect(res.output.specification.version).to.equal('3.0');
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
}); });
@@ -798,6 +799,7 @@ describe('bundle files method - 3.0', function () {
bundleFormat: 'JSON' bundleFormat: 'JSON'
}; };
const res = await Converter.bundle(input); const res = await Converter.bundle(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
expect(res.result).to.be.true; expect(res.result).to.be.true;
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
@@ -935,6 +937,185 @@ describe('bundle files method - 3.0', function () {
expect(error.message).to.equal('Root file content not found in data array'); expect(error.message).to.equal('Root file content not found in data array');
} }
}); });
it('Should return bundled 1 file with 2 root but 1 is missing', async function () {
let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'),
user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'),
expected = fs.readFileSync(schemaFromResponse + '/expected.json', '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: 'multiFile',
specificationVersion: '3.0',
rootFiles: [
{
path: '/root.yaml'
},
{
path: '/root2.yaml'
}
],
data: [
{
path: '/root.yaml',
content: contentRootFile
},
{
path: '/schemas/user.yaml',
content: user
},
{
path: '/responses.yaml',
content: responses
},
{
path: '/schemas/index.yaml',
content: schemasIndex
},
{
path: '/schemas/client.yaml',
content: schemasClient
},
{
path: '/otherSchemas/toy.yaml',
content: toySchema
}
],
options: {},
bundleFormat: 'JSON'
};
const res = await Converter.bundle(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
expect(res.output.data.length).to.equal(1);
});
it('Should bundle only the files with the specified version 3.1', async function () {
let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'),
contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'),
user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'),
expected = fs.readFileSync(schemaFromResponse + '/expected3_1.json', 'utf8'),
input = {
type: 'multiFile',
specificationVersion: '3.1',
rootFiles: [
{
path: '/root3_1.yaml'
},
{
path: '/root.yaml'
}
],
data: [
{
path: '/root.yaml',
content: contentRootFile
},
{
path: '/root3_1.yaml',
content: contentRootFile31
},
{
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.1');
expect(res.output.data.length).to.equal(1);
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
});
it('Should bundle only the files with the specified version 3.0', async function () {
let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'),
contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'),
user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'),
expected = fs.readFileSync(schemaFromResponse + '/expected.json', 'utf8'),
input = {
type: 'multiFile',
specificationVersion: '3.0',
rootFiles: [
{
path: '/root3_1.yaml'
},
{
path: '/root.yaml'
}
],
data: [
{
path: '/root.yaml',
content: contentRootFile
},
{
path: '/root3_1.yaml',
content: contentRootFile31
},
{
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(res.output.data.length).to.equal(1);
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
});
it('should bundle files to 3.0 when specificationVersion is not provided', async function () {
let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'),
contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'),
user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'),
expected = fs.readFileSync(schemaFromResponse + '/expected.json', 'utf8'),
input = {
type: 'multiFile',
rootFiles: [
{
path: '/root3_1.yaml'
},
{
path: '/root.yaml'
}
],
data: [
{
path: '/root.yaml',
content: contentRootFile
},
{
path: '/root3_1.yaml',
content: contentRootFile31
},
{
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(res.output.data.length).to.equal(1);
expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected);
});
}); });
@@ -1041,4 +1222,18 @@ describe('getReferences method when node does not have any reference', function(
expect(error.message).to.equal('"Data" parameter should be provided'); expect(error.message).to.equal('"Data" parameter should be provided');
} }
}); });
it('should return error when "type" parameter is not multiFile', async function () {
try {
await Converter.bundle({
type: 'folder',
bundleFormat: 'JSON',
data: []
});
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Type" parameter value allowed is multiFile');
}
});
}); });

View File

@@ -442,4 +442,51 @@ describe('detectRelatedFiles method', function () {
expect(error.message).to.equal('"Data" parameter should be provided'); expect(error.message).to.equal('"Data" parameter should be provided');
} }
}); });
it('Should throw error when root is not present in data array', async function () {
let contentFileHop = fs.readFileSync(validHopService31x, 'utf8'),
input = {
type: 'multiFile',
rootFiles: [
{
path: '/petstore.yaml'
}
],
data: [
{
path: '/hopService.yaml',
content: contentFileHop
}]
};
try {
await Converter.detectRelatedFiles(input);
}
catch (error) {
expect(error.message).to.equal('Root file content not found in data array');
}
});
it('Should return 1 file with 2 root but 1 is missing', async function () {
let contentFilePet = fs.readFileSync(validPetstore, 'utf8'),
input = {
type: 'multiFile',
rootFiles: [
{
path: '/petstore.yaml'
},
{
path: '/petstore2.yaml'
}
],
data: [{
path: '/petstore.yaml',
content: contentFilePet
}]
};
const res = await Converter.detectRelatedFiles(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
expect(res.output.data[0].rootFile.path).to.equal('/petstore.yaml');
expect(res.output.data.length).to.equal(1);
});
}); });

View File

@@ -32,7 +32,7 @@ describe('detectRoot 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;
expect(res.output.data[0].path).to.equal('/petstore.yaml'); expect(res.output.data[0].path).to.equal('/petstore.yaml');
expect(res.output.specification.version).to.equal('3.0.0'); expect(res.output.specification.version).to.equal('3.0');
}); });
it('should return one root 3.0 correctly', async function() { it('should return one root 3.0 correctly', async function() {