Add getDiff() method to gr-rest-api-interface

+ Replace use of gr-ajax for retrieving the diff in gr-diff.
+ Add opt_params to fetchJSON so that query parameters can be
  passed.

Change-Id: Iaa77c4082d6e83099e23f49205d859a5c938dd31
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 21ee076..a1a5c02 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -20,6 +20,7 @@
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 <link rel="import" href="../../shared/gr-request/gr-request.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 
 <link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../gr-diff-side/gr-diff-side.html">
@@ -57,11 +58,6 @@
         border-right: 1px solid #ddd;
       }
     </style>
-    <gr-ajax id="diffXHR"
-        url="[[_computeDiffPath(changeNum, patchRange.patchNum, path)]]"
-        params="[[_computeDiffQueryParams(patchRange.basePatchNum)]]"
-        last-response="{{_diffResponse}}"
-        loading="{{_loading}}"></gr-ajax>
     <gr-ajax id="baseCommentsXHR"
         url="[[_computeCommentsPath(changeNum, patchRange.basePatchNum)]]"></gr-ajax>
     <gr-ajax id="commentsXHR"
@@ -118,6 +114,7 @@
             on-remove-thread="_handleRemoveThread"></gr-diff-side>
       </div>
     </div>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
   <script src="gr-diff.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 485e2cc..f91785d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -126,7 +126,8 @@
       this.$.rightDiff.scrollToPreviousCommentThread();
     },
 
-    reload: function(changeNum, patchRange, path) {
+    reload: function() {
+      this._loading = true;
       // If a diff takes a considerable amount of time to render, the previous
       // diff can end up showing up while the DOM is constructed. Clear the
       // content on a reload to prevent this.
@@ -135,9 +136,17 @@
         rightSide: [],
       };
 
+      var diffLoaded = this.$.restAPI.getDiff(
+        this.changeNum,
+        this.patchRange.basePatchNum,
+        this.patchRange.patchNum,
+        this.path).then(function(diff) {
+          this._diffResponse = diff;
+        }.bind(this));
+
       var promises = [
         this._prefsReady,
-        this.$.diffXHR.generateRequest().completes
+        diffLoaded,
       ];
 
       var basePatchNum = this.patchRange.basePatchNum;
@@ -146,11 +155,13 @@
         promises.push(this._getCommentsAndDrafts(basePatchNum, app.loggedIn));
         this._diffRequestsPromise = Promise.all(promises).then(function() {
           this._render();
+          this._loading = false;
         }.bind(this)).catch(function(err) {
+          this._loading = false;
           alert('Oops. Something went wrong. Check the console and bug the ' +
               'PolyGerrit team for assistance.');
           throw err;
-        });
+        }.bind(this));
       }.bind(this));
     },
 
@@ -225,11 +236,6 @@
       return Promise.all(promises);
     },
 
-    _computeDiffPath: function(changeNum, patchNum, path) {
-      return this.changeBaseURL(changeNum, patchNum) + '/files/' +
-          encodeURIComponent(path) + '/diff';
-    },
-
     _computeCommentsPath: function(changeNum, patchNum) {
       return this.changeBaseURL(changeNum, patchNum) + '/comments';
     },
@@ -238,17 +244,6 @@
       return this.changeBaseURL(changeNum, patchNum) + '/drafts';
     },
 
-    _computeDiffQueryParams: function(basePatchNum) {
-      var params =  {
-        context: 'ALL',
-        intraline: null
-      };
-      if (basePatchNum != 'PARENT') {
-        params.base = basePatchNum;
-      }
-      return params;
-    },
-
     _handlePrefsTap: function(e) {
       e.preventDefault();
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index 9a8cb81..c7f12e6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -36,6 +36,7 @@
   suite('gr-diff tests', function() {
     var element;
     var server;
+    var getDiffStub;
 
     setup(function() {
       element = fixture('basic');
@@ -46,56 +47,50 @@
         tab_size: 8,
       };
 
+      getDiffStub = sinon.stub(element.$.restAPI, 'getDiff', function() {
+        return Promise.resolve({
+          change_type: 'MODIFIED',
+          content: [
+            {
+              ab: [
+                '<!DOCTYPE html>',
+                '<meta charset="utf-8">',
+                '<title>My great page</title>',
+                '<style>',
+                '  *,',
+                '  *:before,',
+                '  *:after {',
+                '    box-sizing: border-box;',
+                '  }',
+                '</style>',
+                '<header>',
+              ]
+            },
+            {
+              a: [
+                '  Welcome ',
+                '  to the wooorld of tomorrow!',
+              ],
+              b: [
+                '  Hello, world!',
+              ],
+            },
+            {
+              ab: [
+                '</header>',
+                '<body>',
+                'Leela: This is the only place the ship can’t hear us, so ',
+                'everyone pretend to shower.',
+                'Fry: Same as every day. Got it.',
+              ]
+            },
+          ]
+        });
+      });
+
       server = sinon.fakeServer.create();
       server.respondWith(
         'GET',
-        /\/changes\/42\/revisions\/(1|2)\/files\/sieve\.go\/diff(.*)/,
-        [
-          200,
-          {'Content-Type': 'application/json'},
-          ')]}\'\n' +
-          JSON.stringify({
-            change_type: 'MODIFIED',
-            content: [
-              {
-                ab: [
-                  '<!DOCTYPE html>',
-                  '<meta charset="utf-8">',
-                  '<title>My great page</title>',
-                  '<style>',
-                  '  *,',
-                  '  *:before,',
-                  '  *:after {',
-                  '    box-sizing: border-box;',
-                  '  }',
-                  '</style>',
-                  '<header>',
-                ]
-              },
-              {
-                a: [
-                  '  Welcome ',
-                  '  to the wooorld of tomorrow!',
-                ],
-                b: [
-                  '  Hello, world!',
-                ],
-              },
-              {
-                ab: [
-                  '</header>',
-                  '<body>',
-                  'Leela: This is the only place the ship can’t hear us, so ',
-                  'everyone pretend to shower.',
-                  'Fry: Same as every day. Got it.',
-                ]
-              },
-            ]
-          }),
-        ]
-      );
-      server.respondWith(
-        'GET',
         '/changes/42/revisions/1/comments',
         [
           200,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 782d337..97d88b4 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -19,7 +19,7 @@
   Polymer({
     is: 'gr-rest-api-interface',
 
-    fetchJSON: function(url, opt_cancelCondition, opt_opts) {
+    fetchJSON: function(url, opt_cancelCondition, opt_params, opt_opts) {
       opt_opts = opt_opts || {};
 
       var fetchOptions = {
@@ -27,7 +27,24 @@
         headers: opt_opts.headers,
       };
 
-      return fetch(url, fetchOptions).then(function(response) {
+      var urlWithParams = url;
+      if (opt_params) {
+        var params = [];
+        for (var p in opt_params) {
+          if (opt_params[p] == null) {
+            params.push(encodeURIComponent(p));
+            continue;
+          }
+          params.push(
+            encodeURIComponent(p) + '=' +
+            encodeURIComponent(opt_params[p]));
+        }
+        // Sorting the params leaves the order deterministic which is easier
+        // to test.
+        urlWithParams += '?' + params.sort().join('&');
+      }
+
+      return fetch(urlWithParams, fetchOptions).then(function(response) {
         if (opt_cancelCondition && opt_cancelCondition()) {
           response.body.cancel();
           return;
@@ -41,8 +58,8 @@
           throw err;
         } else {
           // This could be because of a 302 auth redirect. Retry the request.
-          return this.fetchJSON(url, opt_cancelCondition, Object.assign(
-              opt_opts, {noCredentials: true}));
+          return this.fetchJSON(url, opt_cancelCondition, opt_params,
+              Object.assign(opt_opts, {noCredentials: true}));
         }
       }.bind(this));
     },
@@ -51,5 +68,28 @@
       return this.fetchJSON('/accounts/self/detail');
     },
 
+    getDiff: function(changeNum, basePatchNum, patchNum, path,
+        opt_cancelCondition) {
+      var url = this._changeBaseURL(changeNum, patchNum) + '/files/' +
+          encodeURIComponent(path) + '/diff';
+      var params =  {
+        context: 'ALL',
+        intraline: null
+      };
+      if (basePatchNum != 'PARENT') {
+        params.base = basePatchNum;
+      }
+
+      return this.fetchJSON(url, opt_cancelCondition, params);
+    },
+
+    _changeBaseURL: function(changeNum, patchNum) {
+      var v = '/changes/' + changeNum;
+      if (patchNum) {
+        v += '/revisions/' + patchNum;
+      }
+      return v;
+    },
+
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 71d747e..9810e15 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -53,6 +53,22 @@
       });
     });
 
+    test('params are properly encoded', function() {
+      var fetchStub = sinon.stub(window, 'fetch', function() {
+        return Promise.resolve({ text: function() {
+          return Promise.resolve(')]}\'\n{}');
+        }});
+      });
+      var params = {
+        sp: 'hola',
+        gr: 'guten tag',
+        noval: null,
+      };
+      element.fetchJSON('/path/', null, params);
+      assert.equal(fetchStub.args[0][0], '/path/?gr=guten%20tag&noval&sp=hola');
+      fetchStub.restore();
+    });
+
     test('request callbacks can be canceled', function(done) {
       var cancelCalled = false;
       var fetchStub = sinon.stub(window, 'fetch', function() {