不要怂,就是干,撸起袖子干!

Commit 7a6cc32f by Tony Brix Committed by Sushant

feat(hooks): beforeDisconnect / afterDisconnect (#11117)

1 parent 92cb9038
...@@ -186,11 +186,13 @@ new Sequelize(..., { ...@@ -186,11 +186,13 @@ new Sequelize(..., {
### Connection Hooks ### Connection Hooks
Sequelize provides two hooks that are executed immediately before and after a database connection is obtained: Sequelize provides four hooks that are executed immediately before and after a database connection is obtained or released:
```text ```text
beforeConnect(config) beforeConnect(config)
afterConnect(connection, config) afterConnect(connection, config)
beforeDisconnect(connection)
afterDisconnect(connection)
``` ```
These hooks can be useful if you need to asynchronously obtain database credentials, or need to directly access the low-level database connection after it has been created. These hooks can be useful if you need to asynchronously obtain database credentials, or need to directly access the low-level database connection after it has been created.
......
...@@ -320,7 +320,9 @@ class ConnectionManager { ...@@ -320,7 +320,9 @@ class ConnectionManager {
* @returns {Promise} * @returns {Promise}
*/ */
_disconnect(connection) { _disconnect(connection) {
return this.dialect.connectionManager.disconnect(connection); return this.sequelize.runHooks('beforeDisconnect', connection)
.then(() => this.dialect.connectionManager.disconnect(connection))
.then(() => this.sequelize.runHooks('afterDisconnect', connection));
} }
/** /**
......
...@@ -42,6 +42,8 @@ const hookTypes = { ...@@ -42,6 +42,8 @@ const hookTypes = {
afterAssociate: { params: 2, sync: true }, afterAssociate: { params: 2, sync: true },
beforeConnect: { params: 1, noModel: true }, beforeConnect: { params: 1, noModel: true },
afterConnect: { params: 2, noModel: true }, afterConnect: { params: 2, noModel: true },
beforeDisconnect: { params: 1, noModel: true },
afterDisconnect: { params: 1, noModel: true },
beforeSync: { params: 1 }, beforeSync: { params: 1 },
afterSync: { params: 1 }, afterSync: { params: 1 },
beforeBulkSync: { params: 1 }, beforeBulkSync: { params: 1 },
...@@ -501,12 +503,28 @@ exports.applyTo = applyTo; ...@@ -501,12 +503,28 @@ exports.applyTo = applyTo;
/** /**
* A hook that is run after a connection is created * A hook that is run after a connection is created
* @param {string} name * @param {string} name
* @param {Function} fn A callback function that is called with the connection object and thye config passed to connection * @param {Function} fn A callback function that is called with the connection object and the config passed to connection
* @name afterConnect * @name afterConnect
* @memberof Sequelize * @memberof Sequelize
*/ */
/** /**
* A hook that is run before a connection is disconnected
* @param {string} name
* @param {Function} fn A callback function that is called with the connection object
* @name beforeDisconnect
* @memberof Sequelize
*/
/**
* A hook that is run after a connection is disconnected
* @param {string} name
* @param {Function} fn A callback function that is called with the connection object
* @name afterDisconnect
* @memberof Sequelize
*/
/**
* A hook that is run before Model.sync call * A hook that is run before Model.sync call
* @param {string} name * @param {string} name
* @param {Function} fn A callback function that is called with options passed to Model.sync * @param {Function} fn A callback function that is called with options passed to Model.sync
......
...@@ -167,7 +167,7 @@ class Sequelize { ...@@ -167,7 +167,7 @@ class Sequelize {
* @param {number} [options.retry.max] How many times a failing query is automatically retried. Set to 0 to disable retrying on SQL_BUSY error. * @param {number} [options.retry.max] How many times a failing query is automatically retried. Set to 0 to disable retrying on SQL_BUSY error.
* @param {boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like. * @param {boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like.
* @param {Object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators. * @param {Object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators.
* @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()` and `afterConnect()` hooks may be defined here. * @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()`, `afterConnect()`, `beforeDisconnect()`, and `afterDisconnect()` hooks may be defined here.
*/ */
constructor(database, username, password, options) { constructor(database, username, password, options) {
let config; let config;
......
...@@ -66,4 +66,42 @@ describe('connection manager', () => { ...@@ -66,4 +66,42 @@ describe('connection manager', () => {
}); });
}); });
}); });
describe('_disconnect', () => {
beforeEach(function() {
this.connection = {};
this.dialect = {
connectionManager: {
disconnect: sinon.stub().resolves(this.connection)
}
};
this.sequelize = Support.createSequelizeInstance();
});
it('should call beforeDisconnect', function() {
const spy = sinon.spy();
this.sequelize.beforeDisconnect(spy);
const connectionManager = new ConnectionManager(this.dialect, this.sequelize);
return connectionManager._disconnect(this.connection).then(() => {
expect(spy.callCount).to.equal(1);
expect(spy.firstCall.args[0]).to.equal(this.connection);
});
});
it('should call afterDisconnect', function() {
const spy = sinon.spy();
this.sequelize.afterDisconnect(spy);
const connectionManager = new ConnectionManager(this.dialect, this.sequelize);
return connectionManager._disconnect(this.connection).then(() => {
expect(spy.callCount).to.equal(1);
expect(spy.firstCall.args[0]).to.equal(this.connection);
});
});
});
}); });
...@@ -15,7 +15,7 @@ describe(Support.getTestDialectTeaser('Hooks'), () => { ...@@ -15,7 +15,7 @@ describe(Support.getTestDialectTeaser('Hooks'), () => {
}); });
it('does not expose non-model hooks', function() { it('does not expose non-model hooks', function() {
for (const badHook of ['beforeDefine', 'afterDefine', 'beforeConnect', 'afterConnect', 'beforeInit', 'afterInit']) { for (const badHook of ['beforeDefine', 'afterDefine', 'beforeConnect', 'afterConnect', 'beforeDisconnect', 'afterDisconnect', 'beforeInit', 'afterInit']) {
expect(this.Model).to.not.have.property(badHook); expect(this.Model).to.not.have.property(badHook);
} }
}); });
......
...@@ -54,6 +54,8 @@ export interface SequelizeHooks extends ModelHooks { ...@@ -54,6 +54,8 @@ export interface SequelizeHooks extends ModelHooks {
afterInit(sequelize: Sequelize): void; afterInit(sequelize: Sequelize): void;
beforeConnect(config: Config): HookReturn; beforeConnect(config: Config): HookReturn;
afterConnect(connection: unknown, config: Config): HookReturn; afterConnect(connection: unknown, config: Config): HookReturn;
beforeDisconnect(connection: unknown): HookReturn;
afterDisconnect(connection: unknown): HookReturn;
} }
/** /**
......
...@@ -57,12 +57,12 @@ export interface SyncOptions extends Logging { ...@@ -57,12 +57,12 @@ export interface SyncOptions extends Logging {
* The schema that the tables should be created in. This can be overridden for each table in sequelize.define * The schema that the tables should be created in. This can be overridden for each table in sequelize.define
*/ */
schema?: string; schema?: string;
/** /**
* An optional parameter to specify the schema search_path (Postgres only) * An optional parameter to specify the schema search_path (Postgres only)
*/ */
searchPath?: string; searchPath?: string;
/** /**
* If hooks is true then beforeSync, afterSync, beforeBulkSync, afterBulkSync hooks will be called * If hooks is true then beforeSync, afterSync, beforeBulkSync, afterBulkSync hooks will be called
*/ */
...@@ -622,6 +622,24 @@ export class Sequelize extends Hooks { ...@@ -622,6 +622,24 @@ export class Sequelize extends Hooks {
public static afterConnect(fn: (connection: unknown, options: Config) => void): void; public static afterConnect(fn: (connection: unknown, options: Config) => void): void;
/** /**
* A hook that is run before a connection is released
*
* @param name
* @param fn A callback function that is called with options
*/
public static beforeDisconnect(name: string, fn: (connection: unknown) => void): void;
public static beforeDisconnect(fn: (connection: unknown) => void): void;
/**
* A hook that is run after a connection is released
*
* @param name
* @param fn A callback function that is called with options
*/
public static afterDisconnect(name: string, fn: (connection: unknown) => void): void;
public static afterDisconnect(fn: (connection: unknown) => void): void;
/**
* A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded * A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
* *
* @param name * @param name
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!