From c182c291614ba5856a3d6784821ec6292f09d3c1 Mon Sep 17 00:00:00 2001 From: chenos Date: Wed, 8 Sep 2021 00:18:41 +0800 Subject: [PATCH] feat: improve code --- .../database/src/__tests__/emitter.test.ts | 96 ++++++++++++ packages/database/src/__tests__/hooks.test.ts | 57 ------- packages/database/src/__tests__/index.ts | 12 +- packages/database/src/database.ts | 148 +++++++++++------- packages/database/src/fields/field-types.ts | 38 +++-- 5 files changed, 223 insertions(+), 128 deletions(-) create mode 100644 packages/database/src/__tests__/emitter.test.ts diff --git a/packages/database/src/__tests__/emitter.test.ts b/packages/database/src/__tests__/emitter.test.ts new file mode 100644 index 000000000..96370e96f --- /dev/null +++ b/packages/database/src/__tests__/emitter.test.ts @@ -0,0 +1,96 @@ +import { getDatabase } from './'; +import Database from '../database'; + +let db: Database; + +beforeEach(async () => { + db = getDatabase(); +}); + +afterEach(async () => { + await db.close(); +}); + +describe('emitter', () => { + it('event emitter', async () => { + db.table({ + name: 'test', + fields: [ + { + type: 'json', + name: 'arr', + defaultValue: [], + }, + ], + }); + db.table({ + name: 'test2', + fields: [ + { + type: 'json', + name: 'arr', + defaultValue: [], + }, + ], + }); + await db.sync(); + const arr = []; + db.on('afterCreate', async function abc1(...args) { + return new Promise((resolve) => { + setTimeout(() => { + console.log('db.on.afterCreate') + arr.push(1); + resolve(); + }, 200); + }); + }); + db.on('test.afterCreate', async function abc2(model, options) { + return new Promise((resolve) => { + setTimeout(() => { + console.log('db.on.test.afterCreate') + arr.push(2); + resolve(); + }, 100); + }); + }); + db.on('test2.afterCreate', async (model, options) => { + return new Promise((resolve) => { + setTimeout(() => { + console.log('db.on.test2.afterCreate') + arr.push(3); + resolve(); + }, 100); + }); + }); + const Test2 = db.getModel('test2'); + await Test2.create(); + const Test1 = db.getModel('test'); + await Test1.create(); + expect(arr).toEqual([3, 1, 2, 1]); + }); + + it.only('a', async () => { + db.table({ + name: 'test', + fields: [ + { type: 'string', name: 'name' } + ], + }); + db.on('test.afterBulkCreate', async (...args) => { + console.log('afterBulkCreate1', args); + }); + db.on('test2.afterBulkCreate', async (...args) => { + console.log('afterBulkCreate2', args); + }); + await db.sync(); + const Test = db.getModel('test'); + await Test.create(); + await Test.bulkCreate([{}, {}]); + await Test.update({ + name: 'name' + }, { + where: {}, + }); + await Test.findAll(); + }); +}); diff --git a/packages/database/src/__tests__/hooks.test.ts b/packages/database/src/__tests__/hooks.test.ts index f68ef3f15..df6e1b4f8 100644 --- a/packages/database/src/__tests__/hooks.test.ts +++ b/packages/database/src/__tests__/hooks.test.ts @@ -211,61 +211,4 @@ describe('hooks', () => { const test = await Test3.create({}); expect(test.get('arr')).toEqual([3, 5, 1, 4, 6, 2]); }); - - it('event emitter', async () => { - const table = db.table({ - name: 'test', - fields: [ - { - type: 'json', - name: 'arr', - defaultValue: [], - }, - ], - }); - db.table({ - name: 'test2', - fields: [ - { - type: 'json', - name: 'arr', - defaultValue: [], - }, - ], - }); - await db.sync(); - const arr = []; - db.on('afterCreate', async function abc1(...args) { - return new Promise((resolve) => { - setTimeout(() => { - console.log('db.on.afterCreate') - arr.push(1); - resolve(); - }, 200); - }); - }); - db.on('test.afterCreate', async function abc2(model, options) { - return new Promise((resolve) => { - setTimeout(() => { - console.log('db.on.test.afterCreate') - arr.push(2); - resolve(); - }, 100); - }); - }); - db.on('test2.afterCreate', async (model, options) => { - return new Promise((resolve) => { - setTimeout(() => { - console.log('db.on.test2.afterCreate') - arr.push(3); - resolve(); - }, 100); - }); - }); - const Test2 = db.getModel('test2'); - await Test2.create(); - const Test1 = db.getModel('test'); - await Test1.create(); - expect(arr).toEqual([3,1,2,1]); - }); }); diff --git a/packages/database/src/__tests__/index.ts b/packages/database/src/__tests__/index.ts index 475c18964..88449a989 100644 --- a/packages/database/src/__tests__/index.ts +++ b/packages/database/src/__tests__/index.ts @@ -39,9 +39,13 @@ export function getDatabase() { } }, hooks: { - beforeDefine(columns, model) { - model.tableName = `${getTestKey()}_${model.tableName || model.name.plural}`; - } - } + beforeDefine(model, options) { + // @ts-ignore + options.tableNamePrefix = `${getTestKey()}_`; + // @ts-ignore + options.hookModelName = options.tableName; + options.tableName = `${getTestKey()}_${options.tableName || options.name.plural}`; + }, + }, }); }; diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index 3f92a5b0e..a5a05af7b 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -34,7 +34,7 @@ export interface ImportOptions { export interface DatabaseOptions extends Options { } -export type HookType = 'beforeTableInit' | 'afterTableInit' | 'beforeAddField' | 'afterAddField'; +// export type HookType = 'beforeTableInit' | 'afterTableInit' | 'beforeAddField' | 'afterAddField'; export class Extend { tableOptions: TableOptions; @@ -49,7 +49,41 @@ export function extend(tableOptions: TableOptions, mergeOptions: MergeOptions = return new Extend(tableOptions, mergeOptions); } -export default class Database extends EventEmitter { +type HookType = + 'beforeValidate' | + 'afterValidate' | + 'beforeCreate' | + 'afterCreate' | + 'beforeDestroy' | + 'afterDestroy' | + 'beforeRestore' | + 'afterRestore' | + 'beforeUpdate' | + 'afterUpdate' | + 'beforeSave' | + 'afterSave' | + 'beforeBulkCreate' | + 'afterBulkCreate' | + 'beforeBulkDestroy' | + 'afterBulkDestroy' | + 'beforeBulkRestore' | + 'afterBulkRestore' | + 'beforeBulkUpdate' | + 'afterBulkUpdate' | + 'beforeSync' | + 'afterSync' | + 'beforeBulkSync' | + 'afterBulkSync' | + 'beforeDefine' | + 'afterDefine' | + 'beforeInit' | + 'afterInit' | + 'beforeConnect' | + 'afterConnect' | + 'beforeDisconnect' | + 'afterDisconnect'; + + export default class Database extends EventEmitter { public readonly sequelize: Sequelize; @@ -71,52 +105,44 @@ export default class Database extends EventEmitter { protected extTableOptions = new Map(); - protected hookTypes = new Set([ - 'beforeValidate', - 'afterValidate', - 'validationFailed', - 'beforeCreate', - 'afterCreate', - 'beforeDestroy', - 'afterDestroy', - 'beforeRestore', - 'afterRestore', - 'beforeUpdate', - 'afterUpdate', - 'beforeSave', - 'afterSave', - 'beforeUpsert', - 'afterUpsert', - 'beforeBulkCreate', - 'afterBulkCreate', - 'beforeBulkDestroy', - 'afterBulkDestroy', - 'beforeBulkRestore', - 'afterBulkRestore', - 'beforeBulkUpdate', - 'afterBulkUpdate', - 'beforeFind', - 'beforeFindAfterExpandIncludeAll', - 'beforeFindAfterOptions', - 'afterFind', - 'beforeCount', - 'beforeDefine', - 'afterDefine', - 'beforeInit', - 'afterInit', - 'beforeAssociate', - 'afterAssociate', - 'beforeConnect', - 'afterConnect', - 'beforeDisconnect', - 'afterDisconnect', - 'beforeSync', - 'afterSync', - 'beforeBulkSync', - 'afterBulkSync', - 'beforeQuery', - 'afterQuery' - ]); + protected hookTypes = new Map(Object.entries({ + beforeValidate: 1, + afterValidate: 1, + beforeCreate: 1, + afterCreate: 1, + beforeDestroy: 1, + afterDestroy: 1, + beforeRestore: 1, + afterRestore: 1, + beforeUpdate: 1, + afterUpdate: 1, + beforeSave: 1, + afterSave: 1, + + beforeBulkCreate: 2, + afterBulkCreate: 2, + + beforeBulkDestroy: 3, + afterBulkDestroy: 3, + beforeBulkRestore: 3, + afterBulkRestore: 3, + beforeBulkUpdate: 3, + afterBulkUpdate: 3, + + beforeSync: 4, + afterSync: 4, + beforeBulkSync: 4, + afterBulkSync: 4, + + beforeDefine: 0, + afterDefine: 0, + beforeInit: 0, + afterInit: 0, + beforeConnect: 0, + afterConnect: 0, + beforeDisconnect: 0, + afterDisconnect: 0, + })); constructor(options?: DatabaseOptions) { super(); @@ -138,17 +164,33 @@ export default class Database extends EventEmitter { return hookType; } - on(event: string | symbol, listener: (...args: any[]) => void) { + on(event: HookType | Omit | symbol, listener: (...args: any[]) => void) { const hookType = this._getHookType(event); if (hookType) { + const state = this.hookTypes.get(hookType); console.log('sequelize.addHook', hookType) - this.sequelize.addHook(hookType, async (model) => { - await this.emitAsync(`${model.constructor.name}.${hookType}`); - await this.emitAsync(hookType); + this.sequelize.addHook(hookType, async (...args: any[]) => { + let modelName: string; + switch (state) { + case 1: + modelName = args?.[0]?.constructor?.name; + break; + case 2: + modelName = args?.[1]?.model?.name; + break; + case 3: + modelName = args?.[0]?.model?.name; + break; + } + console.log({ modelName, args }); + if (modelName) { + await this.emitAsync(`${modelName}.${hookType}`, ...args); + } + await this.emitAsync(hookType, ...args); }); this.hookTypes.delete(hookType); } - return super.on(event, listener); + return super.on(event as any, listener); } async emitAsync(event: string | symbol, ...args: any[]): Promise { diff --git a/packages/database/src/fields/field-types.ts b/packages/database/src/fields/field-types.ts index eb27f4d70..2cdc6afd3 100644 --- a/packages/database/src/fields/field-types.ts +++ b/packages/database/src/fields/field-types.ts @@ -298,9 +298,10 @@ export class PASSWORD extends STRING { constructor(options: Options.StringOptions, context: FieldContext) { super(options, context); - const Model = context.sourceTable.getModel(); - Model.addHook('beforeCreate', PASSWORD.hash.bind(this)); - Model.addHook('beforeUpdate', PASSWORD.hash.bind(this)); + const { database, sourceTable } = context; + const name = sourceTable.getName(); + database.on(`${name}.beforeCreate`, PASSWORD.hash.bind(this)); + database.on(`${name}.beforeUpdate`, PASSWORD.hash.bind(this)); } public static async hash(this: PASSWORD, model) { @@ -361,9 +362,9 @@ export class UID extends Column { constructor(options: Options.StringOptions, context: FieldContext) { super(options, context); + const { sourceTable, database } = context; const { name, prefix = '' } = options; - const Model = context.sourceTable.getModel(); - Model.addHook('beforeCreate', (model) => { + database.on(`${sourceTable.getName()}.beforeCreate`, (model) => { if (!model.get(name)) { model.set(name, `${prefix}${uid()}`); } @@ -819,10 +820,14 @@ export class SORT extends NUMBER { constructor(options: Options.SortOptions, context: FieldContext) { super(options, context); - const Model = context.sourceTable.getModel(); + // const Model = context.sourceTable.getModel(); // TODO(feature): 可考虑策略模式,以在需要时对外提供接口 - Model.addHook('beforeCreate', SORT.beforeCreateHook.bind(this)); - Model.addHook('beforeBulkCreate', SORT.beforeBulkCreateHook.bind(this)); + const { database, sourceTable } = context; + const name = sourceTable.getName(); + database.on(`${name}.beforeCreate`, SORT.beforeCreateHook.bind(this)); + database.on(`${name}.beforeBulkCreate`, SORT.beforeBulkCreateHook.bind(this)); + // Model.addHook('beforeCreate', SORT.beforeCreateHook.bind(this)); + // Model.addHook('beforeBulkCreate', SORT.beforeBulkCreateHook.bind(this)); } public getDataType(): Function { @@ -901,15 +906,20 @@ export class Radio extends BOOLEAN { } constructor({ type, ...options }: Options.RadioOptions, context: FieldContext) { - super({ ...options, type: 'boolean' }, context); - const Model = context.sourceTable.getModel(); + super({ ...options, type: 'radio' }, context); + // const Model = context.sourceTable.getModel(); // TODO(feature): 可考虑策略模式,以在需要时对外提供接口 - Model.addHook('beforeCreate', Radio.beforeCreateHook.bind(this)); - Model.addHook('beforeUpdate', Radio.beforeUpdateHook.bind(this)); - // Model.addHook('beforeUpsert', beforeSaveHook); - Model.addHook('beforeBulkCreate', Radio.beforeBulkCreateHook.bind(this)); + // Model.addHook('beforeCreate', Radio.beforeCreateHook.bind(this)); + // Model.addHook('beforeUpdate', Radio.beforeUpdateHook.bind(this)); + // // Model.addHook('beforeUpsert', beforeSaveHook); + // Model.addHook('beforeBulkCreate', Radio.beforeBulkCreateHook.bind(this)); // TODO(optimize): bulkUpdate 的 hooks 参数不一样,没有对象列表,考虑到很少用,暂时不实现 // Model.addHook('beforeBulkUpdate', beforeBulkCreateHook); + const { database, sourceTable } = context; + const name = sourceTable.getName(); + database.on(`${name}.beforeCreate`, Radio.beforeCreateHook.bind(this)); + database.on(`${name}.beforeUpdate`, Radio.beforeUpdateHook.bind(this)); + database.on(`${name}.beforeBulkCreate`, Radio.beforeBulkCreateHook.bind(this)); } public getDataType() {