fix(config-schema): allow field pattern to be a regex (#3971)

This commit is contained in:
Erez Rokah 2020-07-01 13:30:26 +03:00 committed by GitHub
parent 7ee9f2f188
commit 652045c9de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 20 deletions

View File

@ -173,13 +173,13 @@ describe('config', () => {
it('should throw if collection publish is not a boolean', () => {
expect(() => {
validateConfig(merge(validConfig, { collections: [{ publish: 'false' }] }));
validateConfig(merge({}, validConfig, { collections: [{ publish: 'false' }] }));
}).toThrowError("'collections[0].publish' should be boolean");
});
it('should not throw if collection publish is a boolean', () => {
expect(() => {
validateConfig(merge(validConfig, { collections: [{ publish: false }] }));
validateConfig(merge({}, validConfig, { collections: [{ publish: false }] }));
}).not.toThrowError();
});
@ -201,10 +201,48 @@ describe('config', () => {
}).not.toThrow();
});
it('should throw if collection names are not unique', () => {
expect(() => {
validateConfig(
merge({}, validConfig, {
collections: [validConfig.collections[0], validConfig.collections[0]],
}),
);
}).toThrowError("'collections' collections names must be unique");
});
it('should throw if collection file names are not unique', () => {
expect(() => {
validateConfig(
merge({}, validConfig, {
collections: [
{},
{
files: [
{
name: 'a',
label: 'a',
file: 'a.md',
fields: [{ name: 'title', label: 'title', widget: 'string' }],
},
{
name: 'a',
label: 'b',
file: 'b.md',
fields: [{ name: 'title', label: 'title', widget: 'string' }],
},
],
},
],
}),
);
}).toThrowError("'collections[1].files' files names must be unique");
});
it('should throw if collection fields names are not unique', () => {
expect(() => {
validateConfig(
merge(validConfig, {
merge({}, validConfig, {
collections: [
{
fields: [
@ -221,7 +259,7 @@ describe('config', () => {
it('should not throw if collection fields are unique across nesting levels', () => {
expect(() => {
validateConfig(
merge(validConfig, {
merge({}, validConfig, {
collections: [
{
fields: [
@ -257,7 +295,7 @@ describe('config', () => {
it('should throw if nested relation displayFields and searchFields are not arrays', () => {
expect(() => {
validateConfig(
merge(validConfig, {
merge({}, validConfig, {
collections: [
{
fields: [
@ -288,7 +326,7 @@ describe('config', () => {
it('should not throw if nested relation displayFields and searchFields are arrays', () => {
expect(() => {
validateConfig(
merge(validConfig, {
merge({}, validConfig, {
collections: [
{
fields: [
@ -358,5 +396,45 @@ describe('config', () => {
);
}).not.toThrow();
});
it('should throw if collection field pattern is not an array', () => {
expect(() => {
validateConfig(merge({}, validConfig, { collections: [{ fields: [{ pattern: '' }] }] }));
}).toThrowError("'collections[0].fields[0].pattern' should be array");
});
it('should throw if collection field pattern is not an array of [string|regex, string]', () => {
expect(() => {
validateConfig(
merge({}, validConfig, { collections: [{ fields: [{ pattern: [1, ''] }] }] }),
);
}).toThrowError(
"'collections[0].fields[0].pattern[0]' should be string\n'collections[0].fields[0].pattern[0]' should be a regular expression",
);
expect(() => {
validateConfig(
merge({}, validConfig, { collections: [{ fields: [{ pattern: ['', 1] }] }] }),
);
}).toThrowError("'collections[0].fields[0].pattern[1]' should be string");
});
it('should allow collection field pattern to be an array of [string|regex, string]', () => {
expect(() => {
validateConfig(
merge({}, validConfig, {
collections: [{ fields: [{ pattern: ['pattern', 'error'] }] }],
}),
);
}).not.toThrow();
expect(() => {
validateConfig(
merge({}, validConfig, {
collections: [{ fields: [{ pattern: [/pattern/, 'error'] }] }],
}),
);
}).not.toThrow();
});
});
});

View File

@ -1,5 +1,5 @@
import AJV from 'ajv';
import { select, uniqueItemProperties } from 'ajv-keywords/keywords';
import { select, uniqueItemProperties, instanceof as instanceOf } from 'ajv-keywords/keywords';
import ajvErrors from 'ajv-errors';
import { formatExtensions, frontmatterFormats, extensionFormatters } from 'Formats/formats';
import { getWidgets } from 'Lib/registry';
@ -21,7 +21,11 @@ const fieldsConfig = () => ({
widget: { type: 'string' },
required: { type: 'boolean' },
hint: { type: 'string' },
pattern: { type: 'array', minItems: 2, items: { type: 'string' } },
pattern: {
type: 'array',
minItems: 2,
items: [{ oneOf: [{ type: 'string' }, { instanceof: 'RegExp' }] }, { type: 'string' }],
},
field: { $ref: 'field' },
fields: { $ref: 'fields' },
types: { $ref: 'fields' },
@ -274,13 +278,15 @@ export function validateConfig(config) {
const ajv = new AJV({ allErrors: true, jsonPointers: true, $data: true });
uniqueItemProperties(ajv);
select(ajv);
instanceOf(ajv);
ajvErrors(ajv);
const valid = ajv.validate(getConfigSchema(), config);
if (!valid) {
const errors = ajv.errors.map(e => {
switch (e.keyword) {
// TODO: remove after https://github.com/ajv-validator/ajv-keywords/pull/123 is merged
if (e.keyword === 'uniqueItemProperties') {
case 'uniqueItemProperties': {
const path = e.dataPath || '';
let newError = e;
if (path.endsWith('/fields')) {
@ -292,7 +298,20 @@ export function validateConfig(config) {
}
return newError;
}
case 'instanceof': {
const path = e.dataPath || '';
let newError = e;
if (/fields\/\d+\/pattern\/\d+/.test(path)) {
newError = {
...e,
message: 'should be a regular expression',
};
}
return newError;
}
default:
return e;
}
});
console.error('Config Errors', errors);
throw new ConfigError(errors);