mirror of
https://github.com/typicode/json-server.git
synced 2025-07-27 20:23:34 +08:00
Refactor and migrate to Express 0.4.x
This commit is contained in:
@ -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",
|
||||
|
103
src/routes.js
103
src/routes.js
@ -1,5 +1,5 @@
|
||||
var _ = require('underscore')
|
||||
var low = require('lowdb')
|
||||
var _ = require('underscore')
|
||||
var low = require('lowdb')
|
||||
var utils = require('./utils')
|
||||
|
||||
var routes = {}
|
||||
@ -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()
|
||||
|
||||
return false;
|
||||
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()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
|
||||
// Filter
|
||||
if (_(filters).isEmpty()) {
|
||||
array = low(req.params.resource).value()
|
||||
} else {
|
||||
array = low(req.params.resource).where(filters).value()
|
||||
}
|
||||
}
|
||||
|
||||
if (_start) {
|
||||
res.setHeader('X-Count', resource.length)
|
||||
res.setHeader('Access-Control-Expose-Headers', 'X-Count')
|
||||
// Slicing result
|
||||
if (limit) {
|
||||
res.setHeader('X-Total-Count', array.length)
|
||||
res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
|
||||
|
||||
resource = resource.slice(_start, _end)
|
||||
offset = offset || 0
|
||||
|
||||
array = array.slice(offset, limit)
|
||||
}
|
||||
|
||||
res.jsonp(resource)
|
||||
res.jsonp(array)
|
||||
}
|
||||
|
||||
// GET /:resource/:id
|
||||
@ -67,7 +86,11 @@ routes.show = function(req, res, next) {
|
||||
.get(+req.params.id)
|
||||
.value()
|
||||
|
||||
res.jsonp(resource)
|
||||
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()
|
||||
|
||||
res.jsonp(resource)
|
||||
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
|
||||
|
@ -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,36 +18,38 @@ 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.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)
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// only use in development
|
||||
server.use(errorhandler())
|
||||
}
|
||||
|
||||
server.get( '/db' , routes.db)
|
||||
server.get( '/:resource' , routes.list)
|
||||
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)
|
||||
|
||||
server.low = low
|
||||
|
||||
module.exports = server
|
@ -61,43 +61,45 @@ 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) {
|
||||
it('should return an empty array when nothing is matched', 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) {
|
||||
request(server)
|
||||
.get('/tags?q=nope')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect([])
|
||||
.expect(200, done)
|
||||
.get('/tags?q=nope')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect([])
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
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('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)
|
||||
.end(function(err, res){
|
||||
assert.equal(res.headers['x-count'], 5)
|
||||
done()
|
||||
})
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@ -122,8 +124,17 @@ 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) {
|
||||
request(server)
|
||||
@ -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() {
|
||||
|
Reference in New Issue
Block a user