Repository: googleapis/google-api-python-client

Issue:
Media upload URI does not respect overrides client_opions.api_endpoint
This can happen when using Private Service Connect to access the APIs. This results in media upload URL being `https://bigquery.googleapis.com/upload/bigquery/v2/projects/projectId/jobs?alt=json&uploadType=resumable` instead of `https://<psc-endpoint>.p.googleapis.com/upload/bigquery/v2/projects/projectId/jobs?alt=json&uploadType=resumable` when running a command like `bq --api=https://<psc-endpoint>.p.googleapis.com ...` .



Discussion:

Resolving patch:
diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py
--- a/googleapiclient/discovery.py
+++ b/googleapiclient/discovery.py
@@ -887,6 +887,29 @@ def _fix_up_method_description(method_desc, root_desc, schema):
     return path_url, http_method, method_id, accept, max_size, media_path_url
 
 
+def _fix_up_media_path_base_url(media_path_url, base_url):
+    """
+    Update the media upload base url if its netloc doesn't match base url netloc.
+
+    This can happen in case the base url was overridden by
+    client_options.api_endpoint.
+
+    Args:
+      media_path_url: String; the absolute URI for media upload.
+      base_url: string, base URL for the API. All requests are relative to this URI.
+
+    Returns:
+      String; the absolute URI for media upload.
+    """
+    parsed_media_url = urllib.parse.urlparse(media_path_url)
+    parsed_base_url = urllib.parse.urlparse(base_url)
+    if parsed_media_url.netloc == parsed_base_url.netloc:
+        return media_path_url
+    return urllib.parse.urlunparse(
+        parsed_media_url._replace(netloc=parsed_base_url.netloc)
+    )
+
+
 def _urljoin(base, url):
     """Custom urljoin replacement supporting : before / in url."""
     # In general, it's unsafe to simply join base and url. However, for
@@ -1133,6 +1156,7 @@ def method(self, **kwargs):
             # Use the media path uri for media uploads
             expanded_url = uritemplate.expand(mediaPathUrl, params)
             url = _urljoin(self._baseUrl, expanded_url + query)
+            url = _fix_up_media_path_base_url(url, self._baseUrl)
             if media_upload.resumable():
                 url = _add_query_parameter(url, "uploadType", "resumable")
 


Test patch:
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -58,6 +58,7 @@
     V1_DISCOVERY_URI,
     V2_DISCOVERY_URI,
     ResourceMethodParameters,
+    _fix_up_media_path_base_url,
     _fix_up_media_upload,
     _fix_up_method_description,
     _fix_up_parameters,
@@ -374,6 +375,20 @@ def test_fix_up_method_description_insert(self):
             result, (path_url, http_method, method_id, accept, max_size, media_path_url)
         )
 
+    def test_fix_up_media_path_base_url_same_netloc(self):
+        result = _fix_up_media_path_base_url(
+            "https://www.googleapis.com/upload/foo",
+            "https://www.googleapis.com/upload/bar",
+        )
+        self.assertEqual(result, "https://www.googleapis.com/upload/foo")
+
+    def test_fix_up_media_path_base_url_different_netloc(self):
+        result = _fix_up_media_path_base_url(
+            "https://www.googleapis.com/upload/foo",
+            "https://www.example.com/upload/bar",
+        )
+        self.assertEqual(result, "https://www.example.com/upload/foo")
+
     def test_urljoin(self):
         # We want to exhaustively test various URL combinations.
         simple_bases = ["https://www.googleapis.com", "https://www.googleapis.com/"]