Skip to content

Commit

Permalink
fix: use and export ValidationError (#532)
Browse files Browse the repository at this point in the history
* fix: use and export ValidationError

* fix: separate helix specific config validation

* fix: move strains specific message to HelixConfigValidationError

* fix: add index version information to config
  • Loading branch information
dominique-pfister authored Jul 27, 2021
1 parent e185e0c commit 0c11d92
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 36 deletions.
58 changes: 25 additions & 33 deletions packages/helix-shared-config/src/ConfigValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

const Ajv = require('ajv').default;
const ajvFormats = require('ajv-formats');
const ValidationError = require('./ValidationError.js');

const schemas = [
/* eslint-disable global-require */
Expand All @@ -31,40 +32,31 @@ const schemas = [
/* eslint-enable global-require */
];

class ValidationError extends Error {
class HelixConfigValidationError extends ValidationError {
constructor(msg, errors = []) {
function prettyname(path, schema) {
if (path.startsWith('.strains')) {
return `${schema.title || 'Invalid Strain'} ${path.replace(/\.strains(\.|\[')(.*)/, '$2').replace(/'.*/, '')}`;
}
return `${schema.title || schema.$id} ${path}`;
}
super(msg, errors, HelixConfigValidationError.mapError, HelixConfigValidationError.prettyname);
}

const detail = errors.map(({
keyword, dataPath, message, data, params, parentSchema,
}) => {
if (keyword === 'additionalProperties') {
return `${prettyname(dataPath, parentSchema)} has unknown property '${params.additionalProperty}'`;
}
if (keyword === 'required' && dataPath === '') {
return 'A set of strains and a default strain are missing.';
}
if (keyword === 'required' && dataPath === '.strains') {
return 'A default strain is missing.';
}
if (keyword === 'required') {
return `${prettyname(dataPath, parentSchema)} ${message}`;
}
if (keyword === 'oneOf' && dataPath.startsWith('.strains')) {
return `${prettyname(dataPath, parentSchema)} must be either a Runtime Strain or a Proxy Strain`;
}
return `${prettyname(dataPath, parentSchema)} ${message}: ${keyword}(${JSON.stringify(data)}, ${JSON.stringify(params)})`;
}).join('\n');
super(`Invalid configuration:
${detail}
static prettyname(path, schema) {
if (path && path.startsWith('.strains')) {
return `${schema.title || 'Invalid Strain'} ${path.replace(/\.strains(\.|\[')(.*)/, '$2').replace(/'.*/, '')}`;
}
return ValidationError.prettyname(path, schema);
}

${msg}`);
this._errors = errors;
static mapError({
keyword, dataPath, message, data, params, parentSchema,
}, prettyname) {
if (keyword === 'required' && dataPath === '') {
return 'A set of strains and a default strain are missing.';
}
if (keyword === 'required' && dataPath === '.strains') {
return 'A default strain is missing.';
}
if (keyword === 'oneOf' && dataPath.startsWith('.strains')) {
return `${prettyname(dataPath, parentSchema)} must be either a Runtime Strain or a Proxy Strain`;
}
return ValidationError.mapError(keyword, dataPath, message, data, params, parentSchema);
}
}

Expand All @@ -91,11 +83,11 @@ class ConfigValidator {
if (!config.strains
|| ((config.strains.find && !config.strains.find((s) => s.name === 'default'))
&& !config.strains.default)) {
throw new ValidationError('A list of strains and a strain with the name "default" is required.');
throw new HelixConfigValidationError('A list of strains and a strain with the name "default" is required.');
}
const valid = this.validate(config);
if (!valid) {
throw new ValidationError(this._ajv.errorsText(), this._ajv.errors);
throw new HelixConfigValidationError(this._ajv.errorsText(), this._ajv.errors);
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions packages/helix-shared-config/src/IndexConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ class IndexConfig extends SchemaDerivedConfig {
}
return undefined;
}

/**
* Initialize the configuration
*/
async init() {
await super.init();

this._version = this._cfg.version;
return this;
}
}

module.exports = IndexConfig;
3 changes: 2 additions & 1 deletion packages/helix-shared-config/src/SchemaDerivedConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
const Ajv = require('ajv').default;
const ajvFormats = require('ajv-formats');
const BaseConfig = require('./BaseConfig.js');
const ValidationError = require('./ValidationError.js');

/**
* A Helix Config that is based on a (number of) JSON Schema(s).
Expand Down Expand Up @@ -66,7 +67,7 @@ class SchemaDerivedConfig extends BaseConfig {
if (res) {
return res;
}
throw new Error(ajv.errorsText());
throw new ValidationError(ajv.errorsText(), ajv.errors);
}

/**
Expand Down
43 changes: 43 additions & 0 deletions packages/helix-shared-config/src/ValidationError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2018 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-disable max-classes-per-file */

class ValidationError extends Error {
constructor(
msg, errors = [], mapError = ValidationError.mapError, prettyname = ValidationError.prettyname,
) {
const detail = errors.map((e) => mapError(e, prettyname)).join('\n');
super(`Invalid configuration:
${detail}
${msg}`);
this._errors = errors;
}

static prettyname(path, schema) {
return path ? `${schema.title || schema.$id} ${path}` : `${schema.title || schema.$id}`;
}

static mapError({
keyword, dataPath, message, data, params, parentSchema,
}, prettyname) {
if (keyword === 'additionalProperties') {
return `${prettyname(dataPath, parentSchema)} has unknown property '${params.additionalProperty}'`;
}
if (keyword === 'required') {
return `${prettyname(dataPath, parentSchema)} ${message}`;
}
return `${prettyname(dataPath, parentSchema)} ${message}: ${keyword}(${JSON.stringify(data)}, ${JSON.stringify(params)})`;
}
}

module.exports = ValidationError;
2 changes: 2 additions & 0 deletions packages/helix-shared-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const MarkupConfig = require('./MarkupConfig');
const Condition = require('./Condition.js');
const { optionalConfig, requiredConfig } = require('./config-wrapper');
const DataEmbedValidator = require('./DataEmbedValidator.js');
const ValidationError = require('./ValidationError.js');

module.exports = {
HelixConfig,
Expand All @@ -30,4 +31,5 @@ module.exports = {
optionalConfig,
requiredConfig,
DataEmbedValidator,
ValidationError,
};
2 changes: 1 addition & 1 deletion packages/helix-shared-config/test/markupconfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('Markup Config Loading', () => {
if (e instanceof AssertionError) {
throw e;
}
assert.equal(e.message, 'data/markup/images-in-gallery must have required property \'match\', data/markup/last-section must NOT have additional properties');
assert.equal(e.message, 'Invalid configuration:\nMarkup Mapping must have required property \'match\'\nMarkup Mapping has unknown property \'invalid\'\n\ndata/markup/images-in-gallery must have required property \'match\', data/markup/last-section must NOT have additional properties');
}
});
});
2 changes: 1 addition & 1 deletion packages/helix-shared-config/test/mountpoints.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const tests = [
title: 'fails with a broken config',
config: 'broken.yaml',
result: null,
error: 'Error: data must NOT have additional properties',
error: 'Error: Invalid configuration:\nFSTab (Mount Points) has unknown property \'mounts\'\n\ndata must NOT have additional properties',
},
{
title: 'loads a theblog example',
Expand Down

0 comments on commit 0c11d92

Please sign in to comment.