fix(visitor): include rule name and selector in error messages
This commit is contained in:
parent
5fefe0c79d
commit
fcd4740eee
26
README.md
26
README.md
|
@ -197,8 +197,30 @@ the easiest to read, and can be pretty-printed by redirecting `stderr` through `
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Using `jq` allows for additional filtering, for example `>(jq 'select(.level > 30)')` will only print warnings and
|
Using `jq` allows for additional filtering and formatting. For example, `>(jq 'select(.level > 30)')` will only print
|
||||||
errors (log level is also part of the configuration file).
|
warnings and errors (log level is also part of the configuration file).
|
||||||
|
|
||||||
|
To print the last line's message and error messages: `>(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
> cat test/examples/kubernetes-resources-high.yml | salty-dog \
|
||||||
|
--rules rules/kubernetes.yml \
|
||||||
|
--tag kubernetes 2> >(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')
|
||||||
|
|
||||||
|
[
|
||||||
|
"all rules passed"
|
||||||
|
]
|
||||||
|
|
||||||
|
> cat test/examples/kubernetes-resources-some.yml | salty-dog \
|
||||||
|
--rules rules/kubernetes.yml \
|
||||||
|
--tag kubernetes 2> >(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')
|
||||||
|
|
||||||
|
[
|
||||||
|
"some rules failed",
|
||||||
|
".resources.limits should have required property 'memory' at $.spec.template.spec.containers[*] for kubernetes-resources",
|
||||||
|
".metadata should have required property 'labels' at $ for kubernetes-labels"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
### Modes
|
### Modes
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ export async function parseArgs(argv: Array<string>): Promise<ParseResults> {
|
||||||
.version(VERSION_INFO.package.version)
|
.version(VERSION_INFO.package.version)
|
||||||
.alias('version', 'v');
|
.alias('version', 'v');
|
||||||
|
|
||||||
// @TODO: this should not need a cast but the parser's type only has the last option (include-tag)
|
// @TODO: this should not need a cast but the parser's type omits command options and doesn't expose camelCase
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
const args = parser.argv as any;
|
const args = parser.argv as any;
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -57,7 +57,7 @@ export class SchemaRule implements RuleData, Visitor {
|
||||||
if (filter(node)) {
|
if (filter(node)) {
|
||||||
ctx.logger.debug({ item: node }, 'checking item');
|
ctx.logger.debug({ item: node }, 'checking item');
|
||||||
if (!check(node) && hasItems(check.errors)) {
|
if (!check(node) && hasItems(check.errors)) {
|
||||||
errors.push(...check.errors.map(friendlyError));
|
errors.push(...check.errors.map((err) => friendlyError(ctx, err, this)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.logger.debug({ errors: filter.errors, item: node }, 'skipping item');
|
ctx.logger.debug({ errors: filter.errors, item: node }, 'skipping item');
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
import { ErrorObject } from 'ajv';
|
import { ErrorObject } from 'ajv';
|
||||||
import { isNil } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
|
|
||||||
|
import { SchemaRule } from '../../rule/SchemaRule';
|
||||||
|
import { VisitorContext } from '../../visitor/VisitorContext';
|
||||||
import { VisitorError } from '../../visitor/VisitorError';
|
import { VisitorError } from '../../visitor/VisitorError';
|
||||||
|
|
||||||
export function friendlyError(err: ErrorObject): VisitorError {
|
export function friendlyError(ctx: VisitorContext, err: ErrorObject, rule: SchemaRule): VisitorError {
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
err,
|
err,
|
||||||
|
rule,
|
||||||
},
|
},
|
||||||
level: 'error',
|
level: 'error',
|
||||||
msg: friendlyErrorMessage(err),
|
msg: friendlyErrorMessage(err, rule),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function friendlyErrorMessage(err: ErrorObject): string {
|
export function friendlyErrorMessage(err: ErrorObject, rule: SchemaRule): string {
|
||||||
if (isNil(err.message)) {
|
if (isNil(err.message)) {
|
||||||
return `${err.dataPath} ${err.keyword}`;
|
return `${err.dataPath} ${err.keyword} at ${rule.select} for ${rule.name}`;
|
||||||
} else {
|
} else {
|
||||||
return `${err.dataPath} ${err.message}`;
|
return `${err.dataPath} ${err.message} at ${rule.select} for ${rule.name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
import { NullLogger } from 'noicejs';
|
||||||
|
|
||||||
|
import { SchemaRule } from '../../../src/rule/SchemaRule';
|
||||||
import { friendlyError } from '../../../src/utils/ajv';
|
import { friendlyError } from '../../../src/utils/ajv';
|
||||||
|
import { VisitorContext } from '../../../src/visitor/VisitorContext';
|
||||||
|
|
||||||
|
const TEST_NAME = 'test';
|
||||||
|
|
||||||
describe('friendly errors', () => {
|
describe('friendly errors', () => {
|
||||||
it('should have a message', () => {
|
it('should have a message', () => {
|
||||||
const err = friendlyError({
|
const err = friendlyError(new VisitorContext({
|
||||||
|
innerOptions: {
|
||||||
|
coerce: false,
|
||||||
|
defaults: false,
|
||||||
|
mutate: false,
|
||||||
|
},
|
||||||
|
logger: NullLogger.global,
|
||||||
|
}), {
|
||||||
dataPath: 'test-path',
|
dataPath: 'test-path',
|
||||||
keyword: 'test',
|
keyword: TEST_NAME,
|
||||||
params: { /* ? */ },
|
params: { /* ? */ },
|
||||||
schemaPath: 'test-path',
|
schemaPath: 'test-path',
|
||||||
});
|
}, new SchemaRule({
|
||||||
|
check: {},
|
||||||
|
desc: TEST_NAME,
|
||||||
|
level: 'info',
|
||||||
|
name: TEST_NAME,
|
||||||
|
select: '',
|
||||||
|
tags: [TEST_NAME],
|
||||||
|
}));
|
||||||
expect(err.msg).to.not.equal('');
|
expect(err.msg).to.not.equal('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue