couch-slouch
Advanced tools
Comparing version 0.1.0 to 0.1.1
{ | ||
"name": "couch-slouch", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "A JS API for CouchDB that does the heavy lifting", | ||
@@ -51,3 +51,3 @@ "main": "index.js", | ||
"request": "^2.81.0", | ||
"sporks": "^0.0.2", | ||
"sporks": "^0.0.3", | ||
"squadron": "^0.0.3" | ||
@@ -60,7 +60,7 @@ }, | ||
"chai-as-promised": "^6.0.0", | ||
"gofur": "^0.0.7", | ||
"gofur": "^0.0.8", | ||
"istanbul": "^0.4.5", | ||
"jshint": "^2.9.4", | ||
"memorystream": "^0.3.1", | ||
"mocha": "^3.4.2" | ||
"mocha": "^4.0.0" | ||
}, | ||
@@ -67,0 +67,0 @@ "greenkeeper": { |
@@ -28,1 +28,3 @@ # slouch | ||
* [Don’t Just Relax; Slouch: A JS Client for CouchDB that Does the Heavy Lifting](https://medium.com/@redgeoff/dont-just-relax-slouch-a-js-client-for-couchdb-that-does-the-heavy-lifting-d8232eba8e2c) | ||
## [Testing/Contributing](TESTING.md) |
'use strict'; | ||
var Promise = require('sporks/scripts/promise'); | ||
var Promise = require('sporks/scripts/promise'), | ||
sporks = require('sporks'); | ||
@@ -23,6 +24,9 @@ var Config = function (slouch) { | ||
i = 0; | ||
return self._slouch.membership.get().then(function (members) { | ||
members.cluster_nodes.forEach(function (node) { | ||
if (typeof maxNumNodes === 'undefined' || i++ < maxNumNodes) { | ||
promises.push(self._couchDB2Request(node, path, opts, parseBody)); | ||
// Clone the opts as we need a separate copy per node | ||
var clonedOpts = sporks.clone(opts); | ||
promises.push(self._couchDB2Request(node, path, clonedOpts, parseBody)); | ||
} | ||
@@ -29,0 +33,0 @@ }); |
'use strict'; | ||
var CouchPersistentStreamIterator = require('./couch-persistent-stream-iterator'); | ||
var CouchPersistentStreamIterator = require('./couch-persistent-stream-iterator'), | ||
FilteredStreamIterator = require('quelle').FilteredStreamIterator; | ||
@@ -58,7 +59,16 @@ var DB = function (slouch) { | ||
DB.prototype._setSince = function (opts, lastSeq) { | ||
if (lastSeq) { | ||
opts.qs.since = lastSeq; | ||
} | ||
}; | ||
// Use a JSONStream so that we don't have to load a large JSON structure into memory | ||
DB.prototype.changes = function (dbName, params) { | ||
var indefinite = false, | ||
jsonStreamParseStr = null; | ||
var self = this, | ||
indefinite = false, | ||
jsonStreamParseStr = null, | ||
request = null, | ||
lastSeq = null; | ||
@@ -68,12 +78,26 @@ if (params && params.feed === 'continuous') { | ||
jsonStreamParseStr = undefined; | ||
// Define a wrapper for the request so that we can inject an update "since" on reconnect so that | ||
// our place can be resumed | ||
request = function () { | ||
self._setSince(arguments[0], lastSeq); | ||
return self._slouch._request.apply(self._slouch, arguments); | ||
}; | ||
} else { | ||
jsonStreamParseStr = 'results.*'; | ||
request = self._slouch._request; | ||
} | ||
return new CouchPersistentStreamIterator({ | ||
url: this._slouch._url + '/' + dbName + '/_changes', | ||
var iterator = new CouchPersistentStreamIterator({ | ||
url: self._slouch._url + '/' + dbName + '/_changes', | ||
method: 'GET', | ||
qs: params | ||
}, jsonStreamParseStr, indefinite, this._slouch._request); | ||
}, jsonStreamParseStr, indefinite, request); | ||
return new FilteredStreamIterator(iterator, function (item) { | ||
// Store the lastSeq so that we can resume after a reconnect | ||
lastSeq = item.seq; | ||
return item; | ||
}); | ||
}; | ||
@@ -80,0 +104,0 @@ |
@@ -47,2 +47,15 @@ 'use strict'; | ||
Security.prototype.onlyUserCanView = function (dbName, user) { | ||
return this.set(dbName, { | ||
admins: { | ||
names: ['_admin'], | ||
roles: [] | ||
}, | ||
members: { | ||
names: [user], | ||
roles: [] | ||
} | ||
}); | ||
}; | ||
Security.prototype.onlyAdminCanView = function (dbName) { | ||
@@ -49,0 +62,0 @@ return this.onlyRoleCanView(dbName, '_admin'); |
@@ -16,3 +16,3 @@ 'use strict'; | ||
// Sometimes the DB gets a little backed up so we need more time for our tests | ||
this.timeout(10000); | ||
this.timeout(20000); | ||
@@ -19,0 +19,0 @@ require('./spec'); |
@@ -77,3 +77,8 @@ 'use strict'; | ||
}).then(function (value) { | ||
value.should.eql('warning'); | ||
// value will be an array on a multinode cluster | ||
if (Array.isArray(value)) { | ||
value[0].should.eql('warning'); | ||
} else { | ||
value.should.eql('warning'); | ||
} | ||
@@ -80,0 +85,0 @@ return config.unset('log/level'); |
@@ -32,14 +32,26 @@ 'use strict'; | ||
var createDocs = function () { | ||
var jam = function () { | ||
return slouch.doc.create(utils.createdDB, { | ||
thing: 'jam' | ||
}); | ||
}; | ||
var clean = function () { | ||
return slouch.doc.create(utils.createdDB, { | ||
thing: 'clean', | ||
fun: false | ||
}); | ||
}; | ||
var code = function () { | ||
return slouch.doc.create(utils.createdDB, { | ||
thing: 'code' | ||
}); | ||
}; | ||
var createDocs = function () { | ||
return jam().then(function () { | ||
return clean(); | ||
}).then(function () { | ||
return slouch.doc.create(utils.createdDB, { | ||
thing: 'clean', | ||
fun: false | ||
}); | ||
}).then(function () { | ||
return slouch.doc.create(utils.createdDB, { | ||
thing: 'code' | ||
}); | ||
return code(); | ||
}); | ||
@@ -97,3 +109,3 @@ }; | ||
// Make sure db names were captured | ||
(dbNames.should.length === 0).should.eql(false); | ||
dbNames.should.not.have.lengthOf(0, 'db names were not captured'); | ||
@@ -107,3 +119,3 @@ // Make sure a specific DB like _users was captured | ||
}); | ||
usersFound.should.eql(true); | ||
usersFound.should.eql(true, 'A specific DB like _users was not captured'); | ||
}); | ||
@@ -174,2 +186,68 @@ }); | ||
// Not on PhantomJS? This test does not work on PhantomJS as PhantomJS doesn't properly support | ||
// simultaenous connections which means that the changes are never read. | ||
if (!global.window || !/PhantomJS/.test(window.navigator.userAgent)) { | ||
it('should resume changes', function () { | ||
var changes = {}; | ||
var changesIterator = db.changes(utils.createdDB, { | ||
include_docs: true, | ||
feed: 'continuous', | ||
heartbeat: true | ||
}); | ||
var changesPromise = changesIterator.each(function (change) { | ||
// Use associative array as order is not guaranteed | ||
if (!changes[change.doc.thing]) { | ||
changes[change.doc.thing] = 0; | ||
} | ||
changes[change.doc.thing]++; | ||
}); | ||
var waitForChange = function (thing) { | ||
return sporks.waitFor(function () { | ||
return changes[thing]; | ||
}); | ||
}; | ||
// Create "jam" doc | ||
var mainPromise = jam().then(function () { | ||
return waitForChange('jam'); | ||
}).then(function () { | ||
// Simulate dropped connection | ||
var err = new Error(); | ||
err.code = 'ETIMEDOUT'; | ||
changesIterator._streamIterator._lastRequest.emit('error', err); | ||
}).then(function () { | ||
// Create "code" doc | ||
return code(); | ||
}).then(function () { | ||
// Wait for "code" to be read in changes feed | ||
return waitForChange('code'); | ||
}).then(function () { | ||
// Shut down iterator | ||
changesIterator.abort(); | ||
}).then(function () { | ||
// Make sure we only read each change once, i.e. the reconnect resumed reading after "jam" | ||
changes.should.eql({ | ||
jam: 1, | ||
code: 1 | ||
}); | ||
}); | ||
return Promise.all([changesPromise, mainPromise]); | ||
}); | ||
} | ||
it('should set since', function () { | ||
// Needed for 100% code coverage in PhantomJS | ||
var lastSeq = '1', | ||
opts = { | ||
qs: {} | ||
}; | ||
db._setSince(opts, lastSeq); | ||
opts.qs.since.should.eql(lastSeq); | ||
}); | ||
it('should get view', function () { | ||
@@ -176,0 +254,0 @@ var docs = {}; |
@@ -34,3 +34,3 @@ 'use strict'; | ||
it('should filter', function () { | ||
var docs = []; | ||
var docs = {}; | ||
return createDocs().then(function () { | ||
@@ -40,14 +40,13 @@ return new slouch.ExcludeDesignDocsIterator(slouch.doc.all(utils.createdDB, { | ||
})).each(function (doc) { | ||
docs.push({ | ||
thing: doc.doc.thing | ||
}); | ||
// We cannot guarantee the order when our DB has multiple nodes, but this doesn't matter as | ||
// all we care about is that we didn't receive the design doc. | ||
docs[doc.doc.thing] = true; | ||
}); | ||
}).then(function () { | ||
docs.should.eql([{ | ||
thing: 'play' | ||
}, { | ||
thing: 'write' | ||
}]); | ||
docs.should.eql({ | ||
play: true, | ||
write: true | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -40,2 +40,36 @@ 'use strict'; | ||
it('only role can view', function () { | ||
return slouch.security.onlyRoleCanView(utils.createdDB, 'role').then(function () { | ||
return slouch.security.get(utils.createdDB); | ||
}).then(function (_security) { | ||
return _security.should.eql({ | ||
'admins': { | ||
'names': ['_admin'], | ||
'roles': [] | ||
}, | ||
'members': { | ||
'names': [], | ||
'roles': ['role'] | ||
} | ||
}); | ||
}); | ||
}); | ||
it('only user can view', function () { | ||
return slouch.security.onlyUserCanView(utils.createdDB, 'user').then(function () { | ||
return slouch.security.get(utils.createdDB); | ||
}).then(function (_security) { | ||
return _security.should.eql({ | ||
'admins': { | ||
'names': ['_admin'], | ||
'roles': [] | ||
}, | ||
'members': { | ||
'names': ['user'], | ||
'roles': [] | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |
# Testing | ||
## Set up dev enviornment | ||
## Set up | ||
### Option A: Automated setup via Vagrant | ||
This option will automatically install an isolated dev env that you can destroy or rebuild at any point. | ||
- Install Vagrant (http://www.vagrantup.com) and VirtualBox (https://www.virtualbox.org) | ||
- $ git clone https://github.com/redgeoff/node-couchdb-vagrant.git | ||
- $ cd node-couchdb-vagrant | ||
- $ vagrant up | ||
- $ vagrant ssh | ||
- $ git clone https://github.com/redgeoff/slouch | ||
- $ cd slouch | ||
- $ npm install | ||
- $ npm run test | ||
### Option B: Running CouchDB in Docker | ||
Install CouchDB locally. You can easily run CouchDB via docker with: | ||
@@ -14,3 +29,8 @@ | ||
### Option C: Install CouchDB manually | ||
- Visit http://couchdb.apache.org and install CouchDB | ||
- Make sure to create the missing system DBs: _users, _global_changes, _replicators, e.g. see [run-couchdb-docker.sh](https://github.com/redgeoff/slouch/blob/master/run-couchdb-docker.sh#L22) | ||
- Make sure to set the admin username to _admin_ and the admin password to _admin_ | ||
## Resetting the DB | ||
@@ -25,2 +45,7 @@ | ||
## Beautify | ||
We use [beautify-proj](https://github.com/delta-db/beautify-proj) to beautify all of our code. This helps us to keep our coding style standardized. If the `assert-beautified` test fails then you'll want to run `npm run beautify` and then commit the changes. | ||
## Test in node | ||
@@ -27,0 +52,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
3314
30
220145
61
+ Addedsporks@0.0.3(transitive)
- Removedsporks@0.0.2(transitive)
Updatedsporks@^0.0.3