Skip to content

Commit 1346948

Browse files
committed
template: Show all validation errors
Also parse validation errors to provide a more user-friendly message.
1 parent 8b71d31 commit 1346948

File tree

2 files changed

+143
-2
lines changed

2 files changed

+143
-2
lines changed

lib/template.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,8 @@ class Template {
733733
const ajv = new Ajv({
734734
loadSchema,
735735
unknownFormats: 'ignore',
736-
useDefaults: true
736+
useDefaults: true,
737+
allErrors: true
737738
});
738739
ajv.addFormat('text', /.*/);
739740
ajv.addFormat('hidden', /.*/);
@@ -1067,6 +1068,28 @@ class Template {
10671068
return Object.assign(defaults, mathExprResults);
10681069
}
10691070

1071+
_parseValidationErrors(errors) {
1072+
return errors.map((error) => {
1073+
const param = error.dataPath
1074+
.replace('.', ''); // strip leading dot
1075+
let message = (param !== '') ? `parameter ${param} ${error.message}` : error.message;
1076+
1077+
if (error.keyword === 'type') {
1078+
const typeStr = error.params.type;
1079+
message = `parameter ${param} should be of type ${typeStr}`;
1080+
}
1081+
1082+
if (error.keyword === 'enum') {
1083+
const allowedValues = error.params.allowedValues;
1084+
message = `${message}: ${allowedValues.join(', ')}`;
1085+
}
1086+
1087+
return {
1088+
message
1089+
};
1090+
});
1091+
}
1092+
10701093
/**
10711094
* Validate the supplied parameters object against the template's parameter schema
10721095
*
@@ -1075,7 +1098,7 @@ class Template {
10751098
validateParameters(parameters) {
10761099
const combParams = this.getCombinedParameters(parameters);
10771100
if (!this._parametersValidator(combParams)) {
1078-
const validationErrors = this._parametersValidator.errors;
1101+
const validationErrors = this._parseValidationErrors(this._parametersValidator.errors);
10791102
const err = Error('paramters failed validation');
10801103
err.validationErrors = validationErrors;
10811104
err.parameters = combParams;

test/template.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,4 +1318,122 @@ describe('Template class tests', function () {
13181318
assert.strictEqual(rendered, reference);
13191319
});
13201320
});
1321+
it('validate_bad_type', function () {
1322+
const yamldata = `
1323+
parameters:
1324+
foo: 1
1325+
fooArray: [1, 2]
1326+
fooEnum: baz
1327+
definitions:
1328+
fooArray:
1329+
type: array
1330+
items:
1331+
type: string
1332+
fooEnum:
1333+
type: string
1334+
enum:
1335+
- foo
1336+
- bar
1337+
template: |-
1338+
{{foo}}{{fooArray}}{{fooEnum}}
1339+
`;
1340+
1341+
return Template.loadYaml(yamldata)
1342+
.then((tmpl) => {
1343+
assert.throws(() => tmpl.validateParameters(), {
1344+
validationErrors: [
1345+
{
1346+
message: 'parameter fooArray[0] should be of type string'
1347+
},
1348+
{
1349+
message: 'parameter fooArray[1] should be of type string'
1350+
},
1351+
{
1352+
message: 'parameter fooEnum should be equal to one of the allowed values: foo, bar'
1353+
},
1354+
{
1355+
message: 'parameter foo should be of type string'
1356+
}
1357+
]
1358+
});
1359+
});
1360+
});
1361+
it('validate_bad_min_max', function () {
1362+
const yamldata = `
1363+
parameters:
1364+
minval: 1
1365+
maxval: 10
1366+
minlength: too short
1367+
maxlength: too long
1368+
minitems: []
1369+
maxitems: [1, 2]
1370+
definitions:
1371+
minval:
1372+
type: number
1373+
minimum: 2
1374+
maxval:
1375+
type: number
1376+
maximum: 1
1377+
minlength:
1378+
type: string
1379+
minLength: 10
1380+
maxlength:
1381+
type: string
1382+
maxLength: 1
1383+
minitems:
1384+
type: array
1385+
minItems: 1
1386+
maxitems:
1387+
type: array
1388+
maxItems: 1
1389+
template: |-
1390+
{{minval}}{{maxval}}{{minlength}}{{maxlength}}
1391+
{{minitems}}{{maxitems}}
1392+
`;
1393+
1394+
return Template.loadYaml(yamldata)
1395+
.then((tmpl) => {
1396+
console.log(tmpl.getParametersSchema());
1397+
assert.throws(() => tmpl.validateParameters(), {
1398+
validationErrors: [
1399+
{
1400+
message: 'parameter minval should be >= 2'
1401+
},
1402+
{
1403+
message: 'parameter maxval should be <= 1'
1404+
},
1405+
{
1406+
message: 'parameter minlength should NOT be shorter than 10 characters'
1407+
},
1408+
{
1409+
message: 'parameter maxlength should NOT be longer than 1 characters'
1410+
},
1411+
{
1412+
message: 'parameter minitems should NOT have fewer than 1 items'
1413+
},
1414+
{
1415+
message: 'parameter maxitems should NOT have more than 1 items'
1416+
}
1417+
]
1418+
});
1419+
});
1420+
});
1421+
it('validate_missing_required', function () {
1422+
const yamldata = `
1423+
parameters:
1424+
template: |-
1425+
{{foo}}
1426+
`;
1427+
1428+
return Template.loadYaml(yamldata)
1429+
.then((tmpl) => {
1430+
assert.throws(() => tmpl.validateParameters(), {
1431+
validationErrors: [
1432+
{
1433+
message: "should have required property 'foo'"
1434+
}
1435+
]
1436+
});
1437+
});
1438+
});
13211439
});

0 commit comments

Comments
 (0)