Skip to content

Commit

Permalink
Merge pull request #286 from jonathanvanschenck/feature/improved-yaml…
Browse files Browse the repository at this point in the history
…-error-reports

Add verbose error logging
  • Loading branch information
daniloab authored Feb 8, 2022
2 parents 8c19243 + 699b7e7 commit 9f47d05
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 93 deletions.
2 changes: 1 addition & 1 deletion examples/eventDriven/src/customSpec.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion examples/eventDriven/src/customSpec.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion src/specification.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion test/__snapshots__/cli.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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:
^^^^^^^^^^^^^^…
Expand Down
83 changes: 0 additions & 83 deletions test/lib.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
56 changes: 50 additions & 6 deletions test/specification.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ 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
^^^^^^^^^^^^^^^^^^^^^…
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)', () => {
Expand All @@ -58,16 +61,57 @@ 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:
^^^^^^^^^^^^^^…
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
\`\`\``);
});
});

Expand Down

0 comments on commit 9f47d05

Please sign in to comment.