diff --git a/examples/eventDriven/src/customSpec.json b/examples/eventDriven/src/customSpec.json index b1d3891..63e07d9 100644 --- a/examples/eventDriven/src/customSpec.json +++ b/examples/eventDriven/src/customSpec.json @@ -3,7 +3,7 @@ "title": "User Event API", "version": "1.0.0" }, - "description": "User Event API Specification", + "description": " User Event API Specification", "openapi": "3.0.0", "paths": {}, "components": { diff --git a/examples/eventDriven/src/customSpec.yml b/examples/eventDriven/src/customSpec.yml index f2bdcee..b7fe4f3 100644 --- a/examples/eventDriven/src/customSpec.yml +++ b/examples/eventDriven/src/customSpec.yml @@ -1,7 +1,7 @@ info: title: User Event API version: 1.0.0 -description: 'User Event API Specification' +description: ' User Event API Specification' openapi: 3.0.0 paths: {} components: diff --git a/src/lib.js b/src/lib.js index 618700c..e67895d 100644 --- a/src/lib.js +++ b/src/lib.js @@ -5,6 +5,7 @@ const { build } = require('./specification'); * @param {object} options - Configuration options * @param {string} options.encoding Optional, passed to readFileSync options. Defaults to 'utf8'. * @param {boolean} options.failOnErrors Whether or not to throw when parsing errors. Defaults to false. + * @param {boolean} options.verbose Whether the swagger snippet containing each error should be included in print/throws. Defaults to false. * @param {string} options.format Optional, defaults to '.json' - target file format '.yml' or '.yaml'. * @param {object} options.swaggerDefinition * @param {object} options.definition diff --git a/src/specification.js b/src/specification.js index cce02bd..85856ee 100644 --- a/src/specification.js +++ b/src/specification.js @@ -199,6 +199,10 @@ function build(options) { yamlDocsAnchors.set(anchor, parsed); } } else if (parsed.errors && parsed.errors.length) { + // Attach the relevent yaml section to the error for verbose logging + parsed.errors.forEach((err) => { + err.annotation = annotation; + }); yamlDocsErrors.push(parsed); } else { yamlDocsReady.push(parsed); @@ -218,6 +222,10 @@ function build(options) { yamlDocsAnchors.set(anchor, parsed); } } else if (parsed.errors && parsed.errors.length) { + // Attach the relevent yaml section to the error for verbose logging + parsed.errors.forEach((err) => { + err.annotation = doc; + }); yamlDocsErrors.push(parsed); } else { yamlDocsReady.push(parsed); @@ -259,8 +267,25 @@ function build(options) { } } + // Format errors into a printable/throwable string const errReport = yamlDocsErrors - .map(({ errors, filePath }) => `${filePath}: ${errors.join('\n')}`) + .map(({ errors, filePath }) => { + let str = `Error in ${filePath} :\n`; + if (options.verbose) { + str += errors + .map( + (e) => + `${e.toString()}\nImbedded within:\n\`\`\`\n ${e.annotation.replace( + /\n/g, + '\n ' + )}\n\`\`\`` + ) + .join('\n'); + } else { + str += errors.map((e) => e.toString()).join('\n'); + } + return str; + }) .filter((error) => !!error); if (errReport.length) { diff --git a/test/__snapshots__/cli.spec.js.snap b/test/__snapshots__/cli.spec.js.snap index 7efe09d..88ea2a0 100644 --- a/test/__snapshots__/cli.spec.js.snap +++ b/test/__snapshots__/cli.spec.js.snap @@ -52,7 +52,8 @@ exports[`CLI module should report YAML documents with errors 1`] = ` "Here's the report: - test/files/v2/wrong-yaml-identation.js: YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: + Error in test/files/v2/wrong-yaml-identation.js : +YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: /invalid_yaml: ^^^^^^^^^^^^^^… diff --git a/test/lib.spec.js b/test/lib.spec.js index a1790c0..a23320c 100644 --- a/test/lib.spec.js +++ b/test/lib.spec.js @@ -44,89 +44,6 @@ describe('Main lib module', () => { tags: [], }); }); - - it('should support a flag for throw errors', () => { - expect(() => { - swaggerJsdoc({ - swaggerDefinition: { - info: { - title: 'Example weird characters', - version: '1.0.0', - }, - }, - apis: [path.resolve(__dirname, './files/v2/wrong_syntax.yaml')], - failOnErrors: true, - }); - }) - .toThrow(`YAMLSemanticError: The !!! tag handle is non-default and was not declared. at line 2, column 3: - - !!!title: Hello World - ^^^^^^^^^^^^^^^^^^^^^… - -YAMLSemanticError: Implicit map keys need to be on a single line at line 2, column 3: - - !!!title: Hello World - ^^^^^^^^^^^^^^^^^^^^^…`); - }); - }); - - describe('Error handling', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should require options input', () => { - expect(() => { - swaggerJsdoc(); - }).toThrow(`Missing or invalid input: 'options' is required`); - }); - - it('should require a definition input', () => { - expect(() => { - swaggerJsdoc({}); - }).toThrow( - `Missing or invalid input: 'options.swaggerDefinition' or 'options.definition' is required` - ); - }); - - it('should require an api files input', () => { - expect(() => { - swaggerJsdoc({ definition: {} }); - }).toThrow( - `Missing or invalid input: 'options.apis' is required and it should be an array.` - ); - - expect(() => { - swaggerJsdoc({ definition: {}, apis: {} }); - }).toThrow( - `Missing or invalid input: 'options.apis' is required and it should be an array.` - ); - }); - }); - - describe('Specification v2: Swagger', () => { - it('should support multiple paths', () => { - let testObject = { - swaggerDefinition: {}, - apis: ['./**/*/external/*.yml'], - }; - - testObject = swaggerJsdoc(testObject); - expect(testObject).toEqual({ - swagger: '2.0', - paths: {}, - definitions: {}, - responses: { - api: { - foo: { 200: { description: 'OK' } }, - bar: { 200: { description: 'OK' } }, - }, - }, - parameters: {}, - securityDefinitions: {}, - tags: [], - }); - }); }); describe('Specification v3: OpenAPI', () => { diff --git a/test/specification.spec.js b/test/specification.spec.js index 5f22557..79727b6 100644 --- a/test/specification.spec.js +++ b/test/specification.spec.js @@ -37,8 +37,11 @@ describe('Specification module', () => { apis: [path.resolve(__dirname, './files/v2/wrong_syntax.yaml')], failOnErrors: true, }); - }) - .toThrow(`wrong_syntax.yaml: YAMLSemanticError: The !!! tag handle is non-default and was not declared. at line 2, column 3: + }).toThrow(`Error in ${path.resolve( + __dirname, + './files/v2/wrong_syntax.yaml' + )} : +YAMLSemanticError: The !!! tag handle is non-default and was not declared. at line 2, column 3: !!!title: Hello World ^^^^^^^^^^^^^^^^^^^^^… @@ -46,7 +49,7 @@ describe('Specification module', () => { YAMLSemanticError: Implicit map keys need to be on a single line at line 2, column 3: !!!title: Hello World - ^^^^^^^^^^^^^^^^^^^^^…`); + ^^^^^^^^^^^^^^^^^^^^^…\n`); }); it('should have filepath in error (jsdoc)', () => { @@ -58,8 +61,11 @@ YAMLSemanticError: Implicit map keys need to be on a single line at line 2, colu ], failOnErrors: true, }); - }) - .toThrow(`wrong-yaml-identation.js: YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: + }).toThrow(`Error in ${path.resolve( + __dirname, + './files/v2/wrong-yaml-identation.js' + )} : +YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: /invalid_yaml: ^^^^^^^^^^^^^^… @@ -67,7 +73,45 @@ YAMLSemanticError: Implicit map keys need to be on a single line at line 2, colu YAMLSemanticError: Implicit map keys need to be followed by map values at line 3, column 3: bar - ^^^`); + ^^^\n`); + }); + + it('should support a flag for verbose errors', () => { + expect(() => { + specModule.build({ + swaggerDefinition: {}, + apis: [ + path.resolve(__dirname, './files/v2/wrong-yaml-identation.js'), + ], + failOnErrors: true, + verbose: true, + }); + }).toThrow(`Error in ${path.resolve( + __dirname, + './files/v2/wrong-yaml-identation.js' + )} : +YAMLSyntaxError: All collection items must start at the same column at line 1, column 1: + +/invalid_yaml: +^^^^^^^^^^^^^^… + +Imbedded within: +\`\`\` + /invalid_yaml: + - foo + bar +\`\`\` +YAMLSemanticError: Implicit map keys need to be followed by map values at line 3, column 3: + + bar + ^^^ + +Imbedded within: +\`\`\` + /invalid_yaml: + - foo + bar +\`\`\``); }); });