Skip to content

Commit

Permalink
feat: Allow users to configure client option universe_domain (#2368)
Browse files Browse the repository at this point in the history
* feat: allow users to configure universe client option

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
ohmayr and gcf-owl-bot[bot] authored Mar 25, 2024
1 parent 77666bf commit 359b3ac
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 17 deletions.
13 changes: 13 additions & 0 deletions googleapiclient/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,13 @@ def build_from_document(

# If an API Endpoint is provided on client options, use that as the base URL
base = urllib.parse.urljoin(service["rootUrl"], service["servicePath"])
universe_domain = None
if HAS_UNIVERSE:
universe_domain = universe.determine_domain(
client_options.universe_domain, None
)
base = base.replace(universe.DEFAULT_UNIVERSE, universe_domain)

audience_for_self_signed_jwt = base
if client_options.api_endpoint:
base = client_options.api_endpoint
Expand Down Expand Up @@ -673,6 +680,10 @@ def build_from_document(
if use_mtls_endpoint == "always" or (
use_mtls_endpoint == "auto" and client_cert_to_use
):
if HAS_UNIVERSE and universe_domain != universe.DEFAULT_UNIVERSE:
raise MutualTLSChannelError(
f"mTLS is not supported in any universe other than {universe.DEFAULT_UNIVERSE}."
)
base = mtls_endpoint

if model is None:
Expand All @@ -688,6 +699,7 @@ def build_from_document(
resourceDesc=service,
rootDesc=service,
schema=schema,
universe_domain=universe_domain,
)


Expand Down Expand Up @@ -1517,6 +1529,7 @@ def methodResource(self):
resourceDesc=methodDesc,
rootDesc=rootDesc,
schema=schema,
universe_domain=self._universe_domain,
)

setattr(methodResource, "__doc__", "A collection resource.")
Expand Down
168 changes: 151 additions & 17 deletions tests/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,25 @@ def test_get_media(self):
if HAS_UNIVERSE:

class Universe(unittest.TestCase):
def test_validate_credentials_with_no_client_options(self):
http = build_http()
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
)
assert service._validate_credentials()

def test_validate_credentials_with_client_options_without_universe(self):
http = build_http()
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(),
)
assert service._validate_credentials()

def test_validate_credentials_with_no_universe(self):
fake_universe = "foo.com"

Expand All @@ -2360,9 +2379,20 @@ def test_validate_credentials_with_no_universe(self):

assert service._validate_credentials()

# TODO(google-api-python-client/issues/2365): Add test case for no configured universe and fake credentials' universe.
http = google_auth_httplib2.AuthorizedHttp(
credentials=None, http=build_http()
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=fake_universe
),
)

# TODO(google-api-python-client/issues/2365): Add test case for not specifying universe domain via client option.
with self.assertRaises(universe.UniverseMismatchError):
service._validate_credentials()

def test_validate_credentials_with_default_universe(self):
fake_universe = "foo.com"
Expand All @@ -2382,12 +2412,39 @@ def test_validate_credentials_with_default_universe(self):

assert service._validate_credentials()

# TODO(google-api-python-client/issues/2365): # Add test case for "googleapis.com" configured universe and fake credentials' universe.
http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
http=build_http(),
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=fake_universe
),
)

with self.assertRaises(universe.UniverseMismatchError):
service._validate_credentials()

def test_validate_credentials_with_a_different_universe(self):
fake_universe = "foo.com"

# TODO(google-api-python-client/issues/2365): Add test case for fake configured universe and fake credentials' universe.
http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=fake_universe),
http=build_http(),
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=fake_universe
),
)

assert service._validate_credentials()

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
Expand Down Expand Up @@ -2426,21 +2483,34 @@ def test_validate_credentials_with_already_validated_credentials(self):
# Calling service._validate_credentials() again returns service.credentials_validated.
assert service._validate_credentials()

# TODO(google-api-python-client/issues/2365): Add test case for fake configured universe and fake credentials' universe.

def test_validate_credentials_before_api_request(self):
fake_universe = "foo.com"

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=universe.DEFAULT_UNIVERSE),
http=build_http(),
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
)
discovery = read_datafile("zoo.json")
service = build_from_document(
discovery,
http=http,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=fake_universe
),
)

assert service._validate_credentials()
assert service._credentials_validated

# Calling service._validate_credentials() again returns service.credentials_validated.
assert service._validate_credentials()

def test_validate_credentials_before_api_request_success(self):
fake_universe = "foo.com"
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
credentials.universe_domain = fake_universe
discovery = read_datafile("tasks.json")
tasks = build_from_document(
discovery,
http=http,
credentials=credentials,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
universe_domain=fake_universe
),
)

Expand All @@ -2450,13 +2520,14 @@ def test_validate_credentials_before_api_request(self):
# Check that credentials are indeed verified before request.
assert tasklists._validate_credentials()

http = google_auth_httplib2.AuthorizedHttp(
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
)
def test_validate_credentials_before_api_request_failure(self):
fake_universe = "foo.com"
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
credentials.universe_domain = fake_universe
discovery = read_datafile("tasks.json")
tasks = build_from_document(
discovery,
http=http,
credentials=credentials,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=universe.DEFAULT_UNIVERSE
),
Expand All @@ -2466,6 +2537,69 @@ def test_validate_credentials_before_api_request(self):
with self.assertRaises(universe.UniverseMismatchError):
request = tasks.tasklists().list()

def test_validate_credentials_before_another_universe_api_request_failure(self):
fake_universe = "foo.com"
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
credentials.universe_domain = fake_universe
another_fake_universe = "bar.com"
discovery = read_datafile("tasks.json")
tasks = build_from_document(
discovery,
credentials=credentials,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=another_fake_universe
),
)

# Check that credentials are verified before request.
with self.assertRaises(universe.UniverseMismatchError):
request = tasks.tasklists().list()

def test_client_options_with_empty_universe(self):
fake_universe = "foo.com"
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
discovery = read_datafile("tasks.json")

with self.assertRaises(universe.EmptyUniverseError):
tasks = build_from_document(
discovery,
credentials=credentials,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=""
),
)

def test_client_options_universe_configured_with_mtls(self):
fake_universe = "foo.com"
discovery = read_datafile("tasks.json")

with self.assertRaises(MutualTLSChannelError):
with mock.patch.dict(
"os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}
):
tasks = build_from_document(
discovery,
client_options=google.api_core.client_options.ClientOptions(
universe_domain=fake_universe
),
)

def test_client_options_universe_configured_with_api_override(self):
fake_universe = "foo.com"
fake_api_endpoint = "https://www.bar.com/"
credentials = mock.Mock(universe_domain=fake_universe)
discovery = read_datafile("tasks.json")

tasks = build_from_document(
discovery,
credentials=credentials,
client_options=google.api_core.client_options.ClientOptions(
api_endpoint=fake_api_endpoint, universe_domain=fake_universe
),
)

assert tasks._baseUrl == fake_api_endpoint


if __name__ == "__main__":
unittest.main()

0 comments on commit 359b3ac

Please sign in to comment.