app.dbWrapper.registerWrapper(function (name, db, next) { //'fix' the PouchDB api (support opts arg everywhere) function noop(orig, args) { return orig(); } // TODO: expand wrappers.installWrapperMethods(db, {info: noop}); return next(); });
return utils.nodify(Promise.resolve().then(function () { // add wrappers and make system db if (!info.isOnlineAuthDB) { wrappers.installWrapperMethods(args.db, writeWrappers); systemDB.installSystemDBProtection(args.db); // store validation doc return args.db.put(designDoc); } }).catch(function (err) {
app.dbWrapper.registerWrapper(function (name, db, next) { //'fix' the PouchDB api (support opts arg everywhere) function noop(orig, args) { return orig(); } var wrapperMethods = {}; ['info', 'removeAttachment'].forEach(function (name) { wrapperMethods[name] = noop; }); wrappers.installWrapperMethods(db, wrapperMethods); return next(); });
app.dbWrapper.registerWrapper(function (name, db, next) { // db.info() should just return the non-uri encoded db name wrappers.installWrapperMethods(db, { info: function (orig, args) { return orig().then(function (info) { info.db_name = decodeURIComponent(info.db_name); return info; }); } }); return next(); });
exports.installSizeWrapper = function () { var db = this; wrappers.installWrapperMethods(db, { info: function (orig, args) { var resp; return orig().then(function (info) { resp = info; return exports.getDiskSize.call(db); }).then(function (diskSize) { resp.disk_size = diskSize; return resp; }).catch(function () { //surpress - .info() should keep functioning if for some reason //it's impossible to get the disk_size. return resp; }); } }); };
exports.enableUndo = function (options) { if (!options) { options = {}; } if (!options.hasOwnProperty('limit')) { options.limit = 100; } var db = this, PouchDB = this.constructor, Promise = PouchDB.utils.Promise, uuid = PouchDB.utils.uuid, error = function (options) { var error = new Error(options.reason); error.status = options.status; error.error = options.error; error.reason = options.reason; return error; }, origBulkDocs = db.bulkDocs; var wrapUndo = function (orig, args) { var docs = args.docs; return orig().then(function (result) { var undoId = uuid(); return db.get('_local/_undo').catch(function (err) { if (err.status !== 404) { throw err; } return { _id: '_local/_undo', history: [], undos: {} }; }).then(function (undoDoc) { undoDoc.history.push(undoId); undoDoc.undos[undoId] = result.filter(function (row) { return row.ok; }).map(function (row, i) { return { id: row.id, oldRev: docs[i]._rev, newRev: row.rev }; }); if (undoDoc.history.length > options.limit) { undoDoc.history.slice(0, -options.limit).forEach(function (undoId) { delete undoDoc.undos[undoId]; undoDoc.history.shift(); }); } return origBulkDocs.call(db, [undoDoc]); // FIXME: pouchdb bug? doesn't throw conflict if rev is incorrect }).then(function () { return result.map(function (row) { row.undoId = undoId; return row; }); }); }); }; db.undo = function (undoId) { return db.get('_local/_undo').then(function (undoDoc) { var revisions = undoDoc.undos[undoId]; if (typeof revisions === 'undefined') { throw error({ status: 404, error: 'not_found', reason: 'Undo with that ID not found' }); } return Promise.all(revisions.map(function (revision) { // get latest revision return db.get(revision.id, { open_revs: 'all' }).then(function (doc) { var latestRev = doc[0].ok._rev; // HACK: this will stop undo working where there are unresolved conflicts if (revision.newRev !== latestRev) { throw error({ status: 409, error: 'conflict', reason: 'The document has changed since this undoID was issued' }); } return db.get(revision.id, { rev: revision.oldRev }).then(function (doc) { if (!revision.oldRev) { doc._deleted = true; } doc._rev = revision.newRev; return doc; }); }); })).then(function (docs) { return origBulkDocs.call(db, docs); }).then(function () { delete undoDoc.undos[undoId]; return origBulkDocs.call(db, [undoDoc]); }); }).then(function () { return { id: undoId, ok: true }; }); }; db.clearUndo = function () { return db.get('_local/_undo').then(function (doc) { doc._deleted = true; return origBulkDocs.call(db, [doc]); }).catch(function (err) { if (err.status !== 404) { throw err; } // already deleted }); }; wrappers.installWrapperMethods(db, { bulkDocs: wrapUndo }); };
exports.transform = exports.filter = function transform(config) { var db = this; var incoming = function (doc) { if (!isUntransformable(doc) && config.incoming) { return config.incoming(utils.clone(doc)); } return doc; }; var outgoing = function (doc) { if (!isUntransformable(doc) && config.outgoing) { return config.outgoing(utils.clone(doc)); } return doc; }; var handlers = {}; if (db.type() === 'http') { handlers.put = function (orig, args) { args.doc = incoming(args.doc); return orig(); }; handlers.query = function (orig) { return orig().then(function (res) { res.rows.forEach(function (row) { if (row.doc) { row.doc = outgoing(row.doc); } }); return res; }); }; } handlers.get = function (orig) { return orig().then(function (res) { if (Array.isArray(res)) { // open_revs style, it's a list of docs res.forEach(function (doc) { if (doc.ok) { doc.ok = outgoing(doc.ok); } }); } else { res = outgoing(res); } return res; }); }; handlers.bulkDocs = function (orig, args) { for (var i = 0; i < args.docs.length; i++) { args.docs[i] = incoming(args.docs[i]); } return orig(); }; handlers.allDocs = function (orig) { return orig().then(function (res) { res.rows.forEach(function (row) { if (row.doc) { row.doc = outgoing(row.doc); } }); return res; }); }; handlers.changes = function (orig, args) { function modifyChange(change) { if (change.doc) { change.doc = outgoing(change.doc); } return change; } function modifyChanges(res) { res.results = res.results.map(modifyChange); return res; } if (args.options.complete) { var origComplete = args.options.complete; args.options.complete = function (err, res) { /* istanbul ignore next */ if (err) { return origComplete(err); } origComplete(null, modifyChanges(res)); }; } var changes = orig(); // override some events var origOn = changes.on; changes.on = function (event, listener) { if (event === 'change') { return origOn.apply(changes, [event, function (change) { listener(modifyChange(change)); }]); } else if (event === 'complete') { return origOn.apply(changes, [event, function (res) { listener(modifyChanges(res)); }]); } return origOn.apply(changes, [event, listener]); }; var origThen = changes.then; changes.then = function (resolve, reject) { return origThen.apply(changes, [function (res) { resolve(modifyChanges(res)); }, reject]); }; return changes; }; wrappers.installWrapperMethods(db, handlers); };
exports.transform = exports.filter = function transform(config) { var db = this; var incoming = function (doc) { if (!isUntransformable(doc) && config.incoming) { return config.incoming(utils.clone(doc)); } return doc; }; var outgoing = function (doc) { if (!isUntransformable(doc) && config.outgoing) { return config.outgoing(utils.clone(doc)); } return doc; }; var handlers = {}; if (db.type() === 'http') { handlers.query = function (orig) { var none = {}; return orig().then(function (res) { return utils.Promise.all(res.rows.map(function (row) { if (row.doc) { return outgoing(row.doc); } return none; })).then(function (resp) { resp.forEach(function (doc, i) { if (doc === none) { return; } res.rows[i].doc = doc; }); return res; }); }); }; } handlers.get = function (orig) { return orig().then(function (res) { if (Array.isArray(res)) { var none = {}; // open_revs style, it's a list of docs return utils.Promise.all(res.map(function (row) { if (row.ok) { return outgoing(row.ok); } return none; })).then(function (resp) { resp.forEach(function (doc, i) { if (doc === none) { return; } res[i].ok = doc; }); return res; }); } else { return outgoing(res); } }); }; handlers.bulkDocs = function (orig, args) { for (var i = 0; i < args.docs.length; i++) { args.docs[i] = incoming(args.docs[i]); } return Promise.all(args.docs).then(function (docs) { args.docs = docs; return orig(); }); }; handlers.allDocs = function (orig) { return orig().then(function (res) { var none = {}; return utils.Promise.all(res.rows.map(function (row) { if (row.doc) { return outgoing(row.doc); } return none; })).then(function (resp) { resp.forEach(function (doc, i) { if (doc === none) { return; } res.rows[i].doc = doc; }); return res; }); }); }; handlers.bulkGet = function (orig) { return orig().then(function (res) { var none = {}; return utils.Promise.all(res.results.map(function (result) { if (result.id && result.docs && Array.isArray(result.docs)) { return { docs: result.docs.map(function(doc) { if (doc.ok) { return {ok: outgoing(doc.ok)}; } else { return doc; } }), id: result.id }; } else { return none; } })).then(function (results) { return {results: results}; }); }); }; handlers.changes = function (orig) { function modifyChange(change) { if (change.doc) { return utils.Promise.resolve(outgoing(change.doc)).then(function (doc) { change.doc = doc; return change; }); } return utils.Promise.resolve(change); } function modifyChanges(res) { if (res.results) { return utils.Promise.all(res.results.map(modifyChange)).then(function (results) { res.results = results; return res; }); } return utils.Promise.resolve(res); } var changes = orig(); // override some events var origOn = changes.on; changes.on = function (event, listener) { if (event === 'change') { return origOn.apply(changes, [event, function (change) { modifyChange(change).then(function (resp) { immediate(function () { listener(resp); }); }); }]); } else if (event === 'complete') { return origOn.apply(changes, [event, function (res) { modifyChanges(res).then(function (resp) { process.nextTick(function () { listener(resp); }); }); }]); } return origOn.apply(changes, [event, listener]); }; var origThen = changes.then; changes.then = function (resolve, reject) { return origThen.apply(changes, [function (res) { return modifyChanges(res).then(resolve, reject); }, reject]); }; return changes; }; wrappers.installWrapperMethods(db, handlers); };