Repository: googleapis/python-api-core

Issue:
Feat: Add support for universe_domain as a client option for TPC
`universe_domain` is required as an optional field in `google.api_core.ClientOptions` to enable users to explicitly configure universe domain as a client option. 

This will enable API endpoint to be resolved using a templated value if a universe domain is specified or otherwise default to GDU.


```python3

# user configures the universe domain:
client = Client(google.api_core.ClientOptions{universe_domain="foo.com"})

# api_endpoint points to the TPC universe:
api_endpoint = "service.foo.com"

```

```python3

# user does not configure the universe domain:
client = Client(google.api_core.ClientOptions{})

# api_endpoint defaults to GDU:
api_endpoint = "service.googleapis.com"

```


Discussion:

Patch:
diff --git a/google/api_core/client_options.py b/google/api_core/client_options.py
index ee9f28a..e93f958 100644
--- a/google/api_core/client_options.py
+++ b/google/api_core/client_options.py
@@ -75,6 +75,11 @@ class ClientOptions(object):
             authentication flows. Audience is typically a resource identifier.
             If not set, the service endpoint value will be used as a default.
             An example of a valid ``api_audience`` is: "https://language.googleapis.com".
+        universe_domain (Optional[str]): The desired universe domain. This must match
+            the one in credentials. If not set, the default universe domain is
+            `googleapis.com`. If both `api_endpoint` and `universe_domain` are set,
+            then `api_endpoint` is used as the service endpoint. If `api_endpoint` is
+            not specified, the format will be `{service}.{universe_domain}`.
 
     Raises:
         ValueError: If both ``client_cert_source`` and ``client_encrypted_cert_source``
@@ -91,6 +96,7 @@ class ClientOptions(object):
         scopes=None,
         api_key=None,
         api_audience=None,
+        universe_domain=None,
     ):
         if client_cert_source and client_encrypted_cert_source:
             raise ValueError(
@@ -106,6 +112,7 @@ class ClientOptions(object):
         self.scopes = scopes
         self.api_key = api_key
         self.api_audience = api_audience
+        self.universe_domain = universe_domain
 
     def __repr__(self):
         return "ClientOptions: " + repr(self.__dict__)


Test patch:
diff --git a/tests/unit/test_client_options.py b/tests/unit/test_client_options.py
index 336ceea..396d662 100644
--- a/tests/unit/test_client_options.py
+++ b/tests/unit/test_client_options.py
@@ -38,6 +38,7 @@ def test_constructor():
             "https://www.googleapis.com/auth/cloud-platform.read-only",
         ],
         api_audience="foo2.googleapis.com",
+        universe_domain="googleapis.com",
     )
 
     assert options.api_endpoint == "foo.googleapis.com"
@@ -49,6 +50,7 @@ def test_constructor():
         "https://www.googleapis.com/auth/cloud-platform.read-only",
     ]
     assert options.api_audience == "foo2.googleapis.com"
+    assert options.universe_domain == "googleapis.com"
 
 
 def test_constructor_with_encrypted_cert_source():
@@ -110,6 +112,7 @@ def test_from_dict():
     options = client_options.from_dict(
         {
             "api_endpoint": "foo.googleapis.com",
+            "universe_domain": "googleapis.com",
             "client_cert_source": get_client_cert,
             "quota_project_id": "quote-proj",
             "credentials_file": "path/to/credentials.json",
@@ -122,6 +125,7 @@ def test_from_dict():
     )
 
     assert options.api_endpoint == "foo.googleapis.com"
+    assert options.universe_domain == "googleapis.com"
     assert options.client_cert_source() == (b"cert", b"key")
     assert options.quota_project_id == "quote-proj"
     assert options.credentials_file == "path/to/credentials.json"
@@ -148,6 +152,7 @@ def test_repr():
     expected_keys = set(
         [
             "api_endpoint",
+            "universe_domain",
             "client_cert_source",
             "client_encrypted_cert_source",
             "quota_project_id",