UNPKG

3.54 kBJavaScriptView Raw
1// Copyright IBM Corp. 2013,2019. All Rights Reserved.
2// Node module: loopback-datasource-juggler
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6'use strict';
7
8const deprecated = require('depd')('loopback-datasource-juggler');
9const g = require('strong-globalize')();
10
11/*!
12 * Module exports
13 */
14module.exports = Hookable;
15
16/*
17 * Hooks object.
18 * @class Hookable
19 */
20
21function Hookable() {
22}
23
24/**
25 * List of hooks available
26 */
27Hookable.afterInitialize = null;
28Hookable.beforeValidate = null;
29Hookable.afterValidate = null;
30Hookable.beforeSave = null;
31Hookable.afterSave = null;
32Hookable.beforeCreate = null;
33Hookable.afterCreate = null;
34Hookable.beforeUpdate = null;
35Hookable.afterUpdate = null;
36Hookable.beforeDestroy = null;
37Hookable.afterDestroy = null;
38
39/**
40 * @deprecated
41 * Setup a Model-based hook to trigger when the specified action occurs.
42 * The trigger is broken up into three segments: `beforeHook`, `work` and
43 * `afterHook`.
44 * @param {string} actionName The name of the action that triggers the hook.
45 * @param {Function} work The 2nd phase of the trigger.
46 * @param {*} data The value(s) to provide to the 1st phase (`beforeHook`) call.
47 * @callback
48 * @param {Function} callback
49 */
50Hookable.prototype.trigger = function trigger(actionName, work, data, callback) {
51 const capitalizedName = capitalize(actionName);
52 let beforeHook = this.constructor['before' + capitalizedName] ||
53 this.constructor['pre' + capitalizedName];
54 let afterHook = this.constructor['after' + capitalizedName] ||
55 this.constructor['post' + capitalizedName];
56 if (actionName === 'validate') {
57 beforeHook = beforeHook || this.constructor.beforeValidation;
58 afterHook = afterHook || this.constructor.afterValidation;
59 }
60 const inst = this;
61
62 if (actionName !== 'initialize') {
63 if (beforeHook)
64 deprecateHook(inst.constructor, ['before', 'pre'], capitalizedName);
65 if (afterHook)
66 deprecateHook(inst.constructor, ['after', 'post'], capitalizedName);
67 }
68
69 // we only call "before" hook when we have actual action (work) to perform
70 if (work) {
71 if (beforeHook) {
72 // before hook should be called on instance with two parameters: next and data
73 beforeHook.call(inst, function() {
74 // Check arguments to next(err, result)
75 if (arguments.length) {
76 return callback && callback.apply(null, arguments);
77 }
78 // No err & result is present, proceed with the real work
79 // actual action also have one param: callback
80 work.call(inst, next);
81 }, data);
82 } else {
83 work.call(inst, next);
84 }
85 } else {
86 next();
87 }
88
89 function next(done) {
90 if (afterHook) {
91 afterHook.call(inst, done);
92 } else if (done) {
93 done.call(this);
94 }
95 }
96};
97
98function capitalize(string) {
99 return string.charAt(0).toUpperCase() + string.slice(1);
100}
101
102function deprecateHook(ctor, prefixes, capitalizedName) {
103 const candidateNames = prefixes.map(function(p) { return p + capitalizedName; });
104 if (capitalizedName === 'Validate')
105 candidateNames.push(prefixes[0] + 'Validation');
106
107 let hookName = candidateNames.filter(function(hook) { return !!ctor[hook]; })[0];
108 if (!hookName) return; // just to be sure, this should never happen
109 if (ctor.modelName) hookName = ctor.modelName + '.' + hookName;
110 deprecated(g.f('Model hook "%s" is deprecated, ' +
111 'use Operation hooks instead. ' +
112 '{{http://docs.strongloop.com/display/LB/Operation+hooks}}', hookName));
113}