mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-05-13 21:06:16 +08:00
merged
This commit is contained in:
7
ABOUT.md
7
ABOUT.md
@@ -31,13 +31,6 @@ realtime resource server
|
||||
|
||||
Consult the [documentation](http://deployd.github.com/deployd) or contact `ritchie at deployd com`.
|
||||
|
||||
## changelog
|
||||
|
||||
### 0.5
|
||||
|
||||
- removed `property.optional` in favor of `property.required`
|
||||
- changed `object._id` to `object.id` on all stored objects
|
||||
|
||||
## license
|
||||
|
||||
Copyright 2012 deployd, llc
|
||||
|
||||
@@ -4,9 +4,16 @@
|
||||
- Added new data editor
|
||||
- Fixed major bug where calling error() would not always cancel the request
|
||||
- Fixed bug where PUT would fail without an error if you provided a query
|
||||
- Changed root behavior - no longer ignores cancel() in events
|
||||
- Fixed bugs preventing events from being `emit()`ed to users in certain connection states
|
||||
- Fixed bug where boolean query values (?bool=true) were not treated as booleans
|
||||
- Fixed unnecessary error when parsing JSON body
|
||||
- Added more intelegent body parsing
|
||||
|
||||
|
||||
## 0.6.6
|
||||
|
||||
- Added CORS support
|
||||
- Exposed the server object to modules as `process.server`
|
||||
- Fixed a rare bug where the first request after a login would not be authenticated
|
||||
- Fixed minor bug when loading only node modules
|
||||
|
||||
2
bin/dpd
2
bin/dpd
@@ -33,7 +33,7 @@ program
|
||||
.option('-d, --dashboard', 'start the dashboard immediately')
|
||||
.option('-o, --open', 'open in a browser')
|
||||
.option('-e, --environment [env]', 'defaults to development')
|
||||
.option('-h, --host [host]', 'specify host for mongo server')
|
||||
.option('-H, --host [host]', 'specify host for mongo server')
|
||||
.option('-P, --mongoPort [mongoPort]', 'mongodb port to connect to')
|
||||
.option('-n, --dbname [dbname]', 'name of the mongo database')
|
||||
.option('-a, --auth', 'prompts for mongo server credentials')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# About Deployd Server
|
||||
# About Deployd
|
||||
|
||||
We call Deployd a **resource server**. A resource server is not a library, but a complete server that works out of the box, and can be customized to fit the needs of your app by adding resources. Resources are ready-made components that live at a URL and provide functionality to your client app.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ To provide additional collaborators access to push new versions and access the d
|
||||
|
||||
You can also deploy your app on your server, or on a cloud hosting service such as EC2 or Heroku. The server must support [Node.js](http://nodejs.org/).
|
||||
|
||||
Deployd also requires a [MongoDB] (http://www.mongodb.org/) database, which can be hosted on the same server or externally.
|
||||
Deployd also requires a [MongoDB](http://www.mongodb.org/) database, which can be hosted on the same server or externally.
|
||||
|
||||
If you have root shell access on the deployment server, you can install Deployd on it using the command `npm install -g deployd`.
|
||||
Otherwise, you will need to install Deployd as a dependency of your app itself using `npm install deployd` in the root directory of your app.
|
||||
|
||||
@@ -6,8 +6,8 @@ The `internal-client` module is responsible for building a server-side version o
|
||||
|
||||
## internalClient.build(server, [session], [stack])
|
||||
|
||||
var internalClient = require('deployd/lib/internalClient');
|
||||
var dpd = internalClient.build(server, req.session, req.stack);
|
||||
var internalClient = require('deployd/lib/internal-client');
|
||||
var dpd = internalClient.build(server);
|
||||
|
||||
dpd.todos.get(function(data, err) {
|
||||
// Do something...
|
||||
|
||||
@@ -61,7 +61,7 @@ If a callback is provided the script will be run in **async mode**. The callback
|
||||
|
||||
* fn(err, script)
|
||||
|
||||
Load a new `script` at the given file `path`. Callback with an error if one occured or a new `Script` loaded from the contents of the file.
|
||||
Load a new `script` at the given file `path`. Callback with an error if one occured or a new `Script` loaded from the contents of the file.
|
||||
|
||||
## Default Domain
|
||||
|
||||
@@ -71,8 +71,6 @@ Scripts are executed with a default sandbox and set of domain functions. These a
|
||||
|
||||
Throws an error that immediately stops the execution of a context and calls the callback passed to `script.run()` passing the error as the first argument.
|
||||
|
||||
`cancel()` does not have an effect if the current `Context.isRoot` or `Context.internal` is true.
|
||||
|
||||
### emit([collection], [query], event, data)
|
||||
|
||||
Stability: will change in 0.7
|
||||
@@ -85,5 +83,7 @@ The default sandbox or global object in a `Script` comes with several other prop
|
||||
|
||||
- `me` - the current user if one exists on the `Context`
|
||||
- `this` - an empty object if not overridden by the `domain`
|
||||
- `internal` - a boolean property, true if this request has been initiated by another script
|
||||
- `isRoot` - a boolean property, true if this request is authenticated as root (from the dashboard or a custom script)
|
||||
- `query` - the current `Context`'s query
|
||||
- `console` - support for `console.log()` and other `console` methods
|
||||
|
||||
@@ -161,6 +161,29 @@ Dpd.js will prevent recursive queries. This works by returning `null` from a `dp
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
### internal
|
||||
|
||||
Equal to true if this request has been sent by another script.
|
||||
|
||||
// Example: On GET /posts
|
||||
// Posts with a parent are invisible, but are counted by their parent
|
||||
if (this.parentId && !internal) cancel();
|
||||
|
||||
dpd.posts.get({parentId: this.id}, function(posts) {
|
||||
this.childPosts = posts.length;
|
||||
});
|
||||
|
||||
### isRoot
|
||||
|
||||
Equal to true if this request has been authenticated as root (has the `dpd-ssh-key` header with the appropriate key)
|
||||
|
||||
// Example: On PUT /users
|
||||
// Protect reputation property - should only be calculated by a custom script.
|
||||
|
||||
if (!isRoot) protect('reputation');
|
||||
|
||||
|
||||
### console.log()
|
||||
|
||||
console.log([arguments]...)
|
||||
|
||||
@@ -213,6 +213,11 @@
|
||||
return true;
|
||||
};
|
||||
|
||||
vm.edit.isEditableModal = function() {
|
||||
var type = vm.selectedProp().type;
|
||||
return vm.edit.isJson() || type === 'string';
|
||||
};
|
||||
|
||||
vm.edit.hasChanged = ko.observable(false);
|
||||
vm.edit.editValue.subscribe(function(newVal) {
|
||||
vm.edit.hasChanged(true);
|
||||
@@ -418,7 +423,7 @@
|
||||
rowVm._deleteRow = function() {
|
||||
if (rowVm.id()) {
|
||||
var index = vm.data.indexOf(rowVm);
|
||||
dpd(resource).del(rowVm.id(), function(res, err) {
|
||||
dpd(resource).del(rowVm.id(), {$skipEvents: true}, function(res, err) {
|
||||
if (err) return showError("Error deleting row", err);
|
||||
vm.data.remove(rowVm);
|
||||
var data = vm.data();
|
||||
@@ -462,15 +467,23 @@
|
||||
rowVm[name](value);
|
||||
|
||||
if (rowVm.id()) {
|
||||
if (!options.dontNotify) {
|
||||
createUndo("Changed " + vm.selectedProp().name, function() {
|
||||
rowVm._saveProp(prop, lastValue, {dontNotify: true});
|
||||
vm.selectedProp(prop);
|
||||
vm.selectedRow(rowVm);
|
||||
});
|
||||
}
|
||||
|
||||
body[name] = value;
|
||||
dpd(resource).put(rowVm.id(), body);
|
||||
body.$skipEvents = true;
|
||||
dpd(resource).put(rowVm.id(), body, function(res, err) {
|
||||
if (err) {
|
||||
showError("Error updating row", err);
|
||||
rowVm[name](lastValue);
|
||||
}
|
||||
if (!options.dontNotify) {
|
||||
createUndo("Changed " + vm.selectedProp().name, function() {
|
||||
rowVm._saveProp(prop, lastValue, {dontNotify: true});
|
||||
vm.selectedProp(prop);
|
||||
vm.selectedRow(rowVm);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
} else if (!options.dontSave) {
|
||||
rowVm._save();
|
||||
}
|
||||
@@ -516,6 +529,7 @@
|
||||
}
|
||||
|
||||
function postRow(data, fn) {
|
||||
data.$skipEvents = true;
|
||||
dpd(resource).post(data, function(res, err) {
|
||||
if (err) return fn(null, err);
|
||||
vm.fadeInRows.push(getRowById(res.id)); //In case it's already there
|
||||
@@ -647,7 +661,7 @@
|
||||
return false;
|
||||
|
||||
case 13: //enter
|
||||
if (e.ctrlKey) {
|
||||
if (e.ctrlKey && vm.edit.isEditableModal()) {
|
||||
vm.selectedRow()._editProp(vm.selectedProp(), {modal: true});
|
||||
} else {
|
||||
vm.selectedRow()._editProp(vm.selectedProp());
|
||||
@@ -793,7 +807,7 @@
|
||||
var begin = page - 1;
|
||||
begin = Math.max(0, Math.min(begin, getLastPage() - 1));
|
||||
|
||||
dpd(resource).get({$skip: begin*PAGE_SIZE, $limit: PAGE_SIZE*3}, function(res, err) {
|
||||
dpd(resource).get({$skip: begin*PAGE_SIZE, $limit: PAGE_SIZE*3, $skipEvents: true}, function(res, err) {
|
||||
if (err) return;
|
||||
vm.currentPage(page);
|
||||
ko.mapping.fromJS({data: res}, rowMapping, vm);
|
||||
|
||||
@@ -400,7 +400,11 @@ ko.bindingHandlers.aceEditorOptions = {
|
||||
ko.bindingHandlers.typeahead = {
|
||||
update: function(element, valueAccessor) {
|
||||
var value = ko.utils.unwrapObservable(valueAccessor());
|
||||
$(element).typeahead({source: value});
|
||||
if (window.typeahead) {
|
||||
$(element).typeahead({source: value});
|
||||
} else {
|
||||
$(element).typeahead({source: []});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,16 +143,19 @@ Collection.prototype.sanitizeQuery = function (query) {
|
||||
// skip properties that do not exist, but allow $ queries and id
|
||||
if(!prop && key.indexOf('$') !== 0 && key !== 'id') return;
|
||||
|
||||
// hack - $limitRecursion is not a mongo property so we'll get rid of it, too
|
||||
// hack - $limitRecursion and $skipEvents are not mongo properties so we'll get rid of them, too
|
||||
if (key === '$limitRecursion') return;
|
||||
if (key === '$skipEvents') return;
|
||||
|
||||
if(expected == 'number' && actual == 'string') {
|
||||
sanitized[key] = parseFloat(val);
|
||||
} else if(expected == 'boolean' && actual != 'boolean') {
|
||||
sanitized[key] = (val === 'true') ? true : false;
|
||||
} else if (typeof val !== 'undefined') {
|
||||
sanitized[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
@@ -316,9 +319,7 @@ Collection.prototype.find = function (ctx, fn) {
|
||||
errors[key] = val || true;
|
||||
},
|
||||
hide: function(property) {
|
||||
if (!session.isRoot) {
|
||||
delete data[property];
|
||||
}
|
||||
delete data[property];
|
||||
},
|
||||
'this': data,
|
||||
data: data
|
||||
@@ -350,9 +351,7 @@ Collection.prototype.find = function (ctx, fn) {
|
||||
errors[key] = val || true;
|
||||
},
|
||||
hide: function(property) {
|
||||
if (!session.isRoot) {
|
||||
delete data[property];
|
||||
}
|
||||
delete data[property];
|
||||
},
|
||||
'this': data,
|
||||
data: data
|
||||
@@ -381,17 +380,18 @@ Collection.prototype.remove = function (ctx, fn) {
|
||||
, store = this.store
|
||||
, session = ctx.session
|
||||
, query = ctx.query
|
||||
, sanitizedQuery = this.sanitizeQuery(query)
|
||||
, errors;
|
||||
|
||||
if(!(query && query.id)) return fn('You must include a query with an id when deleting an object from a collection.');
|
||||
store.find(query, function (err, result) {
|
||||
store.find(sanitizedQuery, function (err, result) {
|
||||
if(err) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
function done(err) {
|
||||
if(err) return fn(err);
|
||||
store.remove(query, fn);
|
||||
store.remove(sanitizedQuery, fn);
|
||||
if(session.emitToAll) session.emitToAll(collection.name + ':changed');
|
||||
}
|
||||
|
||||
@@ -424,6 +424,7 @@ Collection.prototype.save = function (ctx, fn) {
|
||||
, store = this.store
|
||||
, session = ctx.session
|
||||
, item = ctx.body
|
||||
|
||||
, query = ctx.query || {}
|
||||
, client = ctx.dpd
|
||||
, errors;
|
||||
@@ -453,6 +454,7 @@ Collection.prototype.save = function (ctx, fn) {
|
||||
|
||||
function done(err, item) {
|
||||
errors = errors && {errors: errors};
|
||||
debug('errors: %j', err);
|
||||
fn(errors || err, item);
|
||||
}
|
||||
|
||||
@@ -463,24 +465,21 @@ Collection.prototype.save = function (ctx, fn) {
|
||||
errors[key] = val || true;
|
||||
},
|
||||
hide: function(property) {
|
||||
if (!session.isRoot) {
|
||||
delete item[property];
|
||||
}
|
||||
delete item[property];
|
||||
},
|
||||
protect: function(property) {
|
||||
if (!session.isRoot) {
|
||||
delete item[property];
|
||||
}
|
||||
delete item[property];
|
||||
},
|
||||
'this': item,
|
||||
data: item
|
||||
};
|
||||
|
||||
function put() {
|
||||
var id = query.id;
|
||||
store.first(query, function(err, obj) {
|
||||
var id = query.id
|
||||
, sanitizedQuery = collection.sanitizeQuery(query);
|
||||
store.first(sanitizedQuery, function(err, obj) {
|
||||
if(!obj) {
|
||||
if (Object.keys(query) === 1) {
|
||||
if (Object.keys(sanitizedQuery) === 1) {
|
||||
return done(new Error("No object exists with that id"));
|
||||
} else {
|
||||
return done(new Error("No object exists that matches that query"));
|
||||
@@ -671,8 +670,8 @@ Collection.prototype.execCommands = function (type, obj, commands) {
|
||||
};
|
||||
|
||||
Collection.prototype.shouldRunEvent = function(ev, ctx) {
|
||||
var runEvents = ctx && ((ctx.body && ctx.body.$runEvents) || (ctx.query && ctx.query.$runEevents))
|
||||
, rootPrevent = ctx && ctx.session && ctx.session.isRoot && !runEvents;
|
||||
var skipEvents = ctx && ((ctx.body && ctx.body.$skipEvents) || (ctx.query && ctx.query.$skipEvents))
|
||||
, rootPrevent = ctx && ctx.session && ctx.session.isRoot && skipEvents;
|
||||
return !rootPrevent && ev;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,15 +37,14 @@ Script.prototype.run = function (ctx, domain, fn) {
|
||||
var scriptContext = {
|
||||
'this': {},
|
||||
cancel: function(msg, status) {
|
||||
if (!req.isRoot) {
|
||||
var err = {message: msg, statusCode: status};
|
||||
throw err;
|
||||
}
|
||||
var err = {message: msg, statusCode: status};
|
||||
throw err;
|
||||
},
|
||||
me: session && session.user,
|
||||
console: console,
|
||||
query: ctx.query,
|
||||
internal: req && req.internal,
|
||||
isRoot: req && req.session && req.session.isRoot,
|
||||
emit: function(collection, query, event, data) {
|
||||
if(arguments.length === 4) {
|
||||
session.emitToUsers(collection, query, event, data);
|
||||
|
||||
@@ -25,19 +25,20 @@ function SessionStore(namespace, db, sockets) {
|
||||
var socketQueue = this.socketQueue = new EventEmitter()
|
||||
, socketIndex = this.socketIndex = {};
|
||||
|
||||
// TODO - sockets.on('connection', ...) - map to a session id based on socket.handshake.headers
|
||||
sockets && sockets.on('connection', function (socket) {
|
||||
// NOTE: do not use set here ever, the `Cookies` api is meant to get a req, res
|
||||
// but we are just using it for a cookie parser
|
||||
var cookies = new Cookies(socket.handshake)
|
||||
, sid = cookies.get('sid');
|
||||
if(sockets) {
|
||||
sockets.on('connection', function (socket) {
|
||||
// NOTE: do not use set here ever, the `Cookies` api is meant to get a req, res
|
||||
// but we are just using it for a cookie parser
|
||||
var cookies = new Cookies(socket.handshake)
|
||||
, sid = cookies.get('sid');
|
||||
|
||||
if(sid) {
|
||||
// index sockets against their session id
|
||||
socketIndex[sid] = socket;
|
||||
socketQueue.emit(sid, socket);
|
||||
}
|
||||
});
|
||||
if(sid) {
|
||||
// index sockets against their session id
|
||||
socketIndex[sid] = socket;
|
||||
socketQueue.emit(sid, socket);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Store.apply(this, arguments);
|
||||
}
|
||||
@@ -105,43 +106,52 @@ SessionStore.prototype.createSession = function(sid, fn) {
|
||||
*/
|
||||
|
||||
function Session(data, store, sockets, rawSockets) {
|
||||
<<<<<<< HEAD
|
||||
var sess = this;
|
||||
=======
|
||||
var sid;
|
||||
>>>>>>> 929285c8b89118aa505a218b939cec34d431e38e
|
||||
this.data = data;
|
||||
if(data && data.id) this.sid = data.id;
|
||||
if(data && data.id) this.sid = sid = data.id;
|
||||
this.store = store;
|
||||
|
||||
// create faux socket, to queue any events until
|
||||
// a real socket is available
|
||||
var socketWrapper = this.socket = {
|
||||
on: function () {
|
||||
var s = sockets[sid];
|
||||
// if we have a real socket, use it
|
||||
if(this._socket) {
|
||||
this._socket.apply(this._socket, arguments);
|
||||
if(s) {
|
||||
s.on.apply(s, arguments);
|
||||
} else {
|
||||
// otherwise add to bind queue
|
||||
var queue = this._bindQueue = this._bindQueue || [];
|
||||
queue.push(arguments);
|
||||
}
|
||||
},
|
||||
emit: function () {
|
||||
emit: function (ev) {
|
||||
var s = sockets[sid];
|
||||
|
||||
// if we have a real socket, use it
|
||||
if(this._socket) {
|
||||
this._socket.emit.apply(this._socket, arguments);
|
||||
if(s) {
|
||||
s.emit.apply(s, arguments);
|
||||
} else {
|
||||
// otherwise add to bind queue
|
||||
var queue = this._emitQueue = this._bindQueue || [];
|
||||
queue.push(arguments);
|
||||
}
|
||||
},
|
||||
_socket: sockets[this.sid]
|
||||
}
|
||||
};
|
||||
|
||||
this.emitToUsers = function(collection, query, event, data) {
|
||||
collection.get(query, function(users) {
|
||||
var userSession;
|
||||
// TODO: arguments in weird order
|
||||
if(users && users.id) {
|
||||
<<<<<<< HEAD
|
||||
userSession = userSessionIndex[err.id];
|
||||
=======
|
||||
userSession = userSessionIndex[users.id];
|
||||
>>>>>>> 929285c8b89118aa505a218b939cec34d431e38e
|
||||
if(userSession && userSession.socket) {
|
||||
userSession.socket.emit(event, data);
|
||||
}
|
||||
@@ -169,7 +179,6 @@ function Session(data, store, sockets, rawSockets) {
|
||||
sess.set({host: add + ':' + process.server.options.port}).save();
|
||||
});
|
||||
|
||||
socketWrapper._socket = socket;
|
||||
// drain bind queue
|
||||
if(socketWrapper._bindQueue && socketWrapper._bindQueue.length) {
|
||||
socketWrapper._bindQueue.forEach(function (args) {
|
||||
|
||||
@@ -14,33 +14,41 @@ exports.setup = function(req, res, next) {
|
||||
, handler = corser.create({supportsCredentials: true, methods: ALLOWED_METHODS, origins: origins});
|
||||
|
||||
handler(req, res, function () {
|
||||
if (req.method === "OPTIONS") {
|
||||
// End CORS preflight request.
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
} else {
|
||||
var mime = req.headers['content-type'] || '';
|
||||
mime = mime.split(';')[0]; //Just in case there's multiple mime types, pick the first
|
||||
req.cookies = res.cookies = new Cookies(req, res);
|
||||
|
||||
if(~req.url.indexOf('?')) {
|
||||
try {
|
||||
req.query = parseQuery(req.url);
|
||||
} catch (ex) {
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.statusCode = 400;
|
||||
res.end('Failed to parse querystring: ' + ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(req.method) {
|
||||
case 'OPTIONS':
|
||||
// End CORS preflight request.
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'DELETE':
|
||||
var mime = req.headers['content-type'] || 'application/json';
|
||||
mime = mime.split(';')[0]; //Just in case there's multiple mime types, pick the first
|
||||
|
||||
req.cookies = res.cookies = new Cookies(req, res);
|
||||
|
||||
if(~req.url.indexOf('?')) {
|
||||
try {
|
||||
req.query = parseQuery(req.url);
|
||||
} catch (ex) {
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.statusCode = 400;
|
||||
res.end('Failed to parse querystring: ' + ex);
|
||||
return;
|
||||
if(autoParse[mime]) {
|
||||
autoParse[mime](req, res, mime, next);
|
||||
} else {
|
||||
if(req.headers['content-length']) req.pause();
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
if(autoParse[mime]) {
|
||||
autoParse[mime](req, res, mime, next);
|
||||
} else {
|
||||
if(req.headers['content-length']) req.pause();
|
||||
break;
|
||||
default:
|
||||
next();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -68,7 +76,18 @@ var parseBody = exports.parseBody = function(req, res, mime, callback) {
|
||||
}
|
||||
|
||||
try {
|
||||
req.body = parser.parse(buf);
|
||||
if(buf.length) {
|
||||
if(mime === 'application/json' && '{' != buf[0] && '[' != buf[0]) {
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.statusCode = 400;
|
||||
res.end('Could not parse invalid JSON');
|
||||
return;
|
||||
}
|
||||
|
||||
req.body = parser.parse(buf);
|
||||
} else {
|
||||
req.body = {};
|
||||
}
|
||||
callback();
|
||||
} catch (ex) {
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Mocha Tests</title>
|
||||
<link rel="stylesheet" href="mocha.css" />
|
||||
<script src="/dpd.js"></script>
|
||||
<script src="jquery.js"></script>
|
||||
<script src="chai.js"></script>
|
||||
<script src="mocha.js"></script>
|
||||
<script src="util.js"></script>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*global _dpd:false */
|
||||
describe('Collection', function() {
|
||||
describe('dpd.todos', function() {
|
||||
it('should exist', function() {
|
||||
@@ -67,6 +68,18 @@ describe('Collection', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should create a todo that exists in the store', function(done) {
|
||||
dpd.todos.post({title: 'faux'}, function (todo, err) {
|
||||
expect(todo.id.length).to.equal(16);
|
||||
expect(todo.title).to.equal('faux');
|
||||
expect(err).to.not.exist;
|
||||
dpd.todos.get(todo.id, function(res, err) {
|
||||
if (err) return done(err);
|
||||
expect(res.title).to.equal('faux');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.post({title: "notvalid"}, fn)', function() {
|
||||
@@ -211,6 +224,29 @@ describe('Collection', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /full?boolean=true', function () {
|
||||
it('should filter boolean properties by query string', function(done) {
|
||||
dpd.full.post({boolean: true}, function (full) {
|
||||
dpd.full.post({boolean: false}, function(full){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/full?boolean=true",
|
||||
success: function (res) {
|
||||
expect(res.length).to.be.greaterThan(0);
|
||||
res.forEach(function(obj){
|
||||
expect(obj.boolean).to.equal(true);
|
||||
});
|
||||
done();
|
||||
},
|
||||
error: function (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.get({id: "non existent"}, fn)', function() {
|
||||
it('should return a 404', function(done) {
|
||||
@@ -381,7 +417,6 @@ describe('Collection', function() {
|
||||
todoId = res.id;
|
||||
dpd.todos.put(todoId, {message: "notvalidput"}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
console.log(res, err);
|
||||
expect(err).to.exist;
|
||||
expect(err.errors).to.exist;
|
||||
expect(err.errors.message).to.equal("message should not be notvalidput");
|
||||
@@ -592,6 +627,77 @@ describe('Collection', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('root', function() {
|
||||
afterEach(function(done) {
|
||||
_dpd.ajax.headers = {};
|
||||
cleanCollection(dpd.todos, done);
|
||||
});
|
||||
|
||||
describe('dpd-ssh-key', function() {
|
||||
beforeEach(function() {
|
||||
_dpd.ajax.headers = {
|
||||
'dpd-ssh-key': true
|
||||
};
|
||||
});
|
||||
|
||||
it('should detect root', function(done) {
|
||||
chain(function(next) {
|
||||
dpd.todos.post({title: 'valid'}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
if (err) return done(err);
|
||||
expect(res.isRoot).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow skipping events', function(done) {
|
||||
chain(function(next) {
|
||||
dpd.todos.post({title: 'notvalid', $skipEvents: true}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
if (err) return done(err);
|
||||
expect(res.title).to.equal('notvalid');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow skipping events on get', function(done) {
|
||||
var id;
|
||||
chain(function(next) {
|
||||
dpd.todos.post({title: '$GET_CANCEL'}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
if (err) return done(err);
|
||||
id = res.id;
|
||||
dpd.todos.get(id, {$skipEvents: true}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
if (err) return done(err);
|
||||
expect(res.title).to.equal("$GET_CANCEL");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow skipping events', function(done) {
|
||||
chain(function(next) {
|
||||
dpd.todos.post({title: 'notvalid', $skipEvents: true}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
expect(err).to.exist;
|
||||
expect(err.errors).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not detect root', function(done) {
|
||||
chain(function(next) {
|
||||
dpd.todos.post({title: 'valid'}, next);
|
||||
}).chain(function(next, res, err) {
|
||||
if (err) return done(err);
|
||||
expect(res.isRoot).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('dpd.recursive', function() {
|
||||
beforeEach(function(done) {
|
||||
dpd.recursive.post({name: "dataception"}, function(res) {
|
||||
|
||||
@@ -35,4 +35,8 @@ if (this.title === "$CANCEL_TEST") {
|
||||
|
||||
if (this.title === "$INTERNAL_CANCEL_TEST") {
|
||||
if (!internal) cancel('internal cancel');
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
this.isRoot = true;
|
||||
}
|
||||
@@ -47,19 +47,6 @@ describe('Session', function() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
it('should make the socket available from the session', function(done) {
|
||||
var sockets = new EventEmitter()
|
||||
, store = new SessionStore('sessions', db.create(TEST_DB), sockets);
|
||||
|
||||
store.createSession(function (err, session) {
|
||||
var fauxSocket = {handshake: { headers: {cookie: 'name=value; name2=value2; sid=' + session.sid} } };
|
||||
sockets.emit('connection', fauxSocket);
|
||||
expect(session.socket._socket).to.equal(fauxSocket);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('should make sockets available even before they exist', function(done) {
|
||||
this.timeout(100);
|
||||
|
||||
|
||||
@@ -83,6 +83,17 @@ describe('.parseBody()', function() {
|
||||
req.emit('data', value);
|
||||
req.emit('end');
|
||||
});
|
||||
|
||||
it('should interpret an empty body as an empty object', function(done) {
|
||||
var req = new Stream();
|
||||
|
||||
http.parseBody(req, this.res, 'application/json', function(err) {
|
||||
expect(err).to.not.exist;
|
||||
expect(req.body).to.eql({});
|
||||
done();
|
||||
});
|
||||
req.emit('end');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user