feat: improve code

This commit is contained in:
chenos 2021-09-08 00:18:41 +08:00
parent a8cd70017b
commit c182c29161
5 changed files with 223 additions and 128 deletions

View File

@ -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();
});
});

View File

@ -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]);
});
});

View File

@ -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}`;
},
},
});
};

View File

@ -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,6 +49,40 @@ export function extend(tableOptions: TableOptions, mergeOptions: MergeOptions =
return new Extend(tableOptions, mergeOptions);
}
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<string, any>();
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<string, HookType> | 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<boolean> {

View File

@ -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() {