Refactor and migrate to Express 0.4.x

This commit is contained in:
Typicode
2014-09-16 22:44:02 +02:00
parent 3ae7c5bcec
commit a76ca913ae
4 changed files with 155 additions and 86 deletions

View File

@ -8,12 +8,16 @@
"test": "test"
},
"dependencies": {
"body-parser": "^1.8.1",
"chalk": "^0.4.0",
"cors": "^2.3.0",
"express": "^3.4.8",
"errorhandler": "^1.2.0",
"express": "^4.9.0",
"lowdb": "^0.3.0",
"method-override": "^2.1.2",
"minimist": "0.0.8",
"morgan": "^1.3.1",
"serve-static": "^1.6.1",
"superagent": "~0.15.7",
"underscore": "~1.5.2",
"underscore.inflections": "~0.2.1",

View File

@ -9,56 +9,75 @@ routes.db = function(req, res, next) {
res.jsonp(low.db)
}
// GET /:resource
// GET /:resource?q=
// GET /:resource?attr=&attr=
// GET /:parent/:parentId/:resource
// GET /:parent/:parentId/:resource?attr=&attr=
// GET /*?*&limit=
// GET /*?*&offset=&limit=
routes.list = function(req, res, next) {
var props = {}
var resource
var _start = req.query._start
var _end = req.query._end
// Filters list
var filters = {}
delete req.query._start
delete req.query._end
// Result array
var array
if (req.params.parent) {
props[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
}
// Remove offset and limit from req.query to avoid filtering using those
// parameters
var offset = req.query.offset
var limit = req.query.limit
for (var key in req.query) {
if (key !== 'callback' && key != 'q') props[key] = utils.toNative(req.query[key])
}
delete req.query.offset
delete req.query.limit
if(req.query.q !== undefined) {
var q = req.query.q.toLowerCase(),
keys = _.keys(low(req.params.resource).first()),
callback = function(element) {
for(var i in keys) {
var value = element[keys[i]];
if (req.query.q) {
if (value === q || (_.isString(value) && value.toLowerCase().indexOf(q) !== -1)) {
return true;
var q = req.query.q.toLowerCase()
array = low(req.params.resource).where(function(obj) {
for (var key in obj) {
var value = obj[key]
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
return true
}
}
}).value()
return false;
}
resource = low(req.params.resource).where(callback).value()
} else if (_(props).isEmpty()) {
resource = low(req.params.resource).value()
} else {
resource = low(req.params.resource).where(props).value()
// Add :parentId filter in case URL is like /:parent/:parentId/:resource
if (req.params.parent) {
filters[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
}
if (_start) {
res.setHeader('X-Count', resource.length)
res.setHeader('Access-Control-Expose-Headers', 'X-Count')
resource = resource.slice(_start, _end)
// Add query parameters filters
// Convert query parameters to their native counterparts
for (var key in req.query) {
if (key !== 'callback') {
filters[key] = utils.toNative(req.query[key])
}
}
res.jsonp(resource)
// Filter
if (_(filters).isEmpty()) {
array = low(req.params.resource).value()
} else {
array = low(req.params.resource).where(filters).value()
}
}
// Slicing result
if (limit) {
res.setHeader('X-Total-Count', array.length)
res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
offset = offset || 0
array = array.slice(offset, limit)
}
res.jsonp(array)
}
// GET /:resource/:id
@ -67,7 +86,11 @@ routes.show = function(req, res, next) {
.get(+req.params.id)
.value()
if (resource) {
res.jsonp(resource)
} else {
res.status(404).jsonp({})
}
}
// POST /:resource
@ -94,7 +117,11 @@ routes.update = function(req, res, next) {
.update(+req.params.id, req.body)
.value()
if (resource) {
res.jsonp(resource)
} else {
res.status(404).jsonp({})
}
}
// DELETE /:resource/:id
@ -108,7 +135,7 @@ routes.destroy = function(req, res, next) {
low(item[0]).remove(item[1]);
})
res.send(204)
res.status(204).end()
}
module.exports = routes

View File

@ -1,10 +1,15 @@
var fs = require('fs')
var express = require('express')
var cors = require('cors')
var http = require('http')
var path = require('path')
var http = require('http')
var express = require('express')
var logger = require('morgan')
var cors = require('cors')
var methodOverride = require('method-override')
var bodyParser = require('body-parser')
var serveStatic = require('serve-static')
var errorhandler = require('errorhandler')
var low = require('lowdb')
var utils = require('./utils')
var routes = require('./routes')
@ -13,35 +18,37 @@ low._.createId = utils.createId
var server = express()
server.set('port', process.env.PORT || 3000)
server.use(express.logger('dev'))
server.use(express.json())
server.use(express.urlencoded())
server.use(logger('dev'))
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({ extended: false }))
server.use(methodOverride())
if (fs.existsSync(process.cwd() + '/public')) {
server.use(express.static(process.cwd() + '/public'));
server.use(serveStatic(process.cwd() + '/public'));
} else {
server.use(express.static(path.join(__dirname, './public')));
server.use(serveStatic(path.join(__dirname, './public')));
}
server.use(cors({ origin: true, credentials: true }))
server.use(server.router)
if ('development' == server.get('env')) {
server.use(express.errorHandler());
}
server.get('/db', routes.db)
server.get( '/:resource' , routes.list)
server.route('/:resource')
.get(routes.list)
.post(routes.create)
server.route('/:resource/:id')
.get(routes.show)
.put(routes.update)
.patch(routes.update)
.delete(routes.destroy)
server.get('/:parent/:parentId/:resource', routes.list)
server.get( '/:resource/:id' , routes.show)
server.post( '/:resource' , routes.create)
server.put( '/:resource/:id' , routes.update)
server.patch( '/:resource/:id' , routes.update)
server.delete('/:resource/:id' , routes.destroy)
if (process.env.NODE_ENV === 'development') {
// only use in development
server.use(errorhandler())
}
server.low = low

View File

@ -61,24 +61,16 @@ describe('Server', function() {
})
})
describe('GET /:resource?q=value', function() {
it('should respond with json and filter all begin of fields of resources', function(done) {
describe('GET /:resource?q=', function() {
it('should respond with json and make a full-text search', function(done) {
request(server)
.get('/tags?q=photo')
.get('/tags?q=pho')
.expect('Content-Type', /json/)
.expect([low.db.tags[1], low.db.tags[2]])
.expect(200, done)
})
it('should respond with json and filter everywhere of all fields of resources', function(done) {
request(server)
.get('/tags?q=t')
.expect('Content-Type', /json/)
.expect(low.db.tags)
.expect(200, done)
})
it('should not respond anything when the query does not many any data', function(done) {
it('should return an empty array when nothing is matched', function(done) {
request(server)
.get('/tags?q=nope')
.expect('Content-Type', /json/)
@ -87,18 +79,28 @@ describe('Server', function() {
})
})
describe('GET /:resource?_start=&_end=', function() {
it('should respond with sliced array', function(done) {
describe('GET /:resource?limit=', function() {
it('should respond with a sliced array', function(done) {
request(server)
.get('/comments?_start=1&_end=2')
.get('/comments?limit=2')
.expect('Content-Type', /json/)
.expect(low.db.comments.slice(1, 2))
.expect(200)
.end(function(err, res){
assert.equal(res.headers['x-count'], 5)
done()
.expect('x-total-count', low.db.comments.length.toString())
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
.expect(low.db.comments.slice(0, 2))
.expect(200, done)
})
})
describe('GET /:resource?offset=&limit=', function() {
it('should respond with a sliced array', function(done) {
request(server)
.get('/comments?offset=1&limit=2')
.expect('Content-Type', /json/)
.expect('x-total-count', low.db.comments.length.toString())
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
.expect(low.db.comments.slice(1, 2))
.expect(200, done)
})
})
describe('GET /:parent/:parentId/:resource', function() {
@ -122,7 +124,16 @@ describe('Server', function() {
.expect(low.db.posts[0])
.expect(200, done)
})
it('should respond with 404 if resource is not found', function(done) {
request(server)
.get('/posts/9001')
.expect('Content-Type', /json/)
.expect({})
.expect(404, done)
})
})
describe('POST /:resource', function() {
it('should respond with json and create a resource', function(done) {
@ -150,10 +161,20 @@ describe('Server', function() {
.expect(200)
.end(function(err, res){
if (err) return done(err)
// assert it was created in database too
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar', booleanValue: true, integerValue: 1})
done()
})
})
it('should respond with 404 if resource is not found', function(done) {
request(server)
.put('/posts/9001')
.send({id: 1, body: 'bar', booleanValue: 'true', integerValue: '1'})
.expect('Content-Type', /json/)
.expect({})
.expect(404, done)
})
})
describe('PATCH /:resource/:id', function() {
@ -166,10 +187,20 @@ describe('Server', function() {
.expect(200)
.end(function(err, res){
if (err) return done(err)
// assert it was created in database too
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
done()
})
})
it('should respond with 404 if resource is not found', function(done) {
request(server)
.patch('/posts/9001')
.send({body: 'bar'})
.expect('Content-Type', /json/)
.expect({})
.expect(404, done)
})
})
describe('DELETE /:resource/:id', function() {