Bug 1099085 - HawkClient, HawkRequest and RESTRequest should support PATCH requests. r=ckarlof
authorMark Banner <standard8@mozilla.com>
Mon, 17 Nov 2014 22:12:26 +0000
changeset 240424 a0149f5555a58ec4d272ffa2cc3ebaa8f1046652
parent 240423 872f2f8a6c0d9517fab6cfd3dae2f44a49cfea4e
child 240425 62da96758a246dbdcce1ffdcffd63c84529a9287
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckarlof
bugs1099085
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1099085 - HawkClient, HawkRequest and RESTRequest should support PATCH requests. r=ckarlof
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]