feat add event emitter support to database

This commit is contained in:
chenos 2021-09-07 00:28:16 +08:00
parent d3890b4b66
commit a8cd70017b
2 changed files with 190 additions and 3 deletions

View File

@ -122,7 +122,7 @@ describe('hooks', () => {
expect(test.get('arr')).toEqual([1, 2]); expect(test.get('arr')).toEqual([1, 2]);
}); });
it.only('add hook in custom field', async () => { it('add hook in custom field', async () => {
const table = db.table({ const table = db.table({
name: 'test3', name: 'test3',
fields: [ fields: [
@ -211,4 +211,61 @@ describe('hooks', () => {
const test = await Test3.create({}); const test = await Test3.create({});
expect(test.get('arr')).toEqual([3, 5, 1, 4, 6, 2]); 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]);
});
}); });

View File

@ -8,6 +8,7 @@ import Table, { MergeOptions, TableOptions } from './table';
import { Model, ModelCtor } from './model'; import { Model, ModelCtor } from './model';
import { requireModule } from './utils'; import { requireModule } from './utils';
import _ from 'lodash'; import _ from 'lodash';
import { EventEmitter } from 'events';
export interface SyncOptions extends SequelizeSyncOptions { export interface SyncOptions extends SequelizeSyncOptions {
@ -48,7 +49,7 @@ export function extend(tableOptions: TableOptions, mergeOptions: MergeOptions =
return new Extend(tableOptions, mergeOptions); return new Extend(tableOptions, mergeOptions);
} }
export default class Database { export default class Database extends EventEmitter {
public readonly sequelize: Sequelize; public readonly sequelize: Sequelize;
@ -70,11 +71,140 @@ export default class Database {
protected extTableOptions = new Map<string, any>(); protected extTableOptions = new Map<string, any>();
constructor(options: DatabaseOptions) { 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'
]);
constructor(options?: DatabaseOptions) {
super();
this.options = options; this.options = options;
this.sequelize = new Sequelize(options); this.sequelize = new Sequelize(options);
} }
private _getHookType(event: any) {
if (typeof event === 'string') {
event = event.split('.');
}
if (!Array.isArray(event)) {
return;
}
const hookType = [...event].pop();
if (!this.hookTypes.has(hookType)) {
return;
}
return hookType;
}
on(event: string | symbol, listener: (...args: any[]) => void) {
const hookType = this._getHookType(event);
if (hookType) {
console.log('sequelize.addHook', hookType)
this.sequelize.addHook(hookType, async (model) => {
await this.emitAsync(`${model.constructor.name}.${hookType}`);
await this.emitAsync(hookType);
});
this.hookTypes.delete(hookType);
}
return super.on(event, listener);
}
async emitAsync(event: string | symbol, ...args: any[]): Promise<boolean> {
// @ts-ignore
const events = this._events;
let callbacks = events?.[event];
if (!callbacks) {
return false;
}
// helper function to reuse as much code as possible
const run = (cb) => {
switch (args.length) {
// fast cases
case 0:
cb = cb.call(this);
break;
case 1:
cb = cb.call(this, args[0]);
break;
case 2:
cb = cb.call(this, args[0], args[1]);
break;
case 3:
cb = cb.call(this, args[0], args[1], args[2]);
break;
// slower
default:
cb = cb.apply(this, args);
}
if (
cb && (
cb instanceof Promise ||
typeof cb.then === 'function'
)
) {
return cb;
}
return Promise.resolve(true);
};
if (typeof callbacks === 'function') {
await run(callbacks);
} else if (typeof callbacks === 'object') {
callbacks = callbacks.slice().filter(Boolean);
await callbacks.reduce((prev, next) => {
return prev.then((res) => {
return run(next).then((result) => Promise.resolve(res.concat(result)));
});
}, Promise.resolve([]));
}
return true;
}
/** /**
* tables * tables
* *