-
-
-
-
-
- Congrats! You're successfully running JSON Server.
+
+
+
+
-
-
Resources
+
+
+
+ Congrats! You're successfully running JSON Server.
+
+
+
+
+
+
Routes
+
+ Here are the resources that JSON Server has loaded:
+
- To view database current state:
+ You can view database current state at any time:
-
Requests
- Resources can be accessed in various ways.
-
-
- JSON Server supports:
-
- - GET, POST, PUT, PATCH, DESTROY and OPTIONS verbs.
- - JSONP or CORS cross domain requests.
-
+ You can use GET, POST, PUT... and access your resources from anywhere
+ using CORS and JSONP.
-
Documentation
+
Documentation
View
README
on GitHub.
-
Issues
+
Issues
Please go
here.
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index 1464e6c..e46e5a1 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -1,43 +1,12 @@
-body {
- padding: 50px;
- font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+img {
+ padding-top: 50px;
+ padding-bottom: 20px;
}
-h1 {
- font-weight: lighter;
+li {
+ list-style-type: square;
}
-a {
- /*color: #00B7FF;*/
- color: #3D72D1;
-}
-
-hr {
- border: 1px #EEE solid;
-}
-
-a.logo {
- display: block;
- text-align: center;
- margin-bottom: 50px;
-}
-
-.alert-success {
- font-size: 18px;
-}
-
-/*.logo {
- background: #00B7FF;
- color: white;
- font-size: 48px;
- width: 100px;
- height: 100px;
- text-align: center;
- vertical-align: middle;
- line-height: 90px;
- border-radius: 100px;
- transform:rotate(90deg);
- -ms-transform:rotate(90deg);
- -webkit-transform:rotate(90deg);
- margin-bottom: 50px;
-}*/
\ No newline at end of file
+h4 {
+ padding-top: 20px;
+}
\ No newline at end of file
diff --git a/routes/read-only.js b/routes/read-only.js
deleted file mode 100644
index da0b2f7..0000000
--- a/routes/read-only.js
+++ /dev/null
@@ -1,71 +0,0 @@
-var _ = require('underscore'),
- utils = require('../utils/utils'),
- db = {};
-
-_.mixin(require('../utils/db-mixins'));
-
-exports.setDatabase = function(object) {
- db = object;
-}
-
-exports.database = function(req, res) {
- res.jsonp(db)
-}
-
-// GET /:resource?attr=&attr=
-exports.list = function(req, res) {
- var collection = db[req.params.resource],
- properties = {},
- result;
-
- Object.keys(req.query).forEach(function (key) {
- var value = req.query[key];
- properties[key] = utils.toNative(value);
- });
-
- if (_(properties).isEmpty()) {
- result = collection;
- } else {
- result = _(collection).where(properties);
- }
-
- res.jsonp(result);
-}
-
-// GET /:parent/:parentId/:resource
-exports.nestedList = function(req, res) {
- var properties = {},
- resource;
-
- // Set parentID
- properties[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId;
-
- // Filter using parentID
- resource = _.where(db[req.params.resource], properties);
-
- res.jsonp(resource);
-}
-
-// GET /:resource/:id
-exports.show = function(req, res) {
- var resource = _.get(db, req.params.resource, +req.params.id);
-
- res.jsonp(resource);
-}
-
-exports.create = function(req, res) {
- req.body.id = Math.round(new Date().getTime() / 1000);
- res.jsonp(req.body);
-}
-
-exports.update = function(req, res) {
- var resource = _.get(db, req.params.resource, +req.params.id),
- clonedResource = _.clone(resource),
- result = _.extend(clonedResource, req.body);
-
- res.jsonp(result);
-}
-
-exports.destroy = function(req, res) {
- res.send(204)
-}
diff --git a/routes/read-write.js b/routes/read-write.js
deleted file mode 100644
index 61f89a8..0000000
--- a/routes/read-write.js
+++ /dev/null
@@ -1,83 +0,0 @@
-var _ = require('underscore'),
- utils = require('../utils/utils'),
- db = {};
-
-_.mixin(require('../utils/db-mixins'));
-
-exports.setDatabase = function(object) {
- db = object;
-}
-
-exports.database = function(req, res) {
- res.jsonp(db)
-}
-
-exports.db = function() {
- return db;
-}
-
-// GET /:resource?attr=&attr=
-exports.list = function(req, res) {
- var collection = db[req.params.resource],
- properties = {},
- result;
-
- Object.keys(req.query).forEach(function (key) {
- var value = req.query[key];
- properties[key] = utils.toNative(value);
- });
-
- if (_(properties).isEmpty()) {
- result = collection;
- } else {
- result = _(collection).where(properties);
- }
-
- res.jsonp(result);
-}
-
-// GET /:parent/:parentId/:resource
-exports.nestedList = function(req, res) {
- var properties = {},
- resource;
-
- // Set parentID
- properties[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId;
-
- // Filter using parentID
- resource = _.where(db[req.params.resource], properties);
-
- res.jsonp(resource);
-}
-
-// GET /:resource/:id
-exports.show = function(req, res) {
- var resource = _.get(db, req.params.resource, +req.params.id);
-
- res.jsonp(resource);
-}
-
-// POST /:resource
-exports.create = function(req, res) {
- var resource = _.create(db, req.params.resource, req.body);
-
- res.jsonp(resource);
-}
-
-// PUT /:resource/:id
-// PATCH /:resource/:id
-exports.update = function(req, res) {
- _.update(db, req.params.resource, +req.params.id, req.body);
- var resource = _.get(db, req.params.resource, +req.params.id);
-
- res.jsonp(resource);
-}
-
-// DELETE /:resource/:id
-exports.destroy = function(req, res) {
- _.remove(db, req.params.resource, +req.params.id);
- _.clean(db);
-
- res.send(204);
-}
-
diff --git a/server.js b/server.js
deleted file mode 100644
index f1dc744..0000000
--- a/server.js
+++ /dev/null
@@ -1,96 +0,0 @@
-var express = require('express'),
- cors = require('cors'),
- http = require('http'),
- path = require('path'),
- fs = require('fs'),
- _ = require('underscore'),
- logger = require('./utils/logger');
-
-
-var defaultOptions = {
- port: process.env.PORT || 3000,
- readOnly: false
-}
-
-function createApp(db, options) {
- // Create app
- var app = express(),
- options = options || {},
- routes;
-
- // Configure all environments
- app.use(express.favicon());
- app.use(express.logger('dev'));
- app.use(express.json());
- app.use(express.urlencoded());
- app.use(express.methodOverride());
-
-
- // Configure development
- if ('development' == app.get('env')) {
- app.use(express.errorHandler());
- }
-
- // Configure using options provided
- app.set('port', options.port);
- routes = options.readOnly ? './routes/read-only' : './routes/read-write';
- routes = require(routes);
-
- // Use default or user public directory
- // Note: should be done before CORS and app.router setting
- if (fs.existsSync(process.cwd() + '/public')) {
- app.use(express.static(process.cwd() + '/public'));
- } else {
- app.use(express.static(path.join(__dirname, './public')));
- }
-
- // Enable CORS for everything
- app.use(cors());
- app.options('*', cors());
-
- // Set app.router
- app.use(app.router);
-
- // Set API entry points
- app.get('/db', routes.database)
- app.get('/:resource', routes.list);
- app.get('/:parent/:parentId/:resource', routes.nestedList);
- app.get('/:resource/:id', routes.show);
- app.post('/:resource', routes.create);
- app.put('/:resource/:id', routes.update);
- app.patch('/:resource/:id', routes.update);
- app.del('/:resource/:id', routes.destroy);
-
- // Set database
- routes.setDatabase(db);
- app.db = routes.db;
-
- // And done! Ready to serve JSON!
- return app;
-}
-
-function run(db, options) {
- options = _.defaults(options || {}, defaultOptions);
-
- var app = createApp(db, options);
-
- if (_.isEmpty(db)) {
- logger.error('No resources found!');
- } else {
- logger.success('Available resources');
- for (var prop in db) {
- logger.url(options.port, prop);
- }
- }
-
- http
- .createServer(app)
- .listen((options.port), function(){
- logger.success('Express server listening on port ' + options.port);
- logger.success('Congrats! Open http://localhost:' + options.port);
- });
- return app;
-}
-
-exports.createApp = createApp;
-exports.run = run;
diff --git a/src/cli.js b/src/cli.js
new file mode 100644
index 0000000..b090fff
--- /dev/null
+++ b/src/cli.js
@@ -0,0 +1,87 @@
+var fs = require('fs')
+var chalk = require('chalk')
+var minimist = require('minimist')
+var request = require('superagent')
+var low = require('low')
+var server = require('./server')
+
+// Output version
+function version() {
+ var pkg = require('../package.json')
+ console.log(pkg.version)
+}
+
+// Output help.txt with some colors
+function help() {
+ var txt = fs.readFileSync(__dirname + '/help.txt').toString()
+ txt = txt.replace(/json-server/g, chalk.green('json-server'))
+ console.log(txt)
+}
+
+// Start server
+function start(port) {
+ for (var prop in low.db) {
+ console.log('http://localhost:' + port + '/' + chalk.green(prop))
+ }
+
+ console.log(
+ '\nEnter ' + chalk.green('`s`') + ' at any time to create a snapshot of the db\n'
+ )
+ process.stdin.resume()
+ process.stdin.setEncoding('utf8')
+ process.stdin.on('data', function (chunk) {
+ if (chunk.trim().toLowerCase() === 's') {
+ var file = 'db-' + Date.now() + '.json'
+ low.save(file)
+ console.log('\nSaved snapshot to ' + chalk.green(file) + '\n')
+ }
+ })
+
+ server.listen(port)
+}
+
+// Load source
+function load(source, port) {
+ console.log(chalk.green('\n{^ ^} Heya!\n'))
+
+ console.log('Loading database from ' + source + '\n')
+
+ if (/\.json$/.test(source)) {
+ var path = process.cwd() + '/' + source
+ low.path = path
+ low.db = require(path);
+ start(port)
+ }
+
+ if (/\.js$/.test(source)) {
+ var path = process.cwd() + '/' + source
+ low.db = require(path).run();
+ start(port)
+ }
+
+ if (/^http/.test(source)) {
+ request
+ .get(source)
+ .end(function(err, res) {
+ if (err) {
+ console.error(err)
+ } else {
+ low.db = JSON.parse(res.text)
+ start(port)
+ }
+ })
+ }
+}
+
+// Uses minimist parsed argv
+function run(argv) {
+ var source = argv._[0]
+ var port = argv.port || 3000
+
+ if (argv.version) return version()
+ if (source) return load(source, port)
+
+ help()
+}
+
+module.exports.run = run
diff --git a/src/help.txt b/src/help.txt
new file mode 100644
index 0000000..06fb12b
--- /dev/null
+++ b/src/help.txt
@@ -0,0 +1,14 @@
+
+ Usage: json-server
[options]
+
+ Options:
+
+ --version output version
+ --port set port
+
+ Exemples:
+
+ json-server db.json
+ json-server seed.js
+ json-server http://example.com/db.json
+
\ No newline at end of file
diff --git a/src/routes.js b/src/routes.js
new file mode 100644
index 0000000..3546ba2
--- /dev/null
+++ b/src/routes.js
@@ -0,0 +1,81 @@
+var _ = require('underscore')
+var low = require('low')
+var utils = require('./utils')
+
+var routes = {}
+
+// GET /db
+routes.db = function(req, res, next) {
+ res.jsonp(low.db)
+}
+
+// GET /:resource?attr=&attr=
+// GET /:parent/:parentId/:resource
+routes.list = function(req, res, next) {
+ var props = {}
+ var resource
+
+ var _start = req.query._start
+ var _end = req.query._end
+
+ delete req.query._start
+ delete req.query._end
+
+ if (req.params.parent) {
+ props[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
+ }
+
+ for (var key in req.query) {
+ props[key] = utils.toNative(req.query[key])
+ }
+
+ if (_(props).isEmpty()) {
+ resource = low(req.params.resource).value()
+ } else {
+ resource = low(req.params.resource).where(props).value()
+ }
+
+ if (_start) {
+ resource = resource.slice(_start, _end)
+ }
+
+ res.jsonp(resource)
+}
+
+// GET /:resource/:id
+routes.show = function(req, res, next) {
+ var resource = low(req.params.resource)
+ .get(+req.params.id)
+ .value()
+
+ res.jsonp(resource)
+}
+
+// POST /:resource
+routes.create = function(req, res, next) {
+ var resource = low(req.params.resource)
+ .insert(req.body)
+ .value()
+
+ res.jsonp(resource)
+}
+
+// PUT /:resource/:id
+// PATCH /:resource/:id
+routes.update = function(req, res, next) {
+ var resource = low(req.params.resource)
+ .update(+req.params.id, req.body)
+ .value()
+
+ res.jsonp(resource)
+}
+
+// DELETE /:resource/:id
+routes.destroy = function(req, res, next) {
+ low(req.params.resource).remove(+req.params.id)
+ utils.clean()
+
+ res.send(204)
+}
+
+module.exports = routes
\ No newline at end of file
diff --git a/src/server.js b/src/server.js
new file mode 100644
index 0000000..c8ec484
--- /dev/null
+++ b/src/server.js
@@ -0,0 +1,47 @@
+var fs = require('fs')
+var express = require('express')
+var cors = require('cors')
+var http = require('http')
+var path = require('path')
+var low = require('low')
+var utils = require('./utils')
+var routes = require('./routes')
+
+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(express.methodOverride())
+
+if (fs.existsSync(process.cwd() + '/public')) {
+ server.use(express.static(process.cwd() + '/public'));
+} else {
+ server.use(express.static(path.join(__dirname, './public')));
+}
+
+server.use(cors())
+server.use(server.router)
+
+if ('development' == server.get('env')) {
+ server.use(express.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.del( '/:resource/:id' , routes.destroy)
+
+server.low = low
+
+module.exports = server
\ No newline at end of file
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..45bc2aa
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,60 @@
+var low = require('low')
+var _ = require('underscore')
+_.mixin(require('underscore.inflections'))
+
+
+// Turns string to native.
+// Example:
+// 'true' -> true
+// '1' -> 1
+function toNative(value) {
+ if (value === 'true' || value === 'false') {
+ return value === 'true'
+ } else if (!isNaN(+value)) {
+ return +value
+ } else {
+ return value
+ }
+}
+
+// Creates incremental id.
+function createId(coll) {
+ if (_.isEmpty(coll)) {
+ return 1
+ } else {
+ return _.max(coll, function(doc) {
+ return doc.id
+ }).id + 1
+ }
+}
+
+// Removes empty relations
+function clean() {
+ var toBeRemoved = []
+
+ _(low.db).each(function(coll, collName) {
+ _(coll).each(function(doc) {
+ _(doc).each(function(value, key) {
+ if (/Id$/.test(key)) {
+ var reference = _.pluralize(key.slice(0, - 2))
+ if (!_.isUndefined(low(reference).get(doc[key]).value())) {
+ toBeRemoved.push({
+ collName: collName,
+ id: doc.id
+ })
+ }
+ }
+ })
+ })
+ })
+
+ _(toBeRemoved).each(function(item) {
+ low(item.collName).remove(item.id);
+ })
+}
+
+module.exports = {
+ toNative: toNative,
+ createId: createId,
+ clean: clean
+}
\ No newline at end of file
diff --git a/test/fixture.js b/test/fixture.js
deleted file mode 100644
index 453be2c..0000000
--- a/test/fixture.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Small database to be used during tests
-module.exports = function() {
- var db = {};
-
- db.posts = [
- {id: 1, body: 'foo'},
- {id: 2, body: 'bar'}
- ]
-
- db.comments = [
- {id: 1, published: true, postId: 1},
- {id: 2, published: false, postId: 1},
- {id: 3, published: false, postId: 2},
- {id: 4, published: false, postId: 2},
- ]
-
- return db;
-}
\ No newline at end of file
diff --git a/test/read-only.js b/test/read-only.js
deleted file mode 100644
index aca0e33..0000000
--- a/test/read-only.js
+++ /dev/null
@@ -1,138 +0,0 @@
-var request = require('supertest'),
- assert = require('assert'),
- server = require('../server'),
- fixture = require('./fixture'),
- db,
- app;
-
-describe('Read only routes', function() {
-
- beforeEach(function() {
- db = fixture();
- app = server.createApp(db, { readOnly: true });
- });
-
- describe('GET /:resource', function() {
- it('should respond with json and resources and corresponding resources', function(done) {
- request(app)
- .get('/posts')
- .expect('Content-Type', /json/)
- .expect(db.posts)
- .expect(200, done);
- });
- });
-
- describe('GET /:resource?attr=&attr=', function() {
- it('should respond with json and filter resources', function(done) {
- request(app)
- .get('/comments?postId=1&published=true')
- .expect('Content-Type', /json/)
- .expect([db.comments[0]])
- .expect(200, done);
- });
- });
-
- describe('GET /:parent/:parentId/:resource', function() {
- it('should respond with json and corresponding nested resources', function(done) {
- request(app)
- .get('/posts/1/comments')
- .expect('Content-Type', /json/)
- .expect([db.comments[0], db.comments[1]])
- .expect(200, done);
- });
- });
-
-
- describe('GET /:resource/:id', function() {
- it('should respond with json and corresponding resource', function(done) {
- request(app)
- .get('/posts/1')
- .expect('Content-Type', /json/)
- .expect(db.posts[0])
- .expect(200, done);
- });
- });
-
- describe('GET /db', function() {
- it('should respond with json and full database', function(done) {
- request(app)
- .get('/db')
- .expect('Content-Type', /json/)
- .expect(db)
- .expect(200, done);
- });
- });
-
- describe('POST /:resource', function() {
- it('should respond with fake json and not create a resource', function(done) {
- request(app)
- .post('/posts')
- .send({body: '...'})
- .expect('Content-Type', /json/)
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- assert(res.body.hasOwnProperty('id'));
- assert.equal(res.body.body, '...');
- assert.equal(db.posts.length, 2);
- done()
- });
- });
- });
-
- describe('PUT /:resource/:id', function() {
- it('should respond with fake json and not update resource', function(done) {
- request(app)
- .put('/posts/1')
- .send({id: 999, body: '...'})
- .expect('Content-Type', /json/)
- .expect({id: 999, body: '...'})
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- // Checking that first post wasn't updated
- assert.deepEqual(db.posts[0], {id: 1, body: 'foo'});
- done()
- });
- });
- });
-
- describe('PATCH /:resource/:id', function() {
- it('should respond with fake json and not update resource', function(done) {
- request(app)
- .patch('/posts/1')
- .send({body: '...'})
- .expect('Content-Type', /json/)
- .expect({id: 1, body: '...'})
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- // Checking that first post wasn't updated
- assert.deepEqual(db.posts[0], {id: 1, body: 'foo'});
- done()
- });
- });
- });
-
- describe('DELETE /:resource/:id', function() {
- it('should respond with empty data and not destroy resource', function(done) {
- request(app)
- .del('/posts/1')
- .expect(204)
- .end(function(err, res){
- if (err) return done(err);
- assert.equal(db.posts.length, 2);
- assert.equal(db.comments.length, 4);
- done()
- });
- });
- });
-
- describe('OPTIONS /:resource/:id', function() {
- it('should respond with empty data and not destroy resource', function(done) {
- request(app)
- .options('/posts/1')
- .expect(204, done);
- });
- });
-});
\ No newline at end of file
diff --git a/test/read-write.js b/test/read-write.js
deleted file mode 100644
index 388d4da..0000000
--- a/test/read-write.js
+++ /dev/null
@@ -1,130 +0,0 @@
-var request = require('supertest'),
- assert = require('assert'),
- server = require('../server'),
- routes = require('../routes/read-write'),
- fixture = require('./fixture'),
- db,
- app;
-
-describe('Read write routes', function() {
-
- beforeEach(function() {
- db = fixture();
- app = server.createApp(db);
- });
-
- describe('GET /:resource', function() {
- it('should respond with json and corresponding resources', function(done) {
- request(app)
- .get('/posts')
- .expect('Content-Type', /json/)
- .expect(db.posts)
- .expect(200, done);
- });
- });
-
- describe('GET /:resource?attr=&attr=', function() {
- it('should respond with json and filter resources', function(done) {
- request(app)
- .get('/comments?postId=1&published=true')
- .expect('Content-Type', /json/)
- .expect([db.comments[0]])
- .expect(200, done);
- });
- });
-
- describe('GET /:parent/:parentId/:resource', function() {
- it('should respond with json and corresponding nested resources', function(done) {
- request(app)
- .get('/posts/1/comments')
- .expect('Content-Type', /json/)
- .expect([
- db.comments[0],
- db.comments[1]
- ])
- .expect(200, done);
- });
- });
-
- describe('GET /:resource/:id', function() {
- it('should respond with json and corresponding resource', function(done) {
- request(app)
- .get('/posts/1')
- .expect('Content-Type', /json/)
- .expect(db.posts[0])
- .expect(200, done);
- });
- });
-
- describe('GET /db', function() {
- it('should respond with json and full database', function(done) {
- request(app)
- .get('/db')
- .expect('Content-Type', /json/)
- .expect(db)
- .expect(200, done);
- });
- });
-
- describe('POST /:resource', function() {
- it('should respond with json and create a resource', function(done) {
- request(app)
- .post('/posts')
- .send({body: 'foo'})
- .expect('Content-Type', /json/)
- .expect({id: 3, body: 'foo'})
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- assert.equal(db.posts.length, 3);
- done();
- });
- });
- });
-
- describe('PUT /:resource/:id', function() {
- it('should respond with json and update resource', function(done) {
- request(app)
- .put('/posts/1')
- .send({id: 1, body: 'foo'})
- .expect('Content-Type', /json/)
- .expect({id: 1, body: 'foo'})
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- assert.deepEqual(db.posts[0], {id: 1, body: 'foo'});
- done();
- });
- });
- });
-
- describe('PATCH /:resource/:id', function() {
- it('should respond with json and update resource', function(done) {
- request(app)
- .patch('/posts/1')
- .send({body: 'bar'})
- .expect('Content-Type', /json/)
- .expect({id: 1, body: 'bar'})
- .expect(200)
- .end(function(err, res){
- if (err) return done(err);
- assert.deepEqual(db.posts[0], {id: 1, body: 'bar'});
- done();
- });
- });
- });
-
- describe('DELETE /:resource/:id', function() {
- it('should respond with empty data, destroy resource and dependent resources', function(done) {
- request(app)
- .del('/posts/1')
- .expect(204)
- .end(function(err, res){
- if (err) return done(err);
- assert.equal(db.posts.length, 1);
- assert.equal(db.comments.length, 2);
- done();
- });
- });
- });
-});
\ No newline at end of file
diff --git a/test/server.js b/test/server.js
new file mode 100644
index 0000000..56f462c
--- /dev/null
+++ b/test/server.js
@@ -0,0 +1,171 @@
+var request = require('supertest')
+var assert = require('assert')
+var low = require('low')
+var server = require('../src/server')
+var db
+
+describe('Server', function() {
+
+ beforeEach(function() {
+ low.db = {}
+
+ low.db.posts = [
+ {id: 1, body: 'foo'},
+ {id: 2, body: 'bar'}
+ ]
+
+ low.db.comments = [
+ {id: 1, published: true, postId: 1},
+ {id: 2, published: false, postId: 1},
+ {id: 3, published: false, postId: 2},
+ {id: 4, published: false, postId: 2},
+ ]
+ })
+
+ describe('GET /db', function() {
+ it('should respond with json and full database', function(done) {
+ request(server)
+ .get('/db')
+ .expect('Content-Type', /json/)
+ .expect(low.db)
+ .expect(200, done)
+ })
+ })
+
+ describe('GET /:resource', function() {
+ it('should respond with json and corresponding resources', function(done) {
+ request(server)
+ .get('/posts')
+ .expect('Content-Type', /json/)
+ .expect(low.db.posts)
+ .expect(200, done)
+ })
+ })
+
+ describe('GET /:resource?attr=&attr=', function() {
+ it('should respond with json and filter resources', function(done) {
+ request(server)
+ .get('/comments?postId=1&published=true')
+ .expect('Content-Type', /json/)
+ .expect([low.db.comments[0]])
+ .expect(200, done)
+ })
+ })
+
+ describe('GET /:resource?_start=&_end=', function() {
+ it('should respond with sliced array', function(done) {
+ request(server)
+ .get('/comments?_start=1&_end=2')
+ .expect('Content-Type', /json/)
+ .expect(low.db.comments.slice(1, 2))
+ .expect(200, done)
+ })
+ })
+
+ describe('GET /:parent/:parentId/:resource', function() {
+ it('should respond with json and corresponding nested resources', function(done) {
+ request(server)
+ .get('/posts/1/comments')
+ .expect('Content-Type', /json/)
+ .expect([
+ low.db.comments[0],
+ low.db.comments[1]
+ ])
+ .expect(200, done)
+ })
+ })
+
+ describe('GET /:resource/:id', function() {
+ it('should respond with json and corresponding resource', function(done) {
+ request(server)
+ .get('/posts/1')
+ .expect('Content-Type', /json/)
+ .expect(low.db.posts[0])
+ .expect(200, done)
+ })
+ })
+
+ describe('POST /:resource', function() {
+ it('should respond with json and create a resource', function(done) {
+ request(server)
+ .post('/posts')
+ .send({body: 'foo'})
+ .expect('Content-Type', /json/)
+ .expect({id: 3, body: 'foo'})
+ .expect(200)
+ .end(function(err, res){
+ if (err) return done(err)
+ assert.equal(low.db.posts.length, 3)
+ done()
+ })
+ })
+ })
+
+ describe('PUT /:resource/:id', function() {
+ it('should respond with json and update resource', function(done) {
+ request(server)
+ .put('/posts/1')
+ .send({id: 1, body: 'foo'})
+ .expect('Content-Type', /json/)
+ .expect({id: 1, body: 'foo'})
+ .expect(200)
+ .end(function(err, res){
+ if (err) return done(err)
+ assert.deepEqual(low.db.posts[0], {id: 1, body: 'foo'})
+ done()
+ })
+ })
+ })
+
+ describe('PATCH /:resource/:id', function() {
+ it('should respond with json and update resource', function(done) {
+ request(server)
+ .patch('/posts/1')
+ .send({body: 'bar'})
+ .expect('Content-Type', /json/)
+ .expect({id: 1, body: 'bar'})
+ .expect(200)
+ .end(function(err, res){
+ if (err) return done(err)
+ assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
+ done()
+ })
+ })
+ })
+
+ describe('DELETE /:resource/:id', function() {
+ it('should respond with empty data, destroy resource and dependent resources', function(done) {
+ request(server)
+ .del('/posts/1')
+ .expect(204)
+ .end(function(err, res){
+ if (err) return done(err)
+ assert.equal(low.db.posts.length, 1)
+ assert.equal(low.db.comments.length, 2)
+ done()
+ })
+ })
+ })
+
+ describe('Static routes', function() {
+
+ describe('GET /', function() {
+ it('should respond with html', function(done) {
+ request(server)
+ .get('/')
+ .expect('Content-Type', /html/)
+ .expect(200, done);
+ });
+ });
+
+ describe('GET /stylesheets/style.css', function() {
+ it('should respond with css', function(done) {
+ request(server)
+ .get('/stylesheets/style.css')
+ .expect('Content-Type', /css/)
+ .expect(200, done);
+ });
+ });
+
+ })
+})
\ No newline at end of file
diff --git a/test/static.js b/test/static.js
deleted file mode 100644
index f623d76..0000000
--- a/test/static.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var request = require('supertest'),
- assert = require('assert'),
- server = require('../server'),
- routes = require('../routes/read-write'),
- app;
-
-describe('Static routes', function() {
-
- beforeEach(function() {
- app = server.createApp({}, routes);
- });
-
- describe('GET /', function() {
- it('should respond with html', function(done) {
- request(app)
- .get('/')
- .expect('Content-Type', /html/)
- .expect(200, done);
- });
- });
-
- describe('GET /stylesheets/style.css', function() {
- it('should respond with css', function(done) {
- request(app)
- .get('/stylesheets/style.css')
- .expect('Content-Type', /css/)
- .expect(200, done);
- });
- });
-
-});
\ No newline at end of file
diff --git a/utils/db-mixins.js b/utils/db-mixins.js
deleted file mode 100644
index d06a395..0000000
--- a/utils/db-mixins.js
+++ /dev/null
@@ -1,86 +0,0 @@
-(function(root) {
-
- var _ = root._ || require('underscore');
-
- if (!root._) {
- _.mixin(require('underscore.inflections'));
- }
-
- function get(db, table, id) {
- return _.find(db[table], function (row) {
- return row.id === id
- });
- }
-
- function exist(db, table, id) {
- return !_.isUndefined(_.get(db, table, id));
- }
-
- function createId(db, table) {
- if (_.isEmpty(db[table])) {
- return 1;
- } else {
- return _.max(db[table], function(row) {
- return row.id;
- }).id + 1;
- }
- }
-
- function create(db, table, obj) {
- var clone = _.clone(obj);
-
- if (_.isUndefined(clone.id)) clone.id = _.createId(db, table);
-
- db[table].push(clone);
-
- return clone;
- }
-
- function update(db, table, id, attrs) {
- var row = get(db, table, id),
- updatedRow = _.extend(row, attrs),
- index = _.indexOf(db[table], row);
-
- db[table][index] = updatedRow;
- }
-
- function clean(db) {
- var toBeRemoved = [];
-
- _(db).each(function(table, tableName) {
- _(table).each(function(row) {
- _(row).each(function(value, key) {
- if (/Id$/.test(key)) {
- var reference = _.pluralize(key.slice(0, - 2));
- if (!_.exist(db, reference, row[key])) {
- toBeRemoved.push({
- tableName: tableName,
- id: row.id
- });
- }
- }
- });
- });
- });
-
- _(toBeRemoved).each(function(row) {
- _.remove(db, row.tableName, row.id);
- });
- }
-
- function remove(db, table, id) {
- var newTable = _.reject(db[table], function(row) {
- return row.id === id;
- });
-
- db[table] = newTable;
- }
-
- _.get = get;
- _.exist = exist;
- _.createId = createId;
- _.create = create;
- _.update = update;
- _.clean = clean;
- _.remove = remove;
-})(this);
\ No newline at end of file
diff --git a/utils/logger.js b/utils/logger.js
deleted file mode 100644
index 20a1200..0000000
--- a/utils/logger.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var logan = require('logan');
-
-logan.set({
- error: ['%', 'red'],
- success: ['%', 'green'],
- info: ['%', 'grey'],
- notice: ['%', 'yellow'],
- url: [' http://localhost:%/'.grey + '%'.cyan, '.']
-})
-
-module.exports = logan
\ No newline at end of file
diff --git a/utils/utils.js b/utils/utils.js
deleted file mode 100644
index c70615f..0000000
--- a/utils/utils.js
+++ /dev/null
@@ -1,11 +0,0 @@
-function toNative(value) {
- if (value === 'true' || value === 'false') {
- return value === 'true';
- } else if (!isNaN(+value)) {
- return +value;
- } else {
- return value;
- }
-}
-
-exports.toNative = toNative;
\ No newline at end of file