From 759856f11227d0821ad7acefa26ce2b6362abe5e Mon Sep 17 00:00:00 2001 From: Aaron Spear Date: Wed, 29 May 2019 16:20:41 -0600 Subject: [PATCH] feat: Added -j/--json output option to cli-validator This revision adds a new --json option to the cli-validator that prints the internal result data in json format to stdout. It does additionally include the line number as a 'line' field in the JSON (this is not in the internal json data). Automated test validates that json is received. Closes #43 --- src/cli-validator/index.js | 4 +++ src/cli-validator/runValidator.js | 18 ++++++---- src/cli-validator/utils/printJsonResults.js | 38 ++++++++++++++++++++ test/cli-validator/tests/optionHandling.js | 39 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 src/cli-validator/utils/printJsonResults.js diff --git a/src/cli-validator/index.js b/src/cli-validator/index.js index 9926730d1..0ee1ce6fc 100755 --- a/src/cli-validator/index.js +++ b/src/cli-validator/index.js @@ -24,6 +24,10 @@ program '-n, --no_colors', 'turn off output coloring' ) + .option( + '-j, --json', + 'output as json' + ) .option( '-d, --default_mode', 'ignore config file and run in default mode' diff --git a/src/cli-validator/runValidator.js b/src/cli-validator/runValidator.js index ee671b6cc..d8690d6b2 100644 --- a/src/cli-validator/runValidator.js +++ b/src/cli-validator/runValidator.js @@ -12,6 +12,7 @@ const config = require('./utils/processConfiguration'); const buildSwaggerObject = require('./utils/buildSwaggerObject'); const validator = require('./utils/validator'); const print = require('./utils/printResults'); +const printJson = require('./utils/printJsonResults'); const printError = require('./utils/printError'); const preprocessFile = require('./utils/preprocessFile'); @@ -35,6 +36,7 @@ const processInput = async function(program) { const turnOffColoring = !!program.no_colors; const defaultMode = !!program.default_mode; + const jsonOutput = !!program.json; // turn on coloring by default const colors = turnOffColoring ? false : true; @@ -217,13 +219,17 @@ const processInput = async function(program) { continue; } - if (results.error || results.warning) { - print(results, chalk, printValidators, reportingStats, originalFile); - // fail on errors, but not if there are only warnings - if (results.error) exitCode = 1; + if (jsonOutput) { + printJson(results, originalFile); } else { - console.log(chalk.green(`\n${validFile} passed the validator`)); - if (validFile === last(filesToValidate)) console.log(); + if (results.error || results.warning) { + print(results, chalk, printValidators, reportingStats, originalFile); + // fail on errors, but not if there are only warnings + if (results.error) exitCode = 1; + } else { + console.log(chalk.green(`\n${validFile} passed the validator`)); + if (validFile === last(filesToValidate)) console.log(); + } } } diff --git a/src/cli-validator/utils/printJsonResults.js b/src/cli-validator/utils/printJsonResults.js new file mode 100644 index 000000000..dc5ee4692 --- /dev/null +++ b/src/cli-validator/utils/printJsonResults.js @@ -0,0 +1,38 @@ +const each = require('lodash/each'); + +// get line-number-producing, 'magic' code from Swagger Editor +const getLineNumberForPath = require(__dirname + '/../../plugins/ast/ast') + .getLineNumberForPath; + +// function to print the results as json to the console. +module.exports = function printJson(results, originalFile) { + const types = ['errors', 'warnings']; + types.forEach(type => { + each(results[type], problems => { + problems.forEach(problem => { + // TODO figure out how to include the config option that caused the error/warning + // and inject that as additional data. + + let path = problem.path; + + // path needs to be an array to get the line number + if (!Array.isArray(path)) { + path = path.split('.'); + } + + // get line number from the path of strings to the problem + // as they say in src/plugins/validation/semantic-validators/hook.js, + // + // "it's magic!" + // + const line = getLineNumberForPath(originalFile, path); + + // add the line number to the result JSON + problem.line = line; + }); + }); + }); + // render the results to json in the console with 2 char spacing + const jsonstr = JSON.stringify(results, null, 2); + console.log(jsonstr); +}; diff --git a/test/cli-validator/tests/optionHandling.js b/test/cli-validator/tests/optionHandling.js index 7b1fccfed..53745e165 100644 --- a/test/cli-validator/tests/optionHandling.js +++ b/test/cli-validator/tests/optionHandling.js @@ -183,4 +183,43 @@ describe('cli tool - test option handling', function() { expect(line.includes('statistics')).toEqual(false); }); }); + + it('should print json output when -j option is given', async function() { + const capturedText = []; + + const unhookIntercept = intercept(function(txt) { + capturedText.push(stripAnsiFrom(txt)); + return ''; + }); + + const program = {}; + program.args = ['./test/cli-validator/mockFiles/errAndWarn.yaml']; + program.json = true; + program.default_mode = true; + + await commandLineValidator(program); + unhookIntercept(); + + // capturedText should be JSON object. convert to json and check fields + const outputObject = JSON.parse(capturedText); + + //console.print(JSON.stringify(outputObject)); //FIXME + + expect(outputObject.warning).toEqual(true); + expect(outputObject.error).toEqual(true); + + // {"line": 59, "message": "operationIds must be unique", "path": "paths./pet.put.operationId" + expect(outputObject['errors']['operation-ids'][0]['line']).toEqual(59); + expect(outputObject['errors']['operation-ids'][0]['message']).toEqual( + 'operationIds must be unique' + ); + + // {"operations-shared": [{"line": 36, "message": "Operations must have a non-empty `operationId`.", "path": "paths./pet.post.operationId"}, + expect(outputObject['warnings']['operations-shared'][0]['line']).toEqual( + 36 + ); + expect(outputObject['warnings']['operations-shared'][0]['message']).toEqual( + 'Operations must have a non-empty `operationId`.' + ); + }); });