Skip to content

Commit c6f4b63

Browse files
feat: raise detailed response body error messages
1 parent d622575 commit c6f4b63

File tree

3 files changed

+25
-72
lines changed

3 files changed

+25
-72
lines changed

google/cloud/sql/connector/client.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,19 @@ async def _get_metadata(
128128
resp = await self._client.get(url, headers=headers)
129129
if resp.status >= 500:
130130
resp = await retry_50x(self._client.get, url, headers=headers)
131-
resp.raise_for_status()
132-
ret_dict = await resp.json()
131+
# try to get response json for better error message
132+
try:
133+
ret_dict = await resp.json()
134+
print(ret_dict)
135+
if resp.status >= 400:
136+
# if detailed error message is in json response, use as message
137+
message = ret_dict.get("error", {}).get("message")
138+
if message:
139+
resp.reason = message
140+
except Exception:
141+
pass
142+
finally:
143+
resp.raise_for_status()
133144

134145
if ret_dict["region"] != region:
135146
raise ValueError(
@@ -198,8 +209,18 @@ async def _get_ephemeral(
198209
resp = await self._client.post(url, headers=headers, json=data)
199210
if resp.status >= 500:
200211
resp = await retry_50x(self._client.post, url, headers=headers, json=data)
201-
resp.raise_for_status()
202-
ret_dict = await resp.json()
212+
# try to get response json for better error message
213+
try:
214+
ret_dict = await resp.json()
215+
if resp.status >= 400:
216+
# if detailed error message is in json response, use as message
217+
message = ret_dict.get("error", {}).get("message")
218+
if message:
219+
resp.reason = message
220+
except Exception:
221+
pass
222+
finally:
223+
resp.raise_for_status()
203224

204225
ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
205226

google/cloud/sql/connector/instance.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
from datetime import timezone
2323
import logging
2424

25-
import aiohttp
26-
2725
from google.cloud.sql.connector.client import CloudSQLClient
2826
from google.cloud.sql.connector.connection_info import ConnectionInfo
2927
from google.cloud.sql.connector.connection_name import _parse_instance_connection_name
@@ -128,15 +126,6 @@ async def _perform_refresh(self) -> ConnectionInfo:
128126
f"expiration = {connection_info.expiration.isoformat()}"
129127
)
130128

131-
except aiohttp.ClientResponseError as e:
132-
logger.debug(
133-
f"['{self._conn_name}']: Connection info "
134-
f"refresh operation failed: {str(e)}"
135-
)
136-
if e.status == 403:
137-
e.message = "Forbidden: Authenticated IAM principal does not seem authorized to make API request. Verify 'Cloud SQL Admin API' is enabled within your GCP project and 'Cloud SQL Client' role has been granted to IAM principal."
138-
raise
139-
140129
except Exception as e:
141130
logger.debug(
142131
f"['{self._conn_name}']: Connection info "

tests/unit/test_instance.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
import asyncio
1818
import datetime
1919

20-
from aiohttp import ClientResponseError
21-
from aiohttp import RequestInfo
22-
from aioresponses import aioresponses
23-
from google.auth.credentials import Credentials
2420
from mock import patch
2521
import mocks
2622
import pytest # noqa F401 Needed to run the tests
@@ -266,59 +262,6 @@ async def test_get_preferred_ip_CloudSQLIPTypeError(cache: RefreshAheadCache) ->
266262
instance_metadata.get_preferred_ip(IPTypes.PSC)
267263

268264

269-
@pytest.mark.asyncio
270-
async def test_ClientResponseError(
271-
fake_credentials: Credentials,
272-
) -> None:
273-
"""
274-
Test that detailed error message is applied to ClientResponseError.
275-
"""
276-
# mock Cloud SQL Admin API calls with exceptions
277-
keys = asyncio.create_task(generate_keys())
278-
client = CloudSQLClient(
279-
sqladmin_api_endpoint="https://sqladmin.googleapis.com",
280-
quota_project=None,
281-
credentials=fake_credentials,
282-
)
283-
get_url = "https://sqladmin.googleapis.com/sql/v1beta4/projects/my-project/instances/my-instance/connectSettings"
284-
post_url = "https://sqladmin.googleapis.com/sql/v1beta4/projects/my-project/instances/my-instance:generateEphemeralCert"
285-
with aioresponses() as mocked:
286-
mocked.get(
287-
get_url,
288-
status=403,
289-
exception=ClientResponseError(
290-
RequestInfo(get_url, "GET", headers=[]), history=[], status=403 # type: ignore
291-
),
292-
repeat=True,
293-
)
294-
mocked.post(
295-
post_url,
296-
status=403,
297-
exception=ClientResponseError(
298-
RequestInfo(post_url, "POST", headers=[]), history=[], status=403 # type: ignore
299-
),
300-
repeat=True,
301-
)
302-
cache = RefreshAheadCache(
303-
"my-project:my-region:my-instance",
304-
client,
305-
keys,
306-
)
307-
try:
308-
await cache._current
309-
except ClientResponseError as e:
310-
assert e.status == 403
311-
assert (
312-
e.message == "Forbidden: Authenticated IAM principal does not "
313-
"seem authorized to make API request. Verify "
314-
"'Cloud SQL Admin API' is enabled within your GCP project and "
315-
"'Cloud SQL Client' role has been granted to IAM principal."
316-
)
317-
finally:
318-
await cache.close()
319-
await client.close()
320-
321-
322265
@pytest.mark.asyncio
323266
async def test_AutoIAMAuthNotSupportedError(fake_client: CloudSQLClient) -> None:
324267
"""

0 commit comments

Comments
 (0)