Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/support for multifiles #136

Merged
merged 54 commits into from
Feb 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
7e963b0
added support for multifile
Dhroov7 Dec 4, 2019
3df9ba4
using eachSeries instead of each in async
Dhroov7 Dec 4, 2019
bcbdac9
remove tests of multifile to know why travis is failing
Dhroov7 Dec 4, 2019
df454cd
removed parse tests
Dhroov7 Dec 4, 2019
822a034
added validation for folder
Dhroov7 Dec 4, 2019
1e57ded
added one test and removed another because of travis failing
Dhroov7 Dec 4, 2019
ab5c51b
added path modules
Dhroov7 Dec 4, 2019
3e44749
commented getOasObject function
Dhroov7 Dec 4, 2019
abc948d
using const instead of var
Dhroov7 Dec 4, 2019
cf53c1c
removed speccy and using oas-resolver
Dhroov7 Dec 6, 2019
5a706af
added node-fetch module
Dhroov7 Dec 6, 2019
8ec38a0
commented parse.test-.js
Dhroov7 Dec 6, 2019
7dbce2c
commented one test in parse test
Dhroov7 Dec 6, 2019
683b3c6
remove outer describe
Dhroov7 Dec 6, 2019
a201d19
remove outer describe
Dhroov7 Dec 6, 2019
eeff01e
remove outer describe
Dhroov7 Dec 6, 2019
ef64a9c
remove outer describe
Dhroov7 Dec 6, 2019
d340c1e
commented all inner tests
Dhroov7 Dec 6, 2019
9a4e310
commented all inner tests
Dhroov7 Dec 6, 2019
be28e89
commented all inner tests
Dhroov7 Dec 6, 2019
0386c04
commented all inner tests
Dhroov7 Dec 6, 2019
cb12db0
commented third parse test
Dhroov7 Dec 6, 2019
8c31a7a
moved parse test to utils
Dhroov7 Dec 6, 2019
703a727
commented first and last comment
Dhroov7 Dec 6, 2019
991ba02
commented one test
Dhroov7 Dec 6, 2019
5e5d353
changed tests
Dhroov7 Dec 6, 2019
2cbaa1d
some changes
Dhroov7 Dec 6, 2019
8256209
removed unncessary callback from convert
Dhroov7 Dec 8, 2019
abf9962
changed the function name to mergeFiles
Dhroov7 Dec 9, 2019
e12a1af
consistency across checks in validateRoot
Dhroov7 Dec 9, 2019
5deb2c9
added check for https, changed behaviour of callback back to err thro…
Dhroov7 Dec 9, 2019
7e4eacf
added tests for function readSpecFile
Dhroov7 Dec 9, 2019
7e96690
added test for mergeFiles function
Dhroov7 Dec 9, 2019
08c3136
removed openapi spec from the result in validateRoot function
Dhroov7 Dec 9, 2019
fcd54b1
filtering collections from convertedSpec
Dhroov7 Dec 10, 2019
b349053
removed loop because convertedSpec can only has one collection
Dhroov7 Dec 10, 2019
d0fa30f
changed function name from returnCollectios to returnCollection
Dhroov7 Dec 10, 2019
654394d
removed eslint-disable line from parse.test.js
Dhroov7 Dec 10, 2019
33f9da6
returing callback with an error in mergeFiles catch
Dhroov7 Dec 10, 2019
3a89a0b
Merge branch 'develop' into feature/support_for_multifiles
umeshp7 Jan 7, 2020
9a419d8
Fix eslint
umeshp7 Jan 7, 2020
f15d44d
fix lint issues
umeshp7 Jan 7, 2020
f9adc10
Support for multi file schema in schemapack
umeshp7 Jan 14, 2020
897a5d9
Add tests for multi file support
umeshp7 Jan 14, 2020
738edf8
Use schemapack in tests, add handling for no root files
umeshp7 Jan 21, 2020
3f74a09
Change validating logic, remove validateRoot function
umeshp7 Jan 23, 2020
a2c665c
Reusing parsing logic for oas
umeshp7 Jan 24, 2020
3e0e55d
Remove readSpecFile function
umeshp7 Jan 24, 2020
3ebe86e
Add jsdoc for new functions in parse.js
umeshp7 Feb 5, 2020
ad36fca
Merge develop into feature/support-for-multifiles
umeshp7 Feb 5, 2020
f753524
Remove node-fetch from package.json
umeshp7 Feb 5, 2020
e64c7de
Fix error handling for mergeAndValidate
umeshp7 Feb 17, 2020
fd6cea9
Merge develop into feature/support_for_multifiles
umeshp7 Feb 17, 2020
f047d13
Proper handling of error in mergeAndValidate when mergeFile fails.
umeshp7 Feb 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ module.exports = {
if (schema.validated) {
return schema.convert(cb);
}

return cb(null, schema.validationResult);
},

validate: function(input) {
validate: function (input) {
var schema = new SchemaPack(input);
return schema.validationResult;
},

mergeAndValidate: function (input, cb) {
var schema = new SchemaPack(input);
schema.mergeAndValidate(cb);
},

getOptions: function() {
return SchemaPack.getOptions();
},
Expand Down
184 changes: 167 additions & 17 deletions lib/parse.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
var yaml = require('js-yaml');
var yaml = require('js-yaml'),
fs = require('fs'),
resolver = require('oas-resolver'),
yamlParse = require('yaml');

module.exports = {

asJson: function(spec) {
asJson: function (spec) {
try {
return JSON.parse(spec);
}
Expand All @@ -11,7 +14,7 @@ module.exports = {
}
},

asYaml: function(spec) {
asYaml: function (spec) {
try {
return yaml.safeLoad(spec);
}
Expand All @@ -20,8 +23,15 @@ module.exports = {
}
},

validateRoot: function(spec) {
// Checking for the all the required properties in the specificatio
/**
* Validate Spec to check if some of the required fields are present.
*
* @param {Object} spec OpenAPI spec
* @return {Object} Validation result
*/
validateSpec: function (spec) {

// Checking for the all the required properties in the specification
if (!spec.hasOwnProperty('openapi')) {
return {
result: false,
Expand All @@ -42,25 +52,165 @@ module.exports = {
reason: 'Specification must contain an Info Object for the meta-data of the API'
};
}
if (!spec.info.hasOwnProperty('title')) {
return {
result: false,
reason: 'Specification must contain a title in order to generate a collection'
};
}
if (!spec.info.hasOwnProperty('$ref')) {
if (!spec.info.hasOwnProperty('title')) {
return {
result: false,
reason: 'Specification must contain a title in order to generate a collection'
};
}

if (!spec.info.hasOwnProperty('version')) {
return {
result: false,
reason: 'Specification must contain a semantic version number of the API in the Info Object'
};
if (!spec.info.hasOwnProperty('version')) {
return {
result: false,
reason: 'Specification must contain a semantic version number of the API in the Info Object'
};
}
}


// Valid specification
return {
result: true,
openapi: spec
};
},

/** Converts OpenAPI input to OpenAPI Object
* @param {String} openApiSpec OpenAPI input in string
* @returns {Object} oasObject
*/
getOasObject: function (openApiSpec) {
let oasObject = openApiSpec,
detailedError;
try {
oasObject = this.asYaml(openApiSpec);
}
catch (yamlException) {
// Not valid YAML, could be a JSON as well
try {
oasObject = this.asJson(openApiSpec);
}
catch (jsonException) {
// It's neither JSON nor YAML
// try and determine json-ness or yaml-ness
if (openApiSpec && openApiSpec[0] === '{') {
// probably JSON
detailedError = ' ' + jsonException.message;
}
else if (openApiSpec && openApiSpec.indexOf('openapi:') === 0) {
// probably YAML
detailedError = ' ' + yamlException.message;
}
return {
result: false,
reason: 'Invalid format. Input must be in YAML or JSON format.' + detailedError
};
}
}

return {
result: true,
oasObject
};
},

/** Given an array of files returns the root OAS file if present
*
* @param {Array} filesPathArray Array of file paths
* @return {String} rootFile
*/
getRootFiles: function (filesPathArray) {
let rootFilesArray = [];

filesPathArray.forEach((filePath) => {
try {
let file = fs.readFileSync(filePath.fileName, 'utf8'),
oasObject,
obj = this.getOasObject(file);

if (obj.result) {
oasObject = obj.oasObject;
}
else {
throw new Error(obj.reason);
}
if (this.validateSpec(oasObject).result) {
rootFilesArray.push(filePath.fileName);
}
}
catch (e) {
throw new Error(e.message);
}
});
return rootFilesArray;
},

/**
* Resolve file references and generate single OAS Object
*
* @param {Object} openapi OpenAPI
* @param {Object} options options
* @return {Object} Resolved content
*/
resolveContent: function (openapi, options) {
return resolver.resolve(openapi, options.source, {
options: Object.assign({}, options),
resolve: true,
cache: [],
externals: [],
externalRefs: {},
rewriteRefs: true,
openapi: openapi
});
},

/** Resolves all OpenAPI file references and returns a single OAS Object
*
* @param {Object} source Root file path
* @param {Object} options Configurable options as per oas-resolver module.
* @return {Object} Resolved OpenAPI Schema
*/
mergeFiles: function(source, options = {}) {
options.source = source;
options.origin = source;
return this.readFileAsync(source, 'utf8')
.then((content) => {
try {
return yamlParse.parse(content, { prettyErrors: true });
}
catch (err) {
throw new Error('\nLine: ' + err.linePos.start.line + ', col: ' +
err.linePos.start.col + ' ' + err.message);
}
}, (err) => {
throw new Error(err.message);
})
.then((unresolved) => {
if (options.resolve === true) {
return this.resolveContent(unresolved, options);
}
}, (err) => {
throw err;
})
.then((result) => {
return result.openapi;
}, (err) => {
throw err;
});
},

/** Read File asynchronously
*
* @param {String} filePath Path of the file.
* @param {String} encoding encoding
* @return {String} Contents of the file
*/
readFileAsync: function(filePath, encoding) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, encoding, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
}
};
32 changes: 7 additions & 25 deletions lib/schemaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,42 +287,24 @@ module.exports = {
*/
parseSpec: function (openApiSpec) {
var openApiObj = openApiSpec,
detailedError,
obj,
rootValidation;

// If the open api specification is a string could be YAML or JSON
if (typeof openApiSpec === 'string') {
try {
openApiObj = parse.asYaml(openApiSpec);
obj = parse.getOasObject(openApiSpec);
if (obj.result) {
openApiObj = obj.oasObject;
}
catch (yamlException) {
// Not valid YAML, could be a JSON as well
try {
openApiObj = parse.asJson(openApiSpec);
}
catch (jsonException) {
// It's neither JSON nor YAML
// try and determine json-ness or yaml-ness
if (openApiSpec && openApiSpec[0] === '{') {
// probably JSON
detailedError = ' ' + jsonException.message;
}
else if (openApiSpec && openApiSpec.indexOf('openapi:') === 0) {
// probably YAML
detailedError = ' ' + yamlException.message;
}
return {
result: false,
reason: 'Invalid format. Input must be in YAML or JSON format.' + detailedError
};
}
else {
return obj;
}
}

// spec is a valid JSON object at this point

// Validate the root level object for semantics
rootValidation = parse.validateRoot(openApiObj);
rootValidation = parse.validateSpec(openApiObj);
if (!rootValidation.result) {
return {
result: false,
Expand Down
63 changes: 63 additions & 0 deletions lib/schemapack.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ const COLLECTION_NAME = 'Converted from OpenAPI',
async = require('async'),
sdk = require('postman-collection'),
schemaUtils = require('./schemaUtils.js'),
OasResolverOptions = {
resolve: true, // Resolve external references
jsonSchema: true // Treat $ref like JSON Schema and convert to OpenAPI Schema Objects
},
parse = require('./parse.js'),
getOptions = require('./options').getOptions,
transactionSchema = require('../assets/validationRequestListSchema.json'),
utils = require('./utils.js'),
_ = require('lodash'),
fs = require('fs'),
// options for oas-resolver

// This provides the base class for
// errors with the input OpenAPI spec
Expand Down Expand Up @@ -97,6 +103,14 @@ class SchemaPack {
return this.validationResult;
}
}
else if (input.type === 'folder') {
this.validationResult = {
result: false,
reason: 'Input data not validated, please call mergeAndValidate() for input.type \'folder\''
};

return this.validationResult;
}
else {
// invalid input type
this.validationResult = {
Expand Down Expand Up @@ -133,6 +147,55 @@ class SchemaPack {
return this.validationResult;
}

mergeAndValidate (cb) {
let input = this.input,
validationResult,
rootFiles;

try {
rootFiles = parse.getRootFiles(input.data);
}
catch (e) {
return cb(null, {
result: false,
reason: e
});
}
if (rootFiles.length > 1) {
this.validationResult = {
result: false,
reason: 'More than one root file not supported.'
};
return cb(null, this.validationResult);
}
if (rootFiles.length) {
parse.mergeFiles(rootFiles[0], OasResolverOptions)
.then((spec) => {
this.input = {
type: 'json',
data: spec
};
validationResult = this.validate();

return cb(null, validationResult);
})
.catch((err) => {
this.validationResult = {
result: false,
reason: 'Error while merging files.',
error: err
};
return cb(null, this.validationResult);
});
}
else {
return cb(null, {
result: false,
reason: 'No root files present / input is not an OpenAPI spec.'
});
}
}

// convert method, this is called when you want to convert a schema that you've already loaded
// in the constructor
convert (callback) {
Expand Down
Loading