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();
|
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 () => {
|
test('custom model', async () => {
|
||||||
class CustomModel extends Model {
|
class CustomModel extends Model {
|
||||||
customMethod() {
|
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,
|
offset: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(tags);
|
// console.log(tags);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('update raw attribute', async () => {
|
test('update raw attribute', async () => {
|
||||||
|
@ -14,6 +14,33 @@ describe('sequelize-hooks', () => {
|
|||||||
await db.close();
|
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', () => {
|
describe('afterSync', () => {
|
||||||
test('singular name', async () => {
|
test('singular name', async () => {
|
||||||
const collection = db.collection({
|
const collection = db.collection({
|
||||||
|
@ -173,7 +173,7 @@ describe('update associations', () => {
|
|||||||
await updateAssociations(user1, {
|
await updateAssociations(user1, {
|
||||||
posts: post1,
|
posts: post1,
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(user1, null, 2));
|
|
||||||
expect(user1.toJSON()).toMatchObject({
|
expect(user1.toJSON()).toMatchObject({
|
||||||
name: 'user1',
|
name: 'user1',
|
||||||
});
|
});
|
||||||
@ -191,7 +191,7 @@ describe('update associations', () => {
|
|||||||
name: 'post111',
|
name: 'post111',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(user1, null, 2));
|
|
||||||
expect(user1.toJSON()).toMatchObject({
|
expect(user1.toJSON()).toMatchObject({
|
||||||
name: 'user1',
|
name: 'user1',
|
||||||
});
|
});
|
||||||
@ -216,7 +216,7 @@ describe('update associations', () => {
|
|||||||
post3,
|
post3,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(user1, null, 2));
|
|
||||||
expect(user1.toJSON()).toMatchObject({
|
expect(user1.toJSON()).toMatchObject({
|
||||||
name: 'user1',
|
name: 'user1',
|
||||||
});
|
});
|
||||||
|
@ -356,13 +356,13 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
return this.sequelize.close();
|
return this.sequelize.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
on(event: string | symbol, listener: (...args: any[]) => void): this {
|
on(event: string | symbol, listener): this {
|
||||||
const modelEventName = this.modelHook.isModelHook(event);
|
// NOTE: to match if event is a sequelize or model type
|
||||||
|
const type = this.modelHook.match(event);
|
||||||
|
|
||||||
if (modelEventName && !this.modelHook.hasBindEvent(modelEventName)) {
|
if (type && !this.modelHook.hasBoundEvent(type)) {
|
||||||
this.sequelize.addHook(modelEventName, this.modelHook.sequelizeHookBuilder(modelEventName));
|
this.sequelize.addHook(type, this.modelHook.buildSequelizeHook(type));
|
||||||
|
this.modelHook.bindEvent(type);
|
||||||
this.modelHook.bindEvent(modelEventName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.on(event, listener);
|
return super.on(event, listener);
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
|
|
||||||
import Database from './database';
|
import Database from './database';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
|
import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
|
||||||
|
|
||||||
const { hooks } = require('sequelize/lib/hooks');
|
const { hooks } = require('sequelize/lib/hooks');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class ModelHook {
|
export class ModelHook {
|
||||||
database: Database;
|
database: Database;
|
||||||
boundEvent = new Set<string>();
|
|
||||||
|
boundEvents = new Set<string>();
|
||||||
|
|
||||||
constructor(database: Database) {
|
constructor(database: Database) {
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
isModelHook(eventName: string | symbol): keyof SequelizeHooks | false {
|
match(event: string | Symbol): keyof SequelizeHooks | null {
|
||||||
if (lodash.isString(eventName)) {
|
// NOTE: skip Symbol event
|
||||||
const hookType = eventName.split('.').pop();
|
if (!lodash.isString(event)) {
|
||||||
|
return null;
|
||||||
if (hooks[hookType]) {
|
|
||||||
return <keyof SequelizeHooks>hookType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
const type = event.split('.').pop();
|
||||||
|
|
||||||
|
return type in hooks ? <keyof SequelizeHooks>type : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
findModelName(hookArgs) {
|
findModelName(hookArgs) {
|
||||||
@ -47,25 +49,24 @@ export class ModelHook {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvent(eventName) {
|
bindEvent(type) {
|
||||||
this.boundEvent.add(eventName);
|
this.boundEvents.add(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasBindEvent(eventName) {
|
hasBoundEvent(type): boolean {
|
||||||
return this.boundEvent.has(eventName);
|
return this.boundEvents.has(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
sequelizeHookBuilder(eventName) {
|
buildSequelizeHook(type) {
|
||||||
return async (...args: any[]) => {
|
return async (...args: any[]) => {
|
||||||
const modelName = this.findModelName(args);
|
const modelName = this.findModelName(args);
|
||||||
|
|
||||||
if (modelName) {
|
if (modelName) {
|
||||||
// emit model event
|
// emit model event
|
||||||
await this.database.emitAsync(`${modelName}.${eventName}`, ...args);
|
await this.database.emitAsync(`${modelName}.${type}`, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit sequelize global event
|
// emit sequelize global event
|
||||||
await this.database.emitAsync(eventName, ...args);
|
await this.database.emitAsync(type, ...args);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user