feat add event emitter support to database
This commit is contained in:
parent
d3890b4b66
commit
a8cd70017b
@ -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]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 配置(配置的文件驱动)
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user