diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..30c3973 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,11 @@ +# Code owners file. +# This file controls who is tagged for review for any given pull request. +# +# For syntax help see: +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax + +# The @googleapis/yoshi-python is the default owner for changes in this repo +* @googleapis/yoshi-python + +# The python-samples-reviewers team is the default owner for samples changes +/samples/ @googleapis/python-samples-owners \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5646eff..b3e177b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ [1]: https://pypi.org/project/google-cloud-core/#history +### [1.4.2](https://www.github.com/googleapis/python-cloud-core/compare/v1.4.1...v1.4.2) (2020-09-29) + + +### Bug Fixes + +* handle query_params tuples in JSONConnection.build_api_url ([#34](https://www.github.com/googleapis/python-cloud-core/issues/34)) ([6a9adb3](https://www.github.com/googleapis/python-cloud-core/commit/6a9adb3dddd5f6d82032aba5efd673ea0641593c)) + + +### Performance Improvements + +* use prettyPrint=false by default ([#28](https://www.github.com/googleapis/python-cloud-core/issues/28)) ([c407b5d](https://www.github.com/googleapis/python-cloud-core/commit/c407b5d617c04affbdb4f444c188edffb25d4336)) + ### [1.4.1](https://www.github.com/googleapis/python-cloud-core/compare/v1.4.0...v1.4.1) (2020-08-06) @@ -16,6 +28,33 @@ * update docs build (via synth) ([#14](https://www.github.com/googleapis/python-cloud-core/issues/14)) ([f1a95ce](https://www.github.com/googleapis/python-cloud-core/commit/f1a95ce89c25f5297470299ca1ef7e1e05a9e99f)) + +## 1.4.2rc2 + +09-24-2020 09:29 PDT + +### Implementation Changes + +- fix: handle query_params tuples in JSONConnection.build_api_url ([#34](https://github.com/googleapis/python-cloud-core/pull/34)) + +### Internal / Testing Changes + +- chore: add default CODEOWNERS ([#33](https://github.com/googleapis/python-cloud-core/pull/33)) + + +## 1.4.2rc1 + +09-21-2020 14:45 PDT + + +### Implementation Changes + +- perf: use prettyPrint=false by default ([#28](https://github.com/googleapis/python-cloud-core/pull/28)) + +### Internal / Testing Changes + +- test: fix mock credentials to allow for quota projects ([#29](https://github.com/googleapis/python-cloud-core/pull/29)) + ## [1.4.0](https://www.github.com/googleapis/python-cloud-core/compare/v1.3.0...v1.4.0) (2020-08-04) @@ -83,7 +122,7 @@ 06/11/2019 15:19 PDT -### Internal Changes +### Internal Changes - Prevent requests from hanging on SSL handshake issue by adding a max timeout of 5 minutes. ([#8207](https://github.com/googleapis/google-cloud-python/pull/8207)) ## 1.0.1 diff --git a/google/cloud/_http.py b/google/cloud/_http.py index 12315c2..74539a5 100644 --- a/google/cloud/_http.py +++ b/google/cloud/_http.py @@ -14,11 +14,13 @@ """Shared implementation of connections to API servers.""" +import collections import json import platform import warnings from pkg_resources import get_distribution +from six.moves import collections_abc from six.moves.urllib.parse import urlencode from google.api_core.client_info import ClientInfo @@ -211,8 +213,18 @@ def build_api_url( ) query_params = query_params or {} - if query_params: - url += "?" + urlencode(query_params, doseq=True) + + if isinstance(query_params, collections_abc.Mapping): + query_params = query_params.copy() + else: + query_params_dict = collections.defaultdict(list) + for key, value in query_params: + query_params_dict[key].append(value) + query_params = query_params_dict + + query_params.setdefault("prettyPrint", "false") + + url += "?" + urlencode(query_params, doseq=True) return url diff --git a/setup.py b/setup.py index 6816343..17e6a30 100644 --- a/setup.py +++ b/setup.py @@ -22,12 +22,12 @@ name = "google-cloud-core" description = "Google Cloud API client core library" -version = "1.4.1" +version = "1.4.2" # Should be one of: # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta' # 'Development Status :: 5 - Production/Stable' -release_status = "Development Status :: 4 - Beta" +release_status = "Development Status :: 5 - Production/Stable" dependencies = ["google-api-core >= 1.19.0, < 2.0.0dev"] extras = {"grpc": "grpcio >= 1.8.2, < 2.0dev"} diff --git a/tests/unit/test__http.py b/tests/unit/test__http.py index 145e302..069ddc0 100644 --- a/tests/unit/test__http.py +++ b/tests/unit/test__http.py @@ -184,9 +184,16 @@ def test_build_api_url_no_extra_query_params(self): client = object() conn = self._make_mock_one(client) # Intended to emulate self.mock_template - URI = "/".join([conn.API_BASE_URL, "mock", conn.API_VERSION, "foo"]) + URI = "/".join([conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=false"]) self.assertEqual(conn.build_api_url("/foo"), URI) + def test_build_api_url_w_pretty_print_query_params(self): + client = object() + conn = self._make_mock_one(client) + uri = conn.build_api_url("/foo", {"prettyPrint": "true"}) + URI = "/".join([conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=true"]) + self.assertEqual(uri, URI) + def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qs from six.moves.urllib.parse import urlsplit @@ -203,6 +210,25 @@ def test_build_api_url_w_extra_query_params(self): parms = dict(parse_qs(qs)) self.assertEqual(parms["bar"], ["baz"]) self.assertEqual(parms["qux"], ["quux", "corge"]) + self.assertEqual(parms["prettyPrint"], ["false"]) + + def test_build_api_url_w_extra_query_params_tuples(self): + from six.moves.urllib.parse import parse_qs + from six.moves.urllib.parse import urlsplit + + client = object() + conn = self._make_mock_one(client) + uri = conn.build_api_url("/foo", [("bar", "baz"), ("qux", "quux"), ("qux", "corge")]) + + scheme, netloc, path, qs, _ = urlsplit(uri) + self.assertEqual("%s://%s" % (scheme, netloc), conn.API_BASE_URL) + # Intended to emulate mock_template + PATH = "/".join(["", "mock", conn.API_VERSION, "foo"]) + self.assertEqual(path, PATH) + parms = dict(parse_qs(qs)) + self.assertEqual(parms["bar"], ["baz"]) + self.assertEqual(parms["qux"], ["quux", "corge"]) + self.assertEqual(parms["prettyPrint"], ["false"]) def test__make_request_no_data_no_content_type_no_headers(self): from google.cloud._http import CLIENT_INFO_HEADER @@ -319,7 +345,7 @@ def test_api_request_defaults(self): "User-Agent": conn.user_agent, CLIENT_INFO_HEADER: conn.user_agent, } - expected_url = "{base}/mock/{version}{path}".format( + expected_url = "{base}/mock/{version}{path}?prettyPrint=false".format( base=conn.API_BASE_URL, version=conn.API_VERSION, path=path ) http.request.assert_called_once_with( @@ -481,7 +507,7 @@ def test_api_request_w_timeout(self): "User-Agent": conn.user_agent, CLIENT_INFO_HEADER: conn.user_agent, } - expected_url = "{base}/mock/{version}{path}".format( + expected_url = "{base}/mock/{version}{path}?prettyPrint=false".format( base=conn.API_BASE_URL, version=conn.API_VERSION, path=path ) http.request.assert_called_once_with( diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 59e2ea0..5b22fe8 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -22,7 +22,7 @@ def _make_credentials(): import google.auth.credentials - return mock.Mock(spec=google.auth.credentials.Credentials) + return mock.Mock(spec=google.auth.credentials.CredentialsWithQuotaProject) class Test_ClientFactoryMixin(unittest.TestCase):