Skip to content

Commit

Permalink
feat(excludeAttributes): allow users to exclude attributes
Browse files Browse the repository at this point in the history
This allows a user to explicitly exclude certain attributes from
read and list operations, meaning the data is never requested or
sent to the endpoint and users don't have to manually remove the
data themeselves
  • Loading branch information
mbroadst committed Oct 11, 2015
1 parent 398ba03 commit e30fbfd
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 14 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,11 @@ Create a resource and CRUD actions given a Sequelize model and endpoints. Accep
> ###### actions
>
> Create only the specified list of actions for the resource. Options include `create`, `list`, `read`, `update`, and `delete`. Defaults to all.
>
> ###### excludeAttributes
>
> Explicitly remove the specified list of attributes from read and list operations
>
### Milestones & Context
Expand Down
7 changes: 4 additions & 3 deletions lib/Controllers/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ List.prototype.fetch = function(req, res, context) {
count = +context.count || +req.query.count || defaultCount,
offset = +context.offset || +req.query.offset || 0;

// only look up attributes we care about
options.attributes = options.attributes || this.resource.attributes;

// account for offset and count
offset += context.page * count || req.query.page * count || 0;
if (count > 1000) count = 1000;
if (count < 0) count = defaultCount;
Expand All @@ -52,9 +56,6 @@ List.prototype.fetch = function(req, res, context) {
options.include = include;
}

if(this.resource.attributes.length)
options.attributes = this.resource.attributes;

var searchParam = this.resource.search.param;
if (_.has(req.query, searchParam)) {
var search = [];
Expand Down
13 changes: 6 additions & 7 deletions lib/Controllers/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Read.prototype.fetch = function(req, res, context) {
include = this.include,
includeAttributes = this.includeAttributes || [];

// only look up attributes we care about
options.attributes = options.attributes || this.resource.attributes;

// remove params that are already accounted for in criteria
Object.keys(criteria).forEach(function(attr) { delete req.params[attr]; });
endpoint.attributes.forEach(function(attribute) {
Expand All @@ -41,16 +44,12 @@ Read.prototype.fetch = function(req, res, context) {

if (this.resource.associationOptions.removeForeignKeys) {
// Remove identifier
options.attributes =
Object.keys(model.rawAttributes).filter(function(attr) {
return includeAttributes.indexOf(attr) === -1;
});
options.attributes = options.attributes.filter(function(attr) {
return includeAttributes.indexOf(attr) === -1;
});
}
}

if(this.resource.attributes.length)
options.attributes = this.resource.attributes;

return model
.find(options)
.then(function(instance) {
Expand Down
9 changes: 7 additions & 2 deletions lib/Resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var Resource = function(options) {
pagination: true,
reloadInstances: true,
include: [],
attributes: []
excludeAttributes: []
});

_.defaultsDeep(options, {
Expand All @@ -31,7 +31,12 @@ var Resource = function(options) {
this.include = options.include.map(function(include) {
return include instanceof options.sequelize.Model ? { model: include } : include;
});
this.attributes = options.attributes;

this.attributes = (!options.excludeAttributes.length) ?
Object.keys(this.model.rawAttributes) :
Object.keys(this.model.rawAttributes).filter(function(attr) {
return options.excludeAttributes.indexOf(attr) === -1;
});

this.actions = options.actions;
this.endpoints = {
Expand Down
2 changes: 1 addition & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var epilogue = {
sort: options.sort,
reloadInstances: options.reloadInstances,
associations: options.associations,
attributes: options.attributes
excludeAttributes: options.excludeAttributes
});

return resource;
Expand Down
35 changes: 35 additions & 0 deletions tests/resource/resource.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ describe('Resource(basic)', function() {
endpoints: ['/users', '/users/:id']
});

test.userResourceWithExclude = rest.resource({
model: test.models.User,
endpoints: ['/usersWithExclude', '/usersWithExclude/:id'],
excludeAttributes: [ 'email' ]
});

test.userResource.list.fetch.before(function(req, res, context) {
if (!!test.userResource.enableCriteriaTest) {
context.criteria = { id: 1 };
Expand Down Expand Up @@ -283,6 +289,23 @@ describe('Resource(basic)', function() {
});
});

it('should honor excludeAttributes', function(done) {
request.post({
url: test.baseUrl + '/usersWithExclude',
json: { username: 'jamez', email: 'jamez@gmail.com' }
}, function(error, response, body) {
var path = response.headers.location;
request.get({
url: test.baseUrl + path
}, function(err, response, body) {
expect(response.statusCode).to.equal(200);
var record = _.isObject(body) ? body : JSON.parse(body);
expect(record).to.eql({ id: 1, username: 'jamez' });
done();
});
});
});

});

describe('update', function() {
Expand Down Expand Up @@ -413,6 +436,18 @@ describe('Resource(basic)', function() {
});
});

it('should honor excludeAttributes', function(done) {
request.get({
url: test.baseUrl + '/usersWithExclude?username=henry'
}, function(err, response, body) {
expect(response.statusCode).to.equal(200);
var records = JSON.parse(body).map(function(r) { delete r.id; return r; });
expect(records).to.have.length(1);
expect(records[0]).to.eql({ username: 'henry' });
done();
});
});

it('should list all records matching a field name and value', function(done) {
request.get({
url: test.baseUrl + '/users?username=henry'
Expand Down

0 comments on commit e30fbfd

Please sign in to comment.