Skip to content

Commit 12e7a4e

Browse files
authored
Merge pull request #16 from cloudblue/enh/LITE-26853
LITE-26853 Better handling of DB and Client Server errors
2 parents 9a3e189 + b8fd1f4 commit 12e7a4e

File tree

8 files changed

+55
-57
lines changed

8 files changed

+55
-57
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
* `c-` for Connect SPA directly borrowed ones
1414
* `ez-` for Connect SPA components replicas
1515
* `ui-` for ones imported from Connect UI Toolkit
16-
* Table columns now may be styled directly with a column descriptor `style` property
16+
* Table columns now may be styled directly with a column descriptor `style` property
17+
* 0.2.3: Better handling of DB and Client Server errors

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM cloudblueconnect/connect-extension-runner:27.16
1+
FROM cloudblueconnect/connect-extension-runner:27.17
22

33
COPY pyproject.toml /install_temp/.
44
COPY poetry.* /install_temp/.

dbaas/extension.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"name": "DBaaS",
33
"description": "On-demand provisioning of cloud-based database storages as a service.",
4-
"version": "0.2.2",
4+
"version": "0.2.3",
55
"audience": ["reseller", "distributor", "vendor"],
6-
"readme_url": "https://github.com/cloudblue/connect-extension-dbaas/blob/0.2.2/README.md",
7-
"changelog_url": "https://github.com/cloudblue/connect-extension-dbaas/blob/0.2.2/CHANGELOG.md",
6+
"readme_url": "https://github.com/cloudblue/connect-extension-dbaas/blob/0.2.3/README.md",
7+
"changelog_url": "https://github.com/cloudblue/connect-extension-dbaas/blob/0.2.3/CHANGELOG.md",
88
"icon": "googleExtensionBaseline"
99
}

dbaas/webapp.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from logging import LoggerAdapter
88
from typing import Optional
99

10+
from connect.client import ClientError
1011
from connect.eaas.core.decorators import (
1112
devops_pages,
1213
proxied_connect_api,
@@ -39,14 +40,16 @@
3940
_db_id_type = constr(strict=True, max_length=16)
4041

4142

42-
async def handle_db_exceptions_mw(request: Request, call_next) -> responses.Response:
43-
try:
44-
response = await call_next(request)
43+
async def na_exception_handler(request: Request, exc: Exception) -> responses.JSONResponse:
44+
return responses.JSONResponse({'message': 'Service Unavailable.'}, status_code=503)
4545

46-
except DBException:
47-
response = responses.JSONResponse({'message': 'Service Unavailable.'}, status_code=503)
4846

49-
return response
47+
async def client_error_handler(request: Request, exc: ClientError) -> responses.JSONResponse:
48+
if (not exc.status_code) or exc.status_code >= 500:
49+
return await na_exception_handler(request, exc)
50+
51+
message = exc.errors[0] if exc.errors else exc.message
52+
return responses.JSONResponse({'message': message}, status_code=400)
5053

5154

5255
@web_app(router)
@@ -60,8 +63,11 @@ async def handle_db_exceptions_mw(request: Request, call_next) -> responses.Resp
6063
])
6164
class DBaaSWebApplication(WebApplicationBase):
6265
@classmethod
63-
def get_middlewares(cls):
64-
return [handle_db_exceptions_mw]
66+
def get_exception_handlers(cls, _):
67+
return {
68+
ClientError: client_error_handler,
69+
DBException: na_exception_handler,
70+
}
6571

6672
@router.get(
6773
'/v1/databases',

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

poetry.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ readme = "./README.md"
1414

1515
[tool.poetry.dependencies]
1616
python = ">=3.8,<4"
17-
connect-eaas-core = ">=27.10,<28"
17+
connect-eaas-core = ">=27.13,<28"
1818
motor = "3.*"
1919
cryptography = "39.*"
2020

tests/test_webapp.py

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# Copyright (c) 2023, Ingram Micro
44
# All rights reserved.
55
#
6-
from unittest.mock import AsyncMock
76

87
import pytest
98
from connect.client import ClientError
@@ -18,7 +17,7 @@
1817
DatabaseOutList,
1918
RegionOut,
2019
)
21-
from dbaas.webapp import DBaaSWebApplication, handle_db_exceptions_mw
20+
from dbaas.webapp import client_error_handler, DBaaSWebApplication, na_exception_handler
2221

2322
from tests.constants import DB_DEP_MOCK, INSTALLATION_CLIENT_DEP_MOCK
2423
from tests.factories import DBFactory, RegionFactory
@@ -29,45 +28,37 @@
2928

3029

3130
@pytest.mark.asyncio
32-
async def test_handle_db_exceptions_mw_no_exceptions():
33-
call_next = AsyncMock(return_value='response')
31+
@pytest.mark.parametrize('error, body, code', (
32+
(ClientError(message='abc', status_code=400), b'{"message":"abc"}', 400),
33+
(ClientError(message='abc', status_code=404), b'{"message":"abc"}', 400),
34+
(ClientError(message='abc'), b'{"message":"Service Unavailable."}', 503),
35+
(ClientError(message='abc', status_code=500), b'{"message":"Service Unavailable."}', 503),
36+
(ClientError(errors=['cbd'], status_code=503), b'{"message":"Service Unavailable."}', 503),
37+
(ClientError(errors=['cbd', 'abc'], message="x", status_code=404), b'{"message":"cbd"}', 400),
38+
))
39+
async def test_client_error_handler(error, body, code):
40+
result = await client_error_handler(None, error)
3441

35-
result = await handle_db_exceptions_mw('request', call_next)
36-
assert result == 'response'
37-
38-
call_next.assert_called_once_with('request')
42+
assert result.status_code == code
43+
assert result.body == body
3944

4045

4146
@pytest.mark.asyncio
4247
@pytest.mark.parametrize('error_cls', (PyMongoError, ServerSelectionTimeoutError))
43-
async def test_handle_db_exceptions_mw_db_exception(error_cls):
44-
def raise_err(*a):
45-
raise error_cls('test')
46-
47-
call_next = AsyncMock(side_effect=raise_err)
48+
async def test_na_exception_handler(error_cls):
49+
result = await na_exception_handler(None, error_cls)
4850

49-
result = await handle_db_exceptions_mw('request1', call_next)
5051
assert result.status_code == 503
52+
assert result.body == b'{"message":"Service Unavailable."}'
5153

52-
call_next.assert_called_once_with('request1')
53-
54-
55-
@pytest.mark.asyncio
56-
@pytest.mark.parametrize('error_cls', (RuntimeError, ClientError))
57-
async def test_handle_db_exceptions_mw_other_exception(error_cls):
58-
def raise_err(*a):
59-
raise error_cls('test')
6054

61-
call_next = AsyncMock(side_effect=raise_err)
55+
def test_get_exception_handlers():
56+
handlers = DBaaSWebApplication.get_exception_handlers({RuntimeError: None})
6257

63-
with pytest.raises(error_cls):
64-
await handle_db_exceptions_mw('request', call_next)
65-
66-
call_next.assert_called_once_with('request')
67-
68-
69-
def test_get_middlewares():
70-
assert DBaaSWebApplication.get_middlewares() == [handle_db_exceptions_mw]
58+
assert handlers == {
59+
ClientError: client_error_handler,
60+
PyMongoError: na_exception_handler,
61+
}
7162

7263

7364
@pytest.mark.asyncio

0 commit comments

Comments
 (0)