awesome-errors 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- awesome_errors/__init__.py +232 -0
- awesome_errors/analysis/__init__.py +8 -0
- awesome_errors/analysis/decorators.py +251 -0
- awesome_errors/analysis/error_analyzer.py +463 -0
- awesome_errors/client/__init__.py +7 -0
- awesome_errors/client/exceptions.py +97 -0
- awesome_errors/client/response_parser.py +175 -0
- awesome_errors/converters/__init__.py +31 -0
- awesome_errors/converters/generic.py +23 -0
- awesome_errors/converters/pydantic_converter.py +75 -0
- awesome_errors/converters/python_converter.py +80 -0
- awesome_errors/converters/sql_converter.py +258 -0
- awesome_errors/converters/universal_converter.py +123 -0
- awesome_errors/core/__init__.py +0 -0
- awesome_errors/core/error_codes.py +104 -0
- awesome_errors/core/error_response.py +61 -0
- awesome_errors/core/exceptions.py +613 -0
- awesome_errors/core/renderers.py +115 -0
- awesome_errors/i18n/__init__.py +0 -0
- awesome_errors/i18n/locales/en/errors.json +32 -0
- awesome_errors/i18n/locales/test/errors.json +4 -0
- awesome_errors/i18n/locales/uk/errors.json +26 -0
- awesome_errors/i18n/translator.py +150 -0
- awesome_errors/integrations/__init__.py +11 -0
- awesome_errors/integrations/fastapi_auto_docs.py +220 -0
- awesome_errors/litestar_utils.py +55 -0
- awesome_errors/middleware/__init__.py +0 -0
- awesome_errors/middleware/fastapi.py +247 -0
- awesome_errors/middleware/litestar.py +333 -0
- awesome_errors/py.typed +1 -0
- awesome_errors/websocket/__init__.py +35 -0
- awesome_errors/websocket/error_handler.py +231 -0
- awesome_errors/websocket/exceptions.py +286 -0
- awesome_errors-0.3.0.dist-info/METADATA +239 -0
- awesome_errors-0.3.0.dist-info/RECORD +37 -0
- awesome_errors-0.3.0.dist-info/WHEEL +4 -0
- awesome_errors-0.3.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Dict
|
|
4
|
+
|
|
5
|
+
from .analysis import ErrorAnalyzer, analyze_errors, openapi_errors
|
|
6
|
+
from .client import BackendError, ErrorResponseParser
|
|
7
|
+
from .converters import (
|
|
8
|
+
PydanticErrorConverter,
|
|
9
|
+
PythonErrorConverter,
|
|
10
|
+
SQLErrorConverter,
|
|
11
|
+
UniversalErrorConverter,
|
|
12
|
+
)
|
|
13
|
+
from .core.error_codes import ErrorCode
|
|
14
|
+
from .core.error_response import ErrorDetail, ErrorResponse
|
|
15
|
+
from .core.exceptions import (
|
|
16
|
+
APIError,
|
|
17
|
+
AppError,
|
|
18
|
+
AuthError,
|
|
19
|
+
AuthInsufficientPrivilegesError,
|
|
20
|
+
AuthInvalidTokenError,
|
|
21
|
+
AuthPermissionDeniedError,
|
|
22
|
+
AuthRequiredError,
|
|
23
|
+
AuthTokenExpiredError,
|
|
24
|
+
BusinessLogicError,
|
|
25
|
+
DatabaseConnectionError,
|
|
26
|
+
DatabaseConstraintViolationError,
|
|
27
|
+
DatabaseDuplicateEntryError,
|
|
28
|
+
DatabaseError,
|
|
29
|
+
DatabaseInvalidReferenceError,
|
|
30
|
+
DatabaseMissingRequiredError,
|
|
31
|
+
DatabaseQueryError,
|
|
32
|
+
DatabaseTransactionError,
|
|
33
|
+
EntityNotFoundError,
|
|
34
|
+
InsufficientBalanceError,
|
|
35
|
+
InvalidFormatError,
|
|
36
|
+
InvalidInputError,
|
|
37
|
+
MissingRequiredFieldError,
|
|
38
|
+
NotFoundError,
|
|
39
|
+
OAuthProviderUnknownError,
|
|
40
|
+
OperationNotAllowedError,
|
|
41
|
+
RefreshTokenReuseDetectedError,
|
|
42
|
+
ResourceNotFoundError,
|
|
43
|
+
SessionExpiredError,
|
|
44
|
+
UserNotFoundError,
|
|
45
|
+
ValidationError,
|
|
46
|
+
)
|
|
47
|
+
from .core.renderers import ErrorResponseFormat, ErrorResponseRenderer
|
|
48
|
+
from .i18n.translator import ErrorTranslator
|
|
49
|
+
from .litestar_utils import apply_api_errors, errors, raises_from
|
|
50
|
+
from .middleware.litestar import (
|
|
51
|
+
apply_litestar_openapi_problem_details,
|
|
52
|
+
create_litestar_exception_handlers,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
_setup_error_handling: Callable[..., Any] | None = None
|
|
56
|
+
try: # pragma: no cover - optional dependency
|
|
57
|
+
from .middleware.fastapi import setup_error_handling as _setup_error_handling_impl
|
|
58
|
+
except ImportError: # pragma: no cover
|
|
59
|
+
pass
|
|
60
|
+
else:
|
|
61
|
+
_setup_error_handling = _setup_error_handling_impl
|
|
62
|
+
|
|
63
|
+
_setup_automatic_error_docs: Callable[..., Any] | None = None
|
|
64
|
+
_apply_auto_error_docs_to_router: Callable[..., Any] | None = None
|
|
65
|
+
_auto_analyze_errors: Callable[..., Any] | None = None
|
|
66
|
+
try: # pragma: no cover - optional dependency
|
|
67
|
+
from .integrations.fastapi_auto_docs import (
|
|
68
|
+
apply_auto_error_docs_to_router as _apply_auto_error_docs_to_router_impl,
|
|
69
|
+
)
|
|
70
|
+
from .integrations.fastapi_auto_docs import (
|
|
71
|
+
auto_analyze_errors as _auto_analyze_errors_impl,
|
|
72
|
+
)
|
|
73
|
+
from .integrations.fastapi_auto_docs import (
|
|
74
|
+
setup_automatic_error_docs as _setup_automatic_error_docs_impl,
|
|
75
|
+
)
|
|
76
|
+
except ImportError: # pragma: no cover
|
|
77
|
+
pass
|
|
78
|
+
else:
|
|
79
|
+
_setup_automatic_error_docs = _setup_automatic_error_docs_impl
|
|
80
|
+
_apply_auto_error_docs_to_router = _apply_auto_error_docs_to_router_impl
|
|
81
|
+
_auto_analyze_errors = _auto_analyze_errors_impl
|
|
82
|
+
|
|
83
|
+
_websocket: Any = None
|
|
84
|
+
try: # pragma: no cover - optional dependency
|
|
85
|
+
from . import websocket as _websocket_module
|
|
86
|
+
except ImportError: # pragma: no cover
|
|
87
|
+
pass
|
|
88
|
+
else:
|
|
89
|
+
_websocket = _websocket_module
|
|
90
|
+
|
|
91
|
+
WebSocketError = getattr(_websocket, "WebSocketError", None)
|
|
92
|
+
JSONRPCErrorCode = getattr(_websocket, "JSONRPCErrorCode", None)
|
|
93
|
+
WebSocketAuthError = getattr(_websocket, "WebSocketAuthError", None)
|
|
94
|
+
WebSocketTokenExpiredError = getattr(_websocket, "WebSocketTokenExpiredError", None)
|
|
95
|
+
WebSocketRateLimitError = getattr(_websocket, "WebSocketRateLimitError", None)
|
|
96
|
+
WebSocketValidationError = getattr(_websocket, "WebSocketValidationError", None)
|
|
97
|
+
WebSocketMethodNotFoundError = getattr(_websocket, "WebSocketMethodNotFoundError", None)
|
|
98
|
+
WebSocketInternalError = getattr(_websocket, "WebSocketInternalError", None)
|
|
99
|
+
WebSocketErrorHandler = getattr(_websocket, "WebSocketErrorHandler", None)
|
|
100
|
+
_setup_websocket_error_handling = getattr(
|
|
101
|
+
_websocket, "setup_websocket_error_handling", None
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def setup_error_handling(
|
|
106
|
+
app: Any,
|
|
107
|
+
translator: ErrorTranslator | None = None,
|
|
108
|
+
debug: bool = False,
|
|
109
|
+
log_errors: bool = True,
|
|
110
|
+
locales_dir: str | None = None,
|
|
111
|
+
default_locale: str = "en",
|
|
112
|
+
custom_translations: Dict[str, Dict[str, str]] | None = None,
|
|
113
|
+
response_format: ErrorResponseFormat = ErrorResponseFormat.LEGACY,
|
|
114
|
+
problem_type_resolver: Callable[[AppError], str] | None = None,
|
|
115
|
+
problem_extension_builder: Callable[[AppError], Dict[str, object]] | None = None,
|
|
116
|
+
message_resolver: Callable[
|
|
117
|
+
[AppError, str | None, ErrorTranslator | None], str
|
|
118
|
+
] | None = None,
|
|
119
|
+
) -> Any:
|
|
120
|
+
if _setup_error_handling is None:
|
|
121
|
+
raise ImportError(
|
|
122
|
+
"Install 'awesome-errors[fastapi]' to enable FastAPI middleware integration."
|
|
123
|
+
)
|
|
124
|
+
return _setup_error_handling(
|
|
125
|
+
app=app,
|
|
126
|
+
translator=translator,
|
|
127
|
+
debug=debug,
|
|
128
|
+
log_errors=log_errors,
|
|
129
|
+
locales_dir=locales_dir,
|
|
130
|
+
default_locale=default_locale,
|
|
131
|
+
custom_translations=custom_translations,
|
|
132
|
+
response_format=response_format,
|
|
133
|
+
problem_type_resolver=problem_type_resolver,
|
|
134
|
+
problem_extension_builder=problem_extension_builder,
|
|
135
|
+
message_resolver=message_resolver,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def setup_automatic_error_docs(app: Any, **kwargs: Any) -> Any:
|
|
140
|
+
if _setup_automatic_error_docs is None:
|
|
141
|
+
raise ImportError("FastAPI integration requires fastapi to be installed")
|
|
142
|
+
return _setup_automatic_error_docs(app, **kwargs)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def apply_auto_error_docs_to_router(router: Any, **kwargs: Any) -> Any:
|
|
146
|
+
if _apply_auto_error_docs_to_router is None:
|
|
147
|
+
raise ImportError("FastAPI integration requires fastapi to be installed")
|
|
148
|
+
return _apply_auto_error_docs_to_router(router, **kwargs)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def auto_analyze_errors(func: Any) -> Any:
|
|
152
|
+
if _auto_analyze_errors is None:
|
|
153
|
+
raise ImportError("FastAPI integration requires fastapi to be installed")
|
|
154
|
+
return _auto_analyze_errors(func)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def setup_websocket_error_handling(app: Any) -> Any:
|
|
158
|
+
if _setup_websocket_error_handling is None:
|
|
159
|
+
raise ImportError(
|
|
160
|
+
"Install 'awesome-errors[fastapi]' to enable WebSocket error handling."
|
|
161
|
+
)
|
|
162
|
+
return _setup_websocket_error_handling(app)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
__version__ = "0.1.0"
|
|
166
|
+
|
|
167
|
+
__all__ = [
|
|
168
|
+
"AppError",
|
|
169
|
+
"APIError",
|
|
170
|
+
"ValidationError",
|
|
171
|
+
"InvalidInputError",
|
|
172
|
+
"MissingRequiredFieldError",
|
|
173
|
+
"InvalidFormatError",
|
|
174
|
+
"AuthError",
|
|
175
|
+
"AuthRequiredError",
|
|
176
|
+
"AuthInvalidTokenError",
|
|
177
|
+
"AuthTokenExpiredError",
|
|
178
|
+
"AuthPermissionDeniedError",
|
|
179
|
+
"AuthInsufficientPrivilegesError",
|
|
180
|
+
"SessionExpiredError",
|
|
181
|
+
"RefreshTokenReuseDetectedError",
|
|
182
|
+
"NotFoundError",
|
|
183
|
+
"ResourceNotFoundError",
|
|
184
|
+
"UserNotFoundError",
|
|
185
|
+
"EntityNotFoundError",
|
|
186
|
+
"OAuthProviderUnknownError",
|
|
187
|
+
"DatabaseError",
|
|
188
|
+
"DatabaseConnectionError",
|
|
189
|
+
"DatabaseQueryError",
|
|
190
|
+
"DatabaseTransactionError",
|
|
191
|
+
"DatabaseConstraintViolationError",
|
|
192
|
+
"DatabaseDuplicateEntryError",
|
|
193
|
+
"DatabaseInvalidReferenceError",
|
|
194
|
+
"DatabaseMissingRequiredError",
|
|
195
|
+
"BusinessLogicError",
|
|
196
|
+
"InsufficientBalanceError",
|
|
197
|
+
"OperationNotAllowedError",
|
|
198
|
+
"ErrorCode",
|
|
199
|
+
"ErrorResponse",
|
|
200
|
+
"ErrorDetail",
|
|
201
|
+
"ErrorResponseFormat",
|
|
202
|
+
"ErrorResponseRenderer",
|
|
203
|
+
"SQLErrorConverter",
|
|
204
|
+
"PythonErrorConverter",
|
|
205
|
+
"PydanticErrorConverter",
|
|
206
|
+
"UniversalErrorConverter",
|
|
207
|
+
"ErrorTranslator",
|
|
208
|
+
"setup_error_handling",
|
|
209
|
+
"create_litestar_exception_handlers",
|
|
210
|
+
"apply_litestar_openapi_problem_details",
|
|
211
|
+
"apply_api_errors",
|
|
212
|
+
"errors",
|
|
213
|
+
"raises_from",
|
|
214
|
+
"ErrorResponseParser",
|
|
215
|
+
"BackendError",
|
|
216
|
+
"ErrorAnalyzer",
|
|
217
|
+
"analyze_errors",
|
|
218
|
+
"openapi_errors",
|
|
219
|
+
"setup_automatic_error_docs",
|
|
220
|
+
"apply_auto_error_docs_to_router",
|
|
221
|
+
"auto_analyze_errors",
|
|
222
|
+
"WebSocketError",
|
|
223
|
+
"JSONRPCErrorCode",
|
|
224
|
+
"WebSocketAuthError",
|
|
225
|
+
"WebSocketTokenExpiredError",
|
|
226
|
+
"WebSocketRateLimitError",
|
|
227
|
+
"WebSocketValidationError",
|
|
228
|
+
"WebSocketMethodNotFoundError",
|
|
229
|
+
"WebSocketInternalError",
|
|
230
|
+
"WebSocketErrorHandler",
|
|
231
|
+
"setup_websocket_error_handling",
|
|
232
|
+
]
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import Any, Callable, Dict, List, Optional, Set, cast
|
|
3
|
+
from ..analysis.error_analyzer import ErrorAnalyzer
|
|
4
|
+
from ..core.error_codes import ErrorCode, ERROR_HTTP_STATUS_MAP
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def analyze_errors(include_dependencies: bool = True):
|
|
8
|
+
"""
|
|
9
|
+
Decorator to analyze all possible errors in a function.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
include_dependencies: Whether to analyze called functions too
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Decorated function with error analysis attached
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def decorator(func: Callable) -> Callable:
|
|
19
|
+
# Perform analysis
|
|
20
|
+
analyzer = ErrorAnalyzer(func)
|
|
21
|
+
analysis = analyzer.analyze()
|
|
22
|
+
|
|
23
|
+
# Attach analysis to function
|
|
24
|
+
setattr(func, "_error_analysis", analysis)
|
|
25
|
+
|
|
26
|
+
@functools.wraps(func)
|
|
27
|
+
def wrapper(*args, **kwargs):
|
|
28
|
+
return func(*args, **kwargs)
|
|
29
|
+
|
|
30
|
+
# Copy analysis to wrapper
|
|
31
|
+
setattr(wrapper, "_error_analysis", analysis)
|
|
32
|
+
|
|
33
|
+
return wrapper
|
|
34
|
+
|
|
35
|
+
return decorator
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def openapi_errors(
|
|
39
|
+
additional_errors: Optional[List[str]] = None,
|
|
40
|
+
exclude_errors: Optional[List[str]] = None,
|
|
41
|
+
custom_descriptions: Optional[Dict[str, str]] = None,
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Decorator to generate OpenAPI error responses for a function.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
additional_errors: Additional error codes to include
|
|
48
|
+
exclude_errors: Error codes to exclude from analysis
|
|
49
|
+
custom_descriptions: Custom descriptions for error codes
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Decorated function with OpenAPI responses attached
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def decorator(func: Callable) -> Callable:
|
|
56
|
+
# Analyze function for errors
|
|
57
|
+
analyzer = ErrorAnalyzer(func)
|
|
58
|
+
analysis = analyzer.analyze()
|
|
59
|
+
|
|
60
|
+
# Get all error codes
|
|
61
|
+
error_codes = set(analysis["error_codes"])
|
|
62
|
+
|
|
63
|
+
# Add additional errors
|
|
64
|
+
if additional_errors:
|
|
65
|
+
error_codes.update(additional_errors)
|
|
66
|
+
|
|
67
|
+
# Remove excluded errors
|
|
68
|
+
if exclude_errors:
|
|
69
|
+
error_codes -= set(exclude_errors)
|
|
70
|
+
|
|
71
|
+
# Generate OpenAPI responses
|
|
72
|
+
openapi_responses = _generate_openapi_responses(
|
|
73
|
+
error_codes, custom_descriptions or {}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Attach to function
|
|
77
|
+
setattr(func, "_openapi_error_responses", openapi_responses)
|
|
78
|
+
setattr(func, "_error_analysis", analysis)
|
|
79
|
+
|
|
80
|
+
@functools.wraps(func)
|
|
81
|
+
def wrapper(*args, **kwargs):
|
|
82
|
+
return func(*args, **kwargs)
|
|
83
|
+
|
|
84
|
+
# Copy to wrapper
|
|
85
|
+
setattr(wrapper, "_openapi_error_responses", openapi_responses)
|
|
86
|
+
setattr(wrapper, "_error_analysis", analysis)
|
|
87
|
+
|
|
88
|
+
return wrapper
|
|
89
|
+
|
|
90
|
+
return decorator
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _generate_openapi_responses(
|
|
94
|
+
error_codes: Set[str], custom_descriptions: Dict[str, str]
|
|
95
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
96
|
+
"""Generate OpenAPI response schemas for error codes."""
|
|
97
|
+
responses = {}
|
|
98
|
+
|
|
99
|
+
# Group errors by HTTP status code
|
|
100
|
+
status_groups: Dict[int, List[str]] = {}
|
|
101
|
+
|
|
102
|
+
for error_code in error_codes:
|
|
103
|
+
try:
|
|
104
|
+
error_enum = ErrorCode(error_code)
|
|
105
|
+
status_code = ERROR_HTTP_STATUS_MAP.get(error_enum, 500)
|
|
106
|
+
except ValueError:
|
|
107
|
+
# Unknown error code
|
|
108
|
+
status_code = 500
|
|
109
|
+
|
|
110
|
+
if status_code not in status_groups:
|
|
111
|
+
status_groups[status_code] = []
|
|
112
|
+
status_groups[status_code].append(error_code)
|
|
113
|
+
|
|
114
|
+
# Generate response for each status code
|
|
115
|
+
for status_code, codes in status_groups.items():
|
|
116
|
+
response_schema = {
|
|
117
|
+
"description": _get_status_description(status_code, codes),
|
|
118
|
+
"content": {
|
|
119
|
+
"application/json": {
|
|
120
|
+
"schema": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"properties": {
|
|
123
|
+
"error": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"properties": {
|
|
126
|
+
"code": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"enum": codes,
|
|
129
|
+
"description": "Error code",
|
|
130
|
+
},
|
|
131
|
+
"message": {
|
|
132
|
+
"type": "string",
|
|
133
|
+
"description": "Error message",
|
|
134
|
+
},
|
|
135
|
+
"details": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"description": "Additional error details",
|
|
138
|
+
},
|
|
139
|
+
"timestamp": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"format": "date-time",
|
|
142
|
+
"description": "Error timestamp",
|
|
143
|
+
},
|
|
144
|
+
"request_id": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "Request ID for tracing",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
"required": [
|
|
150
|
+
"code",
|
|
151
|
+
"message",
|
|
152
|
+
"timestamp",
|
|
153
|
+
"request_id",
|
|
154
|
+
],
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"required": ["error"],
|
|
158
|
+
},
|
|
159
|
+
"examples": _generate_examples(codes, custom_descriptions),
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
responses[str(status_code)] = response_schema
|
|
165
|
+
|
|
166
|
+
return responses
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _get_status_description(status_code: int, error_codes: List[str]) -> str:
|
|
170
|
+
"""Get description for HTTP status code."""
|
|
171
|
+
status_descriptions = {
|
|
172
|
+
400: "Bad Request - Validation or input errors",
|
|
173
|
+
401: "Unauthorized - Authentication required",
|
|
174
|
+
403: "Forbidden - Insufficient permissions",
|
|
175
|
+
404: "Not Found - Resource not found",
|
|
176
|
+
409: "Conflict - Resource conflict (e.g., duplicate entry)",
|
|
177
|
+
422: "Unprocessable Entity - Business logic errors",
|
|
178
|
+
500: "Internal Server Error - Server errors",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
base_desc = status_descriptions.get(status_code, f"HTTP {status_code}")
|
|
182
|
+
codes_str = ", ".join(error_codes)
|
|
183
|
+
|
|
184
|
+
return f"{base_desc}. Possible error codes: {codes_str}"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _generate_examples(
|
|
188
|
+
error_codes: List[str], custom_descriptions: Dict[str, str]
|
|
189
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
190
|
+
"""Generate example responses for error codes."""
|
|
191
|
+
examples = {}
|
|
192
|
+
|
|
193
|
+
for error_code in error_codes:
|
|
194
|
+
example_name = error_code.lower().replace("_", "-")
|
|
195
|
+
description = custom_descriptions.get(
|
|
196
|
+
error_code, _get_default_error_description(error_code)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
examples[example_name] = {
|
|
200
|
+
"summary": f"{error_code} example",
|
|
201
|
+
"description": description,
|
|
202
|
+
"value": {
|
|
203
|
+
"error": {
|
|
204
|
+
"code": error_code,
|
|
205
|
+
"message": description,
|
|
206
|
+
"details": _get_example_details(error_code),
|
|
207
|
+
"timestamp": "2024-01-08T12:00:00Z",
|
|
208
|
+
"request_id": "req_abc123",
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return examples
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _get_default_error_description(error_code: str) -> str:
|
|
217
|
+
"""Get default description for error code."""
|
|
218
|
+
descriptions = {
|
|
219
|
+
"VALIDATION_FAILED": "Request validation failed",
|
|
220
|
+
"USER_NOT_FOUND": "User not found",
|
|
221
|
+
"AUTH_REQUIRED": "Authentication required",
|
|
222
|
+
"AUTH_PERMISSION_DENIED": "Permission denied",
|
|
223
|
+
"DB_DUPLICATE_ENTRY": "Duplicate entry in database",
|
|
224
|
+
"BUSINESS_RULE_VIOLATION": "Business rule violated",
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return descriptions.get(error_code, f"Error: {error_code}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _get_example_details(error_code: str) -> Dict[str, Any]:
|
|
231
|
+
"""Get example details for error code."""
|
|
232
|
+
example_details = {
|
|
233
|
+
"VALIDATION_FAILED": {
|
|
234
|
+
"field_errors": [
|
|
235
|
+
{
|
|
236
|
+
"field": "email",
|
|
237
|
+
"message": "invalid email format",
|
|
238
|
+
"input": "not-an-email",
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
"USER_NOT_FOUND": {"resource": "user", "resource_id": 123},
|
|
243
|
+
"DB_DUPLICATE_ENTRY": {
|
|
244
|
+
"table": "users",
|
|
245
|
+
"field": "email",
|
|
246
|
+
"duplicate_value": "user@example.com",
|
|
247
|
+
},
|
|
248
|
+
"AUTH_PERMISSION_DENIED": {"required_permission": "admin.users.delete"},
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return cast(Dict[str, Any], example_details.get(error_code, {}))
|