Repository: googleapis/google-cloud-python

Issue:
gcloud.storage.connection.Connection.api_request defaults path to None, but doesn't handle it
https://github.com/GoogleCloudPlatform/gcloud-python/blob/master/gcloud/storage/connection.py#L165

If the 'path' argument is not passed, the generated URI is nonsensical.  We should either default to a sensible path ('/'), or else test for None and do something reasonable.  As an alternative, we could just make 'path' a required argument (as it is for 'build_api_url').



Discussion:

Resolving patch:
diff --git a/gcloud/storage/connection.py b/gcloud/storage/connection.py
--- a/gcloud/storage/connection.py
+++ b/gcloud/storage/connection.py
@@ -162,7 +162,7 @@ def make_request(self, method, url, data=None, content_type=None,
     return self.http.request(uri=url, method=method, headers=headers,
                                body=data)
 
-  def api_request(self, method, path=None, query_params=None,
+  def api_request(self, method, path, query_params=None,
                   data=None, content_type=None,
                   api_base_url=None, api_version=None,
                   expect_json=True):
@@ -174,32 +174,40 @@ def api_request(self, method, path=None, query_params=None,
 
     :type method: string
     :param method: The HTTP method name (ie, ``GET``, ``POST``, etc).
+                   Required.
 
     :type path: string
     :param path: The path to the resource (ie, ``'/b/bucket-name'``).
+                 Required.
 
     :type query_params: dict
     :param query_params: A dictionary of keys and values to insert into
-                         the query string of the URL.
+                         the query string of the URL.  Default is empty dict.
 
     :type data: string
-    :param data: The data to send as the body of the request.
+    :param data: The data to send as the body of the request. Default is the
+                 empty string.
 
     :type content_type: string
-    :param content_type: The proper MIME type of the data provided.
+    :param content_type: The proper MIME type of the data provided. Default
+                         is None.
 
     :type api_base_url: string
     :param api_base_url: The base URL for the API endpoint.
                          Typically you won't have to provide this.
+                         Default is the standard API base URL.
 
     :type api_version: string
     :param api_version: The version of the API to call.
                         Typically you shouldn't provide this and instead
                         use the default for the library.
+                        Default is the latest API version supported by
+                        gcloud-python.
 
     :type expect_json: bool
     :param expect_json: If True, this method will try to parse the response
                         as JSON and raise an exception if that cannot be done.
+                        Default is True.
 
     :raises: Exception if the response code is not 200 OK.
     """


Test patch:
diff --git a/gcloud/storage/test_bucket.py b/gcloud/storage/test_bucket.py
--- a/gcloud/storage/test_bucket.py
+++ b/gcloud/storage/test_bucket.py
@@ -648,7 +648,7 @@ def test_save_default_object_acl_existing_set_new_passed(self):
         bucket = self._makeOne(connection, NAME, metadata)
         bucket.reload_default_object_acl()
         self.assertTrue(bucket.save_default_object_acl(new_acl) is bucket)
-        # See: https://github.com/GoogleCloudPlatform/gcloud-python/issues/138
+        # See: https://github.com/GoogleCloudPlatform/gcloud-python/issues/139
         #self.assertEqual(list(bucket.default_object_acl), new_acl)
         kw = connection._requested
         self.assertEqual(len(kw), 1)
@@ -666,14 +666,12 @@ def test_clear_default_object_acl(self):
         bucket = self._makeOne(connection, NAME, metadata)
         bucket.reload_default_object_acl()
         self.assertTrue(bucket.clear_default_object_acl() is bucket)
-        # See: https://github.com/GoogleCloudPlatform/gcloud-python/issues/138
+        # See: https://github.com/GoogleCloudPlatform/gcloud-python/issues/139
         #self.assertEqual(list(bucket.default_object_acl), [])
         kw = connection._requested
         self.assertEqual(len(kw), 1)
         self.assertEqual(kw[0]['method'], 'PATCH')
         self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
-        # See: https://github.com/GoogleCloudPlatform/gcloud-python/issues/139
-        #self.assertEqual(list(bucket.default_object_acl), [])
         #self.assertEqual(kw[0]['data'], {'defaultObjectAcl': []})
         self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
 
diff --git a/gcloud/storage/test_connection.py b/gcloud/storage/test_connection.py
--- a/gcloud/storage/test_connection.py
+++ b/gcloud/storage/test_connection.py
@@ -203,38 +203,16 @@ def test_make_request_w_extra_headers(self):
 
     def test_api_request_defaults(self):
         PROJECT = 'project'
+        PATH = '/path/required'
         conn = self._makeOne(PROJECT)
         URI = '/'.join([conn.API_BASE_URL,
                         'storage',
                         conn.API_VERSION,
-                        # see https://github.com/GoogleCloudPlatform/
-                        #          gcloud-python/issues/140
-                        #'?project=%s' % PROJECT,
-                       ]) + 'None?project=%s' % PROJECT # XXX
+                       ]) + '%s?project=%s' % (PATH, PROJECT)
         http = conn._http = Http({'status': '200',
                                   'content-type': 'application/json',
                                  }, '{}')
-        self.assertEqual(conn.api_request('GET'), {})
-        self.assertEqual(http._called_with['method'], 'GET')
-        self.assertEqual(http._called_with['uri'], URI)
-        self.assertEqual(http._called_with['body'], None)
-        self.assertEqual(http._called_with['headers'],
-                         {'Accept-Encoding': 'gzip',
-                          'Content-Length':  0,
-                         })
-
-    def test_api_request_w_path(self):
-        PROJECT = 'project'
-        conn = self._makeOne(PROJECT)
-        URI = '/'.join([conn.API_BASE_URL,
-                        'storage',
-                        conn.API_VERSION,
-                        '?project=%s' % PROJECT,
-                       ])
-        http = conn._http = Http({'status': '200',
-                                  'content-type': 'application/json',
-                                 }, '{}')
-        self.assertEqual(conn.api_request('GET', '/'), {})
+        self.assertEqual(conn.api_request('GET', PATH), {})
         self.assertEqual(http._called_with['method'], 'GET')
         self.assertEqual(http._called_with['uri'], URI)
         self.assertEqual(http._called_with['body'], None)