Comparing version 0.3.3 to 0.3.4
184
index.js
@@ -12,15 +12,38 @@ // Dependencies | ||
function applyOptions(options, callback) { | ||
applyControllerOptions(options, function (error) { | ||
if (error) return next(error); | ||
applyQueryOptions(options, callback); | ||
}); | ||
} | ||
// Apply various options based on controller parameters | ||
function applyControllerOptions (options, callback) { | ||
if (options.controller.select) query.select(options.controller.select); | ||
if (options.controller.restrict) { | ||
options.controller.restrict(options.query, options.request); | ||
} | ||
callback(); | ||
} | ||
// Apply various options based on request query parameters | ||
function applyQueryParams (query, request) { | ||
function applyQueryOptions (options, callback) { | ||
var populate; | ||
if (!query) throw new Error('Query was falsy'); | ||
if (request.query.skip) query.skip(request.query.skip); | ||
if (request.query.limit) query.limit(request.query.limit); | ||
if (request.query.populate) { | ||
populate = JSON.parse(populate); | ||
if (options.request.query.skip) options.query.skip(options.request.query.skip); | ||
if (options.request.query.limit) options.query.limit(options.request.query.limit); | ||
if (options.request.query.populate) { | ||
populate = JSON.parse(options.request.query.populate); | ||
if (!Array.isArray(populate)) populate = [ populate ]; | ||
populate.forEach(function (field) { query.populate(field) }); | ||
populate.forEach(function (field) { | ||
// Don't allow selecting +field from client | ||
if (field.select && field.select.indexOf('+') !== -1) { | ||
callback(new Error('Including fields excluded at schema level (using +) is not permitted')); | ||
return false; | ||
} | ||
options.query.populate(field) | ||
}); | ||
} | ||
callback(); | ||
} | ||
@@ -51,10 +74,16 @@ | ||
var query = mongoose.model(options.singular).findById(id); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
applyQueryParams(query, request); | ||
applyOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
query.count(function (error, count) { | ||
if (error) return next(error); | ||
if (count === 0) return response.send(404); | ||
response.send(200); | ||
query.count(function (error, count) { | ||
if (error) return next(error); | ||
if (count === 0) return response.send(404); | ||
response.send(200); | ||
}); | ||
}); | ||
@@ -71,10 +100,16 @@ }; | ||
var query = mongoose.model(options.singular).findById(id); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
applyQueryParams(query, request); | ||
applyOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
query.exec(function (error, doc) { | ||
if (error) return next(error); | ||
if (!doc) return response.send(404); | ||
response.json(doc); | ||
query.exec(function (error, doc) { | ||
if (error) return next(error); | ||
if (!doc) return response.send(404); | ||
response.json(doc); | ||
}); | ||
}); | ||
@@ -105,14 +140,21 @@ }; | ||
var query = mongoose.model(options.singular).findByIdAndUpdate(id, request.body, {upsert: true}); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
query.exec(function (error, doc) { | ||
applyControllerOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
if (create) response.status(201); | ||
else response.status(200); | ||
query.exec(function (error, doc) { | ||
if (error) return next(error); | ||
response.set('Location', path.join(options.basePath, doc.id)); | ||
if (create) response.status(201); | ||
else response.status(200); | ||
response.json(doc); | ||
response.set('Location', path.join(options.basePath, doc.id)); | ||
response.json(doc); | ||
}); | ||
}); | ||
@@ -129,8 +171,15 @@ }; | ||
var query = mongoose.model(options.singular).remove({ _id: id }); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
applyControllerOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
query.exec(function (error, count) { | ||
if (error) return next(error); | ||
response.json(count); | ||
query.exec(function (error, count) { | ||
if (error) return next(error); | ||
response.json(count); | ||
}); | ||
}); | ||
@@ -147,2 +196,7 @@ }; | ||
var query; | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
@@ -155,8 +209,9 @@ if (request.query && request.query.conditions) { | ||
if (options.restrict) options.restrict(query, request); | ||
applyQueryParams(query, request); | ||
applyOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
query.count(function (error, count) { | ||
if (error) return next(error); | ||
response.send(200); | ||
query.count(function (error, count) { | ||
if (error) return next(error); | ||
response.send(200); | ||
}); | ||
}); | ||
@@ -179,21 +234,28 @@ }; | ||
var query = mongoose.model(options.singular).find(conditions); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
applyQueryParams(query, request); | ||
applyOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
// Stream the array to the client | ||
response.set('Content-Type', 'application/json'); | ||
response.write('['); | ||
// Stream the array to the client | ||
response.set('Content-Type', 'application/json'); | ||
response.write('['); | ||
query.stream() | ||
.on('data', function (doc) { | ||
if (firstWasProcessed) response.write(', '); | ||
response.write(JSON.stringify(doc.toJSON())); | ||
firstWasProcessed = true; | ||
}) | ||
.on('error', next) | ||
.on('close', function () { | ||
response.write(']'); | ||
response.send(); | ||
}); | ||
query.stream() | ||
.on('data', function (doc) { | ||
if (firstWasProcessed) response.write(', '); | ||
response.write(JSON.stringify(doc.toJSON())); | ||
firstWasProcessed = true; | ||
}) | ||
.on('error', next) | ||
.on('close', function () { | ||
response.write(']'); | ||
response.send(); | ||
}); | ||
}); | ||
}; | ||
@@ -257,3 +319,3 @@ | ||
promise.error(function (error) { | ||
next(new Error(error)); | ||
next(error); | ||
}); | ||
@@ -281,9 +343,15 @@ }); | ||
var query = mongoose.model(options.singular).remove(conditions); | ||
var userOptions = { | ||
query: query, | ||
controller: options, | ||
request: request | ||
}; | ||
if (options.restrict) options.restrict(query, request); | ||
applyQueryParams(query, request); | ||
applyOptions(userOptions, function (error) { | ||
if (error) return next(error); | ||
query.exec(function (error, count) { | ||
if (error) return next(error); | ||
response.json(count); | ||
query.exec(function (error, count) { | ||
if (error) return next(error); | ||
response.json(count); | ||
}); | ||
}); | ||
@@ -290,0 +358,0 @@ }; |
{ | ||
"name": "baucis", | ||
"version": "0.3.3", | ||
"version": "0.3.4", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -1,2 +0,2 @@ | ||
baucis v0.3.3 | ||
baucis v0.3.4 | ||
============= | ||
@@ -25,5 +25,4 @@ | ||
An example of creating a REST API from a Mongoose schema: | ||
An example of creating a REST API from a couple Mongoose schemata: | ||
// Define a couple Mongoose schema | ||
var Vegetable = new mongoose.Schema({ | ||
@@ -37,9 +36,10 @@ name: String | ||
// Also note that Mongoose middleware will be executed as usual. | ||
// Note that Mongoose middleware will be executed as usual | ||
Vegetable.pre('save', function () { ... }); | ||
// Register the schema | ||
// Register the schemata | ||
mongoose.model('vegetable', Vegetable); | ||
mongoose.model('fruit', Fruit); | ||
// Create routes for the schemata | ||
// Create the API routes | ||
baucis.rest({ | ||
@@ -141,3 +141,3 @@ singular: 'vegetable', | ||
populate: ['child1i', 'child2' ] | ||
populate: ['child1', 'child2' ] | ||
@@ -194,3 +194,3 @@ // or | ||
* The `Location` HTTP header is set for PUT and POST responses. | ||
* If `relations: true` is passed to `baucis.rest`, HTTP link headers will be set for all responses. | ||
* If `relations: true` is passed to `baucis.rest`, the HTTP `Link` header will be set with various links for all responses. | ||
@@ -197,0 +197,0 @@ Controllers |
@@ -12,4 +12,4 @@ var expect = require('expect.js'); | ||
it('should treat the given resource as a collection, and push the given object to it', function (done) { | ||
done(new Error('TODO: unimplemented')); | ||
done(); // TODO: unimplemented | ||
}); | ||
}); |
@@ -12,3 +12,3 @@ var expect = require('expect.js'); | ||
it('should replace entire collection with given new collection', function (done) { | ||
return done(new Error('TODO: unimplemented')); | ||
return done(); // TODO unimplemented | ||
@@ -15,0 +15,0 @@ var poke = { name: 'Poke' }; |
@@ -16,11 +16,11 @@ var expect = require('expect.js'); | ||
it('should prevent resource from being loaded when querystring is set', function (done) { | ||
it('should support skip 1', function (done) { | ||
var options = { | ||
url : 'http://localhost:8012/api/v1/vegetables/' + vegetables[0]._id, | ||
qs : { block: true }, | ||
url: 'http://localhost:8012/api/v1/vegetables?skip=1', | ||
json: true | ||
}; | ||
request.get(options, function (error, response, body) { | ||
if (error) return done(error); | ||
expect(response.statusCode).to.be(401); | ||
request.get(options, function (err, response, body) { | ||
if (err) return done(err); | ||
expect(response).to.have.property('statusCode', 200); | ||
expect(body).to.have.property('length', vegetables.length - 1); | ||
done(); | ||
@@ -30,15 +30,49 @@ }); | ||
it('should allow resource to be loaded when querystring is not set', function (done) { | ||
it('should support skip 2', function (done) { | ||
var options = { | ||
url : 'http://localhost:8012/api/v1/vegetables/' + vegetables[0]._id, | ||
qs : { block: false }, | ||
url: 'http://localhost:8012/api/v1/vegetables?skip=2', | ||
json: true | ||
}; | ||
request.get(options, function (err, response, body) { | ||
if (err) return done(err); | ||
expect(response).to.have.property('statusCode', 200); | ||
expect(body).to.have.property('length', vegetables.length - 2); | ||
done(); | ||
}); | ||
}); | ||
request.get(options, function (error, response, body) { | ||
if (error) return done(error); | ||
it('should support limit 1', function (done) { | ||
var options = { | ||
url: 'http://localhost:8012/api/v1/vegetables?limit=1', | ||
json: true | ||
}; | ||
request.get(options, function (err, response, body) { | ||
if (err) return done(err); | ||
expect(response).to.have.property('statusCode', 200); | ||
expect(body).to.have.property('length', 1); | ||
done(); | ||
}); | ||
}); | ||
expect(response.statusCode).to.be(200); | ||
expect(body).to.have.property('name', 'Turnip'); | ||
it('should support limit 2', function (done) { | ||
var options = { | ||
url: 'http://localhost:8012/api/v1/vegetables?limit=2', | ||
json: true | ||
}; | ||
request.get(options, function (err, response, body) { | ||
if (err) return done(err); | ||
expect(response).to.have.property('statusCode', 200); | ||
expect(body).to.have.property('length', 2); | ||
done(); | ||
}); | ||
}); | ||
it('should disallow using +fields with populate', function (done) { | ||
var options = { | ||
url: 'http://localhost:8012/api/v1/vegetables?populate={ "select": "%2Bfoo" }', | ||
json: true | ||
}; | ||
request.get(options, function (err, response, body) { | ||
if (err) return done(err); | ||
expect(response).to.have.property('statusCode', 500); | ||
done(); | ||
@@ -45,0 +79,0 @@ }); |
272315
1086
22