1 | ;
|
2 |
|
3 | const _ = require('lodash');
|
4 | const { logger } = require('./utils/logger');
|
5 | const debug = logger.debugContext('hooks');
|
6 |
|
7 | const 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 | };
|
53 | exports.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 | */
|
63 | const getProxiedHooks = hookType =>
|
64 | hookTypes[hookType].proxies
|
65 | ? hookTypes[hookType].proxies.concat(hookType)
|
66 | : [hookType]
|
67 | ;
|
68 |
|
69 | function getHooks(hooked, hookType) {
|
70 | return (hooked.options.hooks || {})[hookType] || [];
|
71 | }
|
72 |
|
73 | const 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 | };
|
212 | Hooks.hasHooks = Hooks.hasHook;
|
213 |
|
214 |
|
215 | function 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 | }
|
227 | exports.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 | */
|