Bug 1333810: Update kinto.js version to 8.0.0, r=MattN a=gchang
authorEthan Glasser-Camp <eglassercamp@mozilla.com>
Fri, 27 Jan 2017 19:23:05 -0500
changeset 378451 9f50d03c4b010080f7dde1ac551f3bf0d764ab91
parent 378450 252e3d832683eeb0b7f5a49b504d31168cabdd7e
child 378452 7c112d81c854136a5ad758155f2b63555b8ac776
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, gchang
bugs1333810
milestone53.0a2
Bug 1333810: Update kinto.js version to 8.0.0, r=MattN a=gchang This allows us to transform deletes. See https://github.com/Kinto/kinto.js/pull/640/. MozReview-Commit-ID: 3EWcxW4Ny6s
services/common/kinto-offline-client.js
--- a/services/common/kinto-offline-client.js
+++ b/services/common/kinto-offline-client.js
@@ -15,17 +15,17 @@
 
 /*
  * This file is generated from kinto.js - do not modify directly.
  */
 
 this.EXPORTED_SYMBOLS = ["Kinto"];
 
 /*
- * Version 7.1.0 - a6f42f1
+ * Version 8.0.0 - 57d2836
  */
 
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Kinto = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
 /*
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -58,17 +58,17 @@ function _interopRequireDefault(obj) { r
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.importGlobalProperties(["fetch"]);
 const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {});
 const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
 // Use standalone kinto-http module landed in FFx.
-const { KintoHttpClient } = Cu.import("resource://services-common/kinto-http-client.js", {});
+const { KintoHttpClient } = Cu.import("resource://services-common/kinto-http-client.js");
 
 class Kinto extends _KintoBase2.default {
   constructor(options = {}) {
     const events = {};
     EventEmitter.decorate(events);
 
     const defaults = {
       events,
@@ -1065,17 +1065,17 @@ class Collection {
     this._name = name;
     this._lastModified = null;
 
     const DBAdapter = options.adapter || _IDB2.default;
     if (!DBAdapter) {
       throw new Error("No adapter provided");
     }
     const dbPrefix = options.dbPrefix || "";
-    const db = new DBAdapter(`${ dbPrefix }${ bucket }/${ name }`, options.adapterOptions);
+    const db = new DBAdapter(`${dbPrefix}${bucket}/${name}`, options.adapterOptions);
     if (!(db instanceof _base2.default)) {
       throw new Error("Unsupported adapter.");
     }
     // public properties
     /**
      * The db adapter instance
      * @type {BaseAdapter}
      */
@@ -1263,36 +1263,36 @@ class Collection {
   /**
    * Encodes a record.
    *
    * @param  {String} type   Either "remote" or "local".
    * @param  {Object} record The record object to encode.
    * @return {Promise}
    */
   _encodeRecord(type, record) {
-    if (!this[`${ type }Transformers`].length) {
+    if (!this[`${type}Transformers`].length) {
       return Promise.resolve(record);
     }
-    return (0, _utils.waterfall)(this[`${ type }Transformers`].map(transformer => {
+    return (0, _utils.waterfall)(this[`${type}Transformers`].map(transformer => {
       return record => transformer.encode(record);
     }), record);
   }
 
   /**
    * Decodes a record.
    *
    * @param  {String} type   Either "remote" or "local".
    * @param  {Object} record The record object to decode.
    * @return {Promise}
    */
   _decodeRecord(type, record) {
-    if (!this[`${ type }Transformers`].length) {
+    if (!this[`${type}Transformers`].length) {
       return Promise.resolve(record);
     }
-    return (0, _utils.waterfall)(this[`${ type }Transformers`].reverse().map(transformer => {
+    return (0, _utils.waterfall)(this[`${type}Transformers`].reverse().map(transformer => {
       return record => transformer.decode(record);
     }), record);
   }
 
   /**
    * Adds a record to the local database, asserting that none
    * already exist with this ID.
    *
@@ -1325,17 +1325,17 @@ class Collection {
     if (!options.synced && !options.useRecordId && record.hasOwnProperty("id")) {
       return reject("Extraneous Id; can't create a record having one set.");
     }
     const newRecord = _extends({}, record, {
       id: options.synced || options.useRecordId ? record.id : this.idSchema.generate(),
       _status: options.synced ? "synced" : "created"
     });
     if (!this.idSchema.validate(newRecord.id)) {
-      return reject(`Invalid Id: ${ newRecord.id }`);
+      return reject(`Invalid Id: ${newRecord.id}`);
     }
     return this.execute(txn => txn.create(newRecord), { preloadIds: [newRecord.id] }).catch(err => {
       if (options.useRecordId) {
         throw new Error("Couldn't create record. It may have been virtually deleted.");
       }
       throw err;
     });
   }
@@ -1358,17 +1358,17 @@ class Collection {
     // to pass the ID to preloadIds.
     if (typeof record !== "object") {
       return Promise.reject(new Error("Record is not an object."));
     }
     if (!record.hasOwnProperty("id")) {
       return Promise.reject(new Error("Cannot update a record missing id."));
     }
     if (!this.idSchema.validate(record.id)) {
-      return Promise.reject(new Error(`Invalid Id: ${ record.id }`));
+      return Promise.reject(new Error(`Invalid Id: ${record.id}`));
     }
 
     return this.execute(txn => txn.update(record, options), { preloadIds: [record.id] });
   }
 
   /**
    * Like {@link CollectionTransaction#upsert}, but wrapped in its own transaction.
    *
@@ -1381,17 +1381,17 @@ class Collection {
     // to pass the ID to preloadIds.
     if (typeof record !== "object") {
       return Promise.reject(new Error("Record is not an object."));
     }
     if (!record.hasOwnProperty("id")) {
       return Promise.reject(new Error("Cannot update a record missing id."));
     }
     if (!this.idSchema.validate(record.id)) {
-      return Promise.reject(new Error(`Invalid Id: ${ record.id }`));
+      return Promise.reject(new Error(`Invalid Id: ${record.id}`));
     }
 
     return this.execute(txn => txn.upsert(record), { preloadIds: [record.id] });
   }
 
   /**
    * Like {@link CollectionTransaction#get}, but wrapped in its own transaction.
    *
@@ -1614,17 +1614,17 @@ class Collection {
    *   the transaction
    *
    * @return {Promise} Resolves with the result of the given function
    *    when the transaction commits.
    */
   execute(doOperations, { preloadIds = [] } = {}) {
     for (let id of preloadIds) {
       if (!this.idSchema.validate(id)) {
-        return Promise.reject(Error(`Invalid Id: ${ id }`));
+        return Promise.reject(Error(`Invalid Id: ${id}`));
       }
     }
 
     return this.db.execute(transaction => {
       const txn = new CollectionTransaction(this, transaction);
       const result = doOperations(txn);
       txn.emitEvents();
       return result;
@@ -1675,20 +1675,17 @@ class Collection {
    */
   gatherLocalChanges() {
     var _this6 = this;
 
     return _asyncToGenerator(function* () {
       const unsynced = yield _this6.list({ filters: { _status: ["created", "updated"] }, order: "" });
       const deleted = yield _this6.list({ filters: { _status: "deleted" }, order: "" }, { includeDeleted: true });
 
-      const toSync = yield Promise.all(unsynced.data.map(_this6._encodeRecord.bind(_this6, "remote")));
-      const toDelete = yield Promise.all(deleted.data.map(_this6._encodeRecord.bind(_this6, "remote")));
-
-      return { toSync, toDelete };
+      return yield Promise.all(unsynced.data.concat(deleted.data).map(_this6._encodeRecord.bind(_this6, "remote")));
     })();
   }
 
   /**
    * Fetch remote changes, import them to the local database, and handle
    * conflicts according to `options.strategy`. Then, updates the passed
    * {@link SyncResultObject} with import results.
    *
@@ -1726,17 +1723,17 @@ class Collection {
         const exclude_id = options.exclude.slice(0, 50).map(function (r) {
           return r.id;
         }).join(",");
         filters = { exclude_id };
       }
       // First fetch remote changes from the server
       const { data, last_modified } = yield client.listRecords({
         // Since should be ETag (see https://github.com/Kinto/kinto.js/issues/356)
-        since: options.lastModified ? `${ options.lastModified }` : undefined,
+        since: options.lastModified ? `${options.lastModified}` : undefined,
         headers: options.headers,
         retry: options.retry,
         filters
       });
       // last_modified is the ETag header value (string).
       // For retro-compatibility with first kinto.js versions
       // parse it to integer.
       const unquoted = last_modified ? parseInt(last_modified, 10) : undefined;
@@ -1775,17 +1772,17 @@ class Collection {
       return Promise.resolve(payload);
     }
     return (0, _utils.waterfall)(this.hooks[hookName].map(hook => {
       return record => {
         const result = hook(payload, this);
         const resultThenable = result && typeof result.then === "function";
         const resultChanges = result && result.hasOwnProperty("changes");
         if (!(resultThenable || resultChanges)) {
-          throw new Error(`Invalid return value for hook: ${ JSON.stringify(result) } has no 'then()' or 'changes' properties`);
+          throw new Error(`Invalid return value for hook: ${JSON.stringify(result)} has no 'then()' or 'changes' properties`);
         }
         return result;
       };
     }), payload);
   }
 
   /**
    * Publish local changes to the remote server and updates the passed
@@ -1794,24 +1791,30 @@ class Collection {
    * @param  {KintoClient.Collection} client           Kinto client Collection instance.
    * @param  {SyncResultObject}       syncResultObject The sync result object.
    * @param  {Object}                 changes          The change object.
    * @param  {Array}                  changes.toDelete The list of records to delete.
    * @param  {Array}                  changes.toSync   The list of records to create/update.
    * @param  {Object}                 options          The options object.
    * @return {Promise}
    */
-  pushChanges(client, { toDelete = [], toSync }, syncResultObject, options = {}) {
+  pushChanges(client, changes, syncResultObject, options = {}) {
     var _this8 = this;
 
     return _asyncToGenerator(function* () {
       if (!syncResultObject.ok) {
         return syncResultObject;
       }
       const safe = !options.strategy || options.strategy !== Collection.CLIENT_WINS;
+      const toDelete = changes.filter(function (r) {
+        return r._status == "deleted";
+      });
+      const toSync = changes.filter(function (r) {
+        return r._status != "deleted";
+      });
 
       // Perform a batch request with every changes.
       const synced = yield client.batch(function (batch) {
         toDelete.forEach(function (r) {
           // never published locally deleted records should not be pusblished
           if (r.last_modified) {
             batch.deleteRecord(r);
           }
@@ -1965,40 +1968,40 @@ class Collection {
 
       const previousRemote = _this9.api.remote;
       if (options.remote) {
         // Note: setting the remote ensures it's valid, throws when invalid.
         _this9.api.remote = options.remote;
       }
       if (!options.ignoreBackoff && _this9.api.backoff > 0) {
         const seconds = Math.ceil(_this9.api.backoff / 1000);
-        return Promise.reject(new Error(`Server is asking clients to back off; retry in ${ seconds }s or use the ignoreBackoff option.`));
+        return Promise.reject(new Error(`Server is asking clients to back off; retry in ${seconds}s or use the ignoreBackoff option.`));
       }
 
       const client = _this9.api.bucket(options.bucket).collection(options.collection);
 
       const result = new SyncResultObject();
       try {
         // Fetch last changes from the server.
         yield _this9.pullChanges(client, result, options);
         const { lastModified } = result;
 
         // Fetch local changes
-        const { toDelete, toSync } = yield _this9.gatherLocalChanges();
+        const toSync = yield _this9.gatherLocalChanges();
 
         // Publish local changes and pull local resolutions
-        yield _this9.pushChanges(client, { toDelete, toSync }, result, options);
+        yield _this9.pushChanges(client, toSync, result, options);
 
         // Publish local resolution of push conflicts to server (on CLIENT_WINS)
         const resolvedUnsynced = result.resolved.filter(function (r) {
           return r._status !== "synced";
         });
         if (resolvedUnsynced.length > 0) {
           const resolvedEncoded = yield Promise.all(resolvedUnsynced.map(_this9._encodeRecord.bind(_this9, "remote")));
-          yield _this9.pushChanges(client, { toSync: resolvedEncoded }, result, options);
+          yield _this9.pushChanges(client, resolvedEncoded, result, options);
         }
         // Perform a last pull to catch changes that occured after the last pull,
         // while local changes were pushed. Do not do it nothing was pushed.
         if (result.published.length > 0) {
           // Avoid redownloading our own changes during the last pull.
           const pullOpts = _extends({}, options, { lastModified, exclude: result.published });
           yield _this9.pullChanges(client, result, pullOpts);
         }
@@ -2136,17 +2139,17 @@ class CollectionTransaction {
    *
    * @param  {String} id
    * @param  {Object} options
    * @return {Object}
    */
   get(id, options = { includeDeleted: false }) {
     const res = this.getAny(id);
     if (!res.data || !options.includeDeleted && res.data._status === "deleted") {
-      throw new Error(`Record with id=${ id } not found.`);
+      throw new Error(`Record with id=${id} not found.`);
     }
 
     return res;
   }
 
   /**
    * Deletes a record from the local database.
    *
@@ -2158,17 +2161,17 @@ class CollectionTransaction {
    * @param  {Object} options  The options object.
    * @return {Object}
    */
   delete(id, options = { virtual: true }) {
     // Ensure the record actually exists.
     const existing = this.adapterTransaction.get(id);
     const alreadyDeleted = existing && existing._status == "deleted";
     if (!existing || alreadyDeleted && options.virtual) {
-      throw new Error(`Record with id=${ id } not found.`);
+      throw new Error(`Record with id=${id} not found.`);
     }
     // Virtual updates status.
     if (options.virtual) {
       this.adapterTransaction.update(markDeleted(existing));
     } else {
       // Delete for real.
       this.adapterTransaction.delete(id);
     }
@@ -2202,17 +2205,17 @@ class CollectionTransaction {
   create(record) {
     if (typeof record !== "object") {
       throw new Error("Record is not an object.");
     }
     if (!record.hasOwnProperty("id")) {
       throw new Error("Cannot create a record missing id");
     }
     if (!this.collection.idSchema.validate(record.id)) {
-      throw new Error(`Invalid Id: ${ record.id }`);
+      throw new Error(`Invalid Id: ${record.id}`);
     }
 
     this.adapterTransaction.create(record);
     this._queueEvent("create", { data: record });
     return { data: record, permissions: {} };
   }
 
   /**
@@ -2230,22 +2233,22 @@ class CollectionTransaction {
   update(record, options = { synced: false, patch: false }) {
     if (typeof record !== "object") {
       throw new Error("Record is not an object.");
     }
     if (!record.hasOwnProperty("id")) {
       throw new Error("Cannot update a record missing id.");
     }
     if (!this.collection.idSchema.validate(record.id)) {
-      throw new Error(`Invalid Id: ${ record.id }`);
+      throw new Error(`Invalid Id: ${record.id}`);
     }
 
     const oldRecord = this.adapterTransaction.get(record.id);
     if (!oldRecord) {
-      throw new Error(`Record with id=${ record.id } not found.`);
+      throw new Error(`Record with id=${record.id} not found.`);
     }
     const newRecord = options.patch ? _extends({}, oldRecord, record) : record;
     const updated = this._updateRaw(oldRecord, newRecord, options);
     this.adapterTransaction.update(updated);
     this._queueEvent("update", { data: updated, oldRecord });
     return { data: updated, oldRecord, permissions: {} };
   }
 
@@ -2287,17 +2290,17 @@ class CollectionTransaction {
   upsert(record) {
     if (typeof record !== "object") {
       throw new Error("Record is not an object.");
     }
     if (!record.hasOwnProperty("id")) {
       throw new Error("Cannot update a record missing id.");
     }
     if (!this.collection.idSchema.validate(record.id)) {
-      throw new Error(`Invalid Id: ${ record.id }`);
+      throw new Error(`Invalid Id: ${record.id}`);
     }
     let oldRecord = this.adapterTransaction.get(record.id);
     const updated = this._updateRaw(oldRecord, record);
     this.adapterTransaction.update(updated);
     // Don't return deleted records -- pretend they are gone
     if (oldRecord && oldRecord._status == "deleted") {
       oldRecord = undefined;
     }
@@ -2458,9 +2461,9 @@ function omitKeys(obj, keys = []) {
     if (keys.indexOf(key) === -1) {
       acc[key] = obj[key];
     }
     return acc;
   }, {});
 }
 
 },{}]},{},[1])(1)
-});
+});
\ No newline at end of file