Skip to content

Commit 27494dd

Browse files
committed
Add error handlers for functions api
1 parent 5ebc54c commit 27494dd

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed
Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,102 @@
11
from common_library.errors_classes import OsparcErrorMixin
2+
from servicelib.aiohttp import status
23

34

45
class FunctionBaseError(OsparcErrorMixin, Exception):
5-
pass
6+
status_code: int
67

78

89
class FunctionJobReadAccessDeniedError(FunctionBaseError):
910
msg_template: str = (
1011
"Function job {function_job_id} read access denied for user {user_id}"
1112
)
13+
status_code: int = status.HTTP_403_FORBIDDEN
1214

1315

1416
class FunctionIDNotFoundError(FunctionBaseError):
1517
msg_template: str = "Function {function_id} not found"
18+
status_code: int = status.HTTP_404_NOT_FOUND
1619

1720

1821
class FunctionJobIDNotFoundError(FunctionBaseError):
1922
msg_template: str = "Function job {function_job_id} not found"
23+
status_code: int = status.HTTP_404_NOT_FOUND
2024

2125

2226
class FunctionInputsValidationError(FunctionBaseError):
2327
msg_template: str = "Function inputs validation failed: {error}"
28+
status_code: int = status.HTTP_422_UNPROCESSABLE_ENTITY
2429

2530

2631
class FunctionReadAccessDeniedError(FunctionBaseError):
2732
msg_template: str = "Function {function_id} read access denied for user {user_id}"
33+
status_code: int = status.HTTP_403_FORBIDDEN
2834

2935

3036
class FunctionJobCollectionIDNotFoundError(FunctionBaseError):
3137
msg_template: str = "Function job collection {function_job_collection_id} not found"
38+
status_code: int = status.HTTP_404_NOT_FOUND
3239

3340

3441
class UnsupportedFunctionClassError(FunctionBaseError):
3542
msg_template: str = "Function class {function_class} is not supported"
43+
status_code: int = status.HTTP_400_BAD_REQUEST
3644

3745

3846
class UnsupportedFunctionJobClassError(FunctionBaseError):
3947
msg_template: str = "Function job class {function_job_class} is not supported"
48+
status_code: int = status.HTTP_400_BAD_REQUEST
4049

4150

4251
class UnsupportedFunctionFunctionJobClassCombinationError(FunctionBaseError):
4352
msg_template: str = (
4453
"Function class {function_class} and function job class {function_job_class} combination is not supported"
4554
)
55+
status_code: int = status.HTTP_400_BAD_REQUEST
4656

4757

4858
class FunctionJobCollectionReadAccessDeniedError(FunctionBaseError):
4959
msg_template: str = (
5060
"Function job collection {function_job_collection_id} read access denied for user {user_id}"
5161
)
62+
status_code: int = status.HTTP_403_FORBIDDEN
5263

5364

5465
class FunctionWriteAccessDeniedError(FunctionBaseError):
5566
msg_template: str = "Function {function_id} write access denied for user {user_id}"
67+
status_code: int = status.HTTP_403_FORBIDDEN
5668

5769

5870
class FunctionJobWriteAccessDeniedError(FunctionBaseError):
5971
msg_template: str = (
6072
"Function job {function_job_id} write access denied for user {user_id}"
6173
)
74+
status_code: int = status.HTTP_403_FORBIDDEN
6275

6376

6477
class FunctionJobCollectionWriteAccessDeniedError(FunctionBaseError):
6578
msg_template: str = (
6679
"Function job collection {function_job_collection_id} write access denied for user {user_id}"
6780
)
81+
status_code: int = status.HTTP_403_FORBIDDEN
6882

6983

7084
class FunctionExecuteAccessDeniedError(FunctionBaseError):
7185
msg_template: str = (
7286
"Function {function_id} execute access denied for user {user_id}"
7387
)
88+
status_code: int = status.HTTP_403_FORBIDDEN
7489

7590

7691
class FunctionJobExecuteAccessDeniedError(FunctionBaseError):
7792
msg_template: str = (
7893
"Function job {function_job_id} execute access denied for user {user_id}"
7994
)
95+
status_code: int = status.HTTP_403_FORBIDDEN
8096

8197

8298
class FunctionJobCollectionExecuteAccessDeniedError(FunctionBaseError):
8399
msg_template: str = (
84100
"Function job collection {function_job_collection_id} execute access denied for user {user_id}"
85101
)
102+
status_code: int = status.HTTP_403_FORBIDDEN

services/api-server/src/simcore_service_api_server/exceptions/handlers/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from fastapi import FastAPI
22
from fastapi.exceptions import RequestValidationError
33
from httpx import HTTPError as HttpxException
4+
from models_library.functions_errors import FunctionBaseError
45
from starlette import status
56
from starlette.exceptions import HTTPException
67

@@ -9,6 +10,7 @@
910
from ..custom_errors import CustomBaseError
1011
from ..log_streaming_errors import LogStreamingBaseError
1112
from ._custom_errors import custom_error_handler
13+
from ._handler_function_errors import function_error_handler
1214
from ._handlers_backend_errors import backend_error_handler
1315
from ._handlers_factory import make_handler_for_exception
1416
from ._http_exceptions import http_exception_handler
@@ -24,6 +26,7 @@ def setup(app: FastAPI, *, is_debug: bool = False):
2426
app.add_exception_handler(LogStreamingBaseError, log_handling_error_handler)
2527
app.add_exception_handler(CustomBaseError, custom_error_handler)
2628
app.add_exception_handler(BaseBackEndError, backend_error_handler)
29+
app.add_exception_handler(FunctionBaseError, function_error_handler)
2730

2831
# SEE https://docs.python.org/3/library/exceptions.html#exception-hierarchy
2932
app.add_exception_handler(
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from fastapi import Request
2+
from models_library.functions_errors import FunctionBaseError
3+
4+
from ._utils import create_error_json_response
5+
6+
7+
async def function_error_handler(request: Request, exc: Exception):
8+
assert request # nosec
9+
assert isinstance(exc, FunctionBaseError)
10+
11+
return create_error_json_response(f"{exc}", status_code=exc.status_code)

services/api-server/tests/unit/api_functions/test_api_routers_functions.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from uuid import uuid4
88

99
import httpx
10-
import pytest
1110
from httpx import AsyncClient
1211
from models_library.api_schemas_webserver.functions import (
1312
FunctionJobCollection,
@@ -17,7 +16,10 @@
1716
RegisteredProjectFunction,
1817
RegisteredProjectFunctionJob,
1918
)
20-
from models_library.functions_errors import FunctionIDNotFoundError
19+
from models_library.functions_errors import (
20+
FunctionIDNotFoundError,
21+
FunctionReadAccessDeniedError,
22+
)
2123
from models_library.rest_pagination import PageMetaInfoLimitOffset
2224
from servicelib.aiohttp import status
2325
from simcore_service_api_server._meta import API_VTAG
@@ -92,8 +94,35 @@ async def test_get_function_not_found(
9294
None,
9395
FunctionIDNotFoundError(function_id=non_existent_function_id),
9496
)
95-
with pytest.raises(FunctionIDNotFoundError):
96-
await client.get(f"{API_VTAG}/functions/{non_existent_function_id}", auth=auth)
97+
response = await client.get(
98+
f"{API_VTAG}/functions/{non_existent_function_id}", auth=auth
99+
)
100+
assert response.status_code == status.HTTP_404_NOT_FOUND
101+
102+
103+
async def test_get_function_read_access_denied(
104+
client: AsyncClient,
105+
mock_handler_in_functions_rpc_interface: Callable[
106+
[str, Any, Exception | None], None
107+
],
108+
mock_registered_function: RegisteredProjectFunction,
109+
auth: httpx.BasicAuth,
110+
) -> None:
111+
unauthorized_user_id = "unauthorized user"
112+
mock_handler_in_functions_rpc_interface(
113+
"get_function",
114+
None,
115+
FunctionReadAccessDeniedError(
116+
function_id=mock_registered_function.uid, user_id=unauthorized_user_id
117+
),
118+
)
119+
response = await client.get(
120+
f"{API_VTAG}/functions/{mock_registered_function.uid}", auth=auth
121+
)
122+
assert response.status_code == status.HTTP_403_FORBIDDEN
123+
assert response.json()["errors"][0] == (
124+
f"Function {mock_registered_function.uid} read access denied for user {unauthorized_user_id}"
125+
)
97126

98127

99128
async def test_list_functions(

0 commit comments

Comments
 (0)