Exemple #1
0
      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(function (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;
          });
        }));
      }))
Exemple #2
0
function allDocsKeysQuery(api, opts, callback) {
  var keys =  ('limit' in opts) ?
      opts.keys.slice(opts.skip, opts.limit + opts.skip) :
      (opts.skip > 0) ? opts.keys.slice(opts.skip) : opts.keys;
  if (opts.descending) {
    keys.reverse();
  }
  if (!keys.length) {
    return api._allDocs({limit: 0}, callback);
  }
  var finalResults = {
    offset: opts.skip
  };
  return Promise.all(keys.map(function (key) {
    var subOpts = extend({key: key, deleted: 'ok'}, opts);
    ['limit', 'skip', 'keys'].forEach(function (optKey) {
      delete subOpts[optKey];
    });
    return new Promise(function (resolve, reject) {
      api._allDocs(subOpts, function (err, res) {
        /* istanbul ignore if */
        if (err) {
          return reject(err);
        }
        finalResults.total_rows = res.total_rows;
        resolve(res.rows[0] || {key: key, error: 'not_found'});
      });
    });
  })).then(function (results) {
    finalResults.rows = results;
    return finalResults;
  });
}
 .then((ddb) => {
   var ddbAlso = new PouchDB('all-changes', {db: require('memdown')})
   return Promise.all([
     ddb.allDocs({include_docs: true}),
     ddbAlso.allDocs({include_docs: true})
   ])
 })
Exemple #4
0
 function fetchAttachments(doc) {
   var atts = doc._attachments;
   var filenames = atts && Object.keys(atts);
   if (!atts || !filenames.length) {
     return;
   }
   // we fetch these manually in separate XHRs, because
   // Sync Gateway would normally send it back as multipart/mixed,
   // which we cannot parse. Also, this is more efficient than
   // receiving attachments as base64-encoded strings.
   return Promise.all(filenames.map(function (filename) {
     var att = atts[filename];
     var path = encodeDocId(doc._id) + '/' + encodeAttachmentId(filename) +
       '?rev=' + doc._rev;
     return ajaxPromise(opts, {
       method: 'GET',
       url: genDBUrl(host, path),
       binary: true
     }).then(function (blob) {
       if (opts.binary) {
         return blob;
       }
       return new Promise(function (resolve) {
         blufferToBase64(blob, resolve);
       });
     }).then(function (data) {
       delete att.stub;
       delete att.length;
       att.data = data;
     });
   }));
 }
Exemple #5
0
 return db.allDocs(opts).then(function (res) {
   var viewsToStatus = {};
   res.rows.forEach(function (row) {
     var ddocName = row.key.substring(8);
     Object.keys(docsToViews[row.key]).forEach(function (viewName) {
       var fullViewName = ddocName + '/' + viewName;
       /* istanbul ignore if */
       if (!metaDoc.views[fullViewName]) {
         // new format, without slashes, to support PouchDB 2.2.0
         // migration test in pouchdb's browser.migration.js verifies this
         fullViewName = viewName;
       }
       var viewDBNames = Object.keys(metaDoc.views[fullViewName]);
       // design doc deleted, or view function nonexistent
       var statusIsGood = row.doc && row.doc.views &&
         row.doc.views[viewName];
       viewDBNames.forEach(function (viewDBName) {
         viewsToStatus[viewDBName] =
           viewsToStatus[viewDBName] || statusIsGood;
       });
     });
   });
   var dbsToDelete = Object.keys(viewsToStatus).filter(
     function (viewDBName) { return !viewsToStatus[viewDBName]; });
   var destroyPromises = dbsToDelete.map(function (viewDBName) {
     return sequentialize(getQueue(viewDBName), function () {
       return new db.constructor(viewDBName, db.__opts).destroy();
     })();
   });
   return Promise.all(destroyPromises).then(function () {
     return {ok: true};
   });
 });
Exemple #6
0
  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) {
Exemple #7
0
 function fetchAllAttachments(docOrDocs) {
   if (Array.isArray(docOrDocs)) {
     return Promise.all(docOrDocs.map(function (doc) {
       if (doc.ok) {
         return fetchAttachments(doc.ok);
       }
     }));
   }
   return fetchAttachments(docOrDocs);
 }
Exemple #8
0
 .then(function (lastSeqDoc) {
   var docIds = Object.keys(docIdsToChangesAndEmits);
   return Promise.all(docIds.map(function (docId) {
     return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
   })).then(function (listOfDocsToPersist) {
     var docsToPersist = flatten(listOfDocsToPersist);
     lastSeqDoc.seq = seq;
     docsToPersist.push(lastSeqDoc);
     // write all docs in a single operation, update the seq once
     return view.db.bulkDocs({docs : docsToPersist});
   });
 });
Exemple #9
0
 function onComplete(resp) {
   var lastSeq = resp.last_seq;
   Promise.all(promises).then(function () {
     return upsert(self, '_local/compaction', function deltaFunc(doc) {
       if (!doc.last_seq || doc.last_seq < lastSeq) {
         doc.last_seq = lastSeq;
         return doc;
       }
       return false; // somebody else got here first, don't update
     });
   }).then(function () {
     callback(null, {ok: true});
   }).catch(callback);
 }
module.exports = function buildRequestObject(db, pathEnd, options) {
  var infoPromise = db.info();
  var pathPromise = infoPromise.then(function (info) {
    pathEnd.unshift(encodeURIComponent(info.db_name));
    return normalizePath(pathEnd);
  });
  var userCtxPromise = infoPromise.then(buildUserContextObject);

  return Promise.all([pathPromise, infoPromise, userCtxPromise]).then(function (args) {
    args.push(getHost(db));
    args.push(uuid());
    args.push(options);
    return actuallyBuildRequestObject.apply(null, args);
  });
};
Exemple #11
0
function preprocessAttachments(doc) {
  if (!doc._attachments || !Object.keys(doc._attachments)) {
    return Promise.resolve();
  }

  return Promise.all(Object.keys(doc._attachments).map(function (key) {
    var attachment = doc._attachments[key];
    if (attachment.data && typeof attachment.data !== 'string') {
      return new Promise(function (resolve) {
        blufferToBase64(attachment.data, resolve);
      }).then(function (b64) {
        attachment.data = b64;
      });
    }
  }));
}
Exemple #12
0
function fetchAttachments(results, stores, opts) {
  var atts = [];
  results.forEach(function (row) {
    if (!(row.doc && row.doc._attachments)) {
      return;
    }
    var attNames = Object.keys(row.doc._attachments);
    attNames.forEach(function (attName) {
      var att = row.doc._attachments[attName];
      if (!('data' in att)) {
        atts.push(att);
      }
    });
  });

  return Promise.all(atts.map(function (att) {
    return fetchAttachment(att, stores, opts);
  }));
}
Exemple #13
0
 self.get('_local/_pouch_dependentDbs', function (err, localDoc) {
   if (err) {
     /* istanbul ignore if */
     if (err.status !== 404) {
       return callback(err);
     } else { // no dependencies
       return destroyDb();
     }
   }
   var dependentDbs = localDoc.dependentDbs;
   var PouchDB = self.constructor;
   var deletedMap = Object.keys(dependentDbs).map(function (name) {
     // use_prefix is only false in the browser
     /* istanbul ignore next */
     var trueName = usePrefix ?
       name.replace(new RegExp('^' + PouchDB.prefix), '') : name;
     return new PouchDB(trueName, self.__opts).destroy();
   });
   Promise.all(deletedMap).then(destroyDb, callback);
 });
 var derivator = function (params) {
   return Promise.all([
     upsert(params.db, 'sum', function (doc) {
       doc.val = doc.val || 0
       doc.val -= params.change.doc._rev.slice(0, 2) !== '1-' ? params.change.doc.val : 0
       if (!params.change.deleted) {
         doc.val += params.change.doc.val
       }
       return doc
     }),
     upsert(params.db, 'count', function (doc) {
       doc.val = doc.val || 0
       if (params.change.deleted) {
         doc.val -= 1
       } else if (params.change.doc._rev.slice(0, 2) === '1-') {
         doc.val += 1
       }
       return doc
     })
   ])
 }
Exemple #15
0
    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(function (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(flatten(results).filter(Boolean));
      });
    });
Exemple #16
0
function queryViewInQueue(view, opts) {
  var totalRows;
  var shouldReduce = view.reduceFun && opts.reduce !== false;
  var skip = opts.skip || 0;
  if (typeof opts.keys !== 'undefined' && !opts.keys.length) {
    // equivalent query
    opts.limit = 0;
    delete opts.keys;
  }

  function fetchFromView(viewOpts) {
    viewOpts.include_docs = true;
    return view.db.allDocs(viewOpts).then(function (res) {
      totalRows = res.total_rows;
      return res.rows.map(function (result) {

        // implicit migration - in older versions of PouchDB,
        // we explicitly stored the doc as {id: ..., key: ..., value: ...}
        // this is tested in a migration test
        /* istanbul ignore next */
        if ('value' in result.doc && typeof result.doc.value === 'object' &&
            result.doc.value !== null) {
          var keys = Object.keys(result.doc.value).sort();
          // this detection method is not perfect, but it's unlikely the user
          // emitted a value which was an object with these 3 exact keys
          var expectedKeys = ['id', 'key', 'value'];
          if (!(keys < expectedKeys || keys > expectedKeys)) {
            return result.doc.value;
          }
        }

        var parsedKeyAndDocId = parseIndexableString(result.doc._id);
        return {
          key: parsedKeyAndDocId[0],
          id: parsedKeyAndDocId[1],
          value: ('value' in result.doc ? result.doc.value : null)
        };
      });
    });
  }

  function onMapResultsReady(rows) {
    var finalResults;
    if (shouldReduce) {
      finalResults = reduceView(view, rows, opts);
    } else {
      finalResults = {
        total_rows: totalRows,
        offset: skip,
        rows: rows
      };
    }
    if (opts.include_docs) {
      var docIds = uniq(rows.map(rowToDocId));

      return view.sourceDB.allDocs({
        keys: docIds,
        include_docs: true,
        conflicts: opts.conflicts,
        attachments: opts.attachments,
        binary: opts.binary
      }).then(function (allDocsRes) {
        var docIdsToDocs = {};
        allDocsRes.rows.forEach(function (row) {
          if (row.doc) {
            docIdsToDocs['$' + row.id] = row.doc;
          }
        });
        rows.forEach(function (row) {
          var docId = rowToDocId(row);
          var doc = docIdsToDocs['$' + docId];
          if (doc) {
            row.doc = doc;
          }
        });
        return finalResults;
      });
    } else {
      return finalResults;
    }
  }

  if (typeof opts.keys !== 'undefined') {
    var keys = opts.keys;
    var fetchPromises = keys.map(function (key) {
      var viewOpts = {
        startkey : toIndexableString([key]),
        endkey   : toIndexableString([key, {}])
      };
      return fetchFromView(viewOpts);
    });
    return Promise.all(fetchPromises).then(flatten).then(onMapResultsReady);
  } else { // normal query, no 'keys'
    var viewOpts = {
      descending : opts.descending
    };
    if (opts.start_key) {
        opts.startkey = opts.start_key;
    }
    if (opts.end_key) {
        opts.endkey = opts.end_key;
    }
    if (typeof opts.startkey !== 'undefined') {
      viewOpts.startkey = opts.descending ?
        toIndexableString([opts.startkey, {}]) :
        toIndexableString([opts.startkey]);
    }
    if (typeof opts.endkey !== 'undefined') {
      var inclusiveEnd = opts.inclusive_end !== false;
      if (opts.descending) {
        inclusiveEnd = !inclusiveEnd;
      }

      viewOpts.endkey = toIndexableString(
        inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]);
    }
    if (typeof opts.key !== 'undefined') {
      var keyStart = toIndexableString([opts.key]);
      var keyEnd = toIndexableString([opts.key, {}]);
      if (viewOpts.descending) {
        viewOpts.endkey = keyStart;
        viewOpts.startkey = keyEnd;
      } else {
        viewOpts.startkey = keyStart;
        viewOpts.endkey = keyEnd;
      }
    }
    if (!shouldReduce) {
      if (typeof opts.limit === 'number') {
        viewOpts.limit = opts.limit;
      }
      viewOpts.skip = skip;
    }
    return fetchFromView(viewOpts).then(onMapResultsReady);
  }
}
Exemple #17
0
function Sync(src, target, opts, callback) {
  var self = this;
  this.canceled = false;

  var optsPush = opts.push ? extend({}, opts, opts.push) : opts;
  var optsPull = opts.pull ? extend({}, opts, opts.pull) : opts;

  this.push = replicate(src, target, optsPush);
  this.pull = replicate(target, src, optsPull);

  this.pushPaused = true;
  this.pullPaused = true;

  function pullChange(change) {
    self.emit('change', {
      direction: 'pull',
      change: change
    });
  }
  function pushChange(change) {
    self.emit('change', {
      direction: 'push',
      change: change
    });
  }
  function pushDenied(doc) {
    self.emit('denied', {
      direction: 'push',
      doc: doc
    });
  }
  function pullDenied(doc) {
    self.emit('denied', {
      direction: 'pull',
      doc: doc
    });
  }
  function pushPaused() {
    self.pushPaused = true;
    /* istanbul ignore if */
    if (self.pullPaused) {
      self.emit('paused');
    }
  }
  function pullPaused() {
    self.pullPaused = true;
    /* istanbul ignore if */
    if (self.pushPaused) {
      self.emit('paused');
    }
  }
  function pushActive() {
    self.pushPaused = false;
    /* istanbul ignore if */
    if (self.pullPaused) {
      self.emit('active', {
        direction: 'push'
      });
    }
  }
  function pullActive() {
    self.pullPaused = false;
    /* istanbul ignore if */
    if (self.pushPaused) {
      self.emit('active', {
        direction: 'pull'
      });
    }
  }

  var removed = {};

  function removeAll(type) { // type is 'push' or 'pull'
    return function (event, func) {
      var isChange = event === 'change' &&
        (func === pullChange || func === pushChange);
      var isDenied = event === 'denied' &&
        (func === pullDenied || func === pushDenied);
      var isPaused = event === 'paused' &&
        (func === pullPaused || func === pushPaused);
      var isActive = event === 'active' &&
        (func === pullActive || func === pushActive);

      if (isChange || isDenied || isPaused || isActive) {
        if (!(event in removed)) {
          removed[event] = {};
        }
        removed[event][type] = true;
        if (Object.keys(removed[event]).length === 2) {
          // both push and pull have asked to be removed
          self.removeAllListeners(event);
        }
      }
    };
  }

  if (opts.live) {
    this.push.on('complete', self.pull.cancel.bind(self.pull));
    this.pull.on('complete', self.push.cancel.bind(self.push));
  }

  this.on('newListener', function (event) {
    if (event === 'change') {
      self.pull.on('change', pullChange);
      self.push.on('change', pushChange);
    } else if (event === 'denied') {
      self.pull.on('denied', pullDenied);
      self.push.on('denied', pushDenied);
    } else if (event === 'active') {
      self.pull.on('active', pullActive);
      self.push.on('active', pushActive);
    } else if (event === 'paused') {
      self.pull.on('paused', pullPaused);
      self.push.on('paused', pushPaused);
    }
  });

  this.on('removeListener', function (event) {
    if (event === 'change') {
      self.pull.removeListener('change', pullChange);
      self.push.removeListener('change', pushChange);
    } else if (event === 'denied') {
      self.pull.removeListener('denied', pullDenied);
      self.push.removeListener('denied', pushDenied);
    } else if (event === 'active') {
      self.pull.removeListener('active', pullActive);
      self.push.removeListener('active', pushActive);
    } else if (event === 'paused') {
      self.pull.removeListener('paused', pullPaused);
      self.push.removeListener('paused', pushPaused);
    }
  });

  this.pull.on('removeListener', removeAll('pull'));
  this.push.on('removeListener', removeAll('push'));

  var promise = Promise.all([
    this.push,
    this.pull
  ]).then(function (resp) {
    var out = {
      push: resp[0],
      pull: resp[1]
    };
    self.emit('complete', out);
    if (callback) {
      callback(null, out);
    }
    self.removeAllListeners();
    return out;
  }, function (err) {
    self.cancel();
    if (callback) {
      // if there's a callback, then the callback can receive
      // the error event
      callback(err);
    } else {
      // if there's no callback, then we're safe to emit an error
      // event, which would otherwise throw an unhandled error
      // due to 'error' being a special event in EventEmitters
      self.emit('error', err);
    }
    self.removeAllListeners();
    if (callback) {
      // no sense throwing if we're already emitting an 'error' event
      throw err;
    }
  });

  this.then = function (success, err) {
    return promise.then(success, err);
  };

  this.catch = function (err) {
    return promise.catch(err);
  };
}
Exemple #18
0
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});
  }));
}
      }).map(function (row) {
        var obj = fromRawDoc(row.doc);

        foundObjects.get(type).set(JSON.stringify(obj.id), obj);

        // fetch all relations
        var subTasks = [];
        Object.keys(typeInfo.relations || {}).forEach(function (field) {
          var relationDef = typeInfo.relations[field];
          var relationType = Object.keys(relationDef)[0];
          var relatedType = relationDef[relationType];
          if (typeof relatedType !== 'string') {
            var relationOptions = relatedType.options;
            if (relationOptions && relationOptions.async) {
              return;
            }
            relatedType = relatedType.type;
          }
          if (relationType === 'belongsTo') {
            var relatedId = obj[field];
            if (typeof relatedId !== 'undefined') {
              subTasks.push(Promise.resolve().then(function () {

                // short-circuit if it's already in the foundObjects
                // else we could get caught in an infinite loop
                if (foundObjects.has(relatedType) &&
                    foundObjects.get(relatedType).has(JSON.stringify(relatedId))) {
                  return;
                }

                // signal that we need to fetch it
                return {
                  relatedType: relatedType,
                  relatedIds: [relatedId]
                };
              }));
            }
          } else { // hasMany
            var relatedIds = extend(true, [], obj[field]);
            if (typeof relatedIds !== 'undefined' && relatedIds.length) {
              subTasks.push(Promise.resolve().then(function () {

                // filter out all ids that are already in the foundObjects
                for (var i = relatedIds.length - 1; i >= 0; i--) {
                  var relatedId = relatedIds[i];
                  if (foundObjects.has(relatedType) &&
                      foundObjects.get(relatedType).has(JSON.stringify(relatedId))) {
                    delete relatedIds[i];
                  }
                }
                relatedIds = relatedIds.filter(function (relatedId) {
                  return typeof relatedId !== 'undefined';
                });

                // just return the ids and the types. We'll find them all
                // in a single bulk operation in order to minimize HTTP requests
                if (relatedIds.length) {
                  return {
                    relatedType: relatedType,
                    relatedIds: relatedIds
                  };
                }
              }));
            }
          }
        });
        return Promise.all(subTasks);
      });
Exemple #20
0
 setup().then(function () {
   return Promise.all(req.docs.map(preprocessAttachments));
 }).then(function () {