feat: fix mode and type coercion
This commit is contained in:
parent
97d5c7aac6
commit
719954e2a8
|
@ -180,7 +180,7 @@ The application mode.
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
- `check` runs each rule and exits with an indicative status
|
- `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
|
#### Rules
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,4 @@ spec:
|
||||||
memory: 5Mi
|
memory: 5Mi
|
||||||
requests:
|
requests:
|
||||||
cpu: 1m
|
cpu: 1m
|
||||||
memory: 2Mi
|
memory: 2Mi
|
||||||
|
|
12
src/index.ts
12
src/index.ts
|
@ -31,12 +31,14 @@ const MAIN_ARGS: Options = {
|
||||||
'rules',
|
'rules',
|
||||||
],
|
],
|
||||||
boolean: [
|
boolean: [
|
||||||
|
'coerce',
|
||||||
'count',
|
'count',
|
||||||
],
|
],
|
||||||
count: ['v'],
|
count: ['v'],
|
||||||
default: {
|
default: {
|
||||||
[CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`,
|
[CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`,
|
||||||
[CONFIG_ARGS_PATH]: [],
|
[CONFIG_ARGS_PATH]: [],
|
||||||
|
'coerce': false,
|
||||||
'count': false,
|
'count': false,
|
||||||
'dest': '-',
|
'dest': '-',
|
||||||
'excludeLevel': [],
|
'excludeLevel': [],
|
||||||
|
@ -81,10 +83,15 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
const data = parser.parse(source);
|
const data = parser.parse(source);
|
||||||
|
|
||||||
const activeRules = await resolveRules(rules, args.argv as any);
|
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) {
|
switch (args.argv.mode) {
|
||||||
case 'check':
|
case 'check':
|
||||||
|
case 'fix':
|
||||||
for (const rule of activeRules) {
|
for (const rule of activeRules) {
|
||||||
if (rule.visit(ctx, data)) {
|
if (rule.visit(ctx, data)) {
|
||||||
logger.info({ rule }, 'passed rule');
|
logger.info({ rule }, 'passed rule');
|
||||||
|
@ -94,8 +101,7 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ctx.logger.error({ mode: args.argv.mode }, 'unsupported mode');
|
ctx.error({ mode: args.argv.mode }, 'unsupported mode');
|
||||||
ctx.errors.push('unsupported mode');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.errors.length > 0) {
|
if (ctx.errors.length > 0) {
|
||||||
|
|
13
src/rule.ts
13
src/rule.ts
|
@ -1,4 +1,3 @@
|
||||||
import * as Ajv from 'ajv';
|
|
||||||
import { JSONPath } from 'jsonpath-plus';
|
import { JSONPath } from 'jsonpath-plus';
|
||||||
import { cloneDeep, intersection, isNil } from 'lodash';
|
import { cloneDeep, intersection, isNil } from 'lodash';
|
||||||
import { LogLevel } from 'noicejs';
|
import { LogLevel } from 'noicejs';
|
||||||
|
@ -103,9 +102,8 @@ export class Rule implements RuleData, Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async visit(ctx: VisitorContext, node: any): Promise<VisitorContext> {
|
public async visit(ctx: VisitorContext, node: any): Promise<VisitorContext> {
|
||||||
const ajv = new ((Ajv as any).default)()
|
const check = ctx.ajv.compile(this.check);
|
||||||
const check = ajv.compile(this.check);
|
const filter = this.compileFilter(ctx);
|
||||||
const filter = this.compileFilter(ajv);
|
|
||||||
const scopes = JSONPath({
|
const scopes = JSONPath({
|
||||||
json: node,
|
json: node,
|
||||||
path: this.select,
|
path: this.select,
|
||||||
|
@ -122,8 +120,7 @@ export class Rule implements RuleData, Visitor {
|
||||||
ctx.logger.debug({ item }, 'checking item')
|
ctx.logger.debug({ item }, 'checking item')
|
||||||
if (!check(item)) {
|
if (!check(item)) {
|
||||||
ctx.logger.warn({
|
ctx.logger.warn({
|
||||||
desc: this.desc,
|
name: this.name,
|
||||||
errors: check.errors,
|
|
||||||
item,
|
item,
|
||||||
}, 'rule failed on item');
|
}, 'rule failed on item');
|
||||||
ctx.errors.push(...check.errors);
|
ctx.errors.push(...check.errors);
|
||||||
|
@ -137,11 +134,11 @@ export class Rule implements RuleData, Visitor {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected compileFilter(ajv: any): any {
|
protected compileFilter(ctx: VisitorContext): any {
|
||||||
if (isNil(this.filter)) {
|
if (isNil(this.filter)) {
|
||||||
return () => true;
|
return () => true;
|
||||||
} else {
|
} else {
|
||||||
return ajv.compile(this.filter);
|
return ctx.ajv.compile(this.filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,30 @@
|
||||||
|
import * as Ajv from 'ajv';
|
||||||
import { Logger } from 'noicejs';
|
import { Logger } from 'noicejs';
|
||||||
|
|
||||||
|
export interface VisitorContextOptions {
|
||||||
|
coerce: boolean;
|
||||||
|
defaults: boolean;
|
||||||
|
logger: Logger;
|
||||||
|
}
|
||||||
|
|
||||||
export class VisitorContext {
|
export class VisitorContext {
|
||||||
|
public readonly ajv: any;
|
||||||
public readonly changes: Array<any>;
|
public readonly changes: Array<any>;
|
||||||
public readonly errors: Array<any>;
|
public readonly errors: Array<any>;
|
||||||
public readonly logger: Logger;
|
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.changes = [];
|
||||||
this.errors = [];
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue