Skip to content

Commit 1308b5b

Browse files
feat: Update interface error messages (#195)
* Broke out various errors when resolving engine urls and added status codes to InterfaceErrors to try to determine why SDK is failing to get engine endpoints. * Updated version number to 0.9.3. * Trying to send engine endpoint, whether get url or get-url-by-name. * Trying to break out request errors. * Added return. * Added a comment. * Updated exception handling logic. * Slight refactoring of error catching code in _resolve_engine_url(). * Slight refactoring of error catching code in _resolve_engine_url() to display url in all cases of failure. * Renamed GET_ENGINE_URL to GET_ENGINE_BY_NAME_URL to clarify it for me as I debugged. * Upped default timeout to 10 seconds. * Removed references to from error messages in timeouts. * Removed some print()s and trying to get tests/unit/test_connection::test_connect_engine_name to work. * Still trying to get test_connect_engine_name() to pass. * Passed final unit tests. Whew. * Switched the order of arguments in an and clause for better short-circuiting.
1 parent 98bb63e commit 1308b5b

File tree

10 files changed

+85
-77
lines changed

10 files changed

+85
-77
lines changed

src/firebolt/async_db/connection.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
InterfaceError,
2121
)
2222
from firebolt.utils.urls import (
23-
ACCOUNT_ENGINE_BY_NAME_URL,
23+
ACCOUNT_ENGINE_ID_BY_NAME_URL,
2424
ACCOUNT_ENGINE_URL,
2525
ACCOUNT_ENGINE_URL_BY_DATABASE_NAME,
2626
)
2727
from firebolt.utils.usage_tracker import get_user_agent_header
2828
from firebolt.utils.util import fix_url_schema
2929

30-
DEFAULT_TIMEOUT_SECONDS: int = 5
30+
DEFAULT_TIMEOUT_SECONDS: int = 60
3131
KEEPALIVE_FLAG: int = 1
3232
KEEPIDLE_RATE: int = 60 # seconds
3333
AUTH_CREDENTIALS_DEPRECATION_MESSAGE = """ Passing connection credentials
@@ -56,32 +56,37 @@ async def _resolve_engine_url(
5656
base_url=api_endpoint,
5757
account_name=account_name,
5858
api_endpoint=api_endpoint,
59+
timeout=Timeout(DEFAULT_TIMEOUT_SECONDS),
5960
) as client:
61+
account_id = await client.account_id
62+
url = ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=account_id)
6063
try:
61-
account_id = await client.account_id
6264
response = await client.get(
63-
url=ACCOUNT_ENGINE_BY_NAME_URL.format(account_id=account_id),
65+
url=url,
6466
params={"engine_name": engine_name},
6567
)
6668
response.raise_for_status()
6769
engine_id = response.json()["engine_id"]["engine_id"]
68-
response = await client.get(
69-
url=ACCOUNT_ENGINE_URL.format(
70-
account_id=account_id, engine_id=engine_id
71-
),
72-
)
70+
url = ACCOUNT_ENGINE_URL.format(account_id=account_id, engine_id=engine_id)
71+
response = await client.get(url=url)
7372
response.raise_for_status()
7473
return response.json()["engine"]["endpoint"]
7574
except HTTPStatusError as e:
7675
# Engine error would be 404.
7776
if e.response.status_code != 404:
78-
raise InterfaceError(f"Unable to retrieve engine endpoint: {e}.")
77+
raise InterfaceError(
78+
f"Error {e.__class__.__name__}: Unable to retrieve engine "
79+
f"endpoint {url}."
80+
)
7981
# Once this is point is reached we've already authenticated with
8082
# the backend so it's safe to assume the cause of the error is
8183
# missing engine.
8284
raise FireboltEngineError(f"Firebolt engine {engine_name} does not exist.")
8385
except (JSONDecodeError, RequestError, RuntimeError, HTTPStatusError) as e:
84-
raise InterfaceError(f"Unable to retrieve engine endpoint: {e}.")
86+
raise InterfaceError(
87+
f"Error {e.__class__.__name__}: "
88+
f"Unable to retrieve engine endpoint {url}."
89+
)
8590

8691

8792
async def _get_database_default_engine_url(
@@ -95,6 +100,7 @@ async def _get_database_default_engine_url(
95100
base_url=api_endpoint,
96101
account_name=account_name,
97102
api_endpoint=api_endpoint,
103+
timeout=Timeout(DEFAULT_TIMEOUT_SECONDS),
98104
) as client:
99105
try:
100106
account_id = await client.account_id

src/firebolt/db/connection.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
from firebolt.utils.exception import ConnectionClosedError
1414
from firebolt.utils.util import AsyncJobThread, async_to_sync
1515

16-
DEFAULT_TIMEOUT_SECONDS: int = 5
17-
1816

1917
class Connection(AsyncBaseConnection):
2018
"""

src/firebolt/service/engine.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
from firebolt.service.types import EngineOrder, EngineType, WarmupMethod
1212
from firebolt.utils.exception import FireboltError
1313
from firebolt.utils.urls import (
14-
ACCOUNT_ENGINE_BY_NAME_URL,
14+
ACCOUNT_ENGINE_ID_BY_NAME_URL,
1515
ACCOUNT_ENGINE_URL,
16-
ACCOUNT_ENGINES_URL,
16+
ACCOUNT_LIST_ENGINES_URL,
1717
ENGINES_BY_IDS_URL,
1818
)
1919
from firebolt.utils.util import prune_dict
@@ -51,7 +51,7 @@ def get_by_name(self, name: str) -> Engine:
5151
"""Get an engine from Firebolt by its name."""
5252

5353
response = self.client.get(
54-
url=ACCOUNT_ENGINE_BY_NAME_URL.format(account_id=self.account_id),
54+
url=ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=self.account_id),
5555
params={"engine_name": name},
5656
)
5757
engine_id = response.json()["engine_id"]["engine_id"]
@@ -88,7 +88,7 @@ def get_many(
8888
).key.region_id
8989

9090
response = self.client.get(
91-
url=ACCOUNT_ENGINES_URL.format(account_id=self.account_id),
91+
url=ACCOUNT_LIST_ENGINES_URL.format(account_id=self.account_id),
9292
params=prune_dict(
9393
{
9494
"page.first": 5000, # FUTURE: pagination support w/ generator
@@ -210,7 +210,7 @@ def _send_create_engine(
210210
"""
211211

212212
response = self.client.post(
213-
url=ACCOUNT_ENGINES_URL.format(account_id=self.account_id),
213+
url=ACCOUNT_LIST_ENGINES_URL.format(account_id=self.account_id),
214214
headers={"Content-type": "application/json"},
215215
json=_EngineCreateRequest(
216216
account_id=self.account_id,

src/firebolt/utils/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
ACCOUNT_ENGINE_START_URL = ACCOUNT_ENGINE_URL + ":start"
1313
ACCOUNT_ENGINE_RESTART_URL = ACCOUNT_ENGINE_URL + ":restart"
1414
ACCOUNT_ENGINE_STOP_URL = ACCOUNT_ENGINE_URL + ":stop"
15-
ACCOUNT_ENGINES_URL = "/core/v1/accounts/{account_id}/engines"
16-
ACCOUNT_ENGINE_BY_NAME_URL = ACCOUNT_ENGINES_URL + ":getIdByName"
15+
ACCOUNT_LIST_ENGINES_URL = "/core/v1/accounts/{account_id}/engines"
16+
ACCOUNT_ENGINE_ID_BY_NAME_URL = ACCOUNT_LIST_ENGINES_URL + ":getIdByName"
1717
ACCOUNT_ENGINE_REVISION_URL = ACCOUNT_ENGINE_URL + "/engineRevisions/{revision_id}"
18-
ACCOUNT_ENGINE_URL_BY_DATABASE_NAME = ACCOUNT_ENGINES_URL + ":getURLByDatabaseName"
18+
ACCOUNT_ENGINE_URL_BY_DATABASE_NAME = ACCOUNT_LIST_ENGINES_URL + ":getURLByDatabaseName"
1919

2020
ACCOUNT_DATABASES_URL = "/core/v1/accounts/{account_id}/databases"
2121
ACCOUNT_DATABASE_URL = "/core/v1/accounts/{account_id}/databases/{database_id}"

tests/integration/dbapi/async/test_queries_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ async def test_server_side_async_execution_query(connection: Connection) -> None
427427
with connection.cursor() as c:
428428
query_id = await c.execute("SELECT 1", [], async_execution=True)
429429
assert (
430-
type(query_id) is str and query_id
430+
query_id and type(query_id) is str
431431
), "Invalid query id was returned from server-side async query."
432432

433433

tests/integration/dbapi/sync/test_queries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ def test_server_side_async_execution_query(connection: Connection) -> None:
423423
with connection.cursor() as c:
424424
query_id = c.execute("SELECT 1", [], async_execution=True)
425425
assert (
426-
type(query_id) is str and query_id
426+
query_id and type(query_id) is str
427427
), "Invalid query id was returned from server-side async query."
428428

429429

tests/unit/async_db/test_connection.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
FireboltEngineError,
1919
)
2020
from firebolt.utils.token_storage import TokenSecureStorage
21-
from firebolt.utils.urls import ACCOUNT_ENGINE_BY_NAME_URL
21+
from firebolt.utils.urls import ACCOUNT_ENGINE_ID_BY_NAME_URL
2222

2323

2424
async def test_closed_connection(connection: Connection) -> None:
@@ -145,8 +145,8 @@ async def test_connect_engine_name(
145145
account_id_url: Pattern,
146146
account_id_callback: Callable,
147147
engine_id: str,
148-
get_engine_url: str,
149-
get_engine_callback: Callable,
148+
get_engine_url_by_id_url: str,
149+
get_engine_url_by_id_callback: Callable,
150150
python_query_data: List[List[ColType]],
151151
account_id: str,
152152
):
@@ -166,14 +166,14 @@ async def test_connect_engine_name(
166166
httpx_mock.add_callback(auth_callback, url=auth_url)
167167
httpx_mock.add_callback(query_callback, url=query_url)
168168
httpx_mock.add_callback(account_id_callback, url=account_id_url)
169-
httpx_mock.add_callback(get_engine_callback, url=get_engine_url)
169+
httpx_mock.add_callback(get_engine_url_by_id_callback, url=get_engine_url_by_id_url)
170170

171171
engine_name = settings.server.split(".")[0]
172172

173173
# Mock engine id lookup error
174174
httpx_mock.add_response(
175175
url=f"https://{settings.server}"
176-
+ ACCOUNT_ENGINE_BY_NAME_URL.format(account_id=account_id)
176+
+ ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=account_id)
177177
+ f"?engine_name={engine_name}",
178178
status_code=codes.NOT_FOUND,
179179
)
@@ -192,7 +192,7 @@ async def test_connect_engine_name(
192192
# Mock engine id lookup by name
193193
httpx_mock.add_response(
194194
url=f"https://{settings.server}"
195-
+ ACCOUNT_ENGINE_BY_NAME_URL.format(account_id=account_id)
195+
+ ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=account_id)
196196
+ f"?engine_name={engine_name}",
197197
status_code=codes.OK,
198198
json={"engine_id": {"engine_id": engine_id}},
@@ -219,15 +219,8 @@ async def test_connect_default_engine(
219219
query_url: str,
220220
account_id_url: Pattern,
221221
account_id_callback: Callable,
222-
engine_id: str,
223-
get_engine_url: str,
224-
get_engine_callback: Callable,
225-
database_by_name_url: str,
226-
database_by_name_callback: Callable,
227-
database_id: str,
228222
engine_by_db_url: str,
229223
python_query_data: List[List[ColType]],
230-
account_id: str,
231224
):
232225
httpx_mock.add_callback(auth_callback, url=auth_url)
233226
httpx_mock.add_callback(query_callback, url=query_url)

tests/unit/conftest.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@
3434
AUTH_URL,
3535
DATABASES_URL,
3636
ENGINES_URL,
37-
PROVIDERS_URL,
3837
)
3938
from tests.unit.db_conftest import * # noqa
40-
from tests.unit.util import list_to_paginated_response
4139

4240

4341
# Register nofakefs mark
@@ -197,25 +195,46 @@ def do_mock(
197195

198196
@fixture
199197
def engine_id() -> str:
200-
return "engine_id"
198+
return "mock_engine_id"
201199

202200

203201
@fixture
204-
def get_engine_url(settings: Settings, account_id: str, engine_id: str) -> str:
202+
def engine_endpoint() -> str:
203+
return "mock_engine_endpoint"
204+
205+
206+
@fixture
207+
def engine_name() -> str:
208+
return "mock_engine_name"
209+
210+
211+
@fixture
212+
def get_engine_name_by_id_url(
213+
settings: Settings, account_id: str, engine_id: str
214+
) -> str:
215+
return f"https://{settings.server}" + ACCOUNT_ENGINE_URL.format(
216+
account_id=account_id, engine_id=engine_id
217+
)
218+
219+
220+
@fixture
221+
def get_engine_url_by_id_url(
222+
settings: Settings, account_id: str, engine_id: str
223+
) -> str:
205224
return f"https://{settings.server}" + ACCOUNT_ENGINE_URL.format(
206225
account_id=account_id, engine_id=engine_id
207226
)
208227

209228

210229
@fixture
211-
def get_engine_callback(
212-
get_engine_url: str, engine_id: str, settings: Settings
230+
def get_engine_url_by_id_callback(
231+
get_engine_url_by_id_url: str, engine_id: str, settings: Settings
213232
) -> Callable:
214233
def do_mock(
215234
request: Request = None,
216235
**kwargs,
217236
) -> Response:
218-
assert request.url == get_engine_url
237+
assert request.url == get_engine_url_by_id_url
219238
return Response(
220239
status_code=httpx.codes.OK,
221240
json={
@@ -240,26 +259,6 @@ def do_mock(
240259
return do_mock
241260

242261

243-
@fixture
244-
def get_providers_url(settings: Settings, account_id: str, engine_id: str) -> str:
245-
return f"https://{settings.server}{PROVIDERS_URL}"
246-
247-
248-
@fixture
249-
def get_providers_callback(get_providers_url: str, provider: Provider) -> Callable:
250-
def do_mock(
251-
request: Request = None,
252-
**kwargs,
253-
) -> Response:
254-
assert request.url == get_providers_url
255-
return Response(
256-
status_code=httpx.codes.OK,
257-
json=list_to_paginated_response([provider]),
258-
)
259-
260-
return do_mock
261-
262-
263262
@fixture
264263
def get_engines_url(settings: Settings) -> str:
265264
return f"https://{settings.server}{ENGINES_URL}"

tests/unit/db/test_connection.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
AccountNotFoundError,
1717
ConfigurationError,
1818
ConnectionClosedError,
19+
FireboltEngineError,
1920
)
2021
from firebolt.utils.token_storage import TokenSecureStorage
21-
from firebolt.utils.urls import ACCOUNT_ENGINE_BY_NAME_URL
22+
from firebolt.utils.urls import ACCOUNT_ENGINE_ID_BY_NAME_URL
2223

2324

2425
def test_closed_connection(connection: Connection) -> None:
@@ -143,10 +144,8 @@ def test_connect_engine_name(
143144
account_id_url: Pattern,
144145
account_id_callback: Callable,
145146
engine_id: str,
146-
get_engine_url: str,
147-
get_engine_callback: Callable,
148-
get_providers_url: str,
149-
get_providers_callback: Callable,
147+
get_engine_url_by_id_url: str,
148+
get_engine_url_by_id_callback: Callable,
150149
python_query_data: List[List[ColType]],
151150
account_id: str,
152151
):
@@ -164,14 +163,32 @@ def test_connect_engine_name(
164163
httpx_mock.add_callback(auth_callback, url=auth_url)
165164
httpx_mock.add_callback(query_callback, url=query_url)
166165
httpx_mock.add_callback(account_id_callback, url=account_id_url)
167-
httpx_mock.add_callback(get_engine_callback, url=get_engine_url)
166+
httpx_mock.add_callback(get_engine_url_by_id_callback, url=get_engine_url_by_id_url)
168167

169168
engine_name = settings.server.split(".")[0]
170169

170+
# Mock engine id lookup error
171+
httpx_mock.add_response(
172+
url=f"https://{settings.server}"
173+
+ ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=account_id)
174+
+ f"?engine_name={engine_name}",
175+
status_code=codes.NOT_FOUND,
176+
)
177+
178+
with raises(FireboltEngineError):
179+
connect(
180+
database="db",
181+
username="username",
182+
password="password",
183+
engine_name=engine_name,
184+
account_name=settings.account_name,
185+
api_endpoint=settings.server,
186+
)
187+
171188
# Mock engine id lookup by name
172189
httpx_mock.add_response(
173190
url=f"https://{settings.server}"
174-
+ ACCOUNT_ENGINE_BY_NAME_URL.format(account_id=account_id)
191+
+ ACCOUNT_ENGINE_ID_BY_NAME_URL.format(account_id=account_id)
175192
+ f"?engine_name={engine_name}",
176193
status_code=codes.OK,
177194
json={"engine_id": {"engine_id": engine_id}},
@@ -198,11 +215,6 @@ def test_connect_default_engine(
198215
query_url: str,
199216
account_id_url: Pattern,
200217
account_id_callback: Callable,
201-
engine_id: str,
202-
get_engine_url: str,
203-
get_engine_callback: Callable,
204-
database_by_name_url: str,
205-
database_by_name_callback: Callable,
206218
database_id: str,
207219
engine_by_db_url: str,
208220
python_query_data: List[List[ColType]],

tests/unit/service/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
ACCOUNT_DATABASE_URL,
2424
ACCOUNT_DATABASES_URL,
2525
ACCOUNT_ENGINE_URL,
26-
ACCOUNT_ENGINES_URL,
2726
ACCOUNT_INSTANCE_TYPES_URL,
27+
ACCOUNT_LIST_ENGINES_URL,
2828
PROVIDERS_URL,
2929
REGIONS_URL,
3030
)
@@ -261,7 +261,7 @@ def do_mock(
261261

262262
@fixture
263263
def engine_url(settings: Settings, account_id) -> str:
264-
return f"https://{settings.server}" + ACCOUNT_ENGINES_URL.format(
264+
return f"https://{settings.server}" + ACCOUNT_LIST_ENGINES_URL.format(
265265
account_id=account_id
266266
)
267267

0 commit comments

Comments
 (0)