1
0
Fork 0

feat: display diff when rule modifies data (fixes #3)

This commit is contained in:
ssube 2019-06-22 11:48:41 -05:00
parent e3588577ac
commit 6f15d1c621
9 changed files with 63 additions and 28 deletions

View File

@ -35,6 +35,9 @@ export default {
}),
commonjs({
namedExports: {
'node_modules/deep-diff/index.js': [
'diff',
],
'node_modules/lodash/lodash.js': [
'cloneDeep',
'intersection',

View File

@ -7,7 +7,6 @@ spec:
- name: test
resources:
limits:
cpu: 2m
memory: 5Mi
requests:
cpu: 1m

View File

@ -21,9 +21,11 @@
},
"devDependencies": {
"@types/bunyan": "^1.8.6",
"@types/deep-diff": "^1.0.0",
"@types/js-yaml": "^3.12.1",
"@types/lodash": "^4.14.134",
"@types/yargs": "^13.0.0",
"deep-diff": "^1.0.2",
"jsonpath-plus": "^0.20.1",
"rollup": "^1.15.5",
"rollup-plugin-commonjs": "^10.0.0",

View File

@ -22,11 +22,13 @@ rules:
required: [cpu, memory]
properties:
cpu: &resources-cpu
oneOf:
- type: number
minimum: 1
- type: string
pattern: "[1-9][0-9]*m"
default: 500m
type: string
# oneOf:
# - type: number
# minimum: 1
# - type: string
# pattern: "[1-9][0-9]*m"
memory: &resources-memory
oneOf:
- type: number

View File

@ -1,4 +1,6 @@
import { createLogger } from 'bunyan';
import { diff } from 'deep-diff';
import { cloneDeep } from 'lodash';
import { Options, usage } from 'yargs';
import { loadConfig } from 'src/config';
@ -11,6 +13,8 @@ import { VisitorContext } from 'src/visitor/context';
const CONFIG_ARGS_NAME = 'config-name';
const CONFIG_ARGS_PATH = 'config-path';
const MODES = ['check', 'fix'];
const RULE_OPTION: Options = {
default: [],
group: 'Rules:',
@ -87,6 +91,11 @@ export async function main(argv: Array<string>): Promise<number> {
logger.info(VERSION_INFO, 'version info');
logger.info({ args }, 'main arguments');
// check mode
if (!MODES.includes(args.mode)) {
logger.error({ mode: args.mode }, 'unsupported mode');
}
// const schema = new Schema();
const result = { errors: [], valid: true }; // schema.match(config);
if (!result.valid) {
@ -97,9 +106,6 @@ export async function main(argv: Array<string>): Promise<number> {
const rules = await loadRules(args.rules);
const source = await loadSource(args.source);
const parser = new YamlParser();
const data = parser.parse(source);
const activeRules = await resolveRules(rules, args as any);
const ctx = new VisitorContext({
coerce: args.coerce,
@ -107,23 +113,29 @@ export async function main(argv: Array<string>): Promise<number> {
logger,
});
switch (args.mode) {
case 'check':
case 'fix':
for (const rule of activeRules) {
if (rule.visit(ctx, data)) {
logger.info({ rule }, 'passed rule');
} else {
logger.warn({ rule }, 'failed rule');
}
const parser = new YamlParser();
let data = parser.parse(source);
for (const rule of activeRules) {
const workingCopy = cloneDeep(data);
const ruleErrors = await rule.visit(ctx, workingCopy);
if (ruleErrors > 0) {
logger.warn({ rule }, 'rule failed');
} else {
const ruleDiff = diff(data, workingCopy);
if (Array.isArray(ruleDiff) && ruleDiff.length > 0) {
logger.info({ diff: ruleDiff, rule }, 'rule passed with modifications');
data = workingCopy;
} else {
logger.info({ rule }, 'rule passed');
}
break;
default:
ctx.error({ mode: args.mode }, 'unsupported mode');
}
}
if (ctx.errors.length > 0) {
logger.error({ errors: ctx.errors }, 'some rules failed');
logger.error({ count: ctx.errors.length, errors: ctx.errors }, 'some rules failed');
if (args.count) {
return Math.min(ctx.errors.length, 255);
} else {

View File

@ -101,7 +101,7 @@ export class Rule implements RuleData, Visitor {
}
}
public async visit(ctx: VisitorContext, node: any): Promise<VisitorContext> {
public async visit(ctx: VisitorContext, node: any): Promise<number> {
const check = ctx.ajv.compile(this.check);
const filter = this.compileFilter(ctx);
const scopes = JSONPath({
@ -111,7 +111,7 @@ export class Rule implements RuleData, Visitor {
if (isNil(scopes) || scopes.length === 0) {
ctx.logger.debug('no data selected');
return ctx;
return 0;
}
for (const item of scopes) {
@ -119,19 +119,22 @@ export class Rule implements RuleData, Visitor {
if (filter(item)) {
ctx.logger.debug({ item }, 'checking item')
if (!check(item)) {
const errors = Array.from(check.errors);
ctx.logger.warn({
errors,
name: this.name,
item,
rule: this,
}, 'rule failed on item');
ctx.errors.push(...check.errors);
return ctx;
ctx.errors.push(...errors);
return errors.length;
}
} else {
ctx.logger.debug({ errors: filter.errors, item }, 'skipping item');
}
}
return ctx;
return 0;
}
protected compileFilter(ctx: VisitorContext): any {

View File

@ -10,6 +10,8 @@ export interface VisitorContextOptions {
export class VisitorContext {
public readonly ajv: any;
public readonly changes: Array<any>;
public readonly coerce: boolean;
public readonly defaults: boolean;
public readonly errors: Array<any>;
public readonly logger: Logger;
@ -19,6 +21,8 @@ export class VisitorContext {
useDefaults: options.defaults,
});
this.changes = [];
this.coerce = options.coerce;
this.defaults = options.defaults;
this.errors = [];
this.logger = options.logger;
}

View File

@ -1,5 +1,5 @@
import { VisitorContext } from 'src/visitor/context';
export interface Visitor {
visit(ctx: VisitorContext, node: any): Promise<VisitorContext>;
visit(ctx: VisitorContext, node: any): Promise<number>;
}

View File

@ -9,6 +9,11 @@
dependencies:
"@types/node" "*"
"@types/deep-diff@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/deep-diff/-/deep-diff-1.0.0.tgz#7eba3202a99b3a207f758f351f7f86387269fc40"
integrity sha512-ENsJcujGbCU/oXhDfQ12mSo/mCBWodT2tpARZKmatoSrf8+cGRCPi0KVj3I0FORhYZfLXkewXu7AoIWqiBLkNw==
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@ -577,6 +582,11 @@ decode-uri-component@^0.2.0:
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
deep-diff@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26"
integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==
define-property@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"