From 719954e2a8209b4be96be01dccdd0427135f28b7 Mon Sep 17 00:00:00 2001 From: ssube Date: Mon, 17 Jun 2019 07:11:36 -0500 Subject: [PATCH] feat: fix mode and type coercion --- README.md | 2 +- examples/kubernetes-resources-low.yml | 2 +- src/index.ts | 12 +++++++++--- src/rule.ts | 13 +++++-------- src/visitor/context.ts | 21 +++++++++++++++++++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a995bc1..71b7985 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ The application mode. Options: - `check` runs each rule and exits with an indicative status -- `clean` runs each rule and updates the source data with any defaults or other changes before running the next rule +- `fix` runs each rule and updates the source data with any defaults before running the next rule #### Rules diff --git a/examples/kubernetes-resources-low.yml b/examples/kubernetes-resources-low.yml index 41ebf8e..5d6c7c3 100644 --- a/examples/kubernetes-resources-low.yml +++ b/examples/kubernetes-resources-low.yml @@ -11,4 +11,4 @@ spec: memory: 5Mi requests: cpu: 1m - memory: 2Mi \ No newline at end of file + memory: 2Mi diff --git a/src/index.ts b/src/index.ts index e4851d9..d395c28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,12 +31,14 @@ const MAIN_ARGS: Options = { 'rules', ], boolean: [ + 'coerce', 'count', ], count: ['v'], default: { [CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`, [CONFIG_ARGS_PATH]: [], + 'coerce': false, 'count': false, 'dest': '-', 'excludeLevel': [], @@ -81,10 +83,15 @@ export async function main(argv: Array): Promise { const data = parser.parse(source); const activeRules = await resolveRules(rules, args.argv as any); - const ctx = new VisitorContext(logger); + const ctx = new VisitorContext({ + coerce: args.argv.coerce, + defaults: args.argv.mode === 'fix', + logger, + }); switch (args.argv.mode) { case 'check': + case 'fix': for (const rule of activeRules) { if (rule.visit(ctx, data)) { logger.info({ rule }, 'passed rule'); @@ -94,8 +101,7 @@ export async function main(argv: Array): Promise { } break; default: - ctx.logger.error({ mode: args.argv.mode }, 'unsupported mode'); - ctx.errors.push('unsupported mode'); + ctx.error({ mode: args.argv.mode }, 'unsupported mode'); } if (ctx.errors.length > 0) { diff --git a/src/rule.ts b/src/rule.ts index e9290ae..a2777cc 100644 --- a/src/rule.ts +++ b/src/rule.ts @@ -1,4 +1,3 @@ -import * as Ajv from 'ajv'; import { JSONPath } from 'jsonpath-plus'; import { cloneDeep, intersection, isNil } from 'lodash'; import { LogLevel } from 'noicejs'; @@ -103,9 +102,8 @@ export class Rule implements RuleData, Visitor { } public async visit(ctx: VisitorContext, node: any): Promise { - const ajv = new ((Ajv as any).default)() - const check = ajv.compile(this.check); - const filter = this.compileFilter(ajv); + const check = ctx.ajv.compile(this.check); + const filter = this.compileFilter(ctx); const scopes = JSONPath({ json: node, path: this.select, @@ -122,8 +120,7 @@ export class Rule implements RuleData, Visitor { ctx.logger.debug({ item }, 'checking item') if (!check(item)) { ctx.logger.warn({ - desc: this.desc, - errors: check.errors, + name: this.name, item, }, 'rule failed on item'); ctx.errors.push(...check.errors); @@ -137,11 +134,11 @@ export class Rule implements RuleData, Visitor { return ctx; } - protected compileFilter(ajv: any): any { + protected compileFilter(ctx: VisitorContext): any { if (isNil(this.filter)) { return () => true; } else { - return ajv.compile(this.filter); + return ctx.ajv.compile(this.filter); } } } \ No newline at end of file diff --git a/src/visitor/context.ts b/src/visitor/context.ts index ec885f8..3a7ddcb 100644 --- a/src/visitor/context.ts +++ b/src/visitor/context.ts @@ -1,13 +1,30 @@ +import * as Ajv from 'ajv'; import { Logger } from 'noicejs'; +export interface VisitorContextOptions { + coerce: boolean; + defaults: boolean; + logger: Logger; +} + export class VisitorContext { + public readonly ajv: any; public readonly changes: Array; public readonly errors: Array; public readonly logger: Logger; - constructor(logger: Logger) { + constructor(options: VisitorContextOptions) { + this.ajv = new ((Ajv as any).default)({ + coerceTypes: options.coerce, + useDefaults: options.defaults, + }); this.changes = []; this.errors = []; - this.logger = logger; + this.logger = options.logger; + } + + public error(options: any, msg: string) { + this.logger.error(options, msg); + this.errors.push(options || msg); } } \ No newline at end of file