From f255d0d7002253914557b2ff1c2975f28f268c20 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Wed, 20 Nov 2019 14:42:53 +0100 Subject: [PATCH] init edit and delete content type with schema manager --- packages/strapi-generate-api/lib/before.js | 30 +++-- .../controllers/Components.js | 3 + .../controllers/ContentTypes.js | 114 +++------------- .../services/ContentTypes.js | 127 +++++++++++------- .../schema-manager/component-builder.js | 17 +-- .../schema-manager/content-type-builder.js | 50 ++++++- .../services/schema-manager/schema-handler.js | 15 +++ 7 files changed, 191 insertions(+), 165 deletions(-) diff --git a/packages/strapi-generate-api/lib/before.js b/packages/strapi-generate-api/lib/before.js index 7f2ebf5039..2d0944d165 100644 --- a/packages/strapi-generate-api/lib/before.js +++ b/packages/strapi-generate-api/lib/before.js @@ -127,20 +127,24 @@ module.exports = (scope, cb) => { // Get default connection try { - scope.connection = - _.get(scope.args, 'connection') || - JSON.parse( - fs.readFileSync( - path.resolve( - scope.rootPath, - 'config', - 'environments', - environment, - 'database.json' + scope.connection = scope.args.connection; + if (!scope.args.connection) { + try { + scope.connection = JSON.parse( + fs.readFileSync( + path.resolve( + scope.rootPath, + 'config', + 'environments', + environment, + 'database.json' + ) ) - ) - ).defaultConnection || - ''; + ).defaultConnection; + } catch (err) { + scope.connection = 'default'; + } + } } catch (err) { return cb.invalid(err); } diff --git a/packages/strapi-plugin-content-type-builder/controllers/Components.js b/packages/strapi-plugin-content-type-builder/controllers/Components.js index f7644ef700..ce59b57ca3 100644 --- a/packages/strapi-plugin-content-type-builder/controllers/Components.js +++ b/packages/strapi-plugin-content-type-builder/controllers/Components.js @@ -67,6 +67,7 @@ module.exports = { ctx.send({ data: { uid: component.uid } }, 201); } catch (error) { + strapi.log.error(error); ctx.send({ error: error.message }, 400); } }, @@ -98,6 +99,7 @@ module.exports = { ctx.send({ data: { uid: component.uid } }); } catch (error) { + strapi.log.error(error); ctx.send({ error: error.message }, 400); } }, @@ -119,6 +121,7 @@ module.exports = { ctx.send({ data: { uid: component.uid } }); } catch (error) { + strapi.log.error(error); ctx.send({ error: error.message }, 400); } }, diff --git a/packages/strapi-plugin-content-type-builder/controllers/ContentTypes.js b/packages/strapi-plugin-content-type-builder/controllers/ContentTypes.js index 3ca7f231b9..5e31c17886 100644 --- a/packages/strapi-plugin-content-type-builder/controllers/ContentTypes.js +++ b/packages/strapi-plugin-content-type-builder/controllers/ContentTypes.js @@ -66,127 +66,53 @@ module.exports = { ctx.send({ data: { uid: component.uid } }, 201); } catch (error) { + strapi.log.error(error); strapi.emit('didNotCreateContentType', error); ctx.send({ error: error.message }, 400); } - - // strapi.reload.isWatching = false; - - // try { - // const contentTypeSchema = contentTypeService.createContentTypeSchema( - // body.contentType - // ); - - // await contentTypeService.generateAPI(modelName, contentTypeSchema); - - // await contentTypeService.generateReversedRelations({ - // attributes: body.contentType.attributes, - // modelName, - // }); - - // if (_.isEmpty(strapi.api)) { - // strapi.emit('didCreateFirstContentType'); - // } else { - // strapi.emit('didCreateContentType'); - // } - // } catch (e) { - // strapi.log.error(e); - // strapi.emit('didNotCreateContentType', e); - // return ctx.badRequest(null, [ - // { messages: [{ id: 'request.error.model.write' }] }, - // ]); - // } - - // setImmediate(() => strapi.reload()); - - // ctx.send( - // { - // data: { - // uid, - // }, - // }, - // 201 - // ); }, async updateContentType(ctx) { const { uid } = ctx.params; const { body } = ctx.request; - const contentType = strapi.contentTypes[uid]; - - if (!contentType) { - return ctx.send({ error: 'contentType.notFound' }, 404); - } - try { await validateUpdateContentTypeInput(body); } catch (error) { return ctx.send({ error }, 400); } - strapi.reload.isWatching = false; - try { - const newSchema = contentTypeService.updateContentTypeSchema( - contentType.__schema__, - body.contentType - ); + strapi.reload.isWatching = false; - await contentTypeService.writeContentType({ uid, schema: newSchema }); - - // delete all relations directed to the updated ct except for oneWay and manyWay - await contentTypeService.deleteBidirectionalRelations(contentType); - - await contentTypeService.generateReversedRelations({ - attributes: body.contentType.attributes, - modelName: contentType.modelName, - plugin: contentType.plugin, + const component = await contentTypeService.editContentType(uid, { + contentType: body.contentType, + components: body.components, }); - if (_.isEmpty(strapi.api)) { - strapi.emit('didCreateFirstContentType'); - } else { - strapi.emit('didCreateContentType'); - } + setImmediate(() => strapi.reload()); + + ctx.send({ data: { uid: component.uid } }, 201); } catch (error) { - strapi.emit('didNotCreateContentType', error); - throw error; + strapi.log.error(error); + ctx.send({ error: error.message }, 400); } - - setImmediate(() => strapi.reload()); - - ctx.send({ - data: { - uid, - }, - }); }, async deleteContentType(ctx) { const { uid } = ctx.params; - const contentType = strapi.contentTypes[uid]; + try { + strapi.reload.isWatching = false; - if (!contentType) { - return ctx.send({ error: 'contentType.notFound' }, 404); + const component = await contentTypeService.deleteContentType(uid); + + setImmediate(() => strapi.reload()); + + ctx.send({ data: { uid: component.uid } }); + } catch (error) { + strapi.log.error(error); + ctx.send({ error: error.message }, 400); } - - if (!_.has(contentType, 'apiName')) { - return ctx.send({ error: 'contentType.not.deletable' }, 400); - } - - strapi.reload.isWatching = false; - - await contentTypeService.deleteAllRelations(contentType); - await contentTypeService.removeContentType(contentType); - - setImmediate(() => strapi.reload()); - - ctx.send({ - data: { - uid, - }, - }); }, }; diff --git a/packages/strapi-plugin-content-type-builder/services/ContentTypes.js b/packages/strapi-plugin-content-type-builder/services/ContentTypes.js index 87f6476c85..f9cf48eb19 100644 --- a/packages/strapi-plugin-content-type-builder/services/ContentTypes.js +++ b/packages/strapi-plugin-content-type-builder/services/ContentTypes.js @@ -1,8 +1,12 @@ 'use strict'; const _ = require('lodash'); +const pluralize = require('pluralize'); +const generator = require('strapi-generate'); +const { formatAttributes } = require('../utils/attributes'); const getSchemaManager = require('./schema-manager'); +const { nameToSlug } = require('../utils/helpers'); /** * Creates a component and handle the nested components sent with it @@ -10,7 +14,10 @@ const getSchemaManager = require('./schema-manager'); * @param {Object} params.component Main component to create * @param {Array} params.components List of nested components to created or edit */ -const createContentType = ({ contentType, components = [] }) => { +const createContentType = async ({ contentType, components = [] }) => { + // generate api squeleton + await generateAPI(contentType.name); + const componentsToCreate = components.filter(compo => !_.has(compo, 'uid')); const componentsToEdit = components.filter(compo => _.has(compo, 'uid')); @@ -24,8 +31,81 @@ const createContentType = ({ contentType, components = [] }) => { }); }; +/** + * Generate a squeleton API + * @param {*} name + * @param {*} contentType + */ +const generateAPI = name => { + return new Promise((resolve, reject) => { + const scope = { + generatorType: 'api', + id: nameToSlug(name), + name: nameToSlug(name), + rootPath: strapi.dir, + args: { + attributes: {}, + }, + }; + + generator(scope, { + success: () => resolve(), + error: err => reject(err), + }); + }); +}; + +/** + * Edits a contentType and handle the nested contentTypes sent with it + * @param {Object} params params object + * @param {Object} params.contentType Main contentType to create + * @param {Array} params.components List of nested components to created or edit + */ +const editContentType = (uid, { contentType, components = [] }) => { + const componentsToCreate = components.filter(compo => !_.has(compo, 'uid')); + const componentsToEdit = components.filter(compo => _.has(compo, 'uid')); + + return getSchemaManager().edit(ctx => { + const updatedComponent = ctx.editContentType({ + uid, + ...contentType, + }); + + componentsToCreate.forEach(ctx.createComponent); + componentsToEdit.forEach(ctx.editComponent); + + return updatedComponent; + }); +}; + +const deleteContentType = uid => { + return getSchemaManager().edit(ctx => { + return ctx.deleteContentType(uid); + }); +}; + +const formatContentType = contentType => { + const { uid, plugin, connection, collectionName, info } = contentType; + + return { + uid, + plugin, + schema: { + name: _.get(info, 'name') || _.upperFirst(pluralize(uid)), + description: _.get(info, 'description', ''), + connection, + collectionName, + attributes: formatAttributes(contentType), + }, + }; +}; + module.exports = { createContentType, + editContentType, + deleteContentType, + + formatContentType, }; // const path = require('path'); @@ -267,51 +347,6 @@ module.exports = { // return fse.writeFile(filePath, JSON.stringify(schema, null, 2)); // }; -// const formatContentType = contentType => { -// const { uid, plugin, connection, collectionName, info } = contentType; - -// return { -// uid, -// plugin, -// schema: { -// name: _.get(info, 'name') || _.upperFirst(pluralize(uid)), -// description: _.get(info, 'description', ''), -// connection, -// collectionName, -// attributes: formatAttributes(contentType), -// }, -// }; -// }; - -// const createContentTypeSchema = infos => ({ -// connection: -// infos.connection || -// _.get( -// strapi, -// ['config', 'currentEnvironment', 'database', 'defaultConnection'], -// 'default' -// ), -// collectionName: -// infos.collectionName || `${nameToCollectionName(pluralize(infos.name))}`, -// info: { -// name: infos.name, -// description: infos.description, -// }, -// attributes: convertAttributes(infos.attributes), -// }); - -// const updateContentTypeSchema = (old, infos) => ({ -// ...old, -// connection: infos.connection || old.connection, -// collectionName: infos.collectionName || old.collectionName, -// info: { -// name: infos.name || old.info.name, -// description: infos.description || old.info.description, -// }, -// // TODO: keep old params like autoMigration, private, configurable -// attributes: convertAttributes(infos.attributes), -// }); - // const generateAPI = (name, contentType) => { // // create api // return new Promise((resolve, reject) => { diff --git a/packages/strapi-plugin-content-type-builder/services/schema-manager/component-builder.js b/packages/strapi-plugin-content-type-builder/services/schema-manager/component-builder.js index c63bb9ba14..e71e12c79c 100644 --- a/packages/strapi-plugin-content-type-builder/services/schema-manager/component-builder.js +++ b/packages/strapi-plugin-content-type-builder/services/schema-manager/component-builder.js @@ -15,7 +15,7 @@ const createSchemaHandler = require('./schema-handler'); const createComponentUID = ({ category, name }) => `${nameToSlug(category)}.${nameToSlug(name)}`; -module.exports = function createComponentBuilder({ tmpComponents }) { +module.exports = function createComponentBuilder() { return { /** * create a component in the tmpComponent map @@ -23,7 +23,7 @@ module.exports = function createComponentBuilder({ tmpComponents }) { createComponent(infos) { const uid = createComponentUID(infos); - if (tmpComponents.has(uid)) { + if (this.components.has(uid)) { throw new Error('component.alreadyExists'); } @@ -51,7 +51,7 @@ module.exports = function createComponentBuilder({ tmpComponents }) { .set(['info', 'description'], infos.description) .set('attributes', convertAttributes(infos.attributes)); - tmpComponents.set(uid, handler); + this.components.set(uid, handler); return handler; }, @@ -62,18 +62,18 @@ module.exports = function createComponentBuilder({ tmpComponents }) { editComponent(infos) { const { uid } = infos; - if (!tmpComponents.has(uid)) { + if (!this.components.has(uid)) { throw new Error('component.notFound'); } - const handler = tmpComponents.get(uid); + const handler = this.components.get(uid); const [, nameUID] = uid.split('.'); const newCategory = nameToSlug(infos.category); const newUID = `${newCategory}.${nameUID}`; - if (newUID !== uid && tmpComponents.has(newUID)) { + if (newUID !== uid && this.components.has(newUID)) { throw new Error('component.edit.alreadyExists'); } @@ -87,6 +87,7 @@ module.exports = function createComponentBuilder({ tmpComponents }) { .set(['info', 'name'], infos.name) .set(['info', 'icon'], infos.icon) .set(['info', 'description'], infos.description) + // TODO: keep configurable args etc... .set('attributes', convertAttributes(infos.attributes)); if (newUID !== uid) { @@ -103,7 +104,7 @@ module.exports = function createComponentBuilder({ tmpComponents }) { }, deleteComponent(uid) { - if (!tmpComponents.has(uid)) { + if (!this.components.has(uid)) { throw new Error('component.notFound'); } @@ -115,7 +116,7 @@ module.exports = function createComponentBuilder({ tmpComponents }) { ct.removeComponent(uid); }); - return tmpComponents.get(uid).delete(); + return this.components.get(uid).delete(); }, }; }; diff --git a/packages/strapi-plugin-content-type-builder/services/schema-manager/content-type-builder.js b/packages/strapi-plugin-content-type-builder/services/schema-manager/content-type-builder.js index df17135b00..bcf1af6cc1 100644 --- a/packages/strapi-plugin-content-type-builder/services/schema-manager/content-type-builder.js +++ b/packages/strapi-plugin-content-type-builder/services/schema-manager/content-type-builder.js @@ -15,7 +15,7 @@ const createSchemaHandler = require('./schema-handler'); const createContentTypeUID = ({ name }) => `application::${nameToSlug(name)}.${nameToSlug(name)}`; -module.exports = function createComponentBuilder({ tmpContentTypes }) { +module.exports = function createComponentBuilder() { return { /** * create a component in the tmpComponent map @@ -23,13 +23,13 @@ module.exports = function createComponentBuilder({ tmpContentTypes }) { createContentType(infos) { const uid = createContentTypeUID(infos); - if (tmpContentTypes.has(uid)) { + if (this.contentTypes.has(uid)) { throw new Error('contentType.alreadyExists'); } const handler = createSchemaHandler({ dir: path.join(strapi.dir, 'api', nameToSlug(infos.name), 'models'), - filename: `${nameToSlug(infos.name)}.json`, + filename: `${nameToSlug(infos.name)}.settings.json`, }); const defaultConnection = _.get( @@ -50,9 +50,51 @@ module.exports = function createComponentBuilder({ tmpContentTypes }) { .set(['info', 'description'], infos.description) .set('attributes', convertAttributes(infos.attributes)); - tmpContentTypes.set(uid, handler); + this.contentTypes.set(uid, handler); + + // TODO: add reversed relations return handler; }, + + editContentType(infos) { + const { uid } = infos; + + if (!this.contentTypes.has(uid)) { + throw new Error('contentType.notFound'); + } + + const handler = this.contentTypes.get(uid); + + handler + .set('connection', infos.connection) + .set('collectionName', infos.collectionName) + .set(['info', 'name'], infos.name) + .set(['info', 'description'], infos.description) + // TODO: keep configurable args etc... + .set('attributes', convertAttributes(infos.attributes)); + + // TODO: clear old relations + // TODO: build new reversed relations + + return handler; + }, + + deleteContentType(uid) { + if (!this.contentTypes.has(uid)) { + throw new Error('contentType.notFound'); + } + + this.components.forEach(compo => { + compo.removeContentType(uid); + }); + + this.contentTypes.forEach(ct => { + ct.removeContentType(uid); + }); + + // TODO: clear api when a contentType is deleted + return this.contentTypes.get(uid).delete(); + }, }; }; diff --git a/packages/strapi-plugin-content-type-builder/services/schema-manager/schema-handler.js b/packages/strapi-plugin-content-type-builder/services/schema-manager/schema-handler.js index 9482d34ada..412bb7e7c3 100644 --- a/packages/strapi-plugin-content-type-builder/services/schema-manager/schema-handler.js +++ b/packages/strapi-plugin-content-type-builder/services/schema-manager/schema-handler.js @@ -78,6 +78,21 @@ module.exports = function createSchemaHandler(infos) { return this; }, + removeContentType(uid) { + const { attributes } = state.schema; + + Object.keys(attributes).forEach(key => { + const attr = attributes[key]; + const target = attr.model || attr.collection; + + if (target === uid) { + this.unset(['attributes', key]); + } + }); + + return this; + }, + // utils removeComponent(uid) { const { attributes } = state.schema;