Fix input validation in multi file APIs

This commit is contained in:
Luis Tejeda
2022-06-06 11:23:39 -05:00
parent 243c9a1dba
commit 2e73ed22bb
5 changed files with 186 additions and 12 deletions

View File

@@ -4815,6 +4815,16 @@ module.exports = {
}; };
}, },
/**
*
* @description Takes in a the root files obtains the related files and
* generates the result object
* @param {object} parsedRootFiles - found parsed root files
* @param {array} inputData - file data information [{path, content}]
* @param {Array} origin - process origin (BROWSER or node)
*
* @returns {object} process result { rootFile, relatedFiles, missingRelatedFiles }
*/
getRelatedFilesData(parsedRootFiles, inputData, origin) { getRelatedFilesData(parsedRootFiles, inputData, origin) {
const data = parsedRootFiles.map((root) => { const data = parsedRootFiles.map((root) => {
let relatedData = getRelatedFiles(root, inputData, origin), let relatedData = getRelatedFiles(root, inputData, origin),
@@ -4828,6 +4838,17 @@ module.exports = {
return data; return data;
}, },
/*
*
* @description Takes in parsed root files and bundle it
* @param {object} parsedRootFiles - found parsed root files
* @param {array} inputData - file data information [{path, content}]
* @param {Array} origin - process origin (BROWSER or node)
* @param {string} format - output format could be either YAML or JSON
* @param {string} version - specification version specified in the input
*
* @returns {object} process result { rootFile, bundledContent }
*/
getBundledFileData(parsedRootFiles, inputData, origin, format) { getBundledFileData(parsedRootFiles, inputData, origin, format) {
const data = parsedRootFiles.map((root) => { const data = parsedRootFiles.map((root) => {
let bundleData = getBundleContentAndComponents(root, inputData, origin); let bundleData = getBundleContentAndComponents(root, inputData, origin);
@@ -4923,6 +4944,28 @@ module.exports = {
} }
}, },
/**
*
* @description Validates the input for multi file APIs
* @param {string} processInput - Process input data
*
* @returns {undefined} - nothing
*/
validateInputMultiFileAPI(processInput) {
if (_.isEmpty(processInput)) {
throw new Error('Input object must have "type" and "data" information');
}
if (!processInput.type) {
throw new Error('"Type" parameter should be provided');
}
if (!processInput.data || processInput.data.length === 0) {
throw new Error('"Data" parameter should be provided');
}
if (processInput.data[0].path === '') {
throw new Error('"Path" of the data element should be provided');
}
},
parseFileOrThrow(fileContent) { parseFileOrThrow(fileContent) {
const result = parse.getOasObject(fileContent); const result = parse.getOasObject(fileContent);
if (result.result === false) { if (result.result === false) {

View File

@@ -629,10 +629,8 @@ class SchemaPack {
*/ */
async detectRootFiles() { async detectRootFiles() {
const input = this.input; const input = this.input;
if (input.data[0].path === '') {
throw new Error('undefined input');
}
schemaUtils.validateInputMultiFileAPI(input);
if (!this.hasDefinedVersion && ('content' in input.data[0])) { if (!this.hasDefinedVersion && ('content' in input.data[0])) {
return schemaUtils.mapGetRootFilesOutputToDetectRootFilesOutput([], input.specificationVersion); return schemaUtils.mapGetRootFilesOutputToDetectRootFilesOutput([], input.specificationVersion);
} }
@@ -668,6 +666,8 @@ class SchemaPack {
*/ */
async detectRelatedFiles() { async detectRelatedFiles() {
const input = this.input; const input = this.input;
schemaUtils.validateInputMultiFileAPI(input);
if (!input.rootFiles || input.rootFiles.length === 0) { if (!input.rootFiles || input.rootFiles.length === 0) {
let rootFiles = await this.detectRootFiles(input); let rootFiles = await this.detectRootFiles(input);
if (rootFiles.output.data) { if (rootFiles.output.data) {
@@ -698,6 +698,8 @@ class SchemaPack {
*/ */
async bundle() { async bundle() {
const input = this.input; const input = this.input;
schemaUtils.validateInputMultiFileAPI(input);
if (!input.rootFiles || input.rootFiles.length === 0) { if (!input.rootFiles || input.rootFiles.length === 0) {
let rootFiles = await this.detectRootFiles(input); let rootFiles = await this.detectRootFiles(input);
if (rootFiles.output.data) { if (rootFiles.output.data) {

View File

@@ -806,4 +806,50 @@ describe('getReferences method when node does not have any reference', function(
expect(result.referencesInNode[0].path).to.equal('./user.yaml'); expect(result.referencesInNode[0].path).to.equal('./user.yaml');
expect(result.referencesInNode[0].newValue.$ref).to.equal('the/parent/user.yaml'); expect(result.referencesInNode[0].newValue.$ref).to.equal('the/parent/user.yaml');
}); });
it('should return error when "type" parameter is not sent', async function () {
let input = {
rootFiles: [
{
path: '/root.yaml',
content: ''
}
],
data: [
{
path: '/examples.yaml',
content: ''
}
],
options: {},
bundleFormat: 'JSON'
};
try {
await Converter.bundle(input);
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Type" parameter should be provided');
}
});
it('should return error when input is an empty object', async function () {
try {
await Converter.bundle({});
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('Input object must have "type" and "data" information');
}
});
it('should return error when input data is an empty array', async function () {
try {
await Converter.bundle({ type: 'folder', data: [] });
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Data" parameter should be provided');
}
});
}); });

View File

@@ -139,8 +139,7 @@ describe('detectRelatedFiles method', function () {
content: contentFileMissedRef content: contentFileMissedRef
} }
], ],
data: [ data: [{}]
]
}, },
res = await Converter.detectRelatedFiles(input); res = await Converter.detectRelatedFiles(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
@@ -242,8 +241,7 @@ describe('detectRelatedFiles method', function () {
content: contentFileHop content: contentFileHop
} }
], ],
data: [ data: [{}]
]
}; };
const res = await Converter.detectRelatedFiles(input); const res = await Converter.detectRelatedFiles(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
@@ -266,8 +264,7 @@ describe('detectRelatedFiles method', function () {
content: contentFileHop content: contentFileHop
} }
], ],
data: [ data: [{}]
]
}; };
const res = await Converter.detectRelatedFiles(input); const res = await Converter.detectRelatedFiles(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
@@ -286,8 +283,7 @@ describe('detectRelatedFiles method', function () {
content: contentFile content: contentFile
} }
], ],
data: [ data: [{}]
]
}; };
const res = await Converter.detectRelatedFiles(input); const res = await Converter.detectRelatedFiles(input);
expect(res).to.not.be.empty; expect(res).to.not.be.empty;
@@ -368,4 +364,50 @@ describe('detectRelatedFiles method', function () {
expect(res.output.data[0].missingRelatedFiles.length).to.equal(6); expect(res.output.data[0].missingRelatedFiles.length).to.equal(6);
}); });
it('should return error when "type" parameter is not sent', async function () {
let contentRootFile = fs.readFileSync(petstoreMultipleFiles, 'utf8'),
contentFileResPets = fs.readFileSync(resourcesPets, 'utf8'),
input = {
rootFiles: [
{
path: '/openapi.yaml',
content: contentRootFile
}
],
data: [
{
path: '/resources/pets.yaml',
content: contentFileResPets
}
]
};
try {
await Converter.detectRelatedFiles(input);
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Type" parameter should be provided');
}
});
it('should return error when input is an empty object', async function () {
try {
await Converter.detectRelatedFiles({});
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('Input object must have "type" and "data" information');
}
});
it('should return error when input data is an empty array', async function () {
try {
await Converter.detectRelatedFiles({ type: 'folder', data: [] });
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Data" parameter should be provided');
}
});
}); });

View File

@@ -218,7 +218,7 @@ describe('detectRoot method', function() {
await Converter.detectRootFiles(input); await Converter.detectRootFiles(input);
} }
catch (ex) { catch (ex) {
expect(ex.message).to.equal('undefined input'); expect(ex.message).to.equal('"Path" of the data element should be provided');
} }
}); });
@@ -266,4 +266,45 @@ describe('detectRoot method', function() {
}); });
it('should return error when "type" parameter is not sent', async function () {
let input = {
data: [
{
path: validPetstore
},
{
path: validHopService31x
}
]
};
try {
await Converter.detectRootFiles(input);
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Type" parameter should be provided');
}
});
it('should return error when input is an empty object', async function () {
try {
await Converter.detectRootFiles({});
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('Input object must have "type" and "data" information');
}
});
it('should return error when input data is an empty array', async function () {
try {
await Converter.detectRootFiles({ type: 'folder', data: [] });
}
catch (error) {
expect(error).to.not.be.undefined;
expect(error.message).to.equal('"Data" parameter should be provided');
}
});
}); });