From 3e1cad643b627440285fec24dc1eb56310e1aefe Mon Sep 17 00:00:00 2001 From: Chareice Date: Tue, 8 Feb 2022 20:58:57 +0800 Subject: [PATCH] feat: server hooks model --- packages/database/src/repository.ts | 12 ++- .../src/models/collection.ts | 2 +- .../src/__tests__/server-hook.test.ts | 98 ++++++++++++++----- .../src/collections/ui_schemas.ts | 2 +- .../plugin-ui-schema-storage/src/model.ts | 12 ++- .../src/server-hooks/index.ts | 57 +++++++++++ .../plugin-ui-schema-storage/src/server.ts | 12 ++- 7 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 packages/plugin-ui-schema-storage/src/server-hooks/index.ts diff --git a/packages/database/src/repository.ts b/packages/database/src/repository.ts index 781d1114d6..7290d6d350 100644 --- a/packages/database/src/repository.ts +++ b/packages/database/src/repository.ts @@ -214,18 +214,20 @@ export class Repository 0) { + // @ts-ignore + const primaryKeyField = model.primaryKeyField || model.primaryKeyAttribute; const ids = ( await model.findAll({ ...opts, includeIgnoreAttributes: false, - attributes: [model.primaryKeyAttribute], - group: `${model.name}.${model.primaryKeyAttribute}`, + attributes: [primaryKeyField], + group: `${model.name}.${primaryKeyField}`, transaction, }) - ).map((row) => row.get(model.primaryKeyAttribute)); + ).map((row) => row.get(primaryKeyField)); const where = { - [model.primaryKeyAttribute]: { + [primaryKeyField]: { [Op.in]: ids, }, }; @@ -415,7 +417,7 @@ export class Repository { let app: MockServer; let db: Database; let uiSchemaRepository: UiSchemaRepository; + let uiSchemaPlugin: PluginUiSchema; + + const schema = { + 'x-uid': 'root', + name: 'root', + properties: { + row: { + 'x-uid': 'table', + 'x-component': 'Table', + 'x-collection': 'posts', + 'x-server-hooks': { + afterDestroyCollection: ['aaa'], + }, + properties: { + col1: { + 'x-uid': 'col1', + 'x-component': 'Col', + properties: { + field1: { + 'x-uid': 'field1', + 'x-component': 'Input', + 'x-collection-field': 'posts.title', + 'x-server-hooks': { + afterDestroyField: ['onFieldDestroy'], + }, + }, + }, + }, + }, + }, + }, + }; afterEach(async () => { await app.destroy(); @@ -20,40 +53,17 @@ describe('server hooks', () => { await app.cleanDb(); app.plugin(PluginUiSchema); + app.plugin(PluginCollectionManager); await app.loadAndInstall(); uiSchemaRepository = db.getRepository('ui_schemas'); + await uiSchemaRepository.insert(schema); + + uiSchemaPlugin = app.getPlugin('PluginUiSchema'); }); it('should save uiSchemaAttrs', async () => { - const schema = { - name: 'row', - 'x-uid': 'table', - 'x-component': 'Table', - 'x-collection': 'posts', - 'x-server-hooks': { - afterDestroyCollection: ['aaa'], - }, - properties: { - col1: { - 'x-uid': 'col1', - 'x-component': 'Col', - properties: { - field1: { - 'x-uid': 'field1', - 'x-component': 'Input', - 'x-collection-field': 'posts.title', - 'x-server-hooks': { - afterDestroyField: ['bbb'], - }, - }, - }, - }, - }, - }; - - await uiSchemaRepository.insert(schema); const node = await uiSchemaRepository.findOne({ filter: { uid: 'table', @@ -74,4 +84,38 @@ describe('server hooks', () => { const nodeFieldAttr = await nodeField.getAttrs(); expect(nodeFieldAttr.get('collectionPath')).toEqual('posts.title'); }); + + it('should call server hooks', async () => { + const PostModel = await db.getRepository('collections').create({ + values: { + name: 'posts', + }, + }); + + const fieldModel = await db.getRepository('fields').create({ + values: { + name: 'title', + type: 'string', + collectionName: 'posts', + }, + }); + + // @ts-ignore + await PostModel.migrate(); + + const serverHooks = uiSchemaPlugin.serverHooks; + const hookFn = jest.fn(); + + serverHooks.register('afterDestroyField', 'onFieldDestroy', hookFn); + + // destroy a field + await db.getRepository('fields').destroy({ + filter: { + name: 'title', + }, + individualHooks: true, + }); + + expect(hookFn).toHaveBeenCalled(); + }); }); diff --git a/packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts b/packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts index 76f1eece64..41b092cae6 100644 --- a/packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts +++ b/packages/plugin-ui-schema-storage/src/collections/ui_schemas.ts @@ -6,7 +6,7 @@ export default { autoGenId: false, timestamps: false, repository: 'UiSchemaRepository', - model: 'MagicAttributeModel', + model: 'UiSchemaModel', magicAttribute: 'schema', fields: [ { diff --git a/packages/plugin-ui-schema-storage/src/model.ts b/packages/plugin-ui-schema-storage/src/model.ts index 22183897de..d9275893d0 100644 --- a/packages/plugin-ui-schema-storage/src/model.ts +++ b/packages/plugin-ui-schema-storage/src/model.ts @@ -1,5 +1,11 @@ -import { Model } from '@nocobase/database'; +import { MagicAttributeModel } from '@nocobase/database'; +import { HookType } from './server-hooks'; -export class UiSchemaModel extends Model { - +class UiSchemaModel extends MagicAttributeModel { + getListenServerHooks(type: HookType) { + const hooks = this.get('x-server-hooks'); + return hooks[type] || []; + } } + +export { UiSchemaModel }; diff --git a/packages/plugin-ui-schema-storage/src/server-hooks/index.ts b/packages/plugin-ui-schema-storage/src/server-hooks/index.ts new file mode 100644 index 0000000000..4ab0b42536 --- /dev/null +++ b/packages/plugin-ui-schema-storage/src/server-hooks/index.ts @@ -0,0 +1,57 @@ +import { Database } from '@nocobase/database'; +import { UiSchemaModel } from '../model'; + +export type HookType = 'afterDestroyField'; + +export class ServerHooks { + hooks = new Map>(); + + constructor(protected db: Database) { + this.listen(); + } + + listen() { + this.db.on('fields.afterDestroy', async (model, options) => { + await this.afterFieldDestroy(model, options); + }); + } + + protected async afterFieldDestroy(fieldModel, options) { + const { transaction } = options; + + const collectionPath = `${fieldModel.get('collectionName')}.${fieldModel.get('name')}`; + + const listenSchemas = (await this.db.getRepository('ui_schemas').find({ + filter: { + 'attrs.collectionPath': collectionPath, + }, + transaction, + })) as UiSchemaModel[]; + + for (const listenSchema of listenSchemas) { + const listenHooksName = listenSchema.getListenServerHooks('afterDestroyField'); + for (const listenHookName of listenHooksName) { + const hookFunc = this.hooks.get('afterDestroyField')?.get(listenHookName); + await hookFunc({ + model: listenSchema, + transaction, + }); + } + } + } + + /** + * register a server hook function + * @param type type of server hook + * @param name name of server hook + * @param hookFunc server hook function + */ + register(type: HookType, name: string, hookFunc: any) { + if (!this.hooks.has(type)) { + this.hooks.set(type, new Map()); + } + + const hookTypeMap = this.hooks.get(type); + hookTypeMap.set(name, hookFunc); + } +} diff --git a/packages/plugin-ui-schema-storage/src/server.ts b/packages/plugin-ui-schema-storage/src/server.ts index b967e18e79..139bf17d95 100644 --- a/packages/plugin-ui-schema-storage/src/server.ts +++ b/packages/plugin-ui-schema-storage/src/server.ts @@ -3,8 +3,12 @@ import { Plugin } from '@nocobase/server'; import path from 'path'; import { uiSchemaActions } from './actions/ui-schema-action'; import UiSchemaRepository from './repository'; +import { ServerHooks } from './server-hooks'; +import { UiSchemaModel } from './model'; export default class PluginUiSchema extends Plugin { + serverHooks: ServerHooks; + registerRepository() { this.app.db.registerRepositories({ UiSchemaRepository, @@ -14,10 +18,16 @@ export default class PluginUiSchema extends Plugin { async beforeLoad() { const db = this.app.db; - this.app.db.registerModels({ MagicAttributeModel }); + this.serverHooks = new ServerHooks(db); + + this.app.db.registerModels({ MagicAttributeModel, UiSchemaModel }); this.registerRepository(); + db.on('ui_schemas.afterDefine', (model) => { + model.primaryKeyAttribute = 'uid'; + }); + db.on('ui_schemas.beforeCreate', function setUid(model) { model.set('uid', model.get('x-uid')); });