Add kind property to content-type-builder

Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
Alexandre Bodin 2020-01-14 18:04:07 +01:00
parent ff1b249d31
commit 57122c4acb
9 changed files with 211 additions and 123 deletions

View File

@ -5,10 +5,19 @@ const _ = require('lodash');
const { const {
validateContentTypeInput, validateContentTypeInput,
validateUpdateContentTypeInput, validateUpdateContentTypeInput,
validateKind,
} = require('./validation/content-type'); } = require('./validation/content-type');
module.exports = { module.exports = {
getContentTypes(ctx) { async getContentTypes(ctx) {
const { kind } = ctx.query;
try {
await validateKind(kind);
} catch (error) {
return ctx.send({ error }, 400);
}
const contentTypeService = const contentTypeService =
strapi.plugins['content-type-builder'].services.contenttypes; strapi.plugins['content-type-builder'].services.contenttypes;
@ -17,6 +26,13 @@ module.exports = {
if (uid.startsWith('strapi::')) return false; if (uid.startsWith('strapi::')) return false;
if (uid === 'plugins::upload.file') return false; // TODO: add a flag in the content type instead if (uid === 'plugins::upload.file') return false; // TODO: add a flag in the content type instead
if (
kind &&
_.get(strapi.contentTypes[uid], 'kind', 'collectionType') !== kind
) {
return false;
}
return true; return true;
}) })
.map(uid => .map(uid =>

View File

@ -6,6 +6,7 @@ const yup = require('yup');
const { isValidCategoryName, isValidIcon } = require('./common'); const { isValidCategoryName, isValidIcon } = require('./common');
const formatYupErrors = require('./yup-formatter'); const formatYupErrors = require('./yup-formatter');
const createSchema = require('./model-schema'); const createSchema = require('./model-schema');
const removeEmptyDefaults = require('./remove-empty-defaults');
const { modelTypes, DEFAULT_TYPES } = require('./constants'); const { modelTypes, DEFAULT_TYPES } = require('./constants');
const VALID_RELATIONS = ['oneWay', 'manyWay']; const VALID_RELATIONS = ['oneWay', 'manyWay'];
@ -63,26 +64,7 @@ const validateComponentInput = data => {
}; };
const validateUpdateComponentInput = data => { const validateUpdateComponentInput = data => {
// convert zero length string on default attributes to undefined removeEmptyDefaults(data);
if (_.has(data, ['component', 'attributes'])) {
Object.keys(data.component.attributes).forEach(attribute => {
if (data.component.attributes[attribute].default === '') {
data.component.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) {
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return yup return yup
.object({ .object({

View File

@ -3,6 +3,9 @@
const CONTENT_TYPE = 'CONTENT_TYPE'; const CONTENT_TYPE = 'CONTENT_TYPE';
const COMPONENT = 'COMPONENT'; const COMPONENT = 'COMPONENT';
const SINGLE_TYPE = 'singleType';
const COLLECTION_TYPE = 'collectionType';
const DEFAULT_TYPES = [ const DEFAULT_TYPES = [
// advanced types // advanced types
'media', 'media',
@ -28,8 +31,15 @@ const DEFAULT_TYPES = [
const FORBIDDEN_ATTRIBUTE_NAMES = ['__component', '__contentType']; const FORBIDDEN_ATTRIBUTE_NAMES = ['__component', '__contentType'];
const CONTENT_TYPE_KINDS = [SINGLE_TYPE, COLLECTION_TYPE];
module.exports = { module.exports = {
DEFAULT_TYPES, DEFAULT_TYPES,
CONTENT_TYPE_KINDS,
typeKinds: {
SINGLE_TYPE,
COLLECTION_TYPE,
},
modelTypes: { modelTypes: {
CONTENT_TYPE, CONTENT_TYPE,
COMPONENT, COMPONENT,

View File

@ -2,35 +2,66 @@
const _ = require('lodash'); const _ = require('lodash');
const yup = require('yup'); const yup = require('yup');
const formatYupErrors = require('./yup-formatter'); const formatYupErrors = require('./yup-formatter');
const createSchema = require('./model-schema'); const createSchema = require('./model-schema');
const removeEmptyDefaults = require('./remove-empty-defaults');
const { nestedComponentSchema } = require('./component'); const { nestedComponentSchema } = require('./component');
const { modelTypes, DEFAULT_TYPES } = require('./constants'); const {
modelTypes,
DEFAULT_TYPES,
CONTENT_TYPE_KINDS,
typeKinds,
} = require('./constants');
const VALID_RELATIONS = [ /**
* Allowed relation per type kind
*/
const VALID_RELATIONS = {
[typeKinds.SINGLE_TYPE]: ['oneWay', 'manyWay'],
[typeKinds.COLLECTION_TYPE]: [
'oneWay', 'oneWay',
'manyWay', 'manyWay',
'oneToOne', 'oneToOne',
'oneToMany', 'oneToMany',
'manyToOne', 'manyToOne',
'manyToMany', 'manyToMany',
]; ],
};
/**
* Allowed types
*/
const VALID_TYPES = [...DEFAULT_TYPES, 'component', 'dynamiczone']; const VALID_TYPES = [...DEFAULT_TYPES, 'component', 'dynamiczone'];
const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS, { /**
modelType: modelTypes.CONTENT_TYPE, * Returns a yup schema to validate a content type payload
}); * @param {Object} data payload
*/
const createContentTypeSchema = data => {
const kind = _.get(data, 'kind', typeKinds.COLLECTION_TYPE);
const createContentTypeSchema = yup const contentTypeSchema = createSchema(
VALID_TYPES,
VALID_RELATIONS[kind] || [],
{
modelType: modelTypes.CONTENT_TYPE,
}
);
return yup
.object({ .object({
contentType: contentTypeSchema.required().noUnknown(), contentType: contentTypeSchema.required().noUnknown(),
components: nestedComponentSchema, components: nestedComponentSchema,
}) })
.noUnknown(); .noUnknown();
};
/**
* Validator for content type creation
*/
const validateContentTypeInput = data => { const validateContentTypeInput = data => {
return createContentTypeSchema return createContentTypeSchema(data)
.validate(data, { .validate(data, {
strict: true, strict: true,
abortEarly: false, abortEarly: false,
@ -38,29 +69,13 @@ const validateContentTypeInput = data => {
.catch(error => Promise.reject(formatYupErrors(error))); .catch(error => Promise.reject(formatYupErrors(error)));
}; };
/**
* Validator for content type edition
*/
const validateUpdateContentTypeInput = data => { const validateUpdateContentTypeInput = data => {
// convert zero length string on default attributes to undefined removeEmptyDefaults(data);
if (_.has(data, 'attributes')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) { return createContentTypeSchema(data)
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return createContentTypeSchema
.validate(data, { .validate(data, {
strict: true, strict: true,
abortEarly: false, abortEarly: false,
@ -68,7 +83,20 @@ const validateUpdateContentTypeInput = data => {
.catch(error => Promise.reject(formatYupErrors(error))); .catch(error => Promise.reject(formatYupErrors(error)));
}; };
/**
* Validates type kind
*/
const validateKind = kind => {
return yup
.string()
.oneOf(CONTENT_TYPE_KINDS)
.nullable()
.validate(kind)
.catch(error => Promise.reject(formatYupErrors(error)));
};
module.exports = { module.exports = {
validateContentTypeInput, validateContentTypeInput,
validateUpdateContentTypeInput, validateUpdateContentTypeInput,
validateKind,
}; };

View File

@ -3,14 +3,17 @@
const _ = require('lodash'); const _ = require('lodash');
const yup = require('yup'); const yup = require('yup');
const { FORBIDDEN_ATTRIBUTE_NAMES } = require('./constants'); const {
modelTypes,
FORBIDDEN_ATTRIBUTE_NAMES,
CONTENT_TYPE_KINDS,
} = require('./constants');
const { isValidCollectionName, isValidKey } = require('./common'); const { isValidCollectionName, isValidKey } = require('./common');
const { getTypeShape } = require('./types'); const { getTypeShape } = require('./types');
const getRelationValidator = require('./relations'); const getRelationValidator = require('./relations');
const createSchema = (types, relations, { modelType } = {}) => const createSchema = (types, relations, { modelType } = {}) => {
yup const schema = yup.object({
.object({
name: yup name: yup
.string() .string()
.min(1) .min(1)
@ -68,7 +71,20 @@ const createSchema = (types, relations, { modelType } = {}) =>
) )
.required('attributes.required'); .required('attributes.required');
}), }),
});
if (modelType === modelTypes.CONTENT_TYPE) {
return schema
.shape({
kind: yup
.string()
.oneOf(CONTENT_TYPE_KINDS)
.required('contentType.kind.required'),
}) })
.noUnknown(); .noUnknown();
}
return schema.noUnknown();
};
module.exports = createSchema; module.exports = createSchema;

View File

@ -0,0 +1,30 @@
'use strict';
const _ = require('lodash');
/**
* Convert zero length string on default attributes to undefined
*/
module.exports = data => {
if (_.has(data, 'attributes')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) {
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return data;
};

View File

@ -17,7 +17,7 @@ const { nameToSlug } = require('../utils/helpers');
* @param {Object} contentType * @param {Object} contentType
*/ */
const formatContentType = contentType => { const formatContentType = contentType => {
const { uid, plugin, connection, collectionName, info } = contentType; const { uid, kind, plugin, connection, collectionName, info } = contentType;
return { return {
uid, uid,
@ -26,6 +26,7 @@ const formatContentType = contentType => {
name: _.get(info, 'name') || _.upperFirst(pluralize(uid)), name: _.get(info, 'name') || _.upperFirst(pluralize(uid)),
description: _.get(info, 'description', ''), description: _.get(info, 'description', ''),
connection, connection,
kind: kind || 'collectionType',
collectionName, collectionName,
attributes: formatAttributes(contentType), attributes: formatAttributes(contentType),
}, },

View File

@ -80,6 +80,7 @@ module.exports = function createComponentBuilder() {
contentType contentType
.setUID(uid) .setUID(uid)
.set('connection', infos.connection || defaultConnection) .set('connection', infos.connection || defaultConnection)
.set('kind', infos.kind)
.set('collectionName', infos.collectionName || defaultCollectionName) .set('collectionName', infos.collectionName || defaultCollectionName)
.set(['info', 'name'], infos.name) .set(['info', 'name'], infos.name)
.set(['info', 'description'], infos.description) .set(['info', 'description'], infos.description)
@ -197,9 +198,12 @@ module.exports = function createComponentBuilder() {
} }
}); });
// TODO: handle kind change => update routes.json file somehow
contentType contentType
.set('connection', infos.connection) .set('connection', infos.connection)
.set('collectionName', infos.collectionName) .set('collectionName', infos.collectionName)
.set('kind', infos.kind)
.set(['info', 'name'], infos.name) .set(['info', 'name'], infos.name)
.set(['info', 'description'], infos.description) .set(['info', 'description'], infos.description)
.setAttributes(this.convertAttributes(newAttributes)); .setAttributes(this.convertAttributes(newAttributes));

View File

@ -16,6 +16,7 @@ module.exports = function createSchemaHandler(infos) {
dir, dir,
filename, filename,
schema: schema || { schema: schema || {
kind: undefined,
info: {}, info: {},
options: {}, options: {},
attributes: {}, attributes: {},