mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 07:03:38 +00:00
allow special characters in an enum
This commit is contained in:
parent
3700b15f76
commit
e85cfe73f0
@ -1,7 +1,9 @@
|
||||
import _ from 'lodash';
|
||||
import * as yup from 'yup';
|
||||
import { translatedErrors as errorsTrads } from '@strapi/helper-plugin';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import getRelationType from '../../../utils/getRelationType';
|
||||
import toGraphQLName from '../../../utils/toGraphQLName';
|
||||
import {
|
||||
alreadyUsedAttributeNames,
|
||||
createTextShape,
|
||||
@ -156,10 +158,23 @@ const types = {
|
||||
},
|
||||
})
|
||||
.test({
|
||||
name: 'valuesMatchesRegex',
|
||||
message: errorsTrads.regex,
|
||||
test: values => {
|
||||
return values.every(val => val === '' || ENUM_REGEX.test(val));
|
||||
name: 'valuesCollide',
|
||||
message: 'Some values collide when normalized',
|
||||
test(values) {
|
||||
const normalizedEnum = values.map(toGraphQLName);
|
||||
const duplicates = _(normalizedEnum)
|
||||
.groupBy()
|
||||
.pickBy(x => x.length > 1)
|
||||
.keys()
|
||||
.value();
|
||||
|
||||
if (duplicates.length) {
|
||||
const message = `Some values collide when normalized: ${duplicates.join(', ')}`;
|
||||
|
||||
return this.createError({ message });
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
})
|
||||
.test({
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
|
||||
const toGraphQLName = value =>
|
||||
slugify(value, {
|
||||
decamelize: false,
|
||||
lowercase: false,
|
||||
separator: '_',
|
||||
});
|
||||
|
||||
export default toGraphQLName;
|
||||
@ -19,7 +19,6 @@ const validators = {
|
||||
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
|
||||
const COLLECTION_NAME_REGEX = new RegExp('^[A-Za-z][-_0-9A-Za-z]*$');
|
||||
const CATEGORY_NAME_REGEX = new RegExp('^[A-Za-z][-_0-9A-Za-z]*$');
|
||||
const ENUM_REGEX = new RegExp('^[_A-Za-z][_0-9A-Za-z]*$');
|
||||
const ICON_REGEX = new RegExp('^[A-Za-z0-9][-A-Za-z0-9]*$');
|
||||
const UID_REGEX = new RegExp('^[A-Za-z0-9-_.~]*$');
|
||||
|
||||
@ -59,12 +58,6 @@ const isValidKey = key => ({
|
||||
test: () => NAME_REGEX.test(key),
|
||||
});
|
||||
|
||||
const isValidEnum = {
|
||||
name: 'isValidEnum',
|
||||
message: '${path} must match the following regex: ' + ENUM_REGEX,
|
||||
test: val => val === '' || ENUM_REGEX.test(val),
|
||||
};
|
||||
|
||||
const areEnumValuesUnique = {
|
||||
name: 'areEnumValuesUnique',
|
||||
message: '${path} cannot contain duplicate values',
|
||||
@ -112,7 +105,6 @@ module.exports = {
|
||||
isValidName,
|
||||
isValidIcon,
|
||||
isValidKey,
|
||||
isValidEnum,
|
||||
isValidUID,
|
||||
isValidRegExpPattern,
|
||||
};
|
||||
|
||||
@ -10,7 +10,6 @@ const {
|
||||
areEnumValuesUnique,
|
||||
isValidDefaultJSON,
|
||||
isValidName,
|
||||
isValidEnum,
|
||||
isValidUID,
|
||||
isValidRegExpPattern,
|
||||
} = require('./common');
|
||||
@ -134,12 +133,7 @@ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
|
||||
return {
|
||||
enum: yup
|
||||
.array()
|
||||
.of(
|
||||
yup
|
||||
.string()
|
||||
.test(isValidEnum)
|
||||
.required()
|
||||
)
|
||||
.of(yup.string().required())
|
||||
.min(1)
|
||||
.test(areEnumValuesUnique)
|
||||
.required(),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { keyBy, mapValues } = require('lodash');
|
||||
const { yup } = require('@strapi/utils');
|
||||
const _ = require('lodash');
|
||||
const { yup, toGraphQLName } = require('@strapi/utils');
|
||||
|
||||
const LIFECYCLES = [
|
||||
'beforeCreate',
|
||||
@ -24,7 +24,7 @@ const LIFECYCLES = [
|
||||
'afterDeleteMany',
|
||||
];
|
||||
|
||||
const lifecyclesShape = mapValues(keyBy(LIFECYCLES), () =>
|
||||
const lifecyclesShape = _.mapValues(_.keyBy(LIFECYCLES), () =>
|
||||
yup
|
||||
.mixed()
|
||||
.nullable()
|
||||
@ -47,6 +47,33 @@ const contentTypeSchemaValidator = yup.object().shape({
|
||||
.required(),
|
||||
})
|
||||
.required(),
|
||||
attributes: yup.object().test({
|
||||
name: 'valuesCollide',
|
||||
message: 'Some values collide when normalized',
|
||||
test(attributes) {
|
||||
for (const attrName in attributes) {
|
||||
const attr = attributes[attrName];
|
||||
if (attr.type === 'enumeration') {
|
||||
const normalizedEnum = attr.enum.map(toGraphQLName);
|
||||
const duplicates = _(normalizedEnum)
|
||||
.groupBy()
|
||||
.pickBy(x => x.length > 1)
|
||||
.keys()
|
||||
.value();
|
||||
|
||||
if (duplicates.length) {
|
||||
const message = `Some enum values of the field '${attrName}' collide when normalized: ${duplicates.join(
|
||||
', '
|
||||
)}. Please modify your enumeration.`;
|
||||
|
||||
return this.createError({ message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
}),
|
||||
actions: yup.object().onlyContainsFunctions(),
|
||||
lifecycles: yup
|
||||
|
||||
@ -6,6 +6,7 @@ const {
|
||||
stringEquals,
|
||||
getCommonBeginning,
|
||||
getCommonPath,
|
||||
toGraphQLName,
|
||||
} = require('../string-formatting');
|
||||
|
||||
describe('string-formatting', () => {
|
||||
@ -97,4 +98,27 @@ describe('string-formatting', () => {
|
||||
expect(result).toBe(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toGraphQLName', () => {
|
||||
test.each([
|
||||
['', ''],
|
||||
['a', 'a'],
|
||||
['aa', 'aa'],
|
||||
['aBa', 'aBa'],
|
||||
['ABa', 'ABa'],
|
||||
['ABA', 'ABA'],
|
||||
['a a', 'a_a'],
|
||||
['aa aa', 'aa_aa'],
|
||||
['aBa aBa', 'aBa_aBa'],
|
||||
['ABa ABa', 'ABa_ABa'],
|
||||
['ABA ABA', 'ABA_ABA'],
|
||||
['û', 'u'],
|
||||
['Û', 'U'],
|
||||
['München', 'Muenchen'],
|
||||
['Baden-Württemberg', 'Baden_Wuerttemberg'],
|
||||
['test_test', 'test_test'],
|
||||
])('%s => %s', (string, expectedResult) => {
|
||||
expect(toGraphQLName(string)).toBe(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,6 +19,7 @@ const {
|
||||
stringEquals,
|
||||
isKebabCase,
|
||||
isCamelCase,
|
||||
toGraphQLName,
|
||||
} = require('./string-formatting');
|
||||
const { removeUndefined } = require('./object-formatting');
|
||||
const { getConfigUrls, getAbsoluteAdminUrl, getAbsoluteServerUrl } = require('./config');
|
||||
@ -47,6 +48,7 @@ module.exports = {
|
||||
traverseEntity,
|
||||
parseType,
|
||||
nameToSlug,
|
||||
toGraphQLName,
|
||||
nameToCollectionName,
|
||||
getCommonBeginning,
|
||||
getConfigUrls,
|
||||
|
||||
@ -6,6 +6,13 @@ const nameToSlug = (name, options = { separator: '-' }) => slugify(name, options
|
||||
|
||||
const nameToCollectionName = name => slugify(name, { separator: '_' });
|
||||
|
||||
const toGraphQLName = value =>
|
||||
slugify(value, {
|
||||
decamelize: false,
|
||||
lowercase: false,
|
||||
separator: '_',
|
||||
});
|
||||
|
||||
const getCommonBeginning = (...strings) =>
|
||||
_.takeWhile(strings[0], (char, index) => strings.every(string => string[index] === char)).join(
|
||||
''
|
||||
@ -46,4 +53,5 @@ module.exports = {
|
||||
stringEquals,
|
||||
isCamelCase,
|
||||
isKebabCase,
|
||||
toGraphQLName,
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
const { enumType } = require('nexus');
|
||||
const { set } = require('lodash/fp');
|
||||
const { toGraphQLName } = require('@strapi/utils');
|
||||
|
||||
/**
|
||||
* Build a Nexus enum type from a Strapi enum attribute
|
||||
@ -13,9 +14,7 @@ const { set } = require('lodash/fp');
|
||||
const buildEnumTypeDefinition = (definition, name) => {
|
||||
return enumType({
|
||||
name,
|
||||
// In Strapi V3, the key of an enum is also its value
|
||||
// todo[V4]: allow passing an object of key/value instead of an array
|
||||
members: definition.enum.reduce((acc, value) => set(value, value, acc), {}),
|
||||
members: definition.enum.reduce((acc, value) => set(toGraphQLName(value), value, acc), {}),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user