mirror of
https://github.com/typicode/json-server.git
synced 2025-07-28 04:32:24 +08:00
@ -1,4 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
|
79
README.md
79
README.md
@ -1,7 +1,6 @@
|
||||
[](https://travis-ci.org/typicode/json-server)
|
||||
[](http://badge.fury.io/js/json-server)
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img height="56" width="64" src="http://i.imgur.com/dLeJmw6.png"/>
|
||||
</p>
|
||||
|
||||
# JSON Server
|
||||
|
||||
@ -9,36 +8,43 @@ Give it a JSON or JS seed file and it will serve it through REST routes.
|
||||
|
||||
Created with :heart: for front-end developers who need a flexible back-end for quick prototyping and mocking.
|
||||
|
||||
[](https://travis-ci.org/typicode/json-server)
|
||||
[](http://badge.fury.io/js/json-server)
|
||||
|
||||
## Examples
|
||||
|
||||
### Command line interface
|
||||
|
||||
```bash
|
||||
$ cat db.json
|
||||
```javascript
|
||||
// db.json
|
||||
{
|
||||
"posts": [
|
||||
{ "id": 1, "body": "foo" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ json-server --file db.json
|
||||
$ curl -i http://localhost:3000/posts/1
|
||||
```
|
||||
|
||||
You can type 's' at any moment to save the current live JSON object to timestamped
|
||||
file.
|
||||
|
||||
### Node module
|
||||
|
||||
```javascript
|
||||
var server = require('json-server');
|
||||
|
||||
var db = {
|
||||
server.low.db = {
|
||||
posts: [
|
||||
{ id: 1, body: 'foo' }
|
||||
]
|
||||
}
|
||||
|
||||
server.run(db);
|
||||
server.get('/another/route', function(req, res, next) {
|
||||
// ...
|
||||
})
|
||||
|
||||
server.listen(3000);
|
||||
```
|
||||
|
||||
You can find a running demo here: http://jsonplaceholder.typicode.com.
|
||||
@ -58,38 +64,25 @@ You can find a running demo here: http://jsonplaceholder.typicode.com.
|
||||
$ npm install -g json-server
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Command line interface
|
||||
|
||||
```bash
|
||||
json-server --help
|
||||
|
||||
Usage: json-server [options]
|
||||
Usage: json-server <source> [options]
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help output usage information
|
||||
-V, --version output the version number
|
||||
-f --file <file> load db from a js or json file
|
||||
-u --url <url> load db from a URL
|
||||
-p --port [port] server port
|
||||
--read-only read only mode
|
||||
```
|
||||
--version output version
|
||||
--port <port> set port
|
||||
|
||||
JSON Server can load JSON from multiple sources:
|
||||
Exemples:
|
||||
|
||||
```bash
|
||||
$ json-server --file db.json
|
||||
$ json-server --file seed.js
|
||||
$ json-server --url http://example.com/db.json
|
||||
```
|
||||
|
||||
And be run in read-only mode (useful if deployed on a public server):
|
||||
|
||||
```bash
|
||||
$ json-server --file db.json --read-only
|
||||
json-server db.json
|
||||
json-server seed.js
|
||||
json-server http://example.com/db.json
|
||||
|
||||
```
|
||||
|
||||
#### Input
|
||||
@ -129,7 +122,6 @@ JSON Server expects JS files to export a ```run``` method that returns an object
|
||||
|
||||
Seed files are useful if you need to programmaticaly create a lot of data.
|
||||
|
||||
|
||||
### Node module
|
||||
|
||||
#### run(db, [options])
|
||||
@ -149,7 +141,7 @@ By default, ```port``` is set to 3000 and ```readOnly``` to false.
|
||||
|
||||
```
|
||||
GET /:resource
|
||||
GET /:resource?attr=&attr=&
|
||||
GET /:resource?filter=&filter=&
|
||||
GET /:parent/:parentId/:resource
|
||||
GET /:resource/:id
|
||||
POST /:resource
|
||||
@ -158,6 +150,8 @@ PATCH /:resource/:id
|
||||
DEL /:resource/:id
|
||||
```
|
||||
|
||||
To slice resources, add `_start` and `_end` query parameters to route.
|
||||
|
||||
For routes usage information, have a look at [JSONPlaceholder](https://github.com/typicode/jsonplaceholder) code examples.
|
||||
|
||||
```
|
||||
@ -166,27 +160,12 @@ GET /db
|
||||
|
||||
Returns database state.
|
||||
|
||||
|
||||
```
|
||||
GET /
|
||||
```
|
||||
|
||||
Returns default index file or content of ./public/index.html (useful if you need to set a custom home page).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
If you like the project, please tell your friends about it, star it or give feedback :) It's very much appreciated!
|
||||
|
||||
For project updates or to get in touch, [@typicode](http://twitter.com/typicode). You can also send me a mail.
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Articles
|
||||
|
||||
[Fast prototyping using Restangular and Json-server](http://bahmutov.calepin.co/fast-prototyping-using-restangular-and-json-server.html)
|
||||
* [Fast prototyping using Restangular and Json-server](http://bahmutov.calepin.co/fast-prototyping-using-restangular-and-json-server.html)
|
||||
|
82
bin/cli.js
82
bin/cli.js
@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
var program = require('commander'),
|
||||
request = require('superagent'),
|
||||
server = require('../server'),
|
||||
logger = require('../utils/logger'),
|
||||
moment = require('moment'),
|
||||
fs = require('fs'),
|
||||
pkg = require('../package.json'),
|
||||
options = {};
|
||||
|
||||
function loadFile(file, cb) {
|
||||
var path = process.cwd() + '/' + file,
|
||||
db;
|
||||
|
||||
if (/\.json$/.test(file)) db = require(path);
|
||||
if (/\.js$/.test(file)) db = require(path).run();
|
||||
|
||||
cb(db);
|
||||
}
|
||||
|
||||
function loadURL(url, cb) {
|
||||
logger.info('Fetching ' + url + '...')
|
||||
request
|
||||
.get(url)
|
||||
.end(function(error, res) {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
} else {
|
||||
cb(JSON.parse(res.text));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveDbOnCommand(app) {
|
||||
console.assert(app, 'expected app object');
|
||||
|
||||
process.stdin.resume();
|
||||
process.stdin.setEncoding('utf8');
|
||||
logger.notice('To save live database at any moment, enter `s`');
|
||||
|
||||
process.stdin.on('data', function (userInput) {
|
||||
if (userInput.trim().toLowerCase() == 's') {
|
||||
var liveDB = app.db();
|
||||
var now = moment().format('YYYY-MM-DD-HH:mm:ss')
|
||||
var filename = 'json-server.' + now + '.json';
|
||||
console.assert(liveDB, 'expected live db object');
|
||||
fs.writeFileSync(filename,
|
||||
JSON.stringify(liveDB, null, 2),
|
||||
'utf-8');
|
||||
console.log('saved db to', filename);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onDatabaseLoaded(db) {
|
||||
var app = server.run(db, options);
|
||||
saveDbOnCommand(app);
|
||||
return app;
|
||||
}
|
||||
|
||||
program
|
||||
.version(pkg.version)
|
||||
.option('-f --file <file>', 'load db from a js or json file')
|
||||
.option('-u --url <url>', 'load db from a URL')
|
||||
.option('-p --port [port]', 'server port')
|
||||
.option('--read-only', 'read only mode');
|
||||
|
||||
program.on('--help', function () {
|
||||
var examples =
|
||||
' Examples:\n\n' +
|
||||
' json-server --file db.json\n' +
|
||||
' json-server --file seed.js\n' +
|
||||
' json-server --url http://example.com/db.json\n'
|
||||
console.log(examples);
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
if (program.port) options.port = program.port;
|
||||
if (program.readOnly) options.readOnly = true;
|
||||
if (program.file) loadFile(program.file, onDatabaseLoaded);
|
||||
if (program.url) loadURL(program.url, onDatabaseLoaded);
|
11
bin/index.js
Normal file
11
bin/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
var minimist = require('minimist')
|
||||
var updateNotifier = require('update-notifier')
|
||||
var cli = require('../src/cli')
|
||||
|
||||
var notifier = updateNotifier({packagePath: '../package'})
|
||||
if (notifier.update) notifier.notify()
|
||||
|
||||
var argv = minimist(process.argv.slice(2))
|
||||
|
||||
cli.run(argv)
|
20
db.json
Normal file
20
db.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"posts": [
|
||||
{
|
||||
"id": 2,
|
||||
"body": "bar"
|
||||
}
|
||||
],
|
||||
"comments": [
|
||||
{
|
||||
"id": 1,
|
||||
"published": true,
|
||||
"postId": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"published": false,
|
||||
"postId": 1
|
||||
}
|
||||
]
|
||||
}
|
19
package.json
19
package.json
@ -1,21 +1,23 @@
|
||||
{
|
||||
"name": "json-server",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Serves JSON files through REST routes.",
|
||||
"main": "server.js",
|
||||
"bin": "./bin/cli.js",
|
||||
"main": "./src/server.js",
|
||||
"bin": "./bin/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "~2.0.0",
|
||||
"cors": "~2.1.0",
|
||||
"express": "~3.4.4",
|
||||
"logan": "~0.0.2",
|
||||
"express": "^3.4.8",
|
||||
"superagent": "~0.15.7",
|
||||
"underscore": "~1.5.2",
|
||||
"underscore.inflections": "~0.2.1",
|
||||
"moment": "~2.4.0"
|
||||
"lowdb": "^0.1.0",
|
||||
"yargs": "^1.2.1",
|
||||
"chalk": "^0.4.0",
|
||||
"minimist": "0.0.8",
|
||||
"update-notifier": "^0.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"supertest": "~0.8.1",
|
||||
@ -42,7 +44,8 @@
|
||||
"testing",
|
||||
"rest",
|
||||
"data",
|
||||
"dummy"
|
||||
"dummy",
|
||||
"sandbox"
|
||||
],
|
||||
"author": "Typicode <typicode@gmail.com>",
|
||||
"license": "MIT",
|
||||
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
BIN
public/images/json.png
Normal file
BIN
public/images/json.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 278 B |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB |
@ -1,28 +1,37 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>JSON Server</title>
|
||||
<link href="//netdna.bootstrapcdn.com/bootswatch/3.0.2/journal/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="//netdna.bootstrapcdn.com/bootswatch/3.1.1/flatly/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="stylesheets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="https://github.com/typicode/json-server" class="logo">
|
||||
<img src="images/logo.png">
|
||||
</a>
|
||||
|
||||
<p class="alert alert-info">
|
||||
Congrats! You're successfully running JSON Server.
|
||||
<p>
|
||||
<a href="https://github.com/typicode/json-server" class="logo">
|
||||
<img src="images/json.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Resources</h2>
|
||||
|
||||
<p>
|
||||
<em>
|
||||
Congrats! You're successfully running JSON Server.
|
||||
</em>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>Routes</h4>
|
||||
<p>
|
||||
Here are the resources that JSON Server has loaded:
|
||||
</p>
|
||||
<p>
|
||||
<ul id="resources">loading, please wait...</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To view database current state:
|
||||
You can view database current state at any time:
|
||||
<ul>
|
||||
<li>
|
||||
<a href="db">db</a>
|
||||
@ -30,26 +39,19 @@
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<h2>Requests</h2>
|
||||
<p>
|
||||
Resources can be accessed in various ways.
|
||||
</p>
|
||||
<p>
|
||||
JSON Server supports:
|
||||
<ul>
|
||||
<li>GET, POST, PUT, PATCH, DESTROY and OPTIONS verbs.</li>
|
||||
<li>JSONP or CORS cross domain requests.</li>
|
||||
</ul>
|
||||
You can use GET, POST, PUT... and access your resources from anywhere
|
||||
using CORS and JSONP.
|
||||
</p>
|
||||
|
||||
<h2>Documentation</h2>
|
||||
<h4>Documentation</h4>
|
||||
<p>
|
||||
View
|
||||
<a href="http://github.com/typicode/jsonserver">README</a>
|
||||
on GitHub.
|
||||
</p>
|
||||
|
||||
<h2>Issues</h2>
|
||||
<h4>Issues</h4>
|
||||
<p>Please go
|
||||
<a href="https://github.com/typicode/jsonplaceholder/issues">here</a>.
|
||||
</p>
|
||||
|
@ -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;
|
||||
}*/
|
||||
h4 {
|
||||
padding-top: 20px;
|
||||
}
|
@ -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)
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
96
server.js
96
server.js
@ -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;
|
87
src/cli.js
Normal file
87
src/cli.js
Normal file
@ -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
|
14
src/help.txt
Normal file
14
src/help.txt
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
Usage: json-server <source> [options]
|
||||
|
||||
Options:
|
||||
|
||||
--version output version
|
||||
--port <port> set port
|
||||
|
||||
Exemples:
|
||||
|
||||
json-server db.json
|
||||
json-server seed.js
|
||||
json-server http://example.com/db.json
|
||||
|
81
src/routes.js
Normal file
81
src/routes.js
Normal file
@ -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
|
47
src/server.js
Normal file
47
src/server.js
Normal file
@ -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
|
60
src/utils.js
Normal file
60
src/utils.js
Normal file
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
171
test/server.js
Normal file
171
test/server.js
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
})
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -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);
|
@ -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
|
@ -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;
|
Reference in New Issue
Block a user