Skip to content

Commit e2c6cf7

Browse files
committed
Merge pull request #91 from digitalsadhu/add_include_whitelist_option
Add include whitelist option
2 parents 6df5f95 + 624f1ed commit e2c6cf7

File tree

5 files changed

+191
-4
lines changed

5 files changed

+191
-4
lines changed

README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ Example:
7676
"hideIrrelevantMethods": true,
7777
"attributes": {
7878
"posts": ["title"]
79-
}
79+
},
80+
"include": [
81+
{"methods": "customMethod"},
82+
{"model": "post", "methods": "customMethod"},
83+
{"model": "person", "methods": ["customMethod1", "customMethod2"]}
84+
]
8085
}
8186
}
8287
```
@@ -171,6 +176,31 @@ The default behavior for modifying input only applies to the following methods o
171176
- `create`
172177
- `updateAttributes`
173178

179+
### include
180+
Allows whitelisting of methods.
181+
Define an array of whitelist objects. Whitelist objects can contain a "methods" key
182+
or both a "models" key and a "methods" key. If just the "methods" key is defined then
183+
the methods specified will be serialized or deserialized using jsonapi on all models that have
184+
the specified methods. If a combination of
185+
"model" and "methods" keys are used then the specific combination of model and methods
186+
specified will be serialized or deserialized using jsonapi.
187+
188+
#### example
189+
```js
190+
{
191+
...
192+
"include": [
193+
{"methods": "customMethod"},
194+
{"model": "post", "methods": "customMethod"},
195+
{"model": "person", "methods": ["customMethod1", "customMethod2"]}
196+
],
197+
...
198+
}
199+
```
200+
201+
- Type: `array`
202+
- Default: `null`
203+
174204
### hideIrrelevantMethods
175205
By default, `loopback-component-jsonapi` disables a number of methods from each endpoint
176206
that are not jsonapi relevant. These methods are:

lib/deserialize.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = function (app, options) {
2525
return regex.test(ctx.method.name);
2626
});
2727

28-
if (matches.length > 0) {
28+
if (utils.shouldApplyJsonApi(ctx, options) || matches.length > 0) {
2929
// set the JSON API Content-Type response header
3030
ctx.res.set({'Content-Type': 'application/vnd.api+json'});
3131

lib/serialize.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ module.exports = function (app, defaults) {
3131
return regex.test(ctx.method.name);
3232
});
3333

34-
if (!matches.length) {
34+
if (!utils.shouldApplyJsonApi(ctx, defaults) && !matches.length) {
3535
return next();
3636
}
3737

lib/utils.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ module.exports = {
1515
pluralForModel: pluralForModel,
1616
urlFromContext: urlFromContext,
1717
primaryKeyForModel: primaryKeyForModel,
18-
shouldNotApplyJsonApi: shouldNotApplyJsonApi
18+
shouldNotApplyJsonApi: shouldNotApplyJsonApi,
19+
shouldApplyJsonApi: shouldApplyJsonApi
1920
};
2021

2122
function primaryKeyForModel (model) {
@@ -167,6 +168,24 @@ function buildModelUrl (protocol, host, apiRoot, modelName, id) {
167168
return result;
168169
}
169170

171+
function shouldApplyJsonApi (ctx, options) {
172+
if (!options.include) return false;
173+
174+
var modelName = ctx.method.sharedClass.name;
175+
var methodName = ctx.method.name;
176+
var model;
177+
var methods;
178+
for (var i = 0; i < options.include.length; i++) {
179+
model = options.include[i].model;
180+
methods = options.include[i].methods;
181+
if (model === modelName && !methods) return true;
182+
if (!model && methods === methodName) return true;
183+
if (model === modelName && methods === methodName) return true;
184+
if (model === modelName && _.contains(methods, methodName)) return true;
185+
}
186+
return false;
187+
}
188+
170189
function shouldNotApplyJsonApi (ctx, options) {
171190
//handle options.exclude
172191
if (!options.exclude) return false;

test/include.test.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
var request = require('supertest');
2+
var loopback = require('loopback');
3+
var expect = require('chai').expect;
4+
var JSONAPIComponent = require('../');
5+
var app;
6+
var Post;
7+
var Comment;
8+
9+
describe('include option', function () {
10+
beforeEach(function (done) {
11+
app = loopback();
12+
app.set('legacyExplorer', false);
13+
var ds = loopback.createDataSource('memory');
14+
15+
Post = ds.createModel('post', { title: String });
16+
app.model(Post);
17+
Post.customMethod = function (cb) {
18+
cb(null, {prop: 'value'});
19+
};
20+
Post.remoteMethod('customMethod', {
21+
http: {verb: 'get', path: '/custom'},
22+
returns: {root: true}
23+
});
24+
Post.customMethod2 = function (cb) {
25+
cb(null, {prop: 'value'});
26+
};
27+
Post.remoteMethod('customMethod2', {
28+
http: {verb: 'get', path: '/custom2'},
29+
returns: {root: true}
30+
});
31+
32+
Comment = ds.createModel('comment', { comment: String });
33+
app.model(Comment);
34+
Comment.customMethod = function (cb) {
35+
cb(null, {prop: 'value'});
36+
};
37+
Comment.remoteMethod('customMethod', {
38+
http: {verb: 'get', path: '/custom'},
39+
returns: {root: true}
40+
});
41+
42+
app.use(loopback.rest());
43+
44+
Post.create({title: 'my post'}, function () {
45+
Comment.create({comment: 'my comment'}, done);
46+
});
47+
});
48+
49+
describe('including a specific method', function () {
50+
beforeEach(function () {
51+
JSONAPIComponent(app, {
52+
include: [
53+
{methods: 'customMethod'}
54+
]
55+
});
56+
});
57+
58+
it('should apply jsonapi to post model output for customMethod method', function (done) {
59+
request(app).get('/posts/custom')
60+
.expect(200)
61+
.end(function (err, res) {
62+
expect(err).to.equal(null);
63+
expect(res.body.data).to.have.keys('type', 'attributes', 'links');
64+
done();
65+
});
66+
});
67+
68+
it('should apply jsonapi to comment model output for customMethod method', function (done) {
69+
request(app).get('/comments/custom')
70+
.expect(200)
71+
.end(function (err, res) {
72+
expect(err).to.equal(null);
73+
expect(res.body.data).to.have.keys('type', 'attributes', 'links');
74+
done();
75+
});
76+
});
77+
});
78+
79+
describe('including a specific method on a specific model', function () {
80+
beforeEach(function () {
81+
JSONAPIComponent(app, {
82+
include: [
83+
{model: 'post', methods: 'customMethod'}
84+
]
85+
});
86+
});
87+
88+
it('should apply jsonapi to post model output for customMethod method', function (done) {
89+
request(app).get('/posts/custom')
90+
.expect(200)
91+
.end(function (err, res) {
92+
expect(err).to.equal(null);
93+
expect(res.body.data).to.have.keys('type', 'attributes', 'links');
94+
done();
95+
});
96+
});
97+
98+
it('should not apply jsonapi to comment model output for customMethod method', function (done) {
99+
request(app).get('/comments/custom')
100+
.expect(200)
101+
.end(function (err, res) {
102+
expect(err).to.equal(null);
103+
expect(res.body).to.deep.equal({ prop: 'value'});
104+
done();
105+
});
106+
});
107+
});
108+
109+
describe('including a specific set of methods on a specific model', function () {
110+
beforeEach(function () {
111+
JSONAPIComponent(app, {
112+
include: [
113+
{model: 'post', methods: ['customMethod', 'customMethod2']}
114+
]
115+
});
116+
});
117+
118+
it('should apply jsonapi to post model output for customMethod method', function (done) {
119+
request(app).get('/posts/custom')
120+
.expect(200)
121+
.end(function (err, res) {
122+
expect(err).to.equal(null);
123+
expect(res.body.data).to.have.keys('type', 'attributes', 'links');
124+
done();
125+
});
126+
});
127+
128+
it('should apply jsonapi to post model output for customMethod method', function (done) {
129+
request(app).get('/posts/custom2')
130+
.expect(200)
131+
.end(function (err, res) {
132+
expect(err).to.equal(null);
133+
expect(res.body.data).to.have.keys('type', 'attributes', 'links');
134+
done();
135+
});
136+
});
137+
});
138+
});

0 commit comments

Comments
 (0)