Created
May 20, 2016 09:19
-
-
Save slaskis/93ff78e4286757f742ec8bddc1c337d3 to your computer and use it in GitHub Desktop.
A PouchDB-like HTTP interface which allows setting defaults and a `baseUrl` for easy CouchDB.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var debug = require('debug')('poachdb'); | |
var http = require('request'); | |
var merge = require('deep-extend'); | |
module.exports = PoachDB; | |
function PoachDB(name, options) { | |
debug('PoachDB(%s, %j)', name, options); | |
this.name = name; | |
this.options = options || {}; | |
} | |
PoachDB.prototype.get = function get(key, options) { | |
return this.request({ | |
uri: '/' + key, | |
qs: options || {} | |
}); | |
}; | |
PoachDB.prototype.put = function put(key, doc, options) { | |
if (typeof key === 'object') { | |
options = doc; | |
doc = key; | |
key = doc._id; | |
} | |
return this.request({ | |
method: 'put', | |
uri: '/' + key, | |
body: doc, | |
qs: options || {} | |
}); | |
}; | |
PoachDB.prototype.post = function post(doc, options) { | |
return this.put(uuid(), doc, options); | |
}; | |
PoachDB.prototype.remove = function remove(key, doc, options) { | |
if (typeof key === 'object') { | |
options = doc; | |
doc = key; | |
key = doc._id; | |
} | |
return this.request({ | |
method: 'delete', | |
uri: '/' + key, | |
qs: merge({}, options, { | |
rev: doc._rev | |
}) | |
}, options); | |
}; | |
PoachDB.prototype.query = function query(path, options) { | |
var parts = path.split('/'); | |
var view = parts.pop(); | |
var ddoc = parts.pop(); | |
return this.request({ | |
uri: view ? ('/_design/' + ddoc + '/_view/' + view) : ('/' + path), | |
qs: options || {} | |
}); | |
}; | |
PoachDB.prototype.list = function listQuery(path, options) { | |
var parts = path.split('/'); | |
var view = parts.pop(); | |
var list = parts.pop(); | |
var ddoc = parts.pop(); | |
return this.request({ | |
uri: ['_design', ddoc, '_list', list, view].join('/'), | |
qs: options || {} | |
}); | |
}; | |
PoachDB.prototype.info = function info() { | |
return this.request({ | |
uri: '/' | |
}); | |
}; | |
PoachDB.prototype.allDocs = function allDocs(options) { | |
return this.request({ | |
uri: '/_all_docs', | |
qs: options | |
}); | |
}; | |
PoachDB.prototype.bulkDocs = function bulkDocs(docs, options) { | |
return this.request({ | |
method: 'post', | |
uri: '/_bulk_docs', | |
qs: options, | |
body: { | |
docs: docs | |
} | |
}); | |
}; | |
PoachDB.prototype.upsert = function upsert(key, diff) { | |
debug('upsert(%j)', key); | |
var db = this; | |
return this.get(key) | |
.then(function(doc) { | |
return doc; | |
}).catch(function(err) { | |
// ignoring 404s (it's a new doc) | |
if (err.status !== 404) { | |
return Promise.reject(err); | |
} | |
return {}; | |
}).then(function(doc) { | |
var rev = doc._rev; | |
var newDoc = diff(doc) || doc; | |
if (newDoc === doc) { | |
return doc; | |
} | |
newDoc._id = key; | |
newDoc._rev = rev; | |
return db.put(newDoc).catch(function(err) { | |
// conflict, try upsert again | |
if (err.status === 409) { | |
return upsert(key, diff); | |
} | |
return Promise.reject(err); | |
}); | |
}); | |
}; | |
PoachDB.prototype.request = function request(options) { | |
debug('request(%j)', options); | |
// strip double slashes | |
options.uri = ('/' + this.name + '/' + (options.uri || '')).replace(/\/\/+/g, '/'); | |
return PoachDB.ajax(merge({}, this.options, options)); | |
}; | |
PoachDB.defaults = function factory(prefix, defaults) { | |
defaults = defaults || {}; | |
if (typeof prefix === 'object') { | |
defaults = prefix; | |
} | |
if (typeof prefix === 'string') { | |
defaults.baseUrl = prefix; | |
} | |
return function clone(name, options) { | |
return new PoachDB(name, merge({}, defaults, options)); | |
}; | |
}; | |
PoachDB.ajax = function ajax(options, fn) { | |
return new Promise(function ajaxPromise(resolve, reject) { | |
debug('ajax(%j)', options); | |
options.json = options.json || true; | |
http(options, function(err, res, body) { | |
if (res && (res.statusCode < 200 || res.statusCode >= 300)) { | |
console.warn('invalid status code: %s', res.statusCode, body, options); | |
body = body || {}; // when 500 body is undefined :/ | |
var msg = body.reason || ('invalid status code:' + res.statusCode); | |
err = new Error(msg); | |
err.name = body.error; | |
err.status = res.statusCode; | |
err.reason = body.reason; | |
err.message = body.reason; | |
} | |
if (err) { | |
if (fn) { | |
return fn(err); | |
} else { | |
return reject(err); | |
} | |
} | |
if (fn) { | |
return fn(null, body, res); | |
} else { | |
return resolve(body); | |
} | |
}); | |
}); | |
}; | |
var chars = ( | |
'0123456789' + | |
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + | |
'abcdefghijklmnopqrstuvwxyz' | |
).split(''); | |
function getValue(radix) { | |
return 0 | Math.random() * radix; | |
} | |
function uuid(len, radix) { | |
radix = radix || chars.length; | |
var out = ''; | |
var i = -1; | |
if (len) { | |
// Compact form | |
while (++i < len) { | |
out += chars[getValue(radix)]; | |
} | |
return out; | |
} | |
// rfc4122, version 4 form | |
// Fill in random data. At i==19 set the high bits of clock sequence as | |
// per rfc4122, sec. 4.1.5 | |
while (++i < 36) { | |
switch (i) { | |
case 8: | |
case 13: | |
case 18: | |
case 23: | |
out += '-'; | |
break; | |
case 19: | |
out += chars[(getValue(16) & 0x3) | 0x8]; | |
break; | |
default: | |
out += chars[getValue(16)]; | |
} | |
} | |
return out; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment