UNPKG

15.4 kBJavaScriptView Raw
1'use strict';
2
3const _ = require('lodash');
4const { logger } = require('./utils/logger');
5const debug = logger.debugContext('hooks');
6
7const hookTypes = {
8 beforeValidate: { params: 2 },
9 afterValidate: { params: 2 },
10 validationFailed: { params: 3 },
11 beforeCreate: { params: 2 },
12 afterCreate: { params: 2 },
13 beforeDestroy: { params: 2 },
14 afterDestroy: { params: 2 },
15 beforeRestore: { params: 2 },
16 afterRestore: { params: 2 },
17 beforeUpdate: { params: 2 },
18 afterUpdate: { params: 2 },
19 beforeSave: { params: 2, proxies: ['beforeUpdate', 'beforeCreate'] },
20 afterSave: { params: 2, proxies: ['afterUpdate', 'afterCreate'] },
21 beforeUpsert: { params: 2 },
22 afterUpsert: { params: 2 },
23 beforeBulkCreate: { params: 2 },
24 afterBulkCreate: { params: 2 },
25 beforeBulkDestroy: { params: 1 },
26 afterBulkDestroy: { params: 1 },
27 beforeBulkRestore: { params: 1 },
28 afterBulkRestore: { params: 1 },
29 beforeBulkUpdate: { params: 1 },
30 afterBulkUpdate: { params: 1 },
31 beforeFind: { params: 1 },
32 beforeFindAfterExpandIncludeAll: { params: 1 },
33 beforeFindAfterOptions: { params: 1 },
34 afterFind: { params: 2 },
35 beforeCount: { params: 1 },
36 beforeDefine: { params: 2, sync: true, noModel: true },
37 afterDefine: { params: 1, sync: true, noModel: true },
38 beforeInit: { params: 2, sync: true, noModel: true },
39 afterInit: { params: 1, sync: true, noModel: true },
40 beforeAssociate: { params: 2, sync: true },
41 afterAssociate: { params: 2, sync: true },
42 beforeConnect: { params: 1, noModel: true },
43 afterConnect: { params: 2, noModel: true },
44 beforeDisconnect: { params: 1, noModel: true },
45 afterDisconnect: { params: 1, noModel: true },
46 beforeSync: { params: 1 },
47 afterSync: { params: 1 },
48 beforeBulkSync: { params: 1 },
49 afterBulkSync: { params: 1 },
50 beforeQuery: { params: 2 },
51 afterQuery: { params: 2 }
52};
53exports.hooks = hookTypes;
54
55
56/**
57 * get array of current hook and its proxies combined
58 *
59 * @param {string} hookType any hook type @see {@link hookTypes}
60 *
61 * @private
62 */
63const getProxiedHooks = hookType =>
64 hookTypes[hookType].proxies
65 ? hookTypes[hookType].proxies.concat(hookType)
66 : [hookType]
67;
68
69function getHooks(hooked, hookType) {
70 return (hooked.options.hooks || {})[hookType] || [];
71}
72
73const Hooks = {
74 /**
75 * Process user supplied hooks definition
76 *
77 * @param {object} hooks hooks definition
78 *
79 * @private
80 * @memberof Sequelize
81 * @memberof Sequelize.Model
82 */
83 _setupHooks(hooks) {
84 this.options.hooks = {};
85 _.map(hooks || {}, (hooksArray, hookName) => {
86 if (!Array.isArray(hooksArray)) hooksArray = [hooksArray];
87 hooksArray.forEach(hookFn => this.addHook(hookName, hookFn));
88 });
89 },
90
91 async runHooks(hooks, ...hookArgs) {
92 if (!hooks) throw new Error('runHooks requires at least 1 argument');
93
94 let hookType;
95
96 if (typeof hooks === 'string') {
97 hookType = hooks;
98 hooks = getHooks(this, hookType);
99
100 if (this.sequelize) {
101 hooks = hooks.concat(getHooks(this.sequelize, hookType));
102 }
103 }
104
105 if (!Array.isArray(hooks)) {
106 hooks = [hooks];
107 }
108
109 // synchronous hooks
110 if (hookTypes[hookType] && hookTypes[hookType].sync) {
111 for (let hook of hooks) {
112 if (typeof hook === 'object') {
113 hook = hook.fn;
114 }
115
116 debug(`running hook(sync) ${hookType}`);
117 hook.apply(this, hookArgs);
118 }
119 return;
120 }
121
122 // asynchronous hooks (default)
123 for (let hook of hooks) {
124 if (typeof hook === 'object') {
125 hook = hook.fn;
126 }
127
128 debug(`running hook ${hookType}`);
129 await hook.apply(this, hookArgs);
130 }
131 },
132
133 /**
134 * Add a hook to the model
135 *
136 * @param {string} hookType hook name @see {@link hookTypes}
137 * @param {string|Function} [name] Provide a name for the hook function. It can be used to remove the hook later or to order hooks based on some sort of priority system in the future.
138 * @param {Function} fn The hook function
139 *
140 * @memberof Sequelize
141 * @memberof Sequelize.Model
142 */
143 addHook(hookType, name, fn) {
144 if (typeof name === 'function') {
145 fn = name;
146 name = null;
147 }
148
149 debug(`adding hook ${hookType}`);
150 // check for proxies, add them too
151 hookType = getProxiedHooks(hookType);
152
153 hookType.forEach(type => {
154 const hooks = getHooks(this, type);
155 hooks.push(name ? { name, fn } : fn);
156 this.options.hooks[type] = hooks;
157 });
158
159 return this;
160 },
161
162 /**
163 * Remove hook from the model
164 *
165 * @param {string} hookType @see {@link hookTypes}
166 * @param {string|Function} name name of hook or function reference which was attached
167 *
168 * @memberof Sequelize
169 * @memberof Sequelize.Model
170 */
171 removeHook(hookType, name) {
172 const isReference = typeof name === 'function' ? true : false;
173
174 if (!this.hasHook(hookType)) {
175 return this;
176 }
177
178 debug(`removing hook ${hookType}`);
179
180 // check for proxies, add them too
181 hookType = getProxiedHooks(hookType);
182
183 for (const type of hookType) {
184 this.options.hooks[type] = this.options.hooks[type].filter(hook => {
185 if (isReference && typeof hook === 'function') {
186 return hook !== name; // check if same method
187 }
188 if (!isReference && typeof hook === 'object') {
189 return hook.name !== name;
190 }
191 return true;
192 });
193 }
194
195 return this;
196 },
197
198 /**
199 * Check whether the mode has any hooks of this type
200 *
201 * @param {string} hookType @see {@link hookTypes}
202 *
203 * @alias hasHooks
204 *
205 * @memberof Sequelize
206 * @memberof Sequelize.Model
207 */
208 hasHook(hookType) {
209 return this.options.hooks[hookType] && !!this.options.hooks[hookType].length;
210 }
211};
212Hooks.hasHooks = Hooks.hasHook;
213
214
215function applyTo(target, isModel = false) {
216 _.mixin(target, Hooks);
217
218 for (const hook of Object.keys(hookTypes)) {
219 if (isModel && hookTypes[hook].noModel) {
220 continue;
221 }
222 target[hook] = function(name, callback) {
223 return this.addHook(hook, name, callback);
224 };
225 }
226}
227exports.applyTo = applyTo;
228
229/**
230 * A hook that is run before validation
231 *
232 * @param {string} name
233 * @param {Function} fn A callback function that is called with instance, options
234 * @name beforeValidate
235 * @memberof Sequelize.Model
236 */
237
238/**
239 * A hook that is run after validation
240 *
241 * @param {string} name
242 * @param {Function} fn A callback function that is called with instance, options
243 * @name afterValidate
244 * @memberof Sequelize.Model
245 */
246
247/**
248 * A hook that is run when validation fails
249 *
250 * @param {string} name
251 * @param {Function} fn A callback function that is called with instance, options, error. Error is the
252 * SequelizeValidationError. If the callback throws an error, it will replace the original validation error.
253 * @name validationFailed
254 * @memberof Sequelize.Model
255 */
256
257/**
258 * A hook that is run before creating a single instance
259 *
260 * @param {string} name
261 * @param {Function} fn A callback function that is called with attributes, options
262 * @name beforeCreate
263 * @memberof Sequelize.Model
264 */
265
266/**
267 * A hook that is run after creating a single instance
268 *
269 * @param {string} name
270 * @param {Function} fn A callback function that is called with attributes, options
271 * @name afterCreate
272 * @memberof Sequelize.Model
273 */
274
275/**
276 * A hook that is run before creating or updating a single instance, It proxies `beforeCreate` and `beforeUpdate`
277 *
278 * @param {string} name
279 * @param {Function} fn A callback function that is called with attributes, options
280 * @name beforeSave
281 * @memberof Sequelize.Model
282 */
283
284/**
285 * A hook that is run before upserting
286 *
287 * @param {string} name
288 * @param {Function} fn A callback function that is called with attributes, options
289 * @name beforeUpsert
290 * @memberof Sequelize.Model
291 */
292
293/**
294 * A hook that is run after upserting
295 *
296 * @param {string} name
297 * @param {Function} fn A callback function that is called with the result of upsert(), options
298 * @name afterUpsert
299 * @memberof Sequelize.Model
300 */
301
302/**
303 * A hook that is run after creating or updating a single instance, It proxies `afterCreate` and `afterUpdate`
304 *
305 * @param {string} name
306 * @param {Function} fn A callback function that is called with attributes, options
307 * @name afterSave
308 * @memberof Sequelize.Model
309 */
310
311/**
312 * A hook that is run before destroying a single instance
313 *
314 * @param {string} name
315 * @param {Function} fn A callback function that is called with instance, options
316 *
317 * @name beforeDestroy
318 * @memberof Sequelize.Model
319 */
320
321/**
322 * A hook that is run after destroying a single instance
323 *
324 * @param {string} name
325 * @param {Function} fn A callback function that is called with instance, options
326 *
327 * @name afterDestroy
328 * @memberof Sequelize.Model
329 */
330
331/**
332 * A hook that is run before restoring a single instance
333 *
334 * @param {string} name
335 * @param {Function} fn A callback function that is called with instance, options
336 *
337 * @name beforeRestore
338 * @memberof Sequelize.Model
339 */
340
341/**
342 * A hook that is run after restoring a single instance
343 *
344 * @param {string} name
345 * @param {Function} fn A callback function that is called with instance, options
346 *
347 * @name afterRestore
348 * @memberof Sequelize.Model
349 */
350
351/**
352 * A hook that is run before updating a single instance
353 *
354 * @param {string} name
355 * @param {Function} fn A callback function that is called with instance, options
356 * @name beforeUpdate
357 * @memberof Sequelize.Model
358 */
359
360/**
361 * A hook that is run after updating a single instance
362 *
363 * @param {string} name
364 * @param {Function} fn A callback function that is called with instance, options
365 * @name afterUpdate
366 * @memberof Sequelize.Model
367 */
368
369/**
370 * A hook that is run before creating instances in bulk
371 *
372 * @param {string} name
373 * @param {Function} fn A callback function that is called with instances, options
374 * @name beforeBulkCreate
375 * @memberof Sequelize.Model
376 */
377
378/**
379 * A hook that is run after creating instances in bulk
380 *
381 * @param {string} name
382 * @param {Function} fn A callback function that is called with instances, options
383 * @name afterBulkCreate
384 * @memberof Sequelize.Model
385 */
386
387/**
388 * A hook that is run before destroying instances in bulk
389 *
390 * @param {string} name
391 * @param {Function} fn A callback function that is called with options
392 *
393 * @name beforeBulkDestroy
394 * @memberof Sequelize.Model
395 */
396
397/**
398 * A hook that is run after destroying instances in bulk
399 *
400 * @param {string} name
401 * @param {Function} fn A callback function that is called with options
402 *
403 * @name afterBulkDestroy
404 * @memberof Sequelize.Model
405 */
406
407/**
408 * A hook that is run before restoring instances in bulk
409 *
410 * @param {string} name
411 * @param {Function} fn A callback function that is called with options
412 *
413 * @name beforeBulkRestore
414 * @memberof Sequelize.Model
415 */
416
417/**
418 * A hook that is run after restoring instances in bulk
419 *
420 * @param {string} name
421 * @param {Function} fn A callback function that is called with options
422 *
423 * @name afterBulkRestore
424 * @memberof Sequelize.Model
425 */
426
427/**
428 * A hook that is run before updating instances in bulk
429 *
430 * @param {string} name
431 * @param {Function} fn A callback function that is called with options
432 * @name beforeBulkUpdate
433 * @memberof Sequelize.Model
434 */
435
436/**
437 * A hook that is run after updating instances in bulk
438 *
439 * @param {string} name
440 * @param {Function} fn A callback function that is called with options
441 * @name afterBulkUpdate
442 * @memberof Sequelize.Model
443 */
444
445/**
446 * A hook that is run before a find (select) query
447 *
448 * @param {string} name
449 * @param {Function} fn A callback function that is called with options
450 * @name beforeFind
451 * @memberof Sequelize.Model
452 */
453
454/**
455 * A hook that is run before a find (select) query, after any { include: {all: ...} } options are expanded
456 *
457 * @param {string} name
458 * @param {Function} fn A callback function that is called with options
459 * @name beforeFindAfterExpandIncludeAll
460 * @memberof Sequelize.Model
461 */
462
463/**
464 * A hook that is run before a find (select) query, after all option parsing is complete
465 *
466 * @param {string} name
467 * @param {Function} fn A callback function that is called with options
468 * @name beforeFindAfterOptions
469 * @memberof Sequelize.Model
470 */
471
472/**
473 * A hook that is run after a find (select) query
474 *
475 * @param {string} name
476 * @param {Function} fn A callback function that is called with instance(s), options
477 * @name afterFind
478 * @memberof Sequelize.Model
479 */
480
481/**
482 * A hook that is run before a count query
483 *
484 * @param {string} name
485 * @param {Function} fn A callback function that is called with options
486 * @name beforeCount
487 * @memberof Sequelize.Model
488 */
489
490/**
491 * A hook that is run before a define call
492 *
493 * @param {string} name
494 * @param {Function} fn A callback function that is called with attributes, options
495 * @name beforeDefine
496 * @memberof Sequelize
497 */
498
499/**
500 * A hook that is run after a define call
501 *
502 * @param {string} name
503 * @param {Function} fn A callback function that is called with factory
504 * @name afterDefine
505 * @memberof Sequelize
506 */
507
508/**
509 * A hook that is run before Sequelize() call
510 *
511 * @param {string} name
512 * @param {Function} fn A callback function that is called with config, options
513 * @name beforeInit
514 * @memberof Sequelize
515 */
516
517/**
518 * A hook that is run after Sequelize() call
519 *
520 * @param {string} name
521 * @param {Function} fn A callback function that is called with sequelize
522 * @name afterInit
523 * @memberof Sequelize
524 */
525
526/**
527 * A hook that is run before a connection is created
528 *
529 * @param {string} name
530 * @param {Function} fn A callback function that is called with config passed to connection
531 * @name beforeConnect
532 * @memberof Sequelize
533 */
534
535/**
536 * A hook that is run after a connection is created
537 *
538 * @param {string} name
539 * @param {Function} fn A callback function that is called with the connection object and the config passed to connection
540 * @name afterConnect
541 * @memberof Sequelize
542 */
543
544/**
545 * A hook that is run before a connection is disconnected
546 *
547 * @param {string} name
548 * @param {Function} fn A callback function that is called with the connection object
549 * @name beforeDisconnect
550 * @memberof Sequelize
551 */
552
553/**
554 * A hook that is run after a connection is disconnected
555 *
556 * @param {string} name
557 * @param {Function} fn A callback function that is called with the connection object
558 * @name afterDisconnect
559 * @memberof Sequelize
560 */
561
562/**
563 * A hook that is run before Model.sync call
564 *
565 * @param {string} name
566 * @param {Function} fn A callback function that is called with options passed to Model.sync
567 * @name beforeSync
568 * @memberof Sequelize
569 */
570
571/**
572 * A hook that is run after Model.sync call
573 *
574 * @param {string} name
575 * @param {Function} fn A callback function that is called with options passed to Model.sync
576 * @name afterSync
577 * @memberof Sequelize
578 */
579
580/**
581 * A hook that is run before sequelize.sync call
582 *
583 * @param {string} name
584 * @param {Function} fn A callback function that is called with options passed to sequelize.sync
585 * @name beforeBulkSync
586 * @memberof Sequelize
587 */
588
589/**
590 * A hook that is run after sequelize.sync call
591 *
592 * @param {string} name
593 * @param {Function} fn A callback function that is called with options passed to sequelize.sync
594 * @name afterBulkSync
595 * @memberof Sequelize
596 */