File tree Expand file tree Collapse file tree 6 files changed +100
-9
lines changed
Expand file tree Collapse file tree 6 files changed +100
-9
lines changed Original file line number Diff line number Diff line change 1010
1111from config import Settings
1212from src .interface_adapters import api_router
13+ from src .interface_adapters .exceptions import AppException
14+ from src .interface_adapters .middleware .error_handler import app_exception_handler
1315
1416# https://brandur.org/logfmt
1517# https://github.com/Delgan/loguru
@@ -44,6 +46,9 @@ async def isConfigured():
4446 openapi_url = "/v1/openapi.json" ,
4547)
4648
49+ # Register global exception handler
50+ app .add_exception_handler (AppException , app_exception_handler )
51+
4752app .add_middleware (
4853 CORSMiddleware ,
4954 allow_origins = ["*" ], # Set this to lock down if applicable to your use case
Original file line number Diff line number Diff line change 44from loguru import logger
55
66from src .app .ports .repositories .issues import IssueRepository
7- from src .app .repository import (
8- UnitOfWork ,
9- )
7+ from src .app .repository import UnitOfWork
108from src .domain .issue import Issue
9+ from src .interface_adapters .exceptions import NotFoundException
1110
1211
1312class AnalyzeIssueProtocol (Protocol ):
@@ -34,4 +33,9 @@ def __init__(
3433 async def analyze (self ) -> Issue :
3534 logger .info (f"analyzing issue: { self .issue_number } " )
3635 issue = await self .repo .get_by_id (self .issue_number )
36+ if issue .issue_number == 0 :
37+ raise NotFoundException (
38+ message = "Issue not found" ,
39+ detail = f"Issue with number { self .issue_number } does not exist"
40+ )
3741 return issue
Original file line number Diff line number Diff line change 44
55from src .app .usecases .analyze_issue import AnalyzeIssue
66from config import Settings
7-
87from src .domain .issue import Issue
8+ from src .interface_adapters .exceptions import UnsupportedOperationException
99from src .resource_adapters .persistence .in_memory .issues import InMemoryIssueRepository
1010from src .resource_adapters .persistence .in_memory .unit_of_work import UnitOfWork
11- from typing import Optional
1211
1312router = APIRouter ()
1413
1514
1615# https://fastapi.tiangolo.com/tutorial/dependencies/
17- async def configure_unit_of_work () -> Optional [ UnitOfWork ] :
16+ async def configure_unit_of_work () -> UnitOfWork :
1817 if Settings .get_settings ().execution_mode == "in-memory" :
1918 return UnitOfWork ()
2019 else :
21- return None
20+ raise UnsupportedOperationException (
21+ message = "Unsupported unit of work configuration" ,
22+ detail = "Only in-memory unit of work is currently supported"
23+ )
2224
2325
24- async def configure_repository () -> Optional [ InMemoryIssueRepository ] :
26+ async def configure_repository () -> InMemoryIssueRepository :
2527 if Settings .get_settings ().execution_mode == "in-memory" :
2628 return InMemoryIssueRepository ()
2729 else :
28- return None
30+ raise UnsupportedOperationException (
31+ message = "Unsupported repository configuration" ,
32+ detail = "Only in-memory repository is currently supported"
33+ )
2934
3035
3136@router .post ("/issues/{issue_number}/analyze" , response_model = Issue )
Original file line number Diff line number Diff line change 1+ from typing import Optional
2+
3+ class AppException (Exception ):
4+ """Base exception class for application exceptions"""
5+ def __init__ (
6+ self ,
7+ message : str ,
8+ status_code : int = 500 ,
9+ detail : Optional [str ] = None
10+ ) -> None :
11+ self .message = message
12+ self .status_code = status_code
13+ self .detail = detail
14+ super ().__init__ (self .message )
15+
16+
17+ class UnsupportedOperationException (AppException ):
18+ """Exception raised when an operation is not supported"""
19+ def __init__ (
20+ self ,
21+ message : str = "Operation not supported" ,
22+ detail : Optional [str ] = None
23+ ) -> None :
24+ super ().__init__ (
25+ message = message ,
26+ status_code = 501 ,
27+ detail = detail
28+ )
29+
30+
31+ class ConfigurationException (AppException ):
32+ """Exception raised for configuration errors"""
33+ def __init__ (
34+ self ,
35+ message : str = "Invalid configuration" ,
36+ detail : Optional [str ] = None
37+ ) -> None :
38+ super ().__init__ (
39+ message = message ,
40+ status_code = 500 ,
41+ detail = detail
42+ )
43+
44+
45+ class NotFoundException (AppException ):
46+ """Exception raised when a requested resource is not found"""
47+ def __init__ (
48+ self ,
49+ message : str = "Resource not found" ,
50+ detail : Optional [str ] = None
51+ ) -> None :
52+ super ().__init__ (
53+ message = message ,
54+ status_code = 404 ,
55+ detail = detail
56+ )
Original file line number Diff line number Diff line change 1+ from fastapi import Request
2+ from fastapi .responses import JSONResponse
3+ from loguru import logger
4+
5+ from src .interface_adapters .exceptions import AppException
6+
7+
8+ async def app_exception_handler (request : Request , exc : AppException ) -> JSONResponse :
9+ """Global exception handler for AppException and its subclasses"""
10+ logger .error (f"Request to { request .url } failed: { exc .message } " )
11+ if exc .detail :
12+ logger .error (f"Detail: { exc .detail } " )
13+
14+ return JSONResponse (
15+ status_code = exc .status_code ,
16+ content = {
17+ "message" : exc .message ,
18+ "detail" : exc .detail ,
19+ "path" : str (request .url )
20+ }
21+ )
You can’t perform that action at this time.
0 commit comments