From 614b1bf966f2b8bf15cb4c599e9d6eba297235f2 Mon Sep 17 00:00:00 2001 From: David Diaz Date: Wed, 18 Nov 2020 17:53:24 -0600 Subject: [PATCH 1/5] Moving the schema over to jsonschema. It's easier to maintain and should allow us to provide autocompletion in the future. --- schema.json | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 schema.json diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..97dbce4 --- /dev/null +++ b/schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://beaujs.com/schema.json", + "title": "Schema for Beau", + "type": "object", + "definitions": { + "request": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "required": ["alias"], + "properties": { + "alias": { + "type": "string", + "description": "The name of this request." + }, + "headers": { + "type": "object", + "additionalProperties": true, + "description": "Headers that are part of this request." + }, + "params": { + "type": "object", + "additionalProperties": true, + "description": "Query String parameters to add to this request." + }, + "payload": { + "description": "This request's body. It is converted to json automatically if given an object. It's sent as a string otherwise.", + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": true + } + ] + }, + "form": { + "type": "object", + "additionalProperties": true, + "description": "This request's body. Sets the content-type to application/x-www-form-urlencoded." + }, + "formdata": { + "type": "object", + "additionalProperties": true, + "description": "This request's body. Sets the content-type to multipart/form-data." + } + }, + "allOf": [ + { "not": { "required": ["payload", "form"] } }, + { "not": { "required": ["payload", "formdata"] } }, + { "not": { "required": ["formdata", "form"] } } + ] + } + ] + } + }, + "properties": { + "version": { + "type": "number", + "enum": [1] + }, + "endpoint": { + "type": "string", + "description": "The root endpoint for this host." + }, + "cookiejar": { + "type": "boolean" + }, + "defaults": { + "$ref": "#/definitions/request" + }, + "plugins": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, + { "type": "object", "additionalProperties": true } + ] + } + }, + "environment": {} + }, + "patternProperties": { + "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH)\\s.*": { + "oneOf": [ + { + "$ref": "#/definitions/request" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/request" + } + } + ] + } + } +} From df41d4fa0bca651ba63a40d02bcff97805b93649 Mon Sep 17 00:00:00 2001 From: David Diaz Date: Wed, 18 Nov 2020 18:37:00 -0600 Subject: [PATCH 2/5] Removed the validate command. We'll be replacing it with jsonschema soon. --- .../__snapshots__/validate.spec.js.snap | 8 -- bin/cli/__tests__/validate.spec.js | 27 ------- bin/cli/commands/validate.js | 30 -------- src/__tests__/schema.spec.js | 15 ---- src/schema.js | 77 ------------------- schema.json => src/schema.json | 0 6 files changed, 157 deletions(-) delete mode 100644 bin/cli/__tests__/__snapshots__/validate.spec.js.snap delete mode 100644 bin/cli/__tests__/validate.spec.js delete mode 100644 bin/cli/commands/validate.js delete mode 100644 src/__tests__/schema.spec.js delete mode 100644 src/schema.js rename schema.json => src/schema.json (100%) diff --git a/bin/cli/__tests__/__snapshots__/validate.spec.js.snap b/bin/cli/__tests__/__snapshots__/validate.spec.js.snap deleted file mode 100644 index f7f7fbb..0000000 --- a/bin/cli/__tests__/__snapshots__/validate.spec.js.snap +++ /dev/null @@ -1,8 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Validate Command should validate the configuration file 1`] = ` -Array [ - "beau.yml is valid. -", -] -`; diff --git a/bin/cli/__tests__/validate.spec.js b/bin/cli/__tests__/validate.spec.js deleted file mode 100644 index 8f99821..0000000 --- a/bin/cli/__tests__/validate.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -const ValidateCommand = require('../commands/validate'); - -jest.mock('../utils'); - -describe('Validate Command', () => { - let result; - - beforeEach(() => { - result = []; - jest.spyOn(process.stdout, 'write').mockImplementation(val => - result.push(require('strip-ansi')(val.toString('utf8'))) - ); - }); - - afterEach(() => jest.restoreAllMocks()); - - it('should validate the configuration file', async () => { - await ValidateCommand.run([]); - expect(result).toMatchSnapshot(); - }); - - it('should show schema errors', async () => { - await expect( - ValidateCommand.run(['invalid-conf.yml']) - ).rejects.toThrow(); - }); -}); diff --git a/bin/cli/commands/validate.js b/bin/cli/commands/validate.js deleted file mode 100644 index 311ad7b..0000000 --- a/bin/cli/commands/validate.js +++ /dev/null @@ -1,30 +0,0 @@ -const { flags, Command } = require('@oclif/command'); -const { baseFlags, openConfigFile } = require('../utils'); -const { validate } = require('../../../src/schema.js'); - -class ValidateCommand extends Command { - async run() { - const { flags, args } = this.parse(ValidateCommand); - const configFile = args.alias || flags.config; - - const config = openConfigFile(configFile); - - let result = await validate(config); - if (result.valid) { - this.log(`${configFile} is valid.`); - } else { - this.error(result.message); - } - } -} - -ValidateCommand.description = `Validates the given configuration file against Beau's configuration schema.`; -ValidateCommand.flags = { ...baseFlags }; -ValidateCommand.args = [ - { - name: 'alias', - required: false, - description: `The configuration file to validate.` - } -]; -module.exports = ValidateCommand; diff --git a/src/__tests__/schema.spec.js b/src/__tests__/schema.spec.js deleted file mode 100644 index c4e818b..0000000 --- a/src/__tests__/schema.spec.js +++ /dev/null @@ -1,15 +0,0 @@ -const schema = require('../schema'); - -describe('Schema', () => { - it(`should validate an object against the schema`, async () => { - await expect( - schema.validate({ endpoint: 'http://example.com' }) - ).resolves.toHaveProperty('valid', true); - }); - - it(`should indicate the error when an schema is invalid`, async () => { - await expect( - schema.validate({ plugins: [{ hello: 1, world: 2 }] }) - ).resolves.toHaveProperty('valid', false); - }); -}); diff --git a/src/schema.js b/src/schema.js deleted file mode 100644 index 7c1a3fc..0000000 --- a/src/schema.js +++ /dev/null @@ -1,77 +0,0 @@ -const Joi = require('joi'); -const { requestRegex } = require('./shared.js'); - -const pluginSchema = [ - Joi.string(), - Joi.object() - .keys(null) - .max(1) -]; - -const requestSchema = [ - Joi.object() - .keys({ - HEADERS: Joi.object().keys(null), - PAYLOAD: [Joi.object().keys(null), Joi.string()], - PARAMS: Joi.object().keys(null), - FORM: Joi.object().keys(null), - ALIAS: Joi.string().required(), - FORMDATA: Joi.object().keys(null) - }) - .without('FORM', ['PAYLOAD', 'FORMDATA']) - .without('PAYLOAD', ['FORM', 'FORMDATA']) - .without('FORMDATA', ['FORM', 'PAYLOAD']) - .rename(/headers/i, 'HEADERS', { override: true }) - .rename(/payload/i, 'PAYLOAD', { override: true }) - .rename(/params/i, 'PARAMS', { override: true }) - .rename(/form/i, 'FORM', { override: true }) - .rename(/alias/i, 'ALIAS', { override: true }), - - Joi.string() -]; - -const hostSchema = Joi.object() - .keys({ - HOST: Joi.string().required(), - ENDPOINT: Joi.string(), - DEFAULTS: Joi.object().keys(null) - }) - .pattern(requestRegex, requestSchema) - .rename(/host/i, 'HOST', { override: true }) - .rename(/defaults/i, 'DEFAULTS', { override: true }) - .rename(/endpoint/i, 'ENDPOINT', { override: true }); - -const schema = Joi.object() - .keys({ - VERSION: Joi.number().integer(), - ENDPOINT: Joi.string().uri(), - PLUGINS: Joi.array().items(pluginSchema), - DEFAULTS: Joi.object(), - ENVIRONMENT: Joi.object(), - HOSTS: Joi.array().items(hostSchema), - COOKIEJAR: Joi.boolean() - }) - .pattern(requestRegex, requestSchema) - .rename(/version/i, 'VERSION', { override: true }) - .rename(/endpoint/i, 'ENDPOINT', { override: true }) - .rename(/hosts/i, 'HOSTS', { override: true }) - .rename(/plugins/i, 'PLUGINS', { override: true }) - .rename(/defaults/i, 'DEFAULTS', { override: true }) - .rename(/environment/i, 'ENVIRONMENT', { override: true }) - .rename(/cookiejar/i, 'COOKIEJAR', { override: true }); - -const validate = async function(config) { - try { - await Joi.validate(config, schema, { allowUnknown: true }); - return { valid: true }; - } catch ({ name, details }) { - return { - valid: false, - message: `${name}: \n ${details - .map(d => d.message + ' @ ' + d.path) - .join(' \n ')}` - }; - } -}; - -module.exports = { schema, validate }; diff --git a/schema.json b/src/schema.json similarity index 100% rename from schema.json rename to src/schema.json From 5029a09b41d8f2612deca9ab8784e7ea3b7ca4e6 Mon Sep 17 00:00:00 2001 From: David Diaz Date: Thu, 19 Nov 2020 21:15:19 -0600 Subject: [PATCH 3/5] Added a base class to cli commands. --- .prettierrc | 3 +- bin/cli/__mocks__/base.js | 30 +++ bin/cli/__mocks__/utils.js | 41 ---- .../__snapshots__/utils.spec.js.snap | 74 ------ bin/cli/__tests__/list.spec.js | 2 +- bin/cli/__tests__/request.spec.js | 2 +- bin/cli/__tests__/utils.spec.js | 32 --- bin/cli/base.js | 61 +++++ bin/cli/commands/list.js | 38 ++- bin/cli/commands/request.js | 10 +- bin/cli/utils.js | 62 ----- package-lock.json | 227 ++++++++++-------- package.json | 9 +- src/schema.json => schema.json | 0 14 files changed, 251 insertions(+), 340 deletions(-) create mode 100644 bin/cli/__mocks__/base.js delete mode 100644 bin/cli/__mocks__/utils.js delete mode 100644 bin/cli/__tests__/__snapshots__/utils.spec.js.snap delete mode 100644 bin/cli/__tests__/utils.spec.js create mode 100644 bin/cli/base.js delete mode 100644 bin/cli/utils.js rename src/schema.json => schema.json (100%) diff --git a/.prettierrc b/.prettierrc index e6bb537..743686c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,7 +5,6 @@ useTabs: false trailingComma: none bracketSpacing: true jsxBracketSameLine: true -parser: babylon -semi: true +semi: false requirePragma: false proseWrap: always diff --git a/bin/cli/__mocks__/base.js b/bin/cli/__mocks__/base.js new file mode 100644 index 0000000..d054911 --- /dev/null +++ b/bin/cli/__mocks__/base.js @@ -0,0 +1,30 @@ +const Beau = require('../../../src/beau') + +const original = require.requireActual('../base') + +const config = { + environment: { + params: { + name: 'David' + } + }, + endpoint: 'https://example.org', + version: 1, + 'GET /anything': { + alias: 'alias', + payload: { + name: '$env.params.name' + } + }, + 'GET /status/418': { + alias: 'teapot' + } +} + +class Base extends original { + loadConfig(configFile, params = []) { + return new Beau(config, {}) + } +} + +module.exports = Base diff --git a/bin/cli/__mocks__/utils.js b/bin/cli/__mocks__/utils.js deleted file mode 100644 index 2978ff1..0000000 --- a/bin/cli/__mocks__/utils.js +++ /dev/null @@ -1,41 +0,0 @@ -const Beau = require('../../../src/beau'); -const original = require.requireActual('../utils'); - -const utils = {}; - -const config = { - environment: { - params: { - name: 'David' - } - }, - endpoint: 'https://example.org', - version: 1, - 'GET /anything': { - alias: 'alias', - payload: { - name: '$env.params.name' - } - }, - 'GET /status/418': { - alias: 'teapot' - } -}; - -utils.loadConfig = function() { - return new Beau(config, {}); -}; - -utils.openConfigFile = function(filename) { - if (filename === 'beau.yml') { - return config; - } - - if (filename === 'invalid-conf.yml') { - return { plugins: [{ hello: 1, world: 2 }] }; - } -}; - -utils.baseFlags = original.baseFlags; - -module.exports = utils; diff --git a/bin/cli/__tests__/__snapshots__/utils.spec.js.snap b/bin/cli/__tests__/__snapshots__/utils.spec.js.snap deleted file mode 100644 index 6041913..0000000 --- a/bin/cli/__tests__/__snapshots__/utils.spec.js.snap +++ /dev/null @@ -1,74 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`utils loadConfig should load load the config onto Beau 1`] = ` -Config { - "COOKIEJAR": false, - "DEFAULTS": Object {}, - "ENDPOINT": "https://example.org/", - "ENVIRONMENT": Object { - "_": Object {}, - }, - "HOSTS": Array [], - "PLUGINS": Plugins { - "autoload": Array [ - "std", - ], - "context": Object { - "createReadStream": [Function], - }, - "registry": Object { - "dynamicValues": Array [ - Object { - "fn": [Function], - "name": "createReadStream", - }, - ], - "postRequestModifiers": Array [], - "preRequestModifiers": Array [], - }, - }, - "REQUESTS": Array [ - Object { - "ALIAS": "anything", - "COOKIEJAR": false, - "ENDPOINT": "https://example.org/", - "PAYLOAD": Object { - "name": "$env.params.name", - }, - "REQUEST": "GET /anything", - }, - ], - "VERSION": 1, - "configKeys": Array [ - "VERSION", - "ENDPOINT", - "PLUGINS", - "DEFAULTS", - "ENVIRONMENT", - "HOSTS", - "COOKIEJAR", - ], -} -`; - -exports[`utils loadConfig should load params onto the environment 1`] = ` -Object { - "_": Object { - "BYE": "MARS", - "HELLO": "WORLD", - }, -} -`; - -exports[`utils openConfigFile should read and parse the given configuration file. 1`] = ` -Object { - "GET /anything": Object { - "alias": "anything", - "payload": Object { - "name": "$env.params.name", - }, - }, - "endpoint": "https://example.org/", - "version": 1, -} -`; diff --git a/bin/cli/__tests__/list.spec.js b/bin/cli/__tests__/list.spec.js index 367aa3a..582fee5 100644 --- a/bin/cli/__tests__/list.spec.js +++ b/bin/cli/__tests__/list.spec.js @@ -2,7 +2,7 @@ const ListCommand = require('../commands/list'); jest.mock('../../../src/shared'); -jest.mock('../utils'); +jest.mock('../base'); describe('List Command', () => { let result; diff --git a/bin/cli/__tests__/request.spec.js b/bin/cli/__tests__/request.spec.js index b09e9a2..85bdda3 100644 --- a/bin/cli/__tests__/request.spec.js +++ b/bin/cli/__tests__/request.spec.js @@ -3,7 +3,7 @@ const requestPromiseNativeMock = require('request-promise-native'); jest.mock('../../../src/shared'); -jest.mock('../utils'); +jest.mock('../base'); describe('Request Command', () => { let result; diff --git a/bin/cli/__tests__/utils.spec.js b/bin/cli/__tests__/utils.spec.js deleted file mode 100644 index 69877c0..0000000 --- a/bin/cli/__tests__/utils.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -const utils = require('../utils.js'); - -jest.mock('../../../src/shared'); - -jest.mock('fs'); - -describe('utils', () => { - describe('openConfigFile', () => { - it('should read and parse the given configuration file.', () => { - expect(utils.openConfigFile('beau.yml')).toMatchSnapshot(); - }); - - it('should throw if given not given a file', () => { - expect(() => utils.openConfigFile('not-a-file.yml')).toThrow(); - }); - }); - - describe('loadConfig', () => { - it('should load load the config onto Beau', () => { - let beau = utils.loadConfig('beau.yml'); - expect(beau.config).toMatchSnapshot(); - }); - - it('should load params onto the environment', () => { - let beau = utils.loadConfig('beau.yml', [ - 'HELLO=WORLD', - 'BYE=MARS' - ]); - expect(beau.config.ENVIRONMENT).toMatchSnapshot(); - }); - }); -}); diff --git a/bin/cli/base.js b/bin/cli/base.js new file mode 100644 index 0000000..1e1dd3f --- /dev/null +++ b/bin/cli/base.js @@ -0,0 +1,61 @@ +const { Command, flags } = require('@oclif/command') +const yaml = require('js-yaml') +const fs = require('fs') +const path = require('path') +const dotenv = require('dotenv') +const Beau = require('../../src/beau') + +class Base extends Command { + openConfigFile(configFile) { + if (!fs.existsSync(configFile)) { + throw new Error(`The config file, ${configFile} was not found.`) + } + + let config + yaml.safeLoadAll(fs.readFileSync(configFile, 'utf-8'), (doc) => { + if (typeof config === 'undefined') { + config = doc + } else { + if (typeof config.hosts === 'undefined') { + config.hosts = [] + } + + config.hosts.push(doc) + } + }) + + return config + } + loadConfig(configFile, params = []) { + const config = this.openConfigFile(configFile) + const env = dotenv.config().parsed || {} + params = dotenv.parse(params.join('\n')) + + const envParams = { _: Object.assign(env, params) } + + const configFileDir = path.dirname( + path.resolve(process.cwd(), configFile) + ) + + process.chdir(configFileDir) + + return new Beau(config, envParams) + } +} + +Base.flags = { + config: flags.string({ + char: 'c', + description: 'The configuration file to be used.', + default: 'beau.yml' + }), + verbose: flags.boolean({ + char: 'V', + description: `Show all additional information available for a command.` + }), + 'no-format': flags.boolean({ + description: `Disables color formatting for usage on external tools.` + }) +} + +module.exports = Base diff --git a/bin/cli/commands/list.js b/bin/cli/commands/list.js index 388df4d..3acbb6b 100644 --- a/bin/cli/commands/list.js +++ b/bin/cli/commands/list.js @@ -1,26 +1,22 @@ -const clc = require('cli-color'); -const { Line } = require('clui'); -const { flags, Command } = require('@oclif/command'); -const { baseFlags, loadConfig } = require('../utils'); +const clc = require('cli-color') +const { Line } = require('clui') +const Base = require('../base') -class ListCommand extends Command { +class ListCommand extends Base { async run() { - const { flags } = this.parse(ListCommand); - const Beau = loadConfig(flags.config); + const { flags } = this.parse(ListCommand) + const Beau = this.loadConfig(flags.config) if (flags['no-format']) { return Beau.requests.list.forEach( ({ VERB, ALIAS, ENDPOINT, PATH }) => this.log( - VERB + - `\t` + - ALIAS + - `\t` + - ENDPOINT.replace(/\/$/, '') + - `/` + - PATH.replace(/^\//, '') + `${VERB}\t${ALIAS}\t${ENDPOINT.replace( + /\/$/, + '' + )}/${PATH.replace(/^\//, '')}` ) - ); + ) } new Line() @@ -28,7 +24,7 @@ class ListCommand extends Command { .column('HTTP Verb', 20, [clc.cyan]) .column('Alias', 30, [clc.cyan]) .column('Endpoint', 20, [clc.cyan]) - .output(); + .output() Beau.requests.list.forEach(({ VERB, ALIAS, ENDPOINT, PATH }) => new Line() @@ -39,13 +35,13 @@ class ListCommand extends Command { ENDPOINT.replace(/\/$/, '') + '/' + PATH.replace(/^\//, '') ) .output() - ); + ) - new Line().output(); + new Line().output() } } -ListCommand.description = `Lists all available requests in the config file.`; -ListCommand.flags = { ...baseFlags }; +ListCommand.description = `Lists all available requests in the config file.` +ListCommand.flags = { ...Base.flags } -module.exports = ListCommand; +module.exports = ListCommand diff --git a/bin/cli/commands/request.js b/bin/cli/commands/request.js index 09fdc56..673f833 100644 --- a/bin/cli/commands/request.js +++ b/bin/cli/commands/request.js @@ -1,10 +1,10 @@ const clc = require('cli-color'); const jsome = require('jsome'); const { Line, Spinner } = require('clui'); -const { flags, Command } = require('@oclif/command'); -const { baseFlags, loadConfig } = require('../utils'); +const { flags } = require('@oclif/command'); +const Base = require('../base'); -class RequestCommand extends Command { +class RequestCommand extends Base { prettyOutput(res, verbose = false) { let { status, body } = res.response; @@ -44,7 +44,7 @@ class RequestCommand extends Command { args } = this.parse(RequestCommand); - const Beau = loadConfig(config, params); + const Beau = this.loadConfig(config, params); const spinnerSprite = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷']; this.spinner = new Spinner('', spinnerSprite); @@ -91,7 +91,7 @@ class RequestCommand extends Command { RequestCommand.description = `Executes a request by name.`; RequestCommand.flags = { - ...baseFlags, + ...Base.flags, param: flags.string({ char: 'P', multiple: true, diff --git a/bin/cli/utils.js b/bin/cli/utils.js deleted file mode 100644 index 695b37c..0000000 --- a/bin/cli/utils.js +++ /dev/null @@ -1,62 +0,0 @@ -const yaml = require('js-yaml'); -const fs = require('fs'); -const path = require('path'); -const dotenv = require('dotenv'); -const Beau = require('../../src/beau'); -const { flags } = require('@oclif/command'); - -const openConfigFile = configFile => { - if (!fs.existsSync(configFile)) { - throw new Error(`The config file, ${configFile} was not found.`); - } - - let config; - yaml.safeLoadAll(fs.readFileSync(configFile, 'utf-8'), doc => { - if (typeof config === 'undefined') { - config = doc; - } else { - if (typeof config.hosts === 'undefined') { - config.hosts = []; - } - - config.hosts.push(doc); - } - }); - - return config; -}; - -const loadConfig = (configFile, params = []) => { - const config = openConfigFile(configFile); - const env = dotenv.config().parsed || {}; - params = dotenv.parse(params.join('\n')); - - const envParams = { _: Object.assign(env, params) }; - - const configFileDir = path.dirname(path.resolve(process.cwd(), configFile)); - - process.chdir(configFileDir); - - return new Beau(config, envParams); -}; - -const baseFlags = { - config: flags.string({ - char: 'c', - description: 'The configuration file to be used.', - default: 'beau.yml' - }), - verbose: flags.boolean({ - char: 'V', - description: `Show all additional information available for a command.` - }), - 'no-format': flags.boolean({ - description: `Disables color formatting for usage on external tools.` - }) -}; - -module.exports = { - openConfigFile, - loadConfig, - baseFlags -}; diff --git a/package-lock.json b/package-lock.json index 2f813c1..644691a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -259,14 +259,30 @@ "p-each-series": "^1.0.0", "realpath-native": "^1.1.0", "rimraf": "^2.5.4", - "slash": "^2.0.0" + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "graceful-fs": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } } } }, @@ -431,43 +447,42 @@ } }, "@oclif/command": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.7.0.tgz", - "integrity": "sha512-TkknFtWcZI8te0E8sW+ohiblExrLx73rIcV4KdIzDX01u+oTZWZaap51F6TSGFnR/Gey0WctaDvJhZlt4xgKdA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.8.0.tgz", + "integrity": "sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==", "requires": { "@oclif/config": "^1.15.1", "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.3", "@oclif/plugin-help": "^3", "debug": "^4.1.1", - "semver": "^5.6.0" + "semver": "^7.3.2" }, "dependencies": { "@oclif/errors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.3.tgz", - "integrity": "sha512-EJR6AIOEkt/NnARNIVAskPDVtdhtO5TTNXmhDrGqMoWVsr0R6DkkLrMyq95BmHvlVWM1nduoq4fQPuCyuF2jaA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.4.tgz", + "integrity": "sha512-pJKXyEqwdfRTUdM8n5FIHiQQHg5ETM0Wlso8bF9GodczO40mF5Z3HufnYWJE7z8sGKxOeJCdbAVZbS8Y+d5GCw==", "requires": { "clean-stack": "^3.0.0", - "fs-extra": "^9.0.1", + "fs-extra": "^8.1", "indent-string": "^4.0.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, "clean-stack": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.0.tgz", - "integrity": "sha512-RHxtgFvXsRQ+1AM7dlozLDY7ssmvUUh0XEnfnyhYgJTO6beNZHBogiaCwGM9Q3rFrUkYxOtsZRC0zAturg5bjg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", "requires": { "escape-string-regexp": "4.0.0" } @@ -486,11 +501,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "emoji-regex": { @@ -504,14 +519,13 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "graceful-fs": { @@ -529,20 +543,16 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -553,11 +563,6 @@ "strip-ansi": "^6.0.0" } }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -726,6 +731,7 @@ "clean-stack": "^1.3.0", "fs-extra": "^7.0.0", "indent-string": "^3.2.0", + "strip-ansi": "^5.0.0", "wrap-ansi": "^4.0.0" }, "dependencies": { @@ -758,6 +764,21 @@ } } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, "wrap-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz", @@ -1189,13 +1210,12 @@ } }, "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "7.0.0-beta.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.0-beta.6.tgz", + "integrity": "sha512-9aDR4p4ReYBS1XxrYONdWuFVRweLjJTv8RaNkBEpJm09jkVcVYhtaul5IL7Y/x1RJ9UakETm0oBze4VHIjq4nA==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^0.5.0", "uri-js": "^4.2.2" } }, @@ -1943,9 +1963,16 @@ "dev": true, "requires": { "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", "wrap-ansi": "^5.1.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -1959,7 +1986,17 @@ "dev": true, "requires": { "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" } }, "wrap-ansi": { @@ -1969,7 +2006,8 @@ "dev": true, "requires": { "ansi-styles": "^3.2.0", - "string-width": "^3.0.0" + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } } } @@ -2893,9 +2931,9 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.2", @@ -3842,6 +3880,24 @@ "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } } }, "hard-rejection": { @@ -3917,11 +3973,6 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - }, "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", @@ -4637,21 +4688,6 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5544,16 +5580,6 @@ } } }, - "joi": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", - "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", - "requires": { - "hoek": "6.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5746,9 +5772,9 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.5.0.tgz", + "integrity": "sha512-x+TRJIQFskrNnFKE2Viz9FCSjK1vIh+H/uaBiOYszh/IcZmAFneQ35H4osWDJp1NPXccuV2I0RMXmi2ZS6Kqcg==" }, "json-stringify-safe": { "version": "5.0.1", @@ -8137,7 +8163,8 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, "semver-diff": { "version": "3.1.1", @@ -8783,14 +8810,6 @@ "repeat-string": "^1.6.1" } }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - } - }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -9028,9 +9047,9 @@ } }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "requires": { "punycode": "^2.1.0" } @@ -9291,6 +9310,12 @@ "yargs-parser": "^13.1.1" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9316,7 +9341,17 @@ "dev": true, "requires": { "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" } }, "y18n": { diff --git a/package.json b/package.json index 83fa670..3a49347 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,11 @@ "/bin" ], "dependencies": { - "@oclif/plugin-help": "3.1.0", - "@oclif/command": "1.7.0", + "@oclif/command": "1.8.0", "@oclif/config": "1.16.0", + "@oclif/plugin-help": "3.1.0", "@oclif/plugin-warn-if-update-available": "1.7.0", + "ajv": "7.0.0-beta.6", "beau-std": "0.9.4", "cli-color": "2.0.0", "clui": "0.3.6", @@ -26,7 +27,6 @@ "dotenv": "8.2.0", "globby": "11.0.1", "is-plain-object": "4.1.0", - "joi": "14.3.1", "js-yaml": "3.14.0", "jsome": "2.5.0", "request": "2.88.2", @@ -36,9 +36,8 @@ "repository": "git@github.com:Seich/Beau.git", "devDependencies": { "jest": "24.9.0", - "strip-ansi": "5.2.0", - "jest-watch-typeahead": "0.6.0", "strip-ansi": "6.0.0", + "jest-watch-typeahead": "0.6.0", "np": "6.3.2" }, "oclif": { diff --git a/src/schema.json b/schema.json similarity index 100% rename from src/schema.json rename to schema.json From a39f3b59778aaffbae82211313a03d68d4aca975 Mon Sep 17 00:00:00 2001 From: David Diaz Date: Thu, 19 Nov 2020 21:44:17 -0600 Subject: [PATCH 4/5] Config files are now validated against the json schema. --- bin/cli/base.js | 11 ++++++ examples/httpbin.yml | 7 ++-- schema.json | 90 +++++++++++++++++++++++--------------------- 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/bin/cli/base.js b/bin/cli/base.js index 1e1dd3f..35dee02 100644 --- a/bin/cli/base.js +++ b/bin/cli/base.js @@ -4,6 +4,10 @@ const fs = require('fs') const path = require('path') const dotenv = require('dotenv') const Beau = require('../../src/beau') +const Ajv = require('ajv').default + +const ajv = new Ajv() +const validate = ajv.compile(require('../../schema.json')) class Base extends Command { openConfigFile(configFile) { @@ -13,6 +17,13 @@ class Base extends Command { let config yaml.safeLoadAll(fs.readFileSync(configFile, 'utf-8'), (doc) => { + const valid = validate(doc) + + if (!valid) { + this.log(validate.errors) + this.error(`The configuration file is not valid.`) + } + if (typeof config === 'undefined') { config = doc } else { diff --git a/examples/httpbin.yml b/examples/httpbin.yml index fffb8b9..3210d24 100644 --- a/examples/httpbin.yml +++ b/examples/httpbin.yml @@ -7,9 +7,10 @@ environment: name: David GET /anything: - alias: anything - payload: - name: $env.params.name + - alias: anything + payload: + name: $env.params.name + - alias: anything2 GET /cookies/set: alias: set-cookies diff --git a/schema.json b/schema.json index 97dbce4..03a2644 100644 --- a/schema.json +++ b/schema.json @@ -9,50 +9,54 @@ { "type": "string" }, { "type": "object", - "required": ["alias"], - "properties": { - "alias": { - "type": "string", - "description": "The name of this request." - }, - "headers": { - "type": "object", - "additionalProperties": true, - "description": "Headers that are part of this request." - }, - "params": { - "type": "object", - "additionalProperties": true, - "description": "Query String parameters to add to this request." - }, - "payload": { - "description": "This request's body. It is converted to json automatically if given an object. It's sent as a string otherwise.", - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "additionalProperties": true - } - ] - }, - "form": { - "type": "object", - "additionalProperties": true, - "description": "This request's body. Sets the content-type to application/x-www-form-urlencoded." - }, - "formdata": { - "type": "object", - "additionalProperties": true, - "description": "This request's body. Sets the content-type to multipart/form-data." - } - }, - "allOf": [ - { "not": { "required": ["payload", "form"] } }, - { "not": { "required": ["payload", "formdata"] } }, - { "not": { "required": ["formdata", "form"] } } - ] + "$ref": "#/definitions/requestObject", + "required": ["alias"] } ] + }, + "requestObject": { + "type": "object", + "properties": { + "alias": { + "type": "string", + "description": "The name of this request." + }, + "headers": { + "type": "object", + "additionalProperties": true, + "description": "Headers that are part of this request." + }, + "params": { + "type": "object", + "additionalProperties": true, + "description": "Query String parameters to add to this request." + }, + "payload": { + "description": "This request's body. It is converted to json automatically if given an object. It's sent as a string otherwise.", + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": true + } + ] + }, + "form": { + "type": "object", + "additionalProperties": true, + "description": "This request's body. Sets the content-type to application/x-www-form-urlencoded." + }, + "formdata": { + "type": "object", + "additionalProperties": true, + "description": "This request's body. Sets the content-type to multipart/form-data." + } + }, + "allOf": [ + { "not": { "required": ["payload", "form"] } }, + { "not": { "required": ["payload", "formdata"] } }, + { "not": { "required": ["formdata", "form"] } } + ] } }, "properties": { @@ -68,7 +72,7 @@ "type": "boolean" }, "defaults": { - "$ref": "#/definitions/request" + "$ref": "#/definitions/requestObject" }, "plugins": { "type": "array", From 3b5e8485301085845c2f60971c8085ba923d62d9 Mon Sep 17 00:00:00 2001 From: David Diaz Date: Thu, 19 Nov 2020 22:06:22 -0600 Subject: [PATCH 5/5] Added better ajv errors. It prints json for now while I figure a nicer way of displaying errors in yaml. --- bin/cli/base.js | 10 ++++--- package-lock.json | 67 +++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/bin/cli/base.js b/bin/cli/base.js index 35dee02..353654c 100644 --- a/bin/cli/base.js +++ b/bin/cli/base.js @@ -5,9 +5,11 @@ const path = require('path') const dotenv = require('dotenv') const Beau = require('../../src/beau') const Ajv = require('ajv').default +const betterAjvErrors = require('better-ajv-errors') +const schema = require('../../schema.json') const ajv = new Ajv() -const validate = ajv.compile(require('../../schema.json')) +const validate = ajv.compile(schema) class Base extends Command { openConfigFile(configFile) { @@ -20,8 +22,10 @@ class Base extends Command { const valid = validate(doc) if (!valid) { - this.log(validate.errors) - this.error(`The configuration file is not valid.`) + this.log(`The configuration file is not valid.`) + this.error( + betterAjvErrors(schema, doc, validate.errors, { indent: 2 }) + ) } if (typeof config === 'undefined') { diff --git a/package-lock.json b/package-lock.json index 644691a..c0ea4a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, "requires": { "@babel/highlight": "^7.0.0" } @@ -128,7 +127,6 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -150,6 +148,14 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", @@ -1556,6 +1562,20 @@ "uuid": "^3.2.1" } }, + "better-ajv-errors": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz", + "integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/runtime": "^7.0.0", + "chalk": "^2.4.1", + "core-js": "^3.2.1", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.0.1", + "leven": "^3.1.0" + } + }, "boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -2113,6 +2133,11 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "code-error-fragment": { + "version": "0.0.230", + "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==" + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -2237,6 +2262,11 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz", + "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2727,8 +2757,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "event-emitter": { "version": "0.3.5", @@ -3835,6 +3864,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -5583,8 +5617,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.0", @@ -5781,6 +5814,15 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "json-to-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "requires": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + } + }, "json5": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", @@ -5798,6 +5840,11 @@ "graceful-fs": "^4.1.6" } }, + "jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5856,8 +5903,7 @@ "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "levn": { "version": "0.3.0", @@ -7845,6 +7891,11 @@ } } }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", diff --git a/package.json b/package.json index 3a49347..dc465d2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@oclif/plugin-warn-if-update-available": "1.7.0", "ajv": "7.0.0-beta.6", "beau-std": "0.9.4", + "better-ajv-errors": "0.6.7", "cli-color": "2.0.0", "clui": "0.3.6", "deepmerge": "4.2.2",