diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a7b1260..c81b6f38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [v4.20.0] - 2024-02-15 + +### Added + +- Added support for reporting UserErrors in case when OpenAPI definition to converted is invalid. + ## [v4.19.0] - 2024-01-18 ## [v4.18.0] - 2023-09-28 @@ -602,7 +608,9 @@ Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0 - Base release -[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.19.0...HEAD +[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.20.0...HEAD + +[v4.20.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.19.0...v4.20.0 [v4.19.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.18.0...v4.19.0 diff --git a/index.js b/index.js index fbd6c9b0..98fc7ffa 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,9 @@ const { MODULE_VERSION } = require('./lib/schemapack.js'); const _ = require('lodash'), - SchemaPack = require('./lib/schemapack.js').SchemaPack; + SchemaPack = require('./lib/schemapack.js').SchemaPack, + UserError = require('./lib/common/UserError'), + DEFAULT_INVALID_ERROR = 'Provided definition is invalid'; module.exports = { // Old API wrapping the new API @@ -13,7 +15,7 @@ module.exports = { if (schema.validated) { return schema.convert(cb); } - return cb(null, schema.validationResult); + return cb(new UserError(_.get(schema, 'validationResult.reason', DEFAULT_INVALID_ERROR))); }, convertV2: function(input, options, cb) { @@ -23,7 +25,7 @@ module.exports = { return schema.convertV2(cb); } - return cb(null, schema.validationResult); + return cb(new UserError(_.get(schema, 'validationResult.reason', DEFAULT_INVALID_ERROR))); }, validate: function (input) { diff --git a/package-lock.json b/package-lock.json index df1de057..e53adc6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openapi-to-postmanv2", - "version": "4.19.0", + "version": "4.20.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "openapi-to-postmanv2", - "version": "4.19.0", + "version": "4.20.0", "license": "Apache-2.0", "dependencies": { "ajv": "8.11.0", diff --git a/package.json b/package.json index 9fb17497..ff9b5789 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openapi-to-postmanv2", - "version": "4.19.0", + "version": "4.20.0", "description": "Convert a given OpenAPI specification to Postman Collection v2.0", "homepage": "https://github.com/postmanlabs/openapi-to-postman", "bugs": "https://github.com/postmanlabs/openapi-to-postman/issues", diff --git a/test/unit/base.test.js b/test/unit/base.test.js index 1e7e9090..991c0384 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -993,19 +993,19 @@ describe('CONVERT FUNCTION TESTS ', function() { }); }); - it('should not return undefined in the error message if spec is not valid JSON/YAML', function(done) { + it('should return correct error message if spec is not valid JSON/YAML', function(done) { // invalid JSON - Converter.convert({ type: 'string', data: '{"key": { "value" : } ' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('undefined'); + Converter.convert({ type: 'string', data: '{"key": { "value" : } ' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.include('Invalid format. Input must be in YAML or JSON format.'); }); // invalid YAML - Converter.convert({ type: 'string', data: ' :' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('undefined'); + Converter.convert({ type: 'string', data: ' :' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.include('Invalid format. Input must be in YAML or JSON format.'); done(); }); }); @@ -1013,10 +1013,10 @@ describe('CONVERT FUNCTION TESTS ', function() { it('should throw an invalid format error and not semantic version missing error when yaml.safeLoad ' + 'does not throw an error while parsing yaml', function(done) { // YAML for which yaml.safeLoad does not throw an error - Converter.convert({ type: 'string', data: 'no error yaml' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('Specification must contain a semantic version number' + + Converter.convert({ type: 'string', data: 'no error yaml' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.not.include('Specification must contain a semantic version number' + ' of the OAS specification'); done(); }); @@ -1349,10 +1349,10 @@ describe('CONVERT FUNCTION TESTS ', function() { it('The converter must throw an error for invalid null info', function (done) { var openapi = fs.readFileSync(invalidNullInfo, 'utf8'); Converter.convert({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain an Info Object for the meta-data of the API'); done(); }); @@ -1361,10 +1361,10 @@ describe('CONVERT FUNCTION TESTS ', function() { it('The converter must throw an error for invalid null info title', function (done) { var openapi = fs.readFileSync(invalidNullInfoTitle, 'utf8'); Converter.convert({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain a title in order to generate a collection'); done(); }); @@ -1373,10 +1373,10 @@ describe('CONVERT FUNCTION TESTS ', function() { it('The converter must throw an error for invalid null info version', function (done) { var openapi = fs.readFileSync(invalidNullInfoVersion, 'utf8'); Converter.convert({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain a semantic version number of the API in the Info Object'); done(); }); @@ -2408,9 +2408,9 @@ describe('INTERFACE FUNCTION TESTS ', function () { validationResult = Converter.validate({ type: 'string', data: openapi }); expect(validationResult.result).to.equal(false); - Converter.convert({ type: 'string', data: openapi }, {}, function(err, conversionResult) { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); + Converter.convert({ type: 'string', data: openapi }, {}, function(err) { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); done(); }); }); @@ -2422,9 +2422,10 @@ describe('INTERFACE FUNCTION TESTS ', function () { var result = Converter.validate({ type: 'fil', data: 'invalid_path' }); expect(result.result).to.equal(false); expect(result.reason).to.contain('input'); - Converter.convert({ type: 'fil', data: 'invalid_path' }, {}, function(err, conversionResult) { - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason).to.equal('Invalid input type (fil). type must be one of file/json/string.'); + Converter.convert({ type: 'fil', data: 'invalid_path' }, {}, function(err) { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.equal('Invalid input type (fil). type must be one of file/json/string.'); done(); }); }); @@ -2434,9 +2435,10 @@ describe('INTERFACE FUNCTION TESTS ', function () { it('(type: file)', function(done) { var result = Converter.validate({ type: 'file', data: 'invalid_path' }); expect(result.result).to.equal(false); - Converter.convert({ type: 'file', data: 'invalid_path' }, {}, function(err, result) { - expect(result.result).to.equal(false); - expect(result.reason).to.equal('ENOENT: no such file or directory, open \'invalid_path\''); + Converter.convert({ type: 'file', data: 'invalid_path' }, {}, function(err) { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.equal('ENOENT: no such file or directory, open \'invalid_path\''); done(); }); }); diff --git a/test/unit/bin.test.js b/test/unit/bin.test.js index b5f2cef5..2996b7c0 100644 --- a/test/unit/bin.test.js +++ b/test/unit/bin.test.js @@ -33,10 +33,11 @@ describe('openapi2postmanv2 ', function() { }); it('should show appropriate messages for invalid input', function (done) { - exec('./bin/openapi2postmanv2.js -s test/data/invalid_openapi/multiple-components.yaml', function(err, stdout) { - expect(err).to.be.null; - expect(stdout).to.include('duplicated mapping key'); - done(); - }); + exec('./bin/openapi2postmanv2.js -s test/data/invalid_openapi/multiple-components.yaml', + function(err, stdout, stderr) { + expect(err).to.be.null; + expect(stderr).to.include('duplicated mapping key'); + done(); + }); }); }); diff --git a/test/unit/convertV2.test.js b/test/unit/convertV2.test.js index 194f9903..85c9d99f 100644 --- a/test/unit/convertV2.test.js +++ b/test/unit/convertV2.test.js @@ -947,19 +947,19 @@ describe('The convert v2 Function', function() { }); }); - it('should not return undefined in the error message if spec is not valid JSON/YAML', function(done) { + it('should return correct error message if spec is not valid JSON/YAML', function(done) { // invalid JSON - Converter.convertV2({ type: 'string', data: '{"key": { "value" : } ' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('undefined'); + Converter.convertV2({ type: 'string', data: '{"key": { "value" : } ' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.include('Invalid format. Input must be in YAML or JSON format.'); }); // invalid YAML - Converter.convertV2({ type: 'string', data: ' :' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('undefined'); + Converter.convertV2({ type: 'string', data: ' :' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.include('Invalid format. Input must be in YAML or JSON format.'); done(); }); }); @@ -967,10 +967,10 @@ describe('The convert v2 Function', function() { it('should throw an invalid format error and not semantic version missing error when yaml.safeLoad ' + 'does not throw an error while parsing yaml', function(done) { // YAML for which yaml.safeLoad does not throw an error - Converter.convertV2({ type: 'string', data: 'no error yaml' }, {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.be.false; - expect(conversionResult.reason).to.not.include('Specification must contain a semantic version number' + + Converter.convertV2({ type: 'string', data: 'no error yaml' }, {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message).to.not.include('Specification must contain a semantic version number' + ' of the OAS specification'); done(); }); @@ -1244,10 +1244,10 @@ describe('The convert v2 Function', function() { it('The converter must throw an error for invalid null info', function (done) { var openapi = fs.readFileSync(invalidNullInfo, 'utf8'); Converter.convertV2({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain an Info Object for the meta-data of the API'); done(); }); @@ -1256,10 +1256,10 @@ describe('The convert v2 Function', function() { it('The converter must throw an error for invalid null info title', function (done) { var openapi = fs.readFileSync(invalidNullInfoTitle, 'utf8'); Converter.convertV2({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain a title in order to generate a collection'); done(); }); @@ -1268,10 +1268,10 @@ describe('The convert v2 Function', function() { it('The converter must throw an error for invalid null info version', function (done) { var openapi = fs.readFileSync(invalidNullInfoVersion, 'utf8'); Converter.convertV2({ type: 'string', data: openapi }, - {}, (err, conversionResult) => { - expect(err).to.be.null; - expect(conversionResult.result).to.equal(false); - expect(conversionResult.reason) + {}, (err) => { + expect(err).to.not.be.null; + expect(err.name).to.eql('UserError'); + expect(err.message) .to.equal('Specification must contain a semantic version number of the API in the Info Object'); done(); });