mirror of
https://github.com/typicode/json-server.git
synced 2025-07-28 04:32:24 +08:00
Refactor and migrate to Express 0.4.x
This commit is contained in:
@ -8,12 +8,16 @@
|
|||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"body-parser": "^1.8.1",
|
||||||
"chalk": "^0.4.0",
|
"chalk": "^0.4.0",
|
||||||
"cors": "^2.3.0",
|
"cors": "^2.3.0",
|
||||||
"express": "^3.4.8",
|
"errorhandler": "^1.2.0",
|
||||||
|
"express": "^4.9.0",
|
||||||
"lowdb": "^0.3.0",
|
"lowdb": "^0.3.0",
|
||||||
"method-override": "^2.1.2",
|
"method-override": "^2.1.2",
|
||||||
"minimist": "0.0.8",
|
"minimist": "0.0.8",
|
||||||
|
"morgan": "^1.3.1",
|
||||||
|
"serve-static": "^1.6.1",
|
||||||
"superagent": "~0.15.7",
|
"superagent": "~0.15.7",
|
||||||
"underscore": "~1.5.2",
|
"underscore": "~1.5.2",
|
||||||
"underscore.inflections": "~0.2.1",
|
"underscore.inflections": "~0.2.1",
|
||||||
|
@ -9,56 +9,75 @@ routes.db = function(req, res, next) {
|
|||||||
res.jsonp(low.db)
|
res.jsonp(low.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET /:resource
|
||||||
|
// GET /:resource?q=
|
||||||
// GET /:resource?attr=&attr=
|
// 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) {
|
routes.list = function(req, res, next) {
|
||||||
var props = {}
|
|
||||||
var resource
|
|
||||||
|
|
||||||
var _start = req.query._start
|
// Filters list
|
||||||
var _end = req.query._end
|
var filters = {}
|
||||||
|
|
||||||
delete req.query._start
|
// Result array
|
||||||
delete req.query._end
|
var array
|
||||||
|
|
||||||
if (req.params.parent) {
|
// Remove offset and limit from req.query to avoid filtering using those
|
||||||
props[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
|
// parameters
|
||||||
}
|
var offset = req.query.offset
|
||||||
|
var limit = req.query.limit
|
||||||
|
|
||||||
for (var key in req.query) {
|
delete req.query.offset
|
||||||
if (key !== 'callback' && key != 'q') props[key] = utils.toNative(req.query[key])
|
delete req.query.limit
|
||||||
}
|
|
||||||
|
|
||||||
if(req.query.q !== undefined) {
|
if (req.query.q) {
|
||||||
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 (value === q || (_.isString(value) && value.toLowerCase().indexOf(q) !== -1)) {
|
var q = req.query.q.toLowerCase()
|
||||||
return true;
|
|
||||||
|
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 {
|
} 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) {
|
// Add query parameters filters
|
||||||
res.setHeader('X-Count', resource.length)
|
// Convert query parameters to their native counterparts
|
||||||
res.setHeader('Access-Control-Expose-Headers', 'X-Count')
|
for (var key in req.query) {
|
||||||
|
if (key !== 'callback') {
|
||||||
resource = resource.slice(_start, _end)
|
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
|
// GET /:resource/:id
|
||||||
@ -67,7 +86,11 @@ routes.show = function(req, res, next) {
|
|||||||
.get(+req.params.id)
|
.get(+req.params.id)
|
||||||
.value()
|
.value()
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
res.jsonp(resource)
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /:resource
|
// POST /:resource
|
||||||
@ -94,7 +117,11 @@ routes.update = function(req, res, next) {
|
|||||||
.update(+req.params.id, req.body)
|
.update(+req.params.id, req.body)
|
||||||
.value()
|
.value()
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
res.jsonp(resource)
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE /:resource/:id
|
// DELETE /:resource/:id
|
||||||
@ -108,7 +135,7 @@ routes.destroy = function(req, res, next) {
|
|||||||
low(item[0]).remove(item[1]);
|
low(item[0]).remove(item[1]);
|
||||||
})
|
})
|
||||||
|
|
||||||
res.send(204)
|
res.status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = routes
|
module.exports = routes
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var http = require('http')
|
|
||||||
var path = require('path')
|
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 methodOverride = require('method-override')
|
||||||
|
var bodyParser = require('body-parser')
|
||||||
|
var serveStatic = require('serve-static')
|
||||||
|
var errorhandler = require('errorhandler')
|
||||||
var low = require('lowdb')
|
var low = require('lowdb')
|
||||||
|
|
||||||
var utils = require('./utils')
|
var utils = require('./utils')
|
||||||
var routes = require('./routes')
|
var routes = require('./routes')
|
||||||
|
|
||||||
@ -13,35 +18,37 @@ low._.createId = utils.createId
|
|||||||
var server = express()
|
var server = express()
|
||||||
|
|
||||||
server.set('port', process.env.PORT || 3000)
|
server.set('port', process.env.PORT || 3000)
|
||||||
server.use(express.logger('dev'))
|
server.use(logger('dev'))
|
||||||
server.use(express.json())
|
server.use(bodyParser.json())
|
||||||
server.use(express.urlencoded())
|
server.use(bodyParser.urlencoded({ extended: false }))
|
||||||
server.use(methodOverride())
|
server.use(methodOverride())
|
||||||
|
|
||||||
if (fs.existsSync(process.cwd() + '/public')) {
|
if (fs.existsSync(process.cwd() + '/public')) {
|
||||||
server.use(express.static(process.cwd() + '/public'));
|
server.use(serveStatic(process.cwd() + '/public'));
|
||||||
} else {
|
} 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(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('/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('/:parent/:parentId/:resource', routes.list)
|
||||||
server.get( '/:resource/:id' , routes.show)
|
|
||||||
|
|
||||||
server.post( '/:resource' , routes.create)
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// only use in development
|
||||||
server.put( '/:resource/:id' , routes.update)
|
server.use(errorhandler())
|
||||||
server.patch( '/:resource/:id' , routes.update)
|
}
|
||||||
|
|
||||||
server.delete('/:resource/:id' , routes.destroy)
|
|
||||||
|
|
||||||
server.low = low
|
server.low = low
|
||||||
|
|
||||||
|
@ -61,24 +61,16 @@ describe('Server', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GET /:resource?q=value', function() {
|
describe('GET /:resource?q=', function() {
|
||||||
it('should respond with json and filter all begin of fields of resources', function(done) {
|
it('should respond with json and make a full-text search', function(done) {
|
||||||
request(server)
|
request(server)
|
||||||
.get('/tags?q=photo')
|
.get('/tags?q=pho')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([low.db.tags[1], low.db.tags[2]])
|
.expect([low.db.tags[1], low.db.tags[2]])
|
||||||
.expect(200, done)
|
.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)
|
request(server)
|
||||||
.get('/tags?q=nope')
|
.get('/tags?q=nope')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
@ -87,18 +79,28 @@ describe('Server', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GET /:resource?_start=&_end=', function() {
|
describe('GET /:resource?limit=', function() {
|
||||||
it('should respond with sliced array', function(done) {
|
it('should respond with a sliced array', function(done) {
|
||||||
request(server)
|
request(server)
|
||||||
.get('/comments?_start=1&_end=2')
|
.get('/comments?limit=2')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(low.db.comments.slice(1, 2))
|
.expect('x-total-count', low.db.comments.length.toString())
|
||||||
.expect(200)
|
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
|
||||||
.end(function(err, res){
|
.expect(low.db.comments.slice(0, 2))
|
||||||
assert.equal(res.headers['x-count'], 5)
|
.expect(200, done)
|
||||||
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() {
|
describe('GET /:parent/:parentId/:resource', function() {
|
||||||
@ -122,7 +124,16 @@ describe('Server', function() {
|
|||||||
.expect(low.db.posts[0])
|
.expect(low.db.posts[0])
|
||||||
.expect(200, done)
|
.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() {
|
describe('POST /:resource', function() {
|
||||||
it('should respond with json and create a resource', function(done) {
|
it('should respond with json and create a resource', function(done) {
|
||||||
@ -150,10 +161,20 @@ describe('Server', function() {
|
|||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
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})
|
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar', booleanValue: true, integerValue: 1})
|
||||||
done()
|
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() {
|
describe('PATCH /:resource/:id', function() {
|
||||||
@ -166,10 +187,20 @@ describe('Server', function() {
|
|||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
|
// assert it was created in database too
|
||||||
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
|
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
|
||||||
done()
|
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() {
|
describe('DELETE /:resource/:id', function() {
|
||||||
|
Reference in New Issue
Block a user