Merge branch 'expand-resources' of https://github.com/adamsea/json-server into adamsea-expand-resources

Conflicts:
	src/router.js
	test/server/plural.js
This commit is contained in:
Typicode
2015-07-31 22:55:07 +02:00
3 changed files with 88 additions and 33 deletions

View File

@ -6,7 +6,7 @@ Created with <3 for front-end developers who need a quick back-end for prototypi
* [Egghead.io free video tutorial - Creating demo APIs with json-server](https://egghead.io/lessons/nodejs-creating-demo-apis-with-json-server) * [Egghead.io free video tutorial - Creating demo APIs with json-server](https://egghead.io/lessons/nodejs-creating-demo-apis-with-json-server)
* [JSONPlaceholder - Live running version](http://jsonplaceholder.typicode.com) * [JSONPlaceholder - Live running version](http://jsonplaceholder.typicode.com)
_See also [hotel](https://github.com/typicode/hotel), a simple process manager for developers._ _See also [hotel](https://github.com/typicode/hotel), a simple process manager for developers._
## Example ## Example
@ -70,6 +70,7 @@ To slice resources, add `_start` and `_end` or `_limit` (an `X-Total-Count` head
``` ```
GET /posts?_start=20&_end=30 GET /posts?_start=20&_end=30
GET /posts/1/comments?_start=20&_end=30 GET /posts/1/comments?_start=20&_end=30
GET /posts/1/comments?_start=20&_limit=10
``` ```
To sort resources, add `_sort` and `_order` (ascending order by default). To sort resources, add `_sort` and `_order` (ascending order by default).
@ -85,12 +86,18 @@ To make a full-text search on resources, add `q`.
GET /posts?q=internet GET /posts?q=internet
``` ```
To embed other resources, add `_embed`. To embed resources, add `_embed`.
``` ```
GET /posts/1?_embed=comments GET /posts/1?_embed=comments
``` ```
To expand inner resources, add `_expand`.
```
GET /comments/1?_expand=post
```
Returns database. Returns database.
``` ```
@ -205,7 +212,7 @@ To modify responses, use `router.render()`:
router.render = function (req, res) { router.render = function (req, res) {
res.jsonp({ res.jsonp({
body: res.locals.data body: res.locals.data
}) })
} }
``` ```

View File

@ -8,11 +8,11 @@ module.exports = function (db, name) {
// Create router // Create router
var router = express.Router() var router = express.Router()
// GET /:resource // GET /name
// GET /:resource?q= // GET /name?q=
// GET /:resource?attr=&attr= // GET /name?attr=&attr=
// GET /:resource?_end=&* // GET /name?_end=&*
// GET /:resource?_start=&_end=&* // GET /name?_start=&_end=&*
function list (req, res, next) { function list (req, res, next) {
// Filters list // Filters list
@ -103,33 +103,51 @@ module.exports = function (db, name) {
next() next()
} }
// GET /:resource/:id // GET /name/:id
// GET /name/:id?_embed=&_expand
function show (req, res, next) { function show (req, res, next) {
var _embed = req.query._embed var _embed = req.query._embed
var _expand = req.query._expand
var id = utils.toNative(req.params.id) var id = utils.toNative(req.params.id)
var resource = db(name) var resource = db(name).getById(id)
.getById(id)
// Filter empty params
function filter (p) {
return p && p.trim().length > 0
}
if (resource) { if (resource) {
// Clone resource to avoid making changes to the underlying object // Clone resource to avoid making changes to the underlying object
resource = _.cloneDeep(resource) resource = _.cloneDeep(resource)
// Always use an array // Always use an array
_embed = _.isArray(_embed) ? _embed : [_embed] _embed = [].concat(_embed)
_expand = [].concat(_expand)
// Embed other resources based on resource id // Embed other resources based on resource id
_embed.forEach(function (otherResource) { // /posts/1?_embed=comments
_embed
.filter(filter)
.forEach(function (otherResource) {
if (db.object[otherResource]) {
var query = {}
var singularResource = pluralize.singular(name)
query[singularResource + 'Id'] = id
resource[otherResource] = db(otherResource).where(query)
}
})
if (otherResource // Expand inner resources based on id
&& otherResource.trim().length > 0 // /posts/1?_expand=user
&& db.object[otherResource]) { _expand
.filter(filter)
var query = {} .forEach(function (innerResource) {
var prop = pluralize.singular(name) + 'Id' var plural = pluralize(innerResource)
query[prop] = id if (db.object[plural]) {
resource[otherResource] = db(otherResource).where(query) var prop = innerResource + 'Id'
resource[innerResource] = db(plural).getById(resource[prop])
} }
}) })
res.locals.data = resource res.locals.data = resource
} }
@ -137,7 +155,7 @@ module.exports = function (db, name) {
next() next()
} }
// POST /:resource // POST /name
function create (req, res, next) { function create (req, res, next) {
for (var key in req.body) { for (var key in req.body) {
req.body[key] = utils.toNative(req.body[key]) req.body[key] = utils.toNative(req.body[key])
@ -151,8 +169,8 @@ module.exports = function (db, name) {
next() next()
} }
// PUT /:resource/:id // PUT /name/:id
// PATCH /:resource/:id // PATCH /name/:id
function update (req, res, next) { function update (req, res, next) {
for (var key in req.body) { for (var key in req.body) {
req.body[key] = utils.toNative(req.body[key]) req.body[key] = utils.toNative(req.body[key])
@ -168,7 +186,7 @@ module.exports = function (db, name) {
next() next()
} }
// DELETE /:resource/:id // DELETE /name/:id
function destroy (req, res, next) { function destroy (req, res, next) {
db(name).removeById(utils.toNative(req.params.id)) db(name).removeById(utils.toNative(req.params.id))

View File

@ -24,12 +24,17 @@ describe('Server', function () {
{id: 3, body: 'photo'} {id: 3, body: 'photo'}
] ]
db.users = [
{id: 1, username: 'Jim'},
{id: 2, username: 'George'}
]
db.comments = [ db.comments = [
{id: 1, body: 'foo', published: true, postId: 1}, {id: 1, body: 'foo', published: true, postId: 1, userId: 1},
{id: 2, body: 'bar', published: false, postId: 1}, {id: 2, body: 'bar', published: false, postId: 1, userId: 2},
{id: 3, body: 'baz', published: false, postId: 2}, {id: 3, body: 'baz', published: false, postId: 2, userId: 1},
{id: 4, body: 'qux', published: true, postId: 2}, {id: 4, body: 'qux', published: true, postId: 2, userId: 2},
{id: 5, body: 'quux', published: false, postId: 2} {id: 5, body: 'quux', published: false, postId: 2, userId: 1}
] ]
db.refs = [ db.refs = [
@ -258,6 +263,31 @@ describe('Server', function () {
}) })
}) })
describe('GET /:resource/:id?_expand=', function () {
it('should respond with corresponding resource and expanded inner resources', function (done) {
var comments = db.comments[0]
comments.post = db.posts[0]
request(server)
.get('/comments/1?_expand=post')
.expect('Content-Type', /json/)
.expect(comments)
.expect(200, done)
})
})
describe('GET /:resource/:id?_expand=&_expand=', function () {
it('should respond with corresponding resource and expanded inner resources', function (done) {
var comments = db.comments[0]
comments.post = db.posts[0]
comments.user = db.users[0]
request(server)
.get('/comments/1?_expand=post&_expand=user')
.expect('Content-Type', /json/)
.expect(comments)
.expect(200, done)
})
})
describe('POST /:resource', function () { describe('POST /:resource', function () {
it('should respond with json, create a resource and increment id', it('should respond with json, create a resource and increment id',
function (done) { function (done) {