-
Notifications
You must be signed in to change notification settings - Fork 31
Add exception handlers for backend #332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
84e0aad
b5c620a
399c447
0b6c71f
5565f99
b74fcf6
998c023
3eccf29
25f0603
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| from fastapi import FastAPI, HTTPException, Request | ||
| from fastapi.responses import JSONResponse | ||
| from starlette.status import ( | ||
| HTTP_400_BAD_REQUEST, | ||
| HTTP_401_UNAUTHORIZED, | ||
| HTTP_404_NOT_FOUND, | ||
| HTTP_409_CONFLICT, | ||
| HTTP_422_UNPROCESSABLE_ENTITY, | ||
| ) | ||
|
|
||
| from gs.backend.exceptions.exceptions import ( | ||
| BaseOrbitalError, | ||
| DatabaseError, | ||
| InvalidArgumentError, | ||
| InvalidStateError, | ||
| NotFoundError, | ||
| ServiceError, | ||
| SunPositionError, | ||
| UnauthorizedError, | ||
| UnknownError, | ||
| ) | ||
|
|
||
|
|
||
| def setup_exception_handlers(app: FastAPI) -> None: | ||
| """ | ||
| @brief Add exception handlers to the FastAPI app | ||
|
|
||
| @attribute app (FastAPI) - The FastAPI app to add the exception handlers to | ||
| """ | ||
|
|
||
| @app.exception_handler(BaseOrbitalError) | ||
| async def base_orbital_exception_handler(request: Request, exc: BaseOrbitalError) -> JSONResponse: | ||
| """ | ||
| @brief handle all BaseOrbitalError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (BaseOrbitalError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_400_BAD_REQUEST, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(ServiceError) | ||
| async def service_exception_handler(request: Request, exc: ServiceError) -> JSONResponse: | ||
| """ | ||
| @brief handle all ServiceError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (ServiceError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_400_BAD_REQUEST, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(NotFoundError) | ||
| async def not_found_exception_handler(request: Request, exc: NotFoundError) -> JSONResponse: | ||
| """ | ||
| @brief handle all NotFoundError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (NotFoundError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_404_NOT_FOUND, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(InvalidArgumentError) | ||
| async def invalid_argument_exception_handler(request: Request, exc: InvalidArgumentError) -> JSONResponse: | ||
| """ | ||
| @brief handle all InvalidArgumentError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (InvalidArgumentError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_422_UNPROCESSABLE_ENTITY, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(InvalidStateError) | ||
| async def invalid_state_exception_handler(request: Request, exc: InvalidStateError) -> JSONResponse: | ||
| """ | ||
| @brief handle all InvalidStateError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (InvalidStateError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_409_CONFLICT, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(DatabaseError) | ||
| async def data_base_exception_handler(request: Request, exc: DatabaseError) -> JSONResponse: | ||
| """ | ||
| @brief handle all DatabaseError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (DatabaseError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_400_BAD_REQUEST, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(UnauthorizedError) | ||
| async def unauthorized_exception_handler(request: Request, exc: UnauthorizedError) -> JSONResponse: | ||
| """ | ||
| @brief handle all UnauthorizedError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (UnauthorizedError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_401_UNAUTHORIZED, | ||
| content={"message": exc.message}, | ||
| ) | ||
|
|
||
| @app.exception_handler(UnknownError) | ||
| async def unknown_exception_handler(request: Request, exc: UnknownError) -> HTTPException: | ||
| """ | ||
| @brief handle all UnknownError exceptions with HTTPException | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (UnknownError) - The exception that was raised | ||
| """ | ||
| raise HTTPException( | ||
| status_code=HTTP_400_BAD_REQUEST, | ||
| detail=exc.message, | ||
| ) | ||
|
|
||
| @app.exception_handler(SunPositionError) | ||
| async def sun_position_exception_handler(request: Request, exc: SunPositionError) -> JSONResponse: | ||
| """ | ||
| @brief handle all SunPositionError exceptions | ||
|
|
||
| @attribute request (Request) - The request that caused the exception | ||
| @attribute exc (SunPositionError) - The exception that was raised | ||
| """ | ||
| return JSONResponse( | ||
| status_code=HTTP_400_BAD_REQUEST, | ||
| content={"message": exc.message}, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| from fastapi import FastAPI | ||
| from fastapi.testclient import TestClient | ||
| from gs.backend.exceptions.exception_handlers import setup_exception_handlers | ||
| from gs.backend.exceptions.exceptions import ( | ||
| BaseOrbitalError, | ||
| DatabaseError, | ||
| InvalidArgumentError, | ||
| InvalidStateError, | ||
| NotFoundError, | ||
| ServiceError, | ||
| SunPositionError, | ||
| UnauthorizedError, | ||
| UnknownError, | ||
| ) | ||
| from starlette.status import ( | ||
| HTTP_400_BAD_REQUEST, | ||
| HTTP_401_UNAUTHORIZED, | ||
| HTTP_404_NOT_FOUND, | ||
| HTTP_409_CONFLICT, | ||
| HTTP_422_UNPROCESSABLE_ENTITY, | ||
| ) | ||
|
|
||
|
|
||
| def test_custom_exceptions(): | ||
| app = FastAPI() | ||
| setup_exception_handlers(app) | ||
|
|
||
| @app.get("/test_base_orbital_exceptions") | ||
| async def test_custom_exceptions(): | ||
| raise BaseOrbitalError("Test BaseOrbitalError") | ||
|
|
||
| @app.get("/test_database_exceptions") | ||
| async def test_database_exceptions(): | ||
| raise DatabaseError("Test DatabaseError") | ||
|
|
||
| @app.get("/test_invalid_argument_exceptions") | ||
| async def test_invalid_argument_exceptions(): | ||
| raise InvalidArgumentError("Test InvalidArgumentError") | ||
|
|
||
| @app.get("/test_invalid_state_exceptions") | ||
| async def test_invalid_state_exceptions(): | ||
| raise InvalidStateError("Test InvalidStateError") | ||
|
|
||
| @app.get("/test_not_found_exceptions") | ||
| async def test_not_found_exceptions(): | ||
| raise NotFoundError("Test NotFoundError") | ||
|
|
||
| @app.get("/test_service_exceptions") | ||
| async def test_service_exceptions(): | ||
| raise ServiceError("Test ServiceError") | ||
|
|
||
| @app.get("/test_sun_position_exceptions") | ||
| async def test_sun_position_exceptions(): | ||
| raise SunPositionError("Test SunPositionError") | ||
|
|
||
| @app.get("/test_unauthorized_exceptions") | ||
| async def test_unauthorized_exceptions(): | ||
| raise UnauthorizedError("Test UnauthorizedError") | ||
|
|
||
| @app.get("/test_unknown_exceptions") | ||
| async def test_unknown_exceptions(): | ||
| raise UnknownError("Test UnknownError") | ||
|
|
||
| fastapi_test_client = TestClient(app) | ||
|
Comment on lines
+25
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you extract this out to a function that creates the needed exception handler test client? Something such as |
||
| # Test BaseOrbitalError | ||
| response = fastapi_test_client.get("/test_base_orbital_exceptions") | ||
| assert response.status_code == HTTP_400_BAD_REQUEST | ||
| assert response.json().get("message") == "Test BaseOrbitalError" | ||
|
|
||
| # Test DatabaseError | ||
| response = fastapi_test_client.get("/test_database_exceptions") | ||
| assert response.status_code == HTTP_400_BAD_REQUEST | ||
| assert response.json().get("message") == "Test DatabaseError" | ||
|
|
||
| # Test InvalidArgumentError | ||
| response = fastapi_test_client.get("/test_invalid_argument_exceptions") | ||
| assert response.status_code == HTTP_422_UNPROCESSABLE_ENTITY | ||
| assert response.json().get("message") == "Test InvalidArgumentError" | ||
|
|
||
| # Test InvalidStateError | ||
| response = fastapi_test_client.get("/test_invalid_state_exceptions") | ||
| assert response.status_code == HTTP_409_CONFLICT | ||
| assert response.json().get("message") == "Test InvalidStateError" | ||
|
|
||
| # Test NotFoundError | ||
| response = fastapi_test_client.get("/test_not_found_exceptions") | ||
| assert response.status_code == HTTP_404_NOT_FOUND | ||
| assert response.json().get("message") == "Test NotFoundError" | ||
|
|
||
| # Test ServiceError | ||
| response = fastapi_test_client.get("/test_service_exceptions") | ||
| assert response.status_code == HTTP_400_BAD_REQUEST | ||
| assert response.json().get("message") == "Test ServiceError" | ||
|
|
||
| # Test SunPositionError | ||
| response = fastapi_test_client.get("/test_sun_position_exceptions") | ||
| assert response.status_code == HTTP_400_BAD_REQUEST | ||
| assert response.json().get("message") == "Test SunPositionError" | ||
|
|
||
| # Test UnauthorizedError | ||
| response = fastapi_test_client.get("/test_unauthorized_exceptions") | ||
| assert response.status_code == HTTP_401_UNAUTHORIZED | ||
| assert response.json().get("message") == "Test UnauthorizedError" | ||
|
|
||
| # Test UnknownError | ||
| response = fastapi_test_client.get("/test_unknown_exceptions") | ||
| assert response.status_code == HTTP_400_BAD_REQUEST | ||
| assert response.json().get("detail") == "Test UnknownError" | ||
|
Comment on lines
+65
to
+108
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally its best not to have lots of different asserts in the same asserts in the same test. If one of the tests fail at the beginning, the tests after will not run and you will lose important contex. Have a look at the |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait is this one the only one is a
HTTPExceptionwhile all the other ones areJSONResponse?