From 4607e0da49aa69ffb385b8d32c4bc4cced2a06f7 Mon Sep 17 00:00:00 2001 From: Chareice Date: Wed, 9 Feb 2022 23:39:50 +0800 Subject: [PATCH] feat: removeEmptyParents server hook --- .../src/__tests__/server-hook-impl.test.ts | 160 ++++++++++++++++++ .../src/__tests__/server-hook.test.ts | 6 +- .../src/server-hooks/index.ts | 2 +- .../src/server-hooks/removeEmptyParents.ts | 52 ++++++ 4 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 packages/plugin-ui-schema-storage/src/__tests__/server-hook-impl.test.ts create mode 100644 packages/plugin-ui-schema-storage/src/server-hooks/removeEmptyParents.ts diff --git a/packages/plugin-ui-schema-storage/src/__tests__/server-hook-impl.test.ts b/packages/plugin-ui-schema-storage/src/__tests__/server-hook-impl.test.ts new file mode 100644 index 000000000..89b7966d2 --- /dev/null +++ b/packages/plugin-ui-schema-storage/src/__tests__/server-hook-impl.test.ts @@ -0,0 +1,160 @@ +import { mockServer, MockServer } from '@nocobase/test'; +import { Database } from '@nocobase/database'; +import PluginUiSchema, { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage'; +import PluginCollectionManager from '@nocobase/plugin-collection-manager'; +import { removeEmptyParents } from '../server-hooks/removeEmptyParents'; + +describe('server hooks', () => { + let app: MockServer; + let db: Database; + let uiSchemaRepository: UiSchemaRepository; + let uiSchemaPlugin: PluginUiSchema; + + afterEach(async () => { + await app.destroy(); + }); + + beforeEach(async () => { + app = mockServer({ + registerActions: true, + }); + + db = app.db; + + await app.cleanDb(); + app.plugin(PluginUiSchema); + app.plugin(PluginCollectionManager); + + await app.loadAndInstall(); + + uiSchemaRepository = db.getRepository('ui_schemas'); + + uiSchemaPlugin = app.getPlugin('PluginUiSchema'); + }); + + it('should clean row struct', async () => { + const PostModel = await db.getRepository('collections').create({ + values: { + name: 'posts', + }, + }); + + await db.getRepository('fields').create({ + values: { + name: 'title', + type: 'string', + collectionName: 'posts', + }, + }); + + await db.getRepository('fields').create({ + values: { + name: 'name', + type: 'string', + collectionName: 'posts', + }, + }); + + await db.getRepository('fields').create({ + values: { + name: 'intro', + type: 'string', + collectionName: 'posts', + }, + }); + + const schema = { + type: 'void', + name: 'grid1', + 'x-decorator': 'Form', + 'x-component': 'Grid', + 'x-item-initializer': 'AddGridFormItem', + 'x-uid': 'grid1', + properties: { + row1: { + type: 'void', + 'x-component': 'Grid.Row', + 'x-uid': 'row1', + properties: { + col11: { + type: 'void', + 'x-uid': 'col11', + 'x-component': 'Grid.Col', + properties: { + name: { + type: 'string', + title: 'Name', + 'x-uid': 'posts-name', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-collection-field': 'posts.name', + 'x-server-hooks': [ + { + type: 'onCollectionFieldDestroy', + collection: 'posts', + fields: ['name'], + method: 'removeEmptyParents', + }, + ], + }, + title: { + type: 'string', + title: 'Title', + 'x-uid': 'posts-title', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-collection-field': 'posts.title', + 'x-server-hooks': [ + { + type: 'onCollectionFieldDestroy', + collection: 'posts', + fields: ['title'], + method: 'removeEmptyParents', + }, + ], + }, + }, + }, + col12: { + type: 'void', + 'x-uid': 'col12', + 'x-component': 'Grid.Col', + properties: { + intro: { + 'x-uid': 'posts-intro', + type: 'string', + title: 'Intro', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-server-hooks': [ + { + type: 'onCollectionFieldDestroy', + collection: 'posts', + fields: ['intro'], + method: 'removeEmptyParents', + }, + ], + }, + }, + }, + }, + }, + }, + }; + + await uiSchemaRepository.insert(schema); + + uiSchemaPlugin.serverHooks.register('onCollectionFieldDestroy', 'removeEmptyParents', removeEmptyParents); + + await db.getRepository('fields').destroy({ + filter: { + name: 'intro', + }, + individualHooks: true, + }); + + const jsonTree = await uiSchemaRepository.getJsonSchema('grid1'); + expect(jsonTree['properties']['row1']['properties']['col11']).toBeDefined(); + expect(jsonTree['properties']['row1']['properties']['col12']).not.toBeDefined(); + }); +}); diff --git a/packages/plugin-ui-schema-storage/src/__tests__/server-hook.test.ts b/packages/plugin-ui-schema-storage/src/__tests__/server-hook.test.ts index 121e6c062..50be924d5 100644 --- a/packages/plugin-ui-schema-storage/src/__tests__/server-hook.test.ts +++ b/packages/plugin-ui-schema-storage/src/__tests__/server-hook.test.ts @@ -178,11 +178,7 @@ describe('server hooks', () => { ], }; - await uiSchemaRepository.create({ - values: { - schema: menuSchema, - }, - }); + await uiSchemaRepository.insert(menuSchema); const PostModel = await db.getRepository('collections').create({ values: { diff --git a/packages/plugin-ui-schema-storage/src/server-hooks/index.ts b/packages/plugin-ui-schema-storage/src/server-hooks/index.ts index cc066aca0..e1d00d3e9 100644 --- a/packages/plugin-ui-schema-storage/src/server-hooks/index.ts +++ b/packages/plugin-ui-schema-storage/src/server-hooks/index.ts @@ -110,7 +110,7 @@ export class ServerHooks { const hookFunc = this.hooks.get(hookRecord.get('type') as HookType)?.get(hoodMethodName); if (hookFunc) { - await hookFunc({ ...hooksArgs, schemaInstance: (hookRecord).uiSchema }); + await hookFunc({ ...hooksArgs, schemaInstance: (hookRecord).uiSchema, db: this.db }); } } } diff --git a/packages/plugin-ui-schema-storage/src/server-hooks/removeEmptyParents.ts b/packages/plugin-ui-schema-storage/src/server-hooks/removeEmptyParents.ts new file mode 100644 index 000000000..af413d803 --- /dev/null +++ b/packages/plugin-ui-schema-storage/src/server-hooks/removeEmptyParents.ts @@ -0,0 +1,52 @@ +import { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage'; + +// given a child uid, if it is a single child ,return its parent +async function isSingleChild(uid, db, transaction) { + const parent = await db.getRepository('ui_schema_tree_path').findOne({ + filter: { + descendant: uid, + depth: 1, + }, + }); + + const countResult = await db.sequelize.query( + `SELECT COUNT(*) FROM ${ + db.getCollection('ui_schema_tree_path').model.tableName + } where ancestor = :ancestor and depth = 1`, + { + replacements: { + ancestor: parent.get('ancestor'), + }, + type: 'SELECT', + transaction, + }, + ); + + const parentChildrenCount = countResult[0]['count']; + + if (parentChildrenCount == 1) { + return parent.get('ancestor'); + } + + return null; +} + +export async function removeEmptyParents({ schemaInstance, options, db }) { + const { transaction } = options; + const uiSchemaRepository: UiSchemaRepository = db.getRepository('ui_schemas'); + const uid = schemaInstance.get('uid'); + + // find parent uid + const parentUid = await isSingleChild(uid, db, transaction); + + if (parentUid) { + const rowUid = await isSingleChild(parentUid, db, transaction); + if (rowUid) { + await uiSchemaRepository.remove(rowUid, { transaction }); + } else { + await uiSchemaRepository.remove(parentUid, { transaction }); + } + } else { + await uiSchemaRepository.remove(uid, { transaction }); + } +}