Merge branch 'develop' of github.com:strapi/strapi into ctm/edit-formadata-upgrade

This commit is contained in:
soupette 2019-08-01 17:24:41 +02:00
commit cb93b4d30b
17 changed files with 334 additions and 56 deletions

View File

@ -7,7 +7,7 @@
},
"options": {
"increments": true,
"timestamps": true,
"timestamps": ["created_at", "updated_at"],
"comment": ""
},
"attributes": {
@ -29,6 +29,15 @@
"number": {
"type": "integer"
},
"big_number": {
"type": "biginteger"
},
"float_number": {
"type": "float"
},
"decimal_number": {
"type": "decimal"
},
"date": {
"type": "date"
},
@ -57,18 +66,16 @@
},
"manyTags": {
"collection": "tag",
"dominant": true,
"via": "linkedArticles"
"via": "linkedArticles",
"dominant": true
},
"fb_cta": {
"type": "group",
"group": "cta_facebook",
"repeatable": false
"group": "cta_facebook"
},
"mainIngredient": {
"type": "group",
"group": "ingredients",
"repeatable": false
"group": "ingredients"
},
"ingredients": {
"type": "group",

View File

@ -18,4 +18,4 @@
"via": "manyTags"
}
}
}
}

View File

@ -12,15 +12,15 @@ import {
import { Wrapper } from './components';
function Add({ data, onClick, pStyle, style }) {
const [state, setState] = useState(false);
const [isOpen, setIsOpen] = useState(false);
return (
<Wrapper isOpen={state} notAllowed={data.length === 0} style={style}>
<Wrapper isOpen={isOpen} notAllowed={data.length === 0} style={style}>
<ButtonDropdown
isOpen={state}
isOpen={isOpen}
toggle={() => {
if (data.length > 0) {
setState(prevState => !prevState);
setIsOpen(prevState => !prevState);
}
}}
>

View File

@ -211,7 +211,6 @@ Group.defaultProps = {
max: Infinity,
min: -Infinity,
modifiedData: {},
onChange: () => {},
};
Group.propTypes = {
@ -227,7 +226,6 @@ Group.propTypes = {
moveGroupField: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
pathname: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
removeField: PropTypes.func.isRequired,
};

View File

@ -59,6 +59,7 @@ function ListItem({
ListItem.defaultProps = {
findRelation: () => {},
moveRelation: () => {},
nextSearch: '',
onRemove: () => {},
targetModel: '',

View File

@ -1,26 +1,6 @@
'use strict';
const yup = require('yup');
const formatYupErrors = require('./utils/yup-formatter');
const groupSchema = yup
.object({
name: yup.string().required('name.required'),
description: yup.string(),
connection: yup.string(),
collectionName: yup.string(),
attributes: yup.object().required('attributes.required'),
})
.noUnknown();
const validateGroupInput = async data =>
groupSchema
.validate(data, {
strict: true,
abortEarly: false,
})
.catch(error => Promise.reject(formatYupErrors(error)));
const validateGroupInput = require('./validation/group');
/**
* Groups controller
*/

View File

@ -0,0 +1,58 @@
'use strict';
const yup = require('yup');
const VALID_TYPES = [
// advanced types
'media',
// scalar types
'string',
'text',
'richtext',
'json',
'enumeration',
'password',
'email',
'integer',
'float',
'decimal',
'date',
'boolean',
];
const validators = {
required: yup.boolean(),
unique: yup.boolean(),
minLength: yup
.number()
.integer()
.positive(),
maxLength: yup
.number()
.integer()
.positive(),
};
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
const isValidName = {
name: 'isValidName',
message: '${path} must match the following regex: /^[_A-Za-z][_0-9A-Za-z]*/^',
test: val => NAME_REGEX.test(val),
};
const isValidKey = key => ({
name: 'isValidKey',
message: `Attribute name '${key}' must match the following regex: /^[_A-Za-z][_0-9A-Za-z]*/^`,
test: () => NAME_REGEX.test(key),
});
module.exports = {
validators,
isValidName,
isValidKey,
VALID_TYPES,
};

View File

@ -0,0 +1,60 @@
'use strict';
const yup = require('yup');
const _ = require('lodash');
const formatYupErrors = require('./yup-formatter');
const { isValidName, isValidKey } = require('./common');
const getTypeValidator = require('./types');
const getRelationValidator = require('./relations');
module.exports = data => {
return groupSchema
.validate(data, {
strict: true,
abortEarly: false,
})
.catch(error => Promise.reject(formatYupErrors(error)));
};
const groupSchema = yup
.object({
name: yup
.string()
.min(1)
.test(isValidName)
.required('name.required'),
description: yup.string(),
connection: yup.string(),
collectionName: yup.string().test(isValidName),
attributes: yup.lazy(obj => {
return yup
.object()
.shape(
_.mapValues(obj, (value, key) => {
return yup.lazy(obj => {
let shape;
if (_.has(obj, 'type')) {
shape = getTypeValidator(obj);
} else if (_.has(obj, 'target')) {
shape = getRelationValidator(obj);
} else {
return yup.object().test({
name: 'mustHaveTypeOrTarget',
message: 'Attribute must have either a type or a target',
test: () => false,
});
}
return yup
.object()
.shape(shape)
.test(isValidKey(key))
.noUnknown();
});
})
)
.required('attributes.required');
}),
})
.noUnknown();

View File

@ -0,0 +1,42 @@
'use strict';
const yup = require('yup');
const _ = require('lodash');
const { validators } = require('./common');
const VALID_NATURES = ['oneWay', 'manyWay'];
module.exports = () => {
return {
target: yup
.mixed()
.when('plugin', plugin => {
if (!plugin)
return yup
.string()
.oneOf(
Object.keys(strapi.models).filter(name => name !== 'core_store')
);
if (plugin === 'admin')
return yup.string().oneOf(Object.keys(strapi.admin.models));
if (plugin)
return yup
.string()
.oneOf(Object.keys(_.get(strapi.plugins, [plugin, 'models'], {})));
})
.required(),
nature: yup
.string()
.oneOf(VALID_NATURES)
.required(),
plugin: yup.string().oneOf(Object.keys(strapi.plugins)),
unique: validators.unique,
// TODO: remove once front-end stop sending them even if useless
columnName: yup.string(),
key: yup.string(),
targetColumnName: yup.string(),
};
};

View File

@ -0,0 +1,126 @@
'use strict';
const yup = require('yup');
const { validators, VALID_TYPES, isValidName } = require('./common');
module.exports = obj => {
return {
type: yup
.string()
.oneOf(VALID_TYPES)
.required(),
...getTypeShape(obj),
};
};
const getTypeShape = obj => {
switch (obj.type) {
/**
* complexe types
*/
case 'media': {
return {
multiple: yup.boolean(),
required: validators.required,
unique: validators.unique,
};
}
/**
* scalar types
*/
case 'string':
case 'text':
case 'richtext': {
return {
default: yup.string(),
required: validators.required,
unique: validators.unique,
min: validators.minLength,
max: validators.maxLength,
};
}
case 'json': {
return {
required: validators.required,
unique: validators.unique,
};
}
case 'enumeration': {
return {
enum: yup
.array()
.of(yup.string().test(isValidName))
.min(1)
.required(),
default: yup
.string()
.when('enum', enumVal => yup.string().oneOf(enumVal)),
enumName: yup.string().test(isValidName),
required: validators.required,
unique: validators.unique,
};
}
case 'password': {
return {
required: validators.required,
min: validators.minLength,
max: validators.maxLength,
};
}
case 'email': {
return {
default: yup.string().email(),
required: validators.required,
unique: validators.unique,
min: validators.minLength,
max: validators.maxLength,
};
}
case 'integer': {
return {
default: yup.number().integer(),
required: validators.required,
unique: validators.unique,
min: yup.number().integer(),
max: yup.number().integer(),
};
}
case 'float': {
return {
default: yup.number(),
required: validators.required,
unique: validators.unique,
min: yup.number(),
max: yup.number(),
};
}
case 'decimal': {
return {
default: yup.number(),
required: validators.required,
unique: validators.unique,
min: yup.number(),
max: yup.number(),
};
}
case 'date': {
return {
default: yup.date(),
required: validators.required,
unique: validators.unique,
};
}
case 'boolean': {
return {
default: yup.boolean(),
required: validators.required,
unique: validators.unique,
};
}
default: {
return {};
}
}
};

View File

@ -201,9 +201,9 @@ const convertAttributes = attributes => {
}
if (_.has(attribute, 'target')) {
const { target, nature, required, unique, plugin } = attribute;
const { target, nature, unique, plugin } = attribute;
// ingore relation which aren't oneWay or manyWay (except for images)
// ingore relation which aren't oneWay or manyWay
if (!['oneWay', 'manyWay'].includes(nature)) {
return acc;
}
@ -211,7 +211,6 @@ const convertAttributes = attributes => {
acc[key] = {
[nature === 'oneWay' ? 'model' : 'collection']: target,
plugin: plugin ? _.trim(plugin) : undefined,
required: required === true ? true : undefined,
unique: unique === true ? true : undefined,
};
}

View File

@ -31,7 +31,7 @@ describe.only('Content Type Builder - Groups', () => {
method: 'POST',
url: '/content-type-builder/groups',
body: {
name: 'some-group',
name: 'SomeGroup',
attributes: {
title: {
type: 'string',
@ -58,7 +58,7 @@ describe.only('Content Type Builder - Groups', () => {
method: 'POST',
url: '/content-type-builder/groups',
body: {
name: 'some-group',
name: 'someGroup',
attributes: {},
},
});
@ -121,7 +121,7 @@ describe.only('Content Type Builder - Groups', () => {
data: {
uid: 'some_group',
schema: {
name: 'some-group',
name: 'SomeGroup',
description: '',
connection: 'default',
collectionName: 'groups_some_groups',
@ -176,7 +176,7 @@ describe.only('Content Type Builder - Groups', () => {
method: 'PUT',
url: '/content-type-builder/groups/some_group',
body: {
name: 'New Group',
name: 'NewGroup',
attributes: {},
},
});

View File

@ -10,6 +10,8 @@ const _ = require('lodash');
module.exports = {
async upload(ctx) {
const uploadService = strapi.plugins.upload.services.upload;
// Retrieve provider configuration.
const config = await strapi
.store({
@ -30,8 +32,8 @@ module.exports = {
}
// Extract optional relational data.
const { refId, ref, source, field, path } = ctx.request.body.fields || {};
const { files = {} } = ctx.request.body.files || {};
const { refId, ref, source, field, path } = ctx.request.body || {};
const { files = {} } = ctx.request.files || {};
if (_.isEmpty(files)) {
return ctx.badRequest(
@ -43,9 +45,8 @@ module.exports = {
}
// Transform stream files to buffer
const buffers = await strapi.plugins.upload.services.upload.bufferize(
ctx.request.body.files.files
);
const buffers = await uploadService.bufferize(files);
const enhancedFiles = buffers.map(file => {
if (file.size > config.sizeLimit) {
return ctx.badRequest(
@ -94,10 +95,7 @@ module.exports = {
return;
}
const uploadedFiles = await strapi.plugins.upload.services.upload.upload(
enhancedFiles,
config
);
const uploadedFiles = await uploadService.upload(enhancedFiles, config);
// Send 200 `ok`
ctx.send(

View File

@ -24,7 +24,7 @@
"inquirer": "^6.2.1",
"kcors": "^2.2.0",
"koa": "^2.1.0",
"koa-body": "^2.5.0",
"koa-body": "^4.1.0",
"koa-compose": "^4.0.0",
"koa-compress": "^2.0.0",
"koa-convert": "^1.2.0",

View File

@ -2236,6 +2236,14 @@
"@types/express-serve-static-core" "*"
"@types/serve-static" "*"
"@types/formidable@^1.0.31":
version "1.0.31"
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
integrity sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==
dependencies:
"@types/events" "*"
"@types/node" "*"
"@types/glob@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
@ -10468,11 +10476,12 @@ knex@^0.19.0:
uuid "^3.3.2"
v8flags "^3.1.3"
koa-body@^2.5.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-2.6.0.tgz#8ed7a192a64a38df610a986342d1801855641a1d"
integrity sha512-8i9ti3TRxelsnPUct0xY8toTFj5gTzGWW45ePBkT8fnzZP75y5woisVpziIdqcnqtt1lMNBD30p+tkiSC+NfjQ==
koa-body@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-4.1.0.tgz#99295ee2e9543884e5730ae696780930b3821c44"
integrity sha512-rWkMfMaCjFmIAMohtjlrg4BqDzcotK5BdZhiwJu1ONuR1ceoFUjnH3wp0hEV39HuBlc9tI3eUUFMK4Cp6ccFtA==
dependencies:
"@types/formidable" "^1.0.31"
co-body "^5.1.1"
formidable "^1.1.1"