Skip to content
This repository was archived by the owner on Apr 20, 2018. It is now read-only.

Commit e140451

Browse files
committed
Updating hooks API to use hook objects (#1)
1 parent d5819da commit e140451

File tree

7 files changed

+404
-216
lines changed

7 files changed

+404
-216
lines changed

README.md

+65-36
Original file line numberDiff line numberDiff line change
@@ -71,52 +71,79 @@ var todoService = app.lookup('todos');
7171

7272
### `service.before(beforeHooks)`
7373

74-
`before` hooks allow to pre-process service call parameters. They will be called with the original service parameters
75-
and a callback which should be called with the same (potentially modified) parameters and an error (for example, when
76-
a user is not authorized, `null` for no error).
74+
`before` hooks allow to pre-process service call parameters. They will be called with the hook object
75+
and a callback which should be called with any errors or no arguments or `null` and the modified hook object.
76+
The hook object contains information about the intercepted method and for `before` hooks can have the following properties:
77+
78+
- __method__ - The method name
79+
- __type__ - The hook type (`before` or `after`)
80+
- __callback__ - The original callback (can be replaced but shouldn't be called in your hook)
81+
- __params__ - The service method parameters
82+
- __data__ - The request data (for `create`, `update` and `patch`)
83+
- __id__ - The id (for `get`, `remove`, `update` and `patch`)
84+
85+
All properties of the hook object can be modified and the modified data will be used for the actual service method
86+
call. This is very helpful for pre-processing parameters and massaging data when creating or updating.
87+
7788
The following example checks if a user has been passed to the services `find` method and returns an error if not
7889
and also adds a `createdAt` property to a newly created todo:
7990

8091
```js
8192
todoService.before({
82-
find: function (params, callback) {
83-
if (!params.user) {
93+
find: function (hook, next) {
94+
if (!hook.params.user) {
8495
return callback(new Error('You are not logged in'));
8596
}
86-
callback(null, params);
97+
98+
next();
8799
},
88100

89-
create: function(data, params, callback) {
90-
data.createdAt = new Date();
101+
create: function(hook, next) {
102+
hook.data.createdAt = new Date();
91103

92-
callback(null, data, params);
104+
next();
105+
// Or
106+
next(null, hook);
93107
}
94108
});
95109
```
96110

97111
### `service.after(afterHooks)`
98112

99-
`after` hooks will be called with the result of the service call, the original parameters and a callback. The following example
100-
filters the data returned by a `find` service call based on a users company id and checks if the current user is allowed
101-
to retrieve the data returned by `get` (that is, they have the same company id):
113+
`after` hooks will be called with a similar hook object than `before` hooks but additionally contain a `result`
114+
property with the service call results:
115+
116+
- __method__ - The method name
117+
- __type__ - The hook type (`before` or `after`)
118+
- __result__ - The service call result data
119+
- __callback__ - The original callback (can be replaced but shouldn't be called in your hook)
120+
- __params__ - The service method parameters
121+
- __data__ - The request data (for `create`, `update` and `patch`)
122+
- __id__ - The id (for `get`, `remove`, `update` and `patch`)
123+
124+
In any `after` hook, only modifications to the `result` object will have any effect. This is a good place to filter or
125+
post-process the data retrieved by a service and also add some additional authorization that needs the actual data.
126+
127+
The following example filters the data returned by a `find` service call based on a users company id
128+
and checks if the current user is allowed to retrieve the data returned by `get` (that is, they have the same company id):
102129

103130
```js
104131
todoService.after({
105-
find: function (data, params, callback) {
132+
find: function (hook, next) {
106133
// Manually filter the find results
107-
var filtered = _.filter(data, function (current) {
134+
hook.result = _.filter(hook.result, function (current) {
108135
return current.companyId === params.user.companyId;
109136
});
110137

111-
callback(null, filtered);
138+
next();
112139
},
113140

114-
get: function (data, id, params, callback) {
115-
if (data.companyId !== params.user.companyId) {
141+
get: function (hook, next) {
142+
if (hook.result.companyId !== hook.params.user.companyId) {
116143
return callback(new Error('You are not authorized to access this information'));
117144
}
118145

119-
callback(null, data);
146+
next();
120147
}
121148
});
122149
```
@@ -129,37 +156,39 @@ You can also add `before` and `after` hooks to your initial service object right
129156
```js
130157
var TodoService = {
131158
before: {
132-
find: function (params, callback) {
133-
if (!params.user) {
159+
find: function (hook, next) {
160+
if (!hook.params.user) {
134161
return callback(new Error('You are not logged in'));
135162
}
136-
callback(null, params);
163+
164+
next();
137165
},
138-
139-
get: function (id, params, callback) {
140-
// Pre-process the params passed to the actual service
141-
params.something = 'test';
142-
143-
callback(null, id, params);
166+
167+
create: function(hook, next) {
168+
hook.data.createdAt = new Date();
169+
170+
next();
171+
// Or
172+
next(null, hook);
144173
}
145174
},
146175

147176
after: {
148-
find: function (data, params, callback) {
177+
find: function (hook, next) {
149178
// Manually filter the find results
150-
var filtered = _.filter(data, function (current) {
179+
hook.result = _.filter(hook.result, function (current) {
151180
return current.companyId === params.user.companyId;
152181
});
153-
154-
callback(null, filtered);
182+
183+
next();
155184
},
156-
157-
get: function (data, id, params, callback) {
158-
if (data.companyId !== params.user.companyId) {
185+
186+
get: function (hook, next) {
187+
if (hook.result.companyId !== hook.params.user.companyId) {
159188
return callback(new Error('You are not authorized to access this information'));
160189
}
161-
162-
callback(null, data);
190+
191+
next();
163192
}
164193
}
165194
}

lib/after.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var _ = require('lodash');
2+
var utils = require('./utils');
23

34
module.exports = function (service) {
45
if (typeof service.mixin !== 'function') {
@@ -19,22 +20,41 @@ module.exports = function (service) {
1920
result[name] = function () {
2021
var self = this;
2122
var args = _.toArray(arguments);
22-
var oldCallback = args.pop();
23-
var newCallback = function (error, data) {
23+
var hookObject = utils.hookObject(name, args);
24+
// The callback for the after hook
25+
var hookCallback = function(error, newHookObject) {
26+
// Call the callback with the result we set in `newCallback`
27+
var hook = newHookObject || hookObject;
28+
29+
if (error) {
30+
// Call the old callback with the hook error
31+
return hook.callback(error);
32+
}
33+
34+
hook.callback(null, hook.result);
35+
};
36+
// The new _super method callback
37+
var newCallback = function (error, result) {
2438
if (error) {
25-
return oldCallback(error);
39+
// Call the old callback with the error
40+
return hookObject.callback(error);
2641
}
2742

28-
var hookArgs = args;
29-
30-
// Add data and the oldCallback before running the hook
31-
hookArgs.unshift(data);
32-
hookArgs.push(oldCallback);
43+
// Set hookObject result
44+
hookObject.result = result;
3345

34-
hook.apply(self, hookArgs);
46+
// Call the hook object
47+
hook.call(self, hookObject, hookCallback);
3548
};
3649

37-
return this._super.apply(this, args.concat(newCallback));
50+
hookObject.type = 'after';
51+
52+
// Remove the old callback and replace with the new callback that runs the hook
53+
args.pop();
54+
args.push(newCallback);
55+
56+
// Call super method
57+
return this._super.apply(this, args);
3858
};
3959
});
4060

lib/before.js

+17-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var _ = require('lodash');
2+
var utils = require('./utils');
23

34
module.exports = function (service) {
45
if (typeof service.mixin !== 'function') {
@@ -20,28 +21,22 @@ module.exports = function (service) {
2021
var self = this;
2122
// The original method
2223
var _super = this._super;
23-
var args = _.toArray(arguments);
24-
// Store the original callback
25-
var oldCallback = args.pop();
26-
27-
// The callback that is being called by the .before hook
28-
var newCallback = function () {
29-
var error = arguments[0];
30-
31-
if (error) {
32-
// We got an error
33-
return oldCallback(error);
34-
}
35-
36-
// Use either the original arguments or the ones we got from the callback
37-
var newArguments = arguments.length > 1 ?
38-
Array.prototype.slice.call(arguments, 1, arguments.length) : args;
39-
40-
// Call the original method with the old callback added
41-
_super.apply(self, newArguments.concat(oldCallback));
42-
};
43-
44-
hook.apply(self, args.concat(newCallback));
24+
// The hook data object
25+
var hookObject = utils.hookObject(name, arguments);
26+
// The callback for the hook
27+
var hookCallback = function(error, newHookObject) {
28+
if (error) {
29+
// We got an error
30+
return hookObject.callback(error);
31+
}
32+
33+
// Call the _super method. We either use the original hook object
34+
// to create the arguments or the new one passed to the hook callback
35+
_super.apply(self, utils.makeArguments(newHookObject || hookObject));
36+
};
37+
38+
hookObject.type = 'before';
39+
hook.call(self, hookObject, hookCallback);
4540
};
4641
});
4742

lib/utils.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
function getOrRemove(args) {
2+
return {
3+
id: args[0],
4+
params: args[1],
5+
callback: args[2]
6+
};
7+
}
8+
9+
function updateOrPatch(args) {
10+
return {
11+
id: args[0],
12+
data: args[1],
13+
params: args[2],
14+
callback: args[3]
15+
};
16+
}
17+
18+
exports.converters = {
19+
find: function(args) {
20+
return {
21+
params: args[0],
22+
callback: args[1]
23+
};
24+
},
25+
create: function(args) {
26+
return {
27+
data: args[0],
28+
params: args[1],
29+
callback: args[2]
30+
};
31+
},
32+
get: getOrRemove,
33+
remove: getOrRemove,
34+
update: updateOrPatch,
35+
patch: updateOrPatch
36+
};
37+
38+
exports.hookObject = function(method, args) {
39+
var hook = exports.converters[method](args);
40+
hook.method = method;
41+
42+
return hook;
43+
};
44+
45+
exports.makeArguments = function(hookObject) {
46+
var result = [];
47+
if(hookObject.id) {
48+
result.push(hookObject.id);
49+
}
50+
51+
if(hookObject.data) {
52+
result.push(hookObject.data);
53+
}
54+
55+
result.push(hookObject.params || {});
56+
result.push(hookObject.callback);
57+
58+
return result;
59+
};

test/after.test.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
var _ = require('lodash');
21
var assert = require('assert');
32
var feathers = require('feathers');
43

@@ -8,8 +7,12 @@ describe('.after hooks', function () {
87
it('gets mixed into a service and modifies data', function (done) {
98
var dummyService = {
109
after: {
11-
create: function (result, data, params, callback) {
12-
callback(null, _.extend({ some: 'thing' }, result));
10+
create: function (hook, next) {
11+
assert.equal(hook.type, 'after');
12+
13+
hook.result.some = 'thing';
14+
15+
next(null, hook);
1316
}
1417
},
1518

@@ -30,8 +33,8 @@ describe('.after hooks', function () {
3033
it('returns errors', function (done) {
3134
var dummyService = {
3235
after: {
33-
update: function (result, id, data, params, callback) {
34-
callback(new Error('This did not work'));
36+
update: function (hook, next) {
37+
next(new Error('This did not work'));
3538
}
3639
},
3740

@@ -84,14 +87,17 @@ describe('.after hooks', function () {
8487
var service = app.lookup('dummy');
8588

8689
service.after({
87-
create: function (result, data, params, callback) {
88-
callback(null, _.extend({ some: 'thing' }, result));
90+
create: function (hook, next) {
91+
hook.result.some = 'thing';
92+
next();
8993
}
9094
});
9195

9296
service.after({
93-
create: function (result, data, params, callback) {
94-
callback(null, _.extend({ other: 'stuff' }, result));
97+
create: function (hook, next) {
98+
hook.result.other = 'stuff';
99+
100+
next();
95101
}
96102
});
97103

0 commit comments

Comments
 (0)