From cd4e8f429422232dd82ef7e9bc685061d5df94a1 Mon Sep 17 00:00:00 2001 From: Dmitry Frenkel Date: Thu, 30 Jul 2020 12:34:03 -0700 Subject: [PATCH] fix: discovery uses V2 when version is None (#975) Passing version=None to discovery.build(), with intent to always get the latest version of the discovery document, no longer attempts to use Discovery V1, which fails with Bad Request errors. Instead, it uses Discovery V2, which supports null version use case. Fixes: #971 --- googleapiclient/discovery.py | 28 ++++++++++++++++++++++++++-- tests/test_discovery.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index c7981157f22..7244c5bd610 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py @@ -29,7 +29,7 @@ # Standard library imports import copy - +from collections import OrderedDict try: from email.generator import BytesGenerator except ImportError: @@ -241,7 +241,8 @@ def build( else: discovery_http = http - for discovery_url in (discoveryServiceUrl, V2_DISCOVERY_URI): + for discovery_url in \ + _discovery_service_uri_options(discoveryServiceUrl, version): requested_url = uritemplate.expand(discovery_url, params) try: @@ -270,6 +271,29 @@ def build( raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName, version)) +def _discovery_service_uri_options(discoveryServiceUrl, version): + """ + Returns Discovery URIs to be used for attemnting to build the API Resource. + + Args: + discoveryServiceUrl: + string, the Original Discovery Service URL preferred by the customer. + version: + string, API Version requested + + Returns: + A list of URIs to be tried for the Service Discovery, in order. + """ + + urls = [discoveryServiceUrl, V2_DISCOVERY_URI] + # V1 Discovery won't work if the requested version is None + if discoveryServiceUrl == V1_DISCOVERY_URI and version is None: + logger.warning( + "Discovery V1 does not support empty versions. Defaulting to V2...") + urls.pop(0) + return list(OrderedDict.fromkeys(urls)) + + def _retrieve_discovery_doc(url, http, cache_discovery, cache=None, developerKey=None, num_retries=1): """Retrieves the discovery_doc from cache or the internet. diff --git a/tests/test_discovery.py b/tests/test_discovery.py index f59ea151df5..87cc8ed0d4c 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -837,6 +837,37 @@ def test_api_endpoint_override_from_client_options_dict(self): ) self.assertEqual(zoo._baseUrl, api_endpoint) + def test_discovery_with_empty_version_uses_v2(self): + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) + build("zoo", version=None, http=http, cache_discovery=False) + validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI) + + def test_discovery_with_empty_version_preserves_custom_uri(self): + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) + custom_discovery_uri = "https://foo.bar/$discovery" + build( + "zoo", version=None, http=http, + cache_discovery=False, discoveryServiceUrl=custom_discovery_uri) + validate_discovery_requests( + self, http, "zoo", None, custom_discovery_uri) + + def test_discovery_with_valid_version_uses_v1(self): + http = HttpMockSequence( + [ + ({"status": "200"}, read_datafile("zoo.json", "rb")), + ] + ) + build("zoo", version="v123", http=http, cache_discovery=False) + validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI) + class DiscoveryRetryFromHttp(unittest.TestCase): def test_repeated_500_retries_and_fails(self):