Code coverage report for lib/replicate/getDocs.js

Statements: 97.96% (48 / 49)      Branches: 88.89% (16 / 18)      Functions: 100% (15 / 15)      Lines: 97.96% (48 / 49)      Ignored: none     

All files » lib/replicate/ » getDocs.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113    1 1 1   1   1 5404                 1 2114   2114   1 450           450 450     450 1646 1646           1 430   430 430 450   450     430 450       1 2082 2082     1 2527     1     1616       1592 8   1584 2538     95       2443 2443         1     2114 2905 2905   2114 1616       1 2082     2114           1
'use strict';
 
var utils = require('./../utils');
var clone = utils.clone;
var Promise = utils.Promise;
 
var MAX_SIMULTANEOUS_REVS = 50;
 
function isGenOne(rev) {
  return /^1-/.test(rev);
}
 
//
// 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, diffs, state) {
  diffs = clone(diffs); // we do not need to modify this
 
  var resultDocs = [];
 
  function fetchMissingRevs(id, missingRevs) {
    var opts = {
      revs: true,
      open_revs: missingRevs,
      attachments: true,
      binary: true
    };
    return src.get(id, opts).then(function (docs) {
      Iif (state.cancelled) {
        throw new Error('cancelled');
      }
      docs.forEach(function (doc) {
        Eif (doc.ok) {
          resultDocs.push(doc.ok);
        }
      });
    });
  }
 
  function processDiffDoc(id) {
    var missing = diffs[id].missing;
    // avoid url too long error by batching
    var missingBatches = [];
    for (var i = 0; i < missing.length; i += MAX_SIMULTANEOUS_REVS) {
      var missingBatch = missing.slice(i,
        Math.min(missing.length, i + MAX_SIMULTANEOUS_REVS));
      missingBatches.push(missingBatch);
    }
 
    return Promise.all(missingBatches.map(function (missingRevs) {
      return fetchMissingRevs(id, missingRevs);
    }));
  }
 
  function getAllDocs() {
    var diffKeys = Object.keys(diffs);
    return Promise.all(diffKeys.map(processDiffDoc));
  }
 
  function hasAttachments(doc) {
    return doc._attachments && Object.keys(doc._attachments).length > 0;
  }
 
  function fetchRevisionOneDocs(ids) {
    // Optimization: fetch gen-1 docs and attachments in
    // a single request using _all_docs
    return src.allDocs({
      keys: ids,
      include_docs: true
    }).then(function (res) {
      if (state.cancelled) {
        throw new Error('cancelled');
      }
      res.rows.forEach(function (row) {
        if (row.deleted || !row.doc || !isGenOne(row.value.rev) ||
            hasAttachments(row.doc)) {
          // if any of these conditions apply, we need to fetch using get()
          return;
        }
 
        // the doc we got back from allDocs() is sufficient
        resultDocs.push(row.doc);
        delete diffs[row.id];
      });
    });
  }
 
  function getRevisionOneDocs() {
    // filter out the generation 1 docs and get them
    // leaving the non-generation one docs to be got otherwise
    var ids = Object.keys(diffs).filter(function (id) {
      var missing = diffs[id].missing;
      return missing.length === 1 && isGenOne(missing[0]);
    });
    if (ids.length > 0) {
      return fetchRevisionOneDocs(ids);
    }
  }
 
  function returnDocs() {
    return resultDocs;
  }
 
  return Promise.resolve()
    .then(getRevisionOneDocs)
    .then(getAllDocs)
    .then(returnDocs);
}
 
module.exports = getDocs;