Skip to content

Commit a76ca91

Browse files
committed
Refactor and migrate to Express 0.4.x
1 parent 3ae7c5b commit a76ca91

File tree

4 files changed

+149
-80
lines changed

4 files changed

+149
-80
lines changed

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
"test": "test"
99
},
1010
"dependencies": {
11+
"body-parser": "^1.8.1",
1112
"chalk": "^0.4.0",
1213
"cors": "^2.3.0",
13-
"express": "^3.4.8",
14+
"errorhandler": "^1.2.0",
15+
"express": "^4.9.0",
1416
"lowdb": "^0.3.0",
1517
"method-override": "^2.1.2",
1618
"minimist": "0.0.8",
19+
"morgan": "^1.3.1",
20+
"serve-static": "^1.6.1",
1721
"superagent": "~0.15.7",
1822
"underscore": "~1.5.2",
1923
"underscore.inflections": "~0.2.1",

src/routes.js

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
var _ = require('underscore')
2-
var low = require('lowdb')
1+
var _ = require('underscore')
2+
var low = require('lowdb')
33
var utils = require('./utils')
44

55
var routes = {}
@@ -9,56 +9,75 @@ routes.db = function(req, res, next) {
99
res.jsonp(low.db)
1010
}
1111

12+
// GET /:resource
13+
// GET /:resource?q=
1214
// GET /:resource?attr=&attr=
13-
// GET /:parent/:parentId/:resource
15+
// GET /:parent/:parentId/:resource?attr=&attr=
16+
// GET /*?*&limit=
17+
// GET /*?*&offset=&limit=
1418
routes.list = function(req, res, next) {
15-
var props = {}
16-
var resource
1719

18-
var _start = req.query._start
19-
var _end = req.query._end
20+
// Filters list
21+
var filters = {}
2022

21-
delete req.query._start
22-
delete req.query._end
23+
// Result array
24+
var array
2325

24-
if (req.params.parent) {
25-
props[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
26-
}
26+
// Remove offset and limit from req.query to avoid filtering using those
27+
// parameters
28+
var offset = req.query.offset
29+
var limit = req.query.limit
2730

28-
for (var key in req.query) {
29-
if (key !== 'callback' && key != 'q') props[key] = utils.toNative(req.query[key])
30-
}
31+
delete req.query.offset
32+
delete req.query.limit
3133

32-
if(req.query.q !== undefined) {
33-
var q = req.query.q.toLowerCase(),
34-
keys = _.keys(low(req.params.resource).first()),
35-
callback = function(element) {
36-
for(var i in keys) {
37-
var value = element[keys[i]];
34+
if (req.query.q) {
3835

39-
if (value === q || (_.isString(value) && value.toLowerCase().indexOf(q) !== -1)) {
40-
return true;
41-
}
42-
}
36+
var q = req.query.q.toLowerCase()
4337

44-
return false;
38+
array = low(req.params.resource).where(function(obj) {
39+
for (var key in obj) {
40+
var value = obj[key]
41+
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
42+
return true
4543
}
44+
}
45+
}).value()
4646

47-
resource = low(req.params.resource).where(callback).value()
48-
} else if (_(props).isEmpty()) {
49-
resource = low(req.params.resource).value()
5047
} else {
51-
resource = low(req.params.resource).where(props).value()
48+
49+
// Add :parentId filter in case URL is like /:parent/:parentId/:resource
50+
if (req.params.parent) {
51+
filters[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
52+
}
53+
54+
// Add query parameters filters
55+
// Convert query parameters to their native counterparts
56+
for (var key in req.query) {
57+
if (key !== 'callback') {
58+
filters[key] = utils.toNative(req.query[key])
59+
}
60+
}
61+
62+
// Filter
63+
if (_(filters).isEmpty()) {
64+
array = low(req.params.resource).value()
65+
} else {
66+
array = low(req.params.resource).where(filters).value()
67+
}
5268
}
5369

54-
if (_start) {
55-
res.setHeader('X-Count', resource.length)
56-
res.setHeader('Access-Control-Expose-Headers', 'X-Count')
70+
// Slicing result
71+
if (limit) {
72+
res.setHeader('X-Total-Count', array.length)
73+
res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
5774

58-
resource = resource.slice(_start, _end)
75+
offset = offset || 0
76+
77+
array = array.slice(offset, limit)
5978
}
6079

61-
res.jsonp(resource)
80+
res.jsonp(array)
6281
}
6382

6483
// GET /:resource/:id
@@ -67,7 +86,11 @@ routes.show = function(req, res, next) {
6786
.get(+req.params.id)
6887
.value()
6988

70-
res.jsonp(resource)
89+
if (resource) {
90+
res.jsonp(resource)
91+
} else {
92+
res.status(404).jsonp({})
93+
}
7194
}
7295

7396
// POST /:resource
@@ -94,7 +117,11 @@ routes.update = function(req, res, next) {
94117
.update(+req.params.id, req.body)
95118
.value()
96119

97-
res.jsonp(resource)
120+
if (resource) {
121+
res.jsonp(resource)
122+
} else {
123+
res.status(404).jsonp({})
124+
}
98125
}
99126

100127
// DELETE /:resource/:id
@@ -108,7 +135,7 @@ routes.destroy = function(req, res, next) {
108135
low(item[0]).remove(item[1]);
109136
})
110137

111-
res.send(204)
138+
res.status(204).end()
112139
}
113140

114141
module.exports = routes

src/server.js

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
var fs = require('fs')
2+
var path = require('path')
3+
var http = require('http')
24
var express = require('express')
5+
var logger = require('morgan')
36
var cors = require('cors')
4-
var http = require('http')
5-
var path = require('path')
67
var methodOverride = require('method-override')
8+
var bodyParser = require('body-parser')
9+
var serveStatic = require('serve-static')
10+
var errorhandler = require('errorhandler')
711
var low = require('lowdb')
12+
813
var utils = require('./utils')
914
var routes = require('./routes')
1015

@@ -13,35 +18,37 @@ low._.createId = utils.createId
1318
var server = express()
1419

1520
server.set('port', process.env.PORT || 3000)
16-
server.use(express.logger('dev'))
17-
server.use(express.json())
18-
server.use(express.urlencoded())
21+
server.use(logger('dev'))
22+
server.use(bodyParser.json())
23+
server.use(bodyParser.urlencoded({ extended: false }))
1924
server.use(methodOverride())
2025

2126
if (fs.existsSync(process.cwd() + '/public')) {
22-
server.use(express.static(process.cwd() + '/public'));
27+
server.use(serveStatic(process.cwd() + '/public'));
2328
} else {
24-
server.use(express.static(path.join(__dirname, './public')));
29+
server.use(serveStatic(path.join(__dirname, './public')));
2530
}
2631

2732
server.use(cors({ origin: true, credentials: true }))
28-
server.use(server.router)
2933

30-
if ('development' == server.get('env')) {
31-
server.use(express.errorHandler());
32-
}
34+
server.get('/db', routes.db)
3335

34-
server.get( '/db' , routes.db)
35-
server.get( '/:resource' , routes.list)
36-
server.get( '/:parent/:parentId/:resource' , routes.list)
37-
server.get( '/:resource/:id' , routes.show)
36+
server.route('/:resource')
37+
.get(routes.list)
38+
.post(routes.create)
3839

39-
server.post( '/:resource' , routes.create)
40+
server.route('/:resource/:id')
41+
.get(routes.show)
42+
.put(routes.update)
43+
.patch(routes.update)
44+
.delete(routes.destroy)
4045

41-
server.put( '/:resource/:id' , routes.update)
42-
server.patch( '/:resource/:id' , routes.update)
46+
server.get('/:parent/:parentId/:resource', routes.list)
4347

44-
server.delete('/:resource/:id' , routes.destroy)
48+
if (process.env.NODE_ENV === 'development') {
49+
// only use in development
50+
server.use(errorhandler())
51+
}
4552

4653
server.low = low
4754

test/server.js

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,43 +61,45 @@ describe('Server', function() {
6161
})
6262
})
6363

64-
describe('GET /:resource?q=value', function() {
65-
it('should respond with json and filter all begin of fields of resources', function(done) {
64+
describe('GET /:resource?q=', function() {
65+
it('should respond with json and make a full-text search', function(done) {
6666
request(server)
67-
.get('/tags?q=photo')
67+
.get('/tags?q=pho')
6868
.expect('Content-Type', /json/)
6969
.expect([low.db.tags[1], low.db.tags[2]])
7070
.expect(200, done)
7171
})
7272

73-
it('should respond with json and filter everywhere of all fields of resources', function(done) {
73+
it('should return an empty array when nothing is matched', function(done) {
7474
request(server)
75-
.get('/tags?q=t')
76-
.expect('Content-Type', /json/)
77-
.expect(low.db.tags)
78-
.expect(200, done)
75+
.get('/tags?q=nope')
76+
.expect('Content-Type', /json/)
77+
.expect([])
78+
.expect(200, done)
7979
})
80+
})
8081

81-
it('should not respond anything when the query does not many any data', function(done) {
82-
request(server)
83-
.get('/tags?q=nope')
84-
.expect('Content-Type', /json/)
85-
.expect([])
86-
.expect(200, done)
82+
describe('GET /:resource?limit=', function() {
83+
it('should respond with a sliced array', function(done) {
84+
request(server)
85+
.get('/comments?limit=2')
86+
.expect('Content-Type', /json/)
87+
.expect('x-total-count', low.db.comments.length.toString())
88+
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
89+
.expect(low.db.comments.slice(0, 2))
90+
.expect(200, done)
8791
})
8892
})
8993

90-
describe('GET /:resource?_start=&_end=', function() {
91-
it('should respond with sliced array', function(done) {
94+
describe('GET /:resource?offset=&limit=', function() {
95+
it('should respond with a sliced array', function(done) {
9296
request(server)
93-
.get('/comments?_start=1&_end=2')
97+
.get('/comments?offset=1&limit=2')
9498
.expect('Content-Type', /json/)
99+
.expect('x-total-count', low.db.comments.length.toString())
100+
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
95101
.expect(low.db.comments.slice(1, 2))
96-
.expect(200)
97-
.end(function(err, res){
98-
assert.equal(res.headers['x-count'], 5)
99-
done()
100-
})
102+
.expect(200, done)
101103
})
102104
})
103105

@@ -122,8 +124,17 @@ describe('Server', function() {
122124
.expect(low.db.posts[0])
123125
.expect(200, done)
124126
})
127+
128+
it('should respond with 404 if resource is not found', function(done) {
129+
request(server)
130+
.get('/posts/9001')
131+
.expect('Content-Type', /json/)
132+
.expect({})
133+
.expect(404, done)
134+
})
125135
})
126136

137+
127138
describe('POST /:resource', function() {
128139
it('should respond with json and create a resource', function(done) {
129140
request(server)
@@ -150,10 +161,20 @@ describe('Server', function() {
150161
.expect(200)
151162
.end(function(err, res){
152163
if (err) return done(err)
164+
// assert it was created in database too
153165
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar', booleanValue: true, integerValue: 1})
154166
done()
155167
})
156168
})
169+
170+
it('should respond with 404 if resource is not found', function(done) {
171+
request(server)
172+
.put('/posts/9001')
173+
.send({id: 1, body: 'bar', booleanValue: 'true', integerValue: '1'})
174+
.expect('Content-Type', /json/)
175+
.expect({})
176+
.expect(404, done)
177+
})
157178
})
158179

159180
describe('PATCH /:resource/:id', function() {
@@ -166,10 +187,20 @@ describe('Server', function() {
166187
.expect(200)
167188
.end(function(err, res){
168189
if (err) return done(err)
190+
// assert it was created in database too
169191
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
170192
done()
171193
})
172194
})
195+
196+
it('should respond with 404 if resource is not found', function(done) {
197+
request(server)
198+
.patch('/posts/9001')
199+
.send({body: 'bar'})
200+
.expect('Content-Type', /json/)
201+
.expect({})
202+
.expect(404, done)
203+
})
173204
})
174205

175206
describe('DELETE /:resource/:id', function() {

0 commit comments

Comments
 (0)