| import { clone, isRemote } from 'pouchdb-utils'; |
| |
| function fileHasChanged(localDoc, remoteDoc, filename) { |
| return !localDoc._attachments || |
| !localDoc._attachments[filename] || |
| localDoc._attachments[filename].digest !== remoteDoc._attachments[filename].digest; |
| } |
| |
| function getDocAttachments(db, doc) { |
| var filenames = Object.keys(doc._attachments); |
| return Promise.all(filenames.map(function (filename) { |
| return db.getAttachment(doc._id, filename, {rev: doc._rev}); |
| })); |
| } |
| |
| function getDocAttachmentsFromTargetOrSource(target, src, doc) { |
| var doCheckForLocalAttachments = isRemote(src) && !isRemote(target); |
| var filenames = Object.keys(doc._attachments); |
| |
| if (!doCheckForLocalAttachments) { |
| return getDocAttachments(src, doc); |
| } |
| |
| return target.get(doc._id).then(function (localDoc) { |
| return Promise.all(filenames.map(function (filename) { |
| if (fileHasChanged(localDoc, doc, filename)) { |
| return src.getAttachment(doc._id, filename); |
| } |
| |
| return target.getAttachment(localDoc._id, filename); |
| })); |
| }).catch(function (error) { |
| /* istanbul ignore if */ |
| if (error.status !== 404) { |
| throw error; |
| } |
| |
| return getDocAttachments(src, doc); |
| }); |
| } |
| |
| function createBulkGetOpts(diffs) { |
| var requests = []; |
| Object.keys(diffs).forEach(function (id) { |
| var missingRevs = diffs[id].missing; |
| missingRevs.forEach(function (missingRev) { |
| requests.push({ |
| id, |
| rev: missingRev |
| }); |
| }); |
| }); |
| |
| return { |
| docs: requests, |
| revs: true, |
| latest: true |
| }; |
| } |
| |
| // |
| // Fetch all the documents from the src as described in the "diffs", |
| // which is a mapping of docs IDs to revisions. If the state ever |
| // changes to "cancelled", then the returned promise will be rejected. |
| // Else it will be resolved with a list of fetched documents. |
| // |
| function getDocs(src, target, diffs, state) { |
| diffs = clone(diffs); // we do not need to modify this |
| |
| var resultDocs = [], |
| ok = true; |
| |
| function getAllDocs() { |
| |
| var bulkGetOpts = createBulkGetOpts(diffs); |
| |
| if (!bulkGetOpts.docs.length) { // optimization: skip empty requests |
| return; |
| } |
| |
| return src.bulkGet(bulkGetOpts).then(function (bulkGetResponse) { |
| /* istanbul ignore if */ |
| if (state.cancelled) { |
| throw new Error('cancelled'); |
| } |
| return Promise.all(bulkGetResponse.results.map(function (bulkGetInfo) { |
| return Promise.all(bulkGetInfo.docs.map(function (doc) { |
| var remoteDoc = doc.ok; |
| |
| if (doc.error) { |
| // when AUTO_COMPACTION is set, docs can be returned which look |
| // like this: {"missing":"1-7c3ac256b693c462af8442f992b83696"} |
| ok = false; |
| } |
| |
| if (!remoteDoc || !remoteDoc._attachments) { |
| return remoteDoc; |
| } |
| |
| return getDocAttachmentsFromTargetOrSource(target, src, remoteDoc).then((attachments) => { |
| var filenames = Object.keys(remoteDoc._attachments); |
| attachments.forEach(function (attachment, i) { |
| var att = remoteDoc._attachments[filenames[i]]; |
| delete att.stub; |
| delete att.length; |
| att.data = attachment; |
| }); |
| |
| return remoteDoc; |
| }); |
| })); |
| })) |
| |
| .then(function (results) { |
| resultDocs = resultDocs.concat(results.flat().filter(Boolean)); |
| }); |
| }); |
| } |
| |
| function returnResult() { |
| return { ok, docs:resultDocs }; |
| } |
| |
| return Promise.resolve() |
| .then(getAllDocs) |
| .then(returnResult); |
| } |
| |
| export default getDocs; |