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

@ -39,9 +39,13 @@ export function getDatabase() {
} }
}, },
hooks: { hooks: {
beforeDefine(columns, model) { beforeDefine(model, options) {
model.tableName = `${getTestKey()}_${model.tableName || model.name.plural}`; // @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 interface DatabaseOptions extends Options {
} }
export type HookType = 'beforeTableInit' | 'afterTableInit' | 'beforeAddField' | 'afterAddField'; // export type HookType = 'beforeTableInit' | 'afterTableInit' | 'beforeAddField' | 'afterAddField';
export class Extend { export class Extend {
tableOptions: TableOptions; tableOptions: TableOptions;
@ -49,7 +49,41 @@ export function extend(tableOptions: TableOptions, mergeOptions: MergeOptions =
return new Extend(tableOptions, 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; public readonly sequelize: Sequelize;
@ -71,52 +105,44 @@ export default class Database extends EventEmitter {
protected extTableOptions = new Map<string, any>(); protected extTableOptions = new Map<string, any>();
protected hookTypes = new Set([ protected hookTypes = new Map(Object.entries({
'beforeValidate', beforeValidate: 1,
'afterValidate', afterValidate: 1,
'validationFailed', beforeCreate: 1,
'beforeCreate', afterCreate: 1,
'afterCreate', beforeDestroy: 1,
'beforeDestroy', afterDestroy: 1,
'afterDestroy', beforeRestore: 1,
'beforeRestore', afterRestore: 1,
'afterRestore', beforeUpdate: 1,
'beforeUpdate', afterUpdate: 1,
'afterUpdate', beforeSave: 1,
'beforeSave', afterSave: 1,
'afterSave',
'beforeUpsert', beforeBulkCreate: 2,
'afterUpsert', afterBulkCreate: 2,
'beforeBulkCreate',
'afterBulkCreate', beforeBulkDestroy: 3,
'beforeBulkDestroy', afterBulkDestroy: 3,
'afterBulkDestroy', beforeBulkRestore: 3,
'beforeBulkRestore', afterBulkRestore: 3,
'afterBulkRestore', beforeBulkUpdate: 3,
'beforeBulkUpdate', afterBulkUpdate: 3,
'afterBulkUpdate',
'beforeFind', beforeSync: 4,
'beforeFindAfterExpandIncludeAll', afterSync: 4,
'beforeFindAfterOptions', beforeBulkSync: 4,
'afterFind', afterBulkSync: 4,
'beforeCount',
'beforeDefine', beforeDefine: 0,
'afterDefine', afterDefine: 0,
'beforeInit', beforeInit: 0,
'afterInit', afterInit: 0,
'beforeAssociate', beforeConnect: 0,
'afterAssociate', afterConnect: 0,
'beforeConnect', beforeDisconnect: 0,
'afterConnect', afterDisconnect: 0,
'beforeDisconnect', }));
'afterDisconnect',
'beforeSync',
'afterSync',
'beforeBulkSync',
'afterBulkSync',
'beforeQuery',
'afterQuery'
]);
constructor(options?: DatabaseOptions) { constructor(options?: DatabaseOptions) {
super(); super();
@ -138,17 +164,33 @@ export default class Database extends EventEmitter {
return hookType; 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); const hookType = this._getHookType(event);
if (hookType) { if (hookType) {
const state = this.hookTypes.get(hookType);
console.log('sequelize.addHook', hookType) console.log('sequelize.addHook', hookType)
this.sequelize.addHook(hookType, async (model) => { this.sequelize.addHook(hookType, async (...args: any[]) => {
await this.emitAsync(`${model.constructor.name}.${hookType}`); let modelName: string;
await this.emitAsync(hookType); 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); 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> { 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) { constructor(options: Options.StringOptions, context: FieldContext) {
super(options, context); super(options, context);
const Model = context.sourceTable.getModel(); const { database, sourceTable } = context;
Model.addHook('beforeCreate', PASSWORD.hash.bind(this)); const name = sourceTable.getName();
Model.addHook('beforeUpdate', PASSWORD.hash.bind(this)); database.on(`${name}.beforeCreate`, PASSWORD.hash.bind(this));
database.on(`${name}.beforeUpdate`, PASSWORD.hash.bind(this));
} }
public static async hash(this: PASSWORD, model) { public static async hash(this: PASSWORD, model) {
@ -361,9 +362,9 @@ export class UID extends Column {
constructor(options: Options.StringOptions, context: FieldContext) { constructor(options: Options.StringOptions, context: FieldContext) {
super(options, context); super(options, context);
const { sourceTable, database } = context;
const { name, prefix = '' } = options; const { name, prefix = '' } = options;
const Model = context.sourceTable.getModel(); database.on(`${sourceTable.getName()}.beforeCreate`, (model) => {
Model.addHook('beforeCreate', (model) => {
if (!model.get(name)) { if (!model.get(name)) {
model.set(name, `${prefix}${uid()}`); model.set(name, `${prefix}${uid()}`);
} }
@ -819,10 +820,14 @@ export class SORT extends NUMBER {
constructor(options: Options.SortOptions, context: FieldContext) { constructor(options: Options.SortOptions, context: FieldContext) {
super(options, context); super(options, context);
const Model = context.sourceTable.getModel(); // const Model = context.sourceTable.getModel();
// TODO(feature): 可考虑策略模式,以在需要时对外提供接口 // TODO(feature): 可考虑策略模式,以在需要时对外提供接口
Model.addHook('beforeCreate', SORT.beforeCreateHook.bind(this)); const { database, sourceTable } = context;
Model.addHook('beforeBulkCreate', SORT.beforeBulkCreateHook.bind(this)); 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 { public getDataType(): Function {
@ -901,15 +906,20 @@ export class Radio extends BOOLEAN {
} }
constructor({ type, ...options }: Options.RadioOptions, context: FieldContext) { constructor({ type, ...options }: Options.RadioOptions, context: FieldContext) {
super({ ...options, type: 'boolean' }, context); super({ ...options, type: 'radio' }, context);
const Model = context.sourceTable.getModel(); // const Model = context.sourceTable.getModel();
// TODO(feature): 可考虑策略模式,以在需要时对外提供接口 // TODO(feature): 可考虑策略模式,以在需要时对外提供接口
Model.addHook('beforeCreate', Radio.beforeCreateHook.bind(this)); // Model.addHook('beforeCreate', Radio.beforeCreateHook.bind(this));
Model.addHook('beforeUpdate', Radio.beforeUpdateHook.bind(this)); // Model.addHook('beforeUpdate', Radio.beforeUpdateHook.bind(this));
// Model.addHook('beforeUpsert', beforeSaveHook); // // Model.addHook('beforeUpsert', beforeSaveHook);
Model.addHook('beforeBulkCreate', Radio.beforeBulkCreateHook.bind(this)); // Model.addHook('beforeBulkCreate', Radio.beforeBulkCreateHook.bind(this));
// TODO(optimize): bulkUpdate 的 hooks 参数不一样,没有对象列表,考虑到很少用,暂时不实现 // TODO(optimize): bulkUpdate 的 hooks 参数不一样,没有对象列表,考虑到很少用,暂时不实现
// Model.addHook('beforeBulkUpdate', beforeBulkCreateHook); // 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() { public getDataType() {