mirror of
https://github.com/strapi/strapi.git
synced 2025-08-14 11:48:43 +00:00
Merge branch 'develop' of github.com:strapi/strapi into ctm/edit-formadata-upgrade
This commit is contained in:
commit
cb93b4d30b
@ -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",
|
||||
|
@ -18,4 +18,4 @@
|
||||
"via": "manyTags"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -59,6 +59,7 @@ function ListItem({
|
||||
|
||||
ListItem.defaultProps = {
|
||||
findRelation: () => {},
|
||||
moveRelation: () => {},
|
||||
nextSearch: '',
|
||||
onRemove: () => {},
|
||||
targetModel: '',
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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,
|
||||
};
|
@ -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();
|
@ -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(),
|
||||
};
|
||||
};
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
};
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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: {},
|
||||
},
|
||||
});
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
17
yarn.lock
17
yarn.lock
@ -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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user