Bug 1099085 - HawkClient, HawkRequest and RESTRequest should support PATCH requests. r=ckarlof a=lsblakk
authorMark Banner <standard8@mozilla.com>
Mon, 17 Nov 2014 22:12:26 +0000
changeset 233970 107c64f93febe8a9ae93c7f8ec49483618b74f3b
parent 233969 a504347b83b1fd61d7d2285c712b97e099f19398
child 233971 c0bc86e8c00095a2992971274305cf903b7e2099
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckarlof, lsblakk
bugs1099085
milestone35.0a2
Bug 1099085 - HawkClient, HawkRequest and RESTRequest should support PATCH requests. r=ckarlof a=lsblakk
services/common/hawkclient.js
services/common/hawkrequest.js
services/common/rest.js
services/common/tests/unit/test_hawkclient.js
services/common/tests/unit/test_restrequest.js
services/common/tests/unit/xpcshell.ini
--- a/services/common/hawkclient.js
+++ b/services/common/hawkclient.js
@@ -257,17 +257,17 @@ this.HawkClient.prototype = {
     }
 
     let extra = {
       now: this.now(),
       localtimeOffsetMsec: this.localtimeOffsetMsec,
     };
 
     let request = this.newHAWKAuthenticatedRESTRequest(uri, credentials, extra);
-    if (method == "post" || method == "put") {
+    if (method == "post" || method == "put" || method == "patch") {
       request[method](payloadObj, onComplete);
     } else {
       request[method](onComplete);
     }
 
     return deferred.promise;
   },
 
--- a/services/common/hawkrequest.js
+++ b/services/common/hawkrequest.js
@@ -62,17 +62,17 @@ this.HAWKAuthenticatedRESTRequest =
   // Expose for testing
   this._intl = getIntl();
 };
 HAWKAuthenticatedRESTRequest.prototype = {
   __proto__: RESTRequest.prototype,
 
   dispatch: function dispatch(method, data, onComplete, onProgress) {
     let contentType = "text/plain";
-    if (method == "POST" || method == "PUT") {
+    if (method == "POST" || method == "PUT" || method == "PATCH") {
       contentType = "application/json";
     }
     if (this.credentials) {
       let options = {
         now: this.now,
         localtimeOffsetMsec: this.localtimeOffsetMsec,
         credentials: this.credentials,
         payload: data && JSON.stringify(data) || "",
--- a/services/common/rest.js
+++ b/services/common/rest.js
@@ -203,16 +203,33 @@ RESTRequest.prototype = {
    *
    * @return the request object.
    */
   get: function get(onComplete, onProgress) {
     return this.dispatch("GET", null, onComplete, onProgress);
   },
 
   /**
+   * Perform an HTTP PATCH.
+   *
+   * @param data
+   *        Data to be used as the request body. If this isn't a string
+   *        it will be JSONified automatically.
+   * @param onComplete
+   *        Short-circuit way to set the 'onComplete' method. Optional.
+   * @param onProgress
+   *        Short-circuit way to set the 'onProgress' method. Optional.
+   *
+   * @return the request object.
+   */
+  patch: function patch(data, onComplete, onProgress) {
+    return this.dispatch("PATCH", data, onComplete, onProgress);
+  },
+
+  /**
    * Perform an HTTP PUT.
    *
    * @param data
    *        Data to be used as the request body. If this isn't a string
    *        it will be JSONified automatically.
    * @param onComplete
    *        Short-circuit way to set the 'onComplete' method. Optional.
    * @param onProgress
@@ -302,17 +319,17 @@ RESTRequest.prototype = {
         this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
       } else {
         this._log.trace("HTTP Header " + key + ": " + headers[key]);
       }
       channel.setRequestHeader(key, headers[key], false);
     }
 
     // Set HTTP request body.
-    if (method == "PUT" || method == "POST") {
+    if (method == "PUT" || method == "POST" || method == "PATCH") {
       // Convert non-string bodies into JSON.
       if (typeof data != "string") {
         data = JSON.stringify(data);
       }
 
       this._log.debug(method + " Length: " + data.length);
       if (this._log.level <= Log.Level.Trace) {
         this._log.trace(method + " Body: " + data);
@@ -361,17 +378,17 @@ RESTRequest.prototype = {
    * Abort the request based on a timeout.
    */
   abortTimeout: function abortTimeout() {
     this.abort();
     let error = Components.Exception("Aborting due to channel inactivity.",
                                      Cr.NS_ERROR_NET_TIMEOUT);
     if (!this.onComplete) {
       this._log.error("Unexpected error: onComplete not defined in " +
-                      "abortTimeout.")
+                      "abortTimeout.");
       return;
     }
     this.onComplete(error);
   },
 
   /*** nsIStreamListener ***/
 
   onStartRequest: function onStartRequest(channel) {
--- a/services/common/tests/unit/test_hawkclient.js
+++ b/services/common/tests/unit/test_hawkclient.js
@@ -61,19 +61,17 @@ add_task(function test_authenticated_get
   let response = yield client.request("/foo", method, TEST_CREDS);
   let result = JSON.parse(response.body);
 
   do_check_eq("Great Success!", result.msg);
 
   yield deferredStop(server);
 });
 
-add_task(function test_authenticated_post_request() {
-  let method = "POST";
-
+function check_authenticated_request(method) {
   let server = httpd_setup({"/foo": (request, response) => {
       do_check_true(request.hasHeader("Authorization"));
 
       response.setStatusLine(request.httpVersion, 200, "OK");
       response.setHeader("Content-Type", "application/json");
       response.bodyOutputStream.writeFrom(request.bodyInputStream, request.bodyInputStream.available());
     }
   });
@@ -81,16 +79,28 @@ add_task(function test_authenticated_pos
   let client = new HawkClient(server.baseURI);
 
   let response = yield client.request("/foo", method, TEST_CREDS, {foo: "bar"});
   let result = JSON.parse(response.body);
 
   do_check_eq("bar", result.foo);
 
   yield deferredStop(server);
+}
+
+add_task(function test_authenticated_post_request() {
+  check_authenticated_request("POST");
+});
+
+add_task(function test_authenticated_put_request() {
+  check_authenticated_request("PUT");
+});
+
+add_task(function test_authenticated_patch_request() {
+  check_authenticated_request("PATCH");
 });
 
 add_task(function test_credentials_optional() {
   let method = "GET";
   let server = httpd_setup({
     "/foo": (request, response) => {
       do_check_false(request.hasHeader("Authorization"));
 
--- a/services/common/tests/unit/test_restrequest.js
+++ b/services/common/tests/unit/test_restrequest.js
@@ -266,19 +266,20 @@ add_test(function test_charsets() {
       do_check_eq(request2.response.charset, "us-ascii");
 
       server.stop(run_next_test);
     });
   });
 });
 
 /**
- * Test HTTP PUT with a simple string argument and default Content-Type.
+ * Used for testing PATCH/PUT/POST methods.
  */
-add_test(function test_put() {
+function check_posting_data(method) {
+  let funcName = method.toLowerCase();
   let handler = httpd_handler(200, "OK", "Got it!");
   let server = httpd_setup({"/resource": handler});
 
   let request = new RESTRequest(server.baseURI + "/resource");
   do_check_eq(request.status, request.NOT_SENT);
 
   request.onProgress = request.onComplete = function () {
     do_throw("This function should have been overwritten!");
@@ -294,83 +295,55 @@ add_test(function test_put() {
   function onComplete(error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "Got it!");
 
-    do_check_eq(handler.request.method, "PUT");
+    do_check_eq(handler.request.method, method);
     do_check_eq(handler.request.body, "Hullo?");
     do_check_eq(handler.request.getHeader("Content-Type"), "text/plain");
 
     do_check_true(onProgress_called);
     CommonUtils.nextTick(function () {
       do_check_eq(request.onComplete, null);
       do_check_eq(request.onProgress, null);
       server.stop(run_next_test);
     });
   };
 
-  do_check_eq(request.put("Hullo?", onComplete, onProgress), request);
+  do_check_eq(request[funcName]("Hullo?", onComplete, onProgress), request);
   do_check_eq(request.status, request.SENT);
-  do_check_eq(request.method, "PUT");
+  do_check_eq(request.method, method);
   do_check_throws(function () {
-    request.put("Hai!");
+    request[funcName]("Hai!");
   });
+}
+
+/**
+ * Test HTTP PATCH with a simple string argument and default Content-Type.
+ */
+add_test(function test_patch() {
+  check_posting_data("PATCH");
+});
+
+/**
+ * Test HTTP PUT with a simple string argument and default Content-Type.
+ */
+add_test(function test_put() {
+  check_posting_data("PUT");
 });
 
 /**
  * Test HTTP POST with a simple string argument and default Content-Type.
  */
 add_test(function test_post() {
-  let handler = httpd_handler(200, "OK", "Got it!");
-  let server = httpd_setup({"/resource": handler});
-
-  let request = new RESTRequest(server.baseURI + "/resource");
-  do_check_eq(request.status, request.NOT_SENT);
-
-  request.onProgress = request.onComplete = function () {
-    do_throw("This function should have been overwritten!");
-  };
-
-  let onProgress_called = false;
-  function onProgress() {
-    onProgress_called = true;
-    do_check_eq(this.status, request.IN_PROGRESS);
-    do_check_true(this.response.body.length > 0);
-  };
-
-  function onComplete(error) {
-    do_check_eq(error, null);
-
-    do_check_eq(this.status, this.COMPLETED);
-    do_check_true(this.response.success);
-    do_check_eq(this.response.status, 200);
-    do_check_eq(this.response.body, "Got it!");
-
-    do_check_eq(handler.request.method, "POST");
-    do_check_eq(handler.request.body, "Hullo?");
-    do_check_eq(handler.request.getHeader("Content-Type"), "text/plain");
-
-    do_check_true(onProgress_called);
-    CommonUtils.nextTick(function () {
-      do_check_eq(request.onComplete, null);
-      do_check_eq(request.onProgress, null);
-      server.stop(run_next_test);
-    });
-  };
-
-  do_check_eq(request.post("Hullo?", onComplete, onProgress), request);
-  do_check_eq(request.status, request.SENT);
-  do_check_eq(request.method, "POST");
-  do_check_throws(function () {
-    request.post("Hai!");
-  });
+  check_posting_data("POST");
 });
 
 /**
  * Test HTTP DELETE.
  */
 add_test(function test_delete() {
   let handler = httpd_handler(200, "OK", "Got it!");
   let server = httpd_setup({"/resource": handler});
--- a/services/common/tests/unit/xpcshell.ini
+++ b/services/common/tests/unit/xpcshell.ini
@@ -23,17 +23,16 @@ skip-if = toolkit == 'gonk'
 [test_utils_utf8.js]
 [test_utils_uuid.js]
 
 [test_async_chain.js]
 [test_async_querySpinningly.js]
 [test_bagheera_server.js]
 [test_bagheera_client.js]
 [test_hawkclient.js]
-run-if = fxaccounts # This test imports some FxAccounts modules.
 [test_hawkrequest.js]
 [test_observers.js]
 [test_restrequest.js]
 [test_tokenauthenticatedrequest.js]
 
 # Storage service APIs
 [test_storageservice_bso.js]
 [test_storageservice_client.js]