Cleanup CTB

This commit is contained in:
Alexandre Bodin 2021-06-30 22:52:12 +02:00
parent 1122d24c90
commit 2d59716f5d
30 changed files with 244 additions and 207 deletions

View File

@ -32,10 +32,8 @@
"inversedBy": "addresses"
},
"cover": {
"model": "file",
"via": "related",
"type": "media",
"allowedTypes": ["files", "images", "videos"],
"plugin": "upload",
"required": false,
"pluginOptions": {
"i18n": {
@ -44,10 +42,9 @@
}
},
"images": {
"collection": "file",
"via": "related",
"type": "media",
"multiple": true,
"allowedTypes": ["images"],
"plugin": "upload",
"required": false,
"pluginOptions": {
"i18n": {

View File

@ -35,17 +35,14 @@
}
},
"single": {
"model": "file",
"via": "related",
"type": "media",
"allowedTypes": ["images", "files", "videos"],
"plugin": "upload",
"required": false
},
"multiple": {
"collection": "file",
"via": "related",
"type": "media",
"multiple": true,
"allowedTypes": ["images", "videos"],
"plugin": "upload",
"required": false
}
}

View File

@ -46,8 +46,8 @@
}
}
},
"category_compo": {
"component": "application::category.category",
"closing_period": {
"component": "default.closingperiod",
"type": "component",
"pluginOptions": {
"i18n": {
@ -80,18 +80,15 @@
"target": "application::address.address"
},
"cover": {
"model": "file",
"via": "related",
"plugin": "upload",
"type": "media",
"required": false
},
"timestamp": {
"type": "timestamp"
},
"images": {
"collection": "file",
"via": "related",
"plugin": "upload",
"type": "media",
"multiple": true,
"required": false
},
"short_description": {

View File

@ -18,9 +18,7 @@
"required": true
},
"media": {
"model": "file",
"via": "related",
"plugin": "upload",
"type": "media",
"required": false
},
"dish": {

View File

@ -18,9 +18,7 @@
"type": "float"
},
"picture": {
"model": "file",
"via": "related",
"plugin": "upload",
"type": "media",
"required": false
},
"very_long_description": {

View File

@ -12,9 +12,7 @@
"default": "something"
},
"media": {
"model": "file",
"via": "related",
"plugin": "upload"
"type": "media"
},
"is_available": {
"type": "boolean",

View File

@ -60,9 +60,7 @@
"configurable": false
},
"picture": {
"model": "file",
"via": "related",
"plugin": "upload"
"type": "media"
}
}
}

View File

@ -22,6 +22,10 @@ module.exports = {
toContentManagerModel(contentType) {
return {
...contentType,
options: {
...contentType.options,
timestamps: [],
},
apiID: contentType.modelName,
isDisplayed: isVisible(contentType),
info: {
@ -30,7 +34,7 @@ module.exports = {
},
attributes: {
id: {
type: contentType.primaryKeyType,
type: 'integer',
},
...formatAttributes(contentType),
...contentTypesUtils.getTimestampsAttributes(contentType),
@ -66,8 +70,11 @@ const formatAttributes = model => {
// FIXME: not needed
const formatAttribute = (key, attribute, { model }) => {
return attribute;
if (attribute.type === 'relation') {
return toRelation(attribute);
}
return attribute;
// if (has('type', attribute)) return attribute;
@ -91,12 +98,12 @@ const toMedia = attribute => {
};
// FIXME: not needed
const toRelation = (attribute, relation) => {
const toRelation = attribute => {
return {
...attribute,
type: 'relation',
targetModel: relation.targetUid,
relationType: relation.nature,
targetModel: attribute.target,
relationType: attribute.relation,
pluginOptions: attribute.pluginOptions,
};
};

View File

@ -0,0 +1,23 @@
import * as components from '../services/components';
import * as configuration from '../services/configuration';
import * as contentTypes from '../services/content-types';
import * as dataMapper from '../services/data-mapper';
import * as entityManager from '../services/entity-manager';
import * as metris from '../services/metris';
import * as permissionChecker from '../services/permission-checker';
import * as permission from '../services/permission';
import * as uid from '../services/uid';
type S = {
['content-types']: typeof contentTypes;
['data-mapper']: typeof dataMapper;
['entity-manager']: typeof entityManager;
['permission-checker']: typeof permissionChecker;
components: typeof components;
configuration: typeof configuration;
metris: typeof metris;
permission: typeof permission;
uid: typeof uid;
};
export function getService<T extends keyof S>(name: T): S[T];

View File

@ -3,7 +3,7 @@
{
"method": "GET",
"path": "/reserved-names",
"handler": "Builder.getReservedNames",
"handler": "builder.getReservedNames",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -11,7 +11,7 @@
{
"method": "GET",
"path": "/content-types",
"handler": "ContentTypes.getContentTypes",
"handler": "content-types.getContentTypes",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -19,7 +19,7 @@
{
"method": "GET",
"path": "/content-types/:uid",
"handler": "ContentTypes.getContentType",
"handler": "content-types.getContentType",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -27,7 +27,7 @@
{
"method": "POST",
"path": "/content-types",
"handler": "ContentTypes.createContentType",
"handler": "content-types.createContentType",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -35,7 +35,7 @@
{
"method": "PUT",
"path": "/content-types/:uid",
"handler": "ContentTypes.updateContentType",
"handler": "content-types.updateContentType",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -43,7 +43,7 @@
{
"method": "DELETE",
"path": "/content-types/:uid",
"handler": "ContentTypes.deleteContentType",
"handler": "content-types.deleteContentType",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -51,7 +51,7 @@
{
"method": "GET",
"path": "/components",
"handler": "Components.getComponents",
"handler": "components.getComponents",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -59,7 +59,7 @@
{
"method": "GET",
"path": "/components/:uid",
"handler": "Components.getComponent",
"handler": "components.getComponent",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -67,7 +67,7 @@
{
"method": "POST",
"path": "/components",
"handler": "Components.createComponent",
"handler": "components.createComponent",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -75,7 +75,7 @@
{
"method": "PUT",
"path": "/components/:uid",
"handler": "Components.updateComponent",
"handler": "components.updateComponent",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -83,7 +83,7 @@
{
"method": "DELETE",
"path": "/components/:uid",
"handler": "Components.deleteComponent",
"handler": "components.deleteComponent",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -91,7 +91,7 @@
{
"method": "PUT",
"path": "/component-categories/:name",
"handler": "ComponentCategories.editCategory",
"handler": "component-categories.editCategory",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}
@ -99,7 +99,7 @@
{
"method": "DELETE",
"path": "/component-categories/:name",
"handler": "ComponentCategories.deleteCategory",
"handler": "component-categories.deleteCategory",
"config": {
"policies": [["admin::hasPermissions", ["plugins::content-type-builder.read"]]]
}

View File

@ -1,7 +0,0 @@
'use strict';
module.exports = {
getReservedNames(ctx) {
ctx.body = strapi.plugins['content-type-builder'].services.builder.getReservedNames();
},
};

View File

@ -0,0 +1,9 @@
'use strict';
const { getService } = require('../utils');
module.exports = {
getReservedNames(ctx) {
ctx.body = getService('builder').getReservedNames();
},
};

View File

@ -1,5 +1,6 @@
'use strict';
const { getService } = require('../utils');
const validateComponentCategory = require('./validation/component-category');
module.exports = {
@ -16,8 +17,7 @@ module.exports = {
strapi.reload.isWatching = false;
const componentCategoryService =
strapi.plugins['content-type-builder'].services.componentcategories;
const componentCategoryService = getService('component-categories');
const newName = await componentCategoryService.editCategory(name, body);
@ -31,8 +31,7 @@ module.exports = {
strapi.reload.isWatching = false;
const componentCategoryService =
strapi.plugins['content-type-builder'].services.componentcategories;
const componentCategoryService = getService('component-categories');
await componentCategoryService.deleteCategory(name);

View File

@ -2,6 +2,7 @@
const _ = require('lodash');
const { getService } = require('../utils');
const { validateComponentInput, validateUpdateComponentInput } = require('./validation/component');
/**
@ -15,7 +16,7 @@ module.exports = {
* @param {Object} ctx - koa context
*/
async getComponents(ctx) {
const componentService = strapi.plugins['content-type-builder'].services.components;
const componentService = getService('components');
const data = Object.keys(strapi.components).map(uid => {
return componentService.formatComponent(strapi.components[uid]);
@ -38,7 +39,7 @@ module.exports = {
return ctx.send({ error: 'component.notFound' }, 404);
}
const componentService = strapi.plugins['content-type-builder'].services.components;
const componentService = getService('components');
ctx.send({ data: componentService.formatComponent(component) });
},
@ -60,7 +61,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const componentService = strapi.plugins['content-type-builder'].services.components;
const componentService = getService('components');
const component = await componentService.createComponent({
component: body.component,
@ -98,7 +99,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const componentService = strapi.plugins['content-type-builder'].services.components;
const componentService = getService('components');
const component = await componentService.editComponent(uid, {
component: body.component,
@ -129,7 +130,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const componentService = strapi.plugins['content-type-builder'].services.components;
const componentService = getService('components');
const component = await componentService.deleteComponent(uid);

View File

@ -3,7 +3,7 @@
const _ = require('lodash');
const { hasDraftAndPublish } = require('@strapi/utils').contentTypes;
const { getService } = require('../utils');
const {
validateContentTypeInput,
validateUpdateContentTypeInput,
@ -20,7 +20,7 @@ module.exports = {
return ctx.send({ error }, 400);
}
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = getService('content-types');
const contentTypes = Object.keys(strapi.contentTypes)
.filter(uid => !kind || _.get(strapi.contentTypes[uid], 'kind', 'collectionType') === kind)
@ -40,7 +40,7 @@ module.exports = {
return ctx.send({ error: 'contentType.notFound' }, 404);
}
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = getService('content-types');
ctx.send({ data: contentTypeService.formatContentType(contentType) });
},
@ -57,7 +57,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = getService('content-types');
const contentType = await contentTypeService.createContentType({
contentType: body.contentType,
@ -102,7 +102,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = getService('content-types');
const component = await contentTypeService.editContentType(uid, {
contentType: body.contentType,
@ -128,7 +128,7 @@ module.exports = {
try {
strapi.reload.isWatching = false;
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = getService('content-types');
const component = await contentTypeService.deleteContentType(uid);

View File

@ -5,6 +5,7 @@ const yup = require('yup');
const { formatYupErrors, nameToSlug } = require('@strapi/utils');
const pluralize = require('pluralize');
const { getService } = require('../../utils');
const { modelTypes, DEFAULT_TYPES, typeKinds } = require('../../services/constants');
const createSchema = require('./model-schema');
const { removeEmptyDefaults, removeDeletedUIDTargetFields } = require('./data-transform');
@ -96,8 +97,7 @@ const validateUpdateContentTypeInput = data => {
};
const forbiddenContentTypeNameValidator = () => {
const reservedNames = strapi.plugins['content-type-builder'].services.builder.getReservedNames()
.models;
const reservedNames = getService('builder').getReservedNames().models;
return {
name: 'forbiddenContentTypeName',

View File

@ -4,6 +4,7 @@ const _ = require('lodash');
const yup = require('yup');
const { modelTypes, FORBIDDEN_ATTRIBUTE_NAMES, typeKinds } = require('../../services/constants');
const { getService } = require('../../utils');
const { isValidCollectionName, isValidKey } = require('./common');
const getTypeValidator = require('./types');
const getRelationValidator = require('./relations');
@ -64,14 +65,14 @@ const createAttributesValidator = ({ types, modelType, relations }) => {
const isForbiddenKey = key => {
return [
...FORBIDDEN_ATTRIBUTE_NAMES,
...strapi.plugins['content-type-builder'].services.builder.getReservedNames().attributes,
...getService('builder').getReservedNames().attributes,
].includes(key);
};
const forbiddenValidator = () => {
const reservedNames = [
...FORBIDDEN_ATTRIBUTE_NAMES,
...strapi.plugins['content-type-builder'].services.builder.getReservedNames().attributes,
...getService('builder').getReservedNames().attributes,
];
return yup.mixed().test({

View File

@ -1,7 +1,9 @@
'use strict';
module.exports = {
// TODO: Implement
getReservedNames() {
return strapi.db.getReservedNames();
return [];
// strapi.db.getReservedNames();
},
};

View File

@ -57,42 +57,39 @@ const formatAttributes = model => {
* @param {Object} context.component - the associated component
*/
const formatAttribute = (key, attribute, { model }) => {
if (_.has(attribute, 'type')) return attribute;
// FIXME: remove
// format relations
const relation = (model.associations || []).find(assoc => assoc.alias === key);
const { plugin, configurable } = attribute;
let targetEntity = attribute.model || attribute.collection;
if (isMediaAttribute(attribute)) {
const { configurable, required, autoPopulate, pluginOptions } = attribute;
if (attribute.type === 'media') {
return {
type: 'media',
multiple: attribute.collection ? true : false,
required: attribute.required ? true : false,
multiple: attribute.multiple ? true : false,
required: required ? true : false,
configurable: configurable === false ? false : undefined,
allowedTypes: attribute.allowedTypes,
pluginOptions: attribute.pluginOptions,
};
} else {
return {
nature: relation.nature,
target: targetEntity === '*' ? targetEntity : toUID(targetEntity, plugin),
plugin: plugin || undefined,
dominant: attribute.dominant ? true : false,
targetAttribute: attribute.via || undefined,
columnName: attribute.columnName || undefined,
configurable: configurable === false ? false : undefined,
targetColumnName: _.get(
strapi.getModel(targetEntity, plugin),
['attributes', attribute.via, 'columnName'],
undefined
),
private: attribute.private ? true : false,
unique: attribute.unique ? true : false,
autoPopulate: attribute.autoPopulate,
pluginOptions: attribute.pluginOptions,
pluginOptions,
};
}
if (attribute.type === 'relation') {
return {
...attribute,
type: 'relation',
nature: attribute.relation,
target: attribute.target,
required: required ? true : false,
configurable: configurable === false ? false : undefined,
private: attribute.private ? true : false,
unique: attribute.unique ? true : false,
// FIXME: remove
autoPopulate,
pluginOptions,
};
}
return attribute;
};
// TODO: move to schema builder

View File

@ -0,0 +1,11 @@
import * as components from '../services/components';
import * as builder from '../services/builder';
import * as contentTypes from '../services/content-types';
type S = {
['content-types']: typeof contentTypes;
components: typeof components;
builder: typeof builder;
};
export function getService<T extends keyof S>(name: T): S[T];

View File

@ -0,0 +1,12 @@
'use strict';
const { prop } = require('lodash/fp');
// retrieve a local service
const getService = name => {
return prop(`content-type-builder.services.${name}`, strapi.plugins);
};
module.exports = {
getService,
};

View File

@ -44,6 +44,10 @@ const createJoinColum = (metadata, { attribute /*attributeName, meta */ }) => {
const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
const targetMeta = metadata.get(attribute.target);
if (!targetMeta) {
throw new Error(`Unknow target ${attribute.target}`);
}
const joinTableName = _.snakeCase(`${meta.tableName}_${attributeName}_links`);
const joinColumnName = _.snakeCase(`${meta.singularName}_id`);
@ -155,7 +159,7 @@ const createMetadata = (models = []) => {
const metadata = new Metadata();
// init pass
for (const model of models) {
for (const model of _.cloneDeep(models)) {
metadata.add({
singularName: model.singularName,
uid: model.uid,
@ -218,6 +222,7 @@ const createMetadata = (models = []) => {
continue;
}
} catch (error) {
console.error(error);
throw new Error(
`Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}`
);

View File

@ -6,9 +6,9 @@ const transformAttribute = attribute => {
// convert to relation
return {
type: 'relation',
relation: attribute.single === true ? 'morphOne' : 'morphMany',
target: 'file',
morphOn: 'related',
relation: attribute.single === true ? 'manyToOne' : 'manyToMany', //'morphOne' : 'morphMany',
target: 'plugins::upload.file',
// morphOn: 'related',
};
}
// case 'component': {
@ -30,11 +30,11 @@ const transformContentTypes = contentTypes => {
singularName: contentType.modelName,
tableName: contentType.collectionName,
attributes: {
created_at: {
createdAt: {
type: 'datetime',
default: () => new Date(),
// default: () => new Date(),
},
updated_at: {
updatedAt: {
type: 'datetime',
},
...Object.keys(contentType.attributes).reduce((attrs, attrName) => {

View File

@ -28,11 +28,6 @@ const Initializer = ({ setPlugin }) => {
const fileModel = data.find(model => model.uid === 'plugins::upload.file');
const timestamps = get(fileModel, ['options', 'timestamps']);
// All connectors must initialise the "timestamps" option as a tuple
if (!Array.isArray(timestamps) || timestamps.length !== 2) {
throw new Error('Unexpected timestamp field configuration.');
}
dispatch(setFileModelTimestamps(timestamps));
ref.current(pluginId);

View File

@ -4,4 +4,90 @@
* Lifecycle callbacks for the `File` model.
*/
module.exports = {};
module.exports = {
collectionName: 'files',
info: {
name: 'file',
description: '',
},
options: {
timestamps: true,
},
pluginOptions: {
'content-manager': {
visible: false,
},
'content-type-builder': {
visible: false,
},
},
attributes: {
name: {
type: 'string',
configurable: false,
required: true,
},
alternativeText: {
type: 'string',
configurable: false,
},
caption: {
type: 'string',
configurable: false,
},
width: {
type: 'integer',
configurable: false,
},
height: {
type: 'integer',
configurable: false,
},
formats: {
type: 'json',
configurable: false,
},
hash: {
type: 'string',
configurable: false,
required: true,
},
ext: {
type: 'string',
configurable: false,
},
mime: {
type: 'string',
configurable: false,
required: true,
},
size: {
type: 'decimal',
configurable: false,
required: true,
},
url: {
type: 'string',
configurable: false,
required: true,
},
previewUrl: {
type: 'string',
configurable: false,
},
provider: {
type: 'string',
configurable: false,
required: true,
},
provider_metadata: {
type: 'json',
configurable: false,
},
// related: {
// collection: '*',
// filter: 'field',
// configurable: false,
// },
},
};

View File

@ -1,87 +0,0 @@
{
"collectionName": "files",
"info": {
"name": "file",
"description": ""
},
"options": {
"timestamps": true
},
"pluginOptions": {
"content-manager": {
"visible": false
},
"content-type-builder": {
"visible": false
}
},
"attributes": {
"name": {
"type": "string",
"configurable": false,
"required": true
},
"alternativeText": {
"type": "string",
"configurable": false
},
"caption": {
"type": "string",
"configurable": false
},
"width": {
"type": "integer",
"configurable": false
},
"height": {
"type": "integer",
"configurable": false
},
"formats": {
"type": "json",
"configurable": false
},
"hash": {
"type": "string",
"configurable": false,
"required": true
},
"ext": {
"type": "string",
"configurable": false
},
"mime": {
"type": "string",
"configurable": false,
"required": true
},
"size": {
"type": "decimal",
"configurable": false,
"required": true
},
"url": {
"type": "string",
"configurable": false,
"required": true
},
"previewUrl": {
"type": "string",
"configurable": false
},
"provider": {
"type": "string",
"configurable": false,
"required": true
},
"provider_metadata": {
"type": "json",
"configurable": false
},
"related": {
"collection": "*",
"filter": "field",
"configurable": false
}
}
}

View File

@ -337,7 +337,7 @@ module.exports = {
await Promise.all(
toRemove.map(permission => {
const { type, controller, action, roleId } = permission;
return query.delete({ type, controller, action, role: { id: roleId } });
return query.delete({ where: { type, controller, action, role: { id: roleId } } });
})
);
}

View File

@ -5,7 +5,7 @@ const { createStrapiInstance } = require('./strapi');
const createHelpers = async ({ strapi: strapiInstance = null, ...options } = {}) => {
const strapi = strapiInstance || (await createStrapiInstance(options));
const contentTypeService = strapi.plugins['content-type-builder'].services.contenttypes;
const contentTypeService = strapi.plugins['content-type-builder'].services['content-types'];
const componentsService = strapi.plugins['content-type-builder'].services.components;
const cleanup = async () => {