Code coverage report for lib/replicate/get-docs.js

Statements: 96.23% (51 / 53)      Branches: 87.5% (21 / 24)      Functions: 100% (15 / 15)      Lines: 96.23% (51 / 53)      Ignored: none     

All files » lib/replicate/ » get-docs.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 114 115 116 117 118 119 120 121    1 1 1   1   1 5107                 1 2026   2026   1 329         329 329     329 1525 1525           1 309   309 309 329   329     309 329       1 2018 2018     1 2408 2408   2408     2408   2408     1     1389         1381     1381 2420     12       2408 2408         1     2026 2725 2725   2026 1389       1 2018     2026           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
    };
    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 hasValidAttachments(doc) {
    var atts = doc._attachments;
    var attNames = atts && Object.keys(atts);
 
    var hasAttachments = atts && attNames.length > 0;
    // These will be stubs in CouchDB < 1.6 because attachments
    // weren't supported in _all_docs
    var hasAttachmentData = hasAttachments && !atts[attNames[0]].stub;
 
    return !hasAttachments || hasAttachmentData;
  }
 
  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,
      attachments: true
    }).then(function (res) {
      Iif (state.cancelled) {
        throw new Error('cancelled');
      }
      res.rows.forEach(function (row) {
        if (row.deleted || !row.doc || !isGenOne(row.value.rev) ||
            !hasValidAttachments(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;