refactor(database): hook proxy (#402)
* refactor(database): make db hooks clearly managed by proxy and support named events * refactor(database): roll back most event logic * refactor(database): rollback module name * fix(database): fix test file path * test: exec order * fix(database): rollback buildSequelizeHook method Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
75f417a006
commit
78071b4cd8
@ -204,6 +204,53 @@ describe('database', () => {
|
||||
expect(listener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('off collection event', async () => {
|
||||
const Post = db.collection({
|
||||
name: 'posts',
|
||||
fields: [{ type: 'string', name: 'title' }],
|
||||
});
|
||||
|
||||
const postAfterCreateListener = jest.fn();
|
||||
|
||||
db.on('posts.afterCreate', postAfterCreateListener);
|
||||
|
||||
await db.sync();
|
||||
|
||||
db.off('posts.afterCreate', postAfterCreateListener);
|
||||
|
||||
await Post.repository.create({
|
||||
values: {
|
||||
title: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
expect(postAfterCreateListener).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('off global event', async () => {
|
||||
const Post = db.collection({
|
||||
name: 'posts',
|
||||
fields: [{ type: 'string', name: 'title' }],
|
||||
});
|
||||
|
||||
const postAfterCreateListener = jest.fn();
|
||||
|
||||
db.on('posts.afterCreate', postAfterCreateListener);
|
||||
db.on('afterCreate', postAfterCreateListener);
|
||||
|
||||
await db.sync();
|
||||
|
||||
db.off('afterCreate', postAfterCreateListener);
|
||||
|
||||
await Post.repository.create({
|
||||
values: {
|
||||
title: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
expect(postAfterCreateListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('custom model', async () => {
|
||||
class CustomModel extends Model {
|
||||
customMethod() {
|
||||
|
54
packages/core/database/src/__tests__/model-hook.test.ts
Normal file
54
packages/core/database/src/__tests__/model-hook.test.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { Database } from '..';
|
||||
import { mockDatabase } from '.';
|
||||
|
||||
describe('model hook', () => {
|
||||
let db: Database;
|
||||
|
||||
beforeEach(async () => {
|
||||
db = mockDatabase();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await db.close();
|
||||
});
|
||||
|
||||
describe('match', () => {
|
||||
test('sequelize db hooks', async () => {
|
||||
const matcher = db.modelHook.match('beforeDefine');
|
||||
expect(matcher).toEqual('beforeDefine');
|
||||
});
|
||||
|
||||
test('sequelize global model hooks', async () => {
|
||||
const matcher = db.modelHook.match('beforeCreate');
|
||||
expect(matcher).toEqual('beforeCreate');
|
||||
});
|
||||
|
||||
test('sequelize model hooks without existing collection', async () => {
|
||||
const matcher = db.modelHook.match('posts.beforeCreate');
|
||||
expect(matcher).toEqual('beforeCreate');
|
||||
});
|
||||
|
||||
test('sequelize model hooks with existing collection', async () => {
|
||||
db.collection({
|
||||
name: 'posts',
|
||||
fields: []
|
||||
});
|
||||
const matcher = db.modelHook.match('posts.beforeCreate');
|
||||
expect(matcher).toEqual('beforeCreate');
|
||||
});
|
||||
|
||||
test('customized global hooks', async () => {
|
||||
const matcher = db.modelHook.match('beforeDefineCollection');
|
||||
expect(matcher).toBeNull();
|
||||
});
|
||||
|
||||
test('customized model hooks', async () => {
|
||||
db.collection({
|
||||
name: 'posts',
|
||||
fields: []
|
||||
});
|
||||
const matcher = db.modelHook.match('posts.beforeCreateWithAssociations');
|
||||
expect(matcher).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@ -308,7 +308,7 @@ describe('belongs to many', () => {
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
console.log(tags);
|
||||
// console.log(tags);
|
||||
});
|
||||
|
||||
test('update raw attribute', async () => {
|
||||
|
@ -14,6 +14,33 @@ describe('sequelize-hooks', () => {
|
||||
await db.close();
|
||||
});
|
||||
|
||||
test('exec order', async () => {
|
||||
const collection = db.collection({
|
||||
name: 't_test',
|
||||
});
|
||||
const orders = [];
|
||||
db.on('beforeCreate', () => {
|
||||
orders.push('beforeCreate');
|
||||
});
|
||||
db.on('t_test.beforeCreate', () => {
|
||||
orders.push('model.beforeCreate');
|
||||
});
|
||||
db.on('afterCreate', () => {
|
||||
orders.push('afterCreate');
|
||||
});
|
||||
db.on('t_test.afterCreate', () => {
|
||||
orders.push('model.afterCreate');
|
||||
});
|
||||
await collection.sync();
|
||||
await collection.model.create();
|
||||
expect(orders).toEqual([
|
||||
'model.beforeCreate',
|
||||
'beforeCreate',
|
||||
'model.afterCreate',
|
||||
'afterCreate'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('afterSync', () => {
|
||||
test('singular name', async () => {
|
||||
const collection = db.collection({
|
||||
|
@ -173,7 +173,7 @@ describe('update associations', () => {
|
||||
await updateAssociations(user1, {
|
||||
posts: post1,
|
||||
});
|
||||
console.log(JSON.stringify(user1, null, 2));
|
||||
|
||||
expect(user1.toJSON()).toMatchObject({
|
||||
name: 'user1',
|
||||
});
|
||||
@ -191,7 +191,7 @@ describe('update associations', () => {
|
||||
name: 'post111',
|
||||
},
|
||||
});
|
||||
console.log(JSON.stringify(user1, null, 2));
|
||||
|
||||
expect(user1.toJSON()).toMatchObject({
|
||||
name: 'user1',
|
||||
});
|
||||
@ -216,7 +216,7 @@ describe('update associations', () => {
|
||||
post3,
|
||||
],
|
||||
});
|
||||
console.log(JSON.stringify(user1, null, 2));
|
||||
|
||||
expect(user1.toJSON()).toMatchObject({
|
||||
name: 'user1',
|
||||
});
|
||||
|
@ -356,13 +356,13 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
return this.sequelize.close();
|
||||
}
|
||||
|
||||
on(event: string | symbol, listener: (...args: any[]) => void): this {
|
||||
const modelEventName = this.modelHook.isModelHook(event);
|
||||
on(event: string | symbol, listener): this {
|
||||
// NOTE: to match if event is a sequelize or model type
|
||||
const type = this.modelHook.match(event);
|
||||
|
||||
if (modelEventName && !this.modelHook.hasBindEvent(modelEventName)) {
|
||||
this.sequelize.addHook(modelEventName, this.modelHook.sequelizeHookBuilder(modelEventName));
|
||||
|
||||
this.modelHook.bindEvent(modelEventName);
|
||||
if (type && !this.modelHook.hasBoundEvent(type)) {
|
||||
this.sequelize.addHook(type, this.modelHook.buildSequelizeHook(type));
|
||||
this.modelHook.bindEvent(type);
|
||||
}
|
||||
|
||||
return super.on(event, listener);
|
||||
|
@ -1,28 +1,30 @@
|
||||
import lodash from 'lodash';
|
||||
import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
|
||||
import Database from './database';
|
||||
import { Model } from './model';
|
||||
import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
|
||||
|
||||
const { hooks } = require('sequelize/lib/hooks');
|
||||
|
||||
|
||||
|
||||
export class ModelHook {
|
||||
database: Database;
|
||||
boundEvent = new Set<string>();
|
||||
|
||||
boundEvents = new Set<string>();
|
||||
|
||||
constructor(database: Database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
isModelHook(eventName: string | symbol): keyof SequelizeHooks | false {
|
||||
if (lodash.isString(eventName)) {
|
||||
const hookType = eventName.split('.').pop();
|
||||
|
||||
if (hooks[hookType]) {
|
||||
return <keyof SequelizeHooks>hookType;
|
||||
}
|
||||
match(event: string | Symbol): keyof SequelizeHooks | null {
|
||||
// NOTE: skip Symbol event
|
||||
if (!lodash.isString(event)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return false;
|
||||
const type = event.split('.').pop();
|
||||
|
||||
return type in hooks ? <keyof SequelizeHooks>type : null;
|
||||
}
|
||||
|
||||
findModelName(hookArgs) {
|
||||
@ -47,25 +49,24 @@ export class ModelHook {
|
||||
return null;
|
||||
}
|
||||
|
||||
bindEvent(eventName) {
|
||||
this.boundEvent.add(eventName);
|
||||
bindEvent(type) {
|
||||
this.boundEvents.add(type);
|
||||
}
|
||||
|
||||
hasBindEvent(eventName) {
|
||||
return this.boundEvent.has(eventName);
|
||||
hasBoundEvent(type): boolean {
|
||||
return this.boundEvents.has(type);
|
||||
}
|
||||
|
||||
sequelizeHookBuilder(eventName) {
|
||||
buildSequelizeHook(type) {
|
||||
return async (...args: any[]) => {
|
||||
const modelName = this.findModelName(args);
|
||||
|
||||
if (modelName) {
|
||||
// emit model event
|
||||
await this.database.emitAsync(`${modelName}.${eventName}`, ...args);
|
||||
await this.database.emitAsync(`${modelName}.${type}`, ...args);
|
||||
}
|
||||
|
||||
// emit sequelize global event
|
||||
await this.database.emitAsync(eventName, ...args);
|
||||
await this.database.emitAsync(type, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user