Skip to content

Commit 615d8de

Browse files
committed
refactor: make it more generic
1 parent ac55194 commit 615d8de

File tree

6 files changed

+10
-166
lines changed

6 files changed

+10
-166
lines changed

tests/core/test_wsgi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,7 @@ def handle_get(self, request):
216216
response_body = b"".join(response_iter).decode()
217217

218218
assert start_response.status.startswith("418 I'm a teapot")
219-
assert '"code":"I_AM_A_TEAPOT"' in response_body
220-
assert '"message":"Test error"' in response_body
219+
assert 'Test error' in response_body
221220

222221

223222
def test_custom_exception_handler():

tests/http/test_response.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
RedirectResponse,
1010
Response,
1111
StreamResponse,
12-
SuccessResponse,
1312
TextResponse,
1413
)
1514
from webspark.utils.exceptions import HTTPException
@@ -269,19 +268,6 @@ def test_stream_response_as_wsgi():
269268
assert body_iter == [content]
270269

271270

272-
def test_success_response():
273-
data = {"id": 1, "name": "Test"}
274-
response = SuccessResponse(data, status=201)
275-
276-
assert isinstance(response, JsonResponse)
277-
assert response.status == 201
278-
assert response.headers["content-type"] == "application/json; charset=utf-8"
279-
280-
body_bytes = response._body_bytes
281-
decoded_data = json.loads(body_bytes.decode("utf-8"))
282-
assert decoded_data == {"success": True, "data": data}
283-
284-
285271
def test_response_as_wsgi_with_unknown_status():
286272
response = Response(body="Hello, World!", status=299)
287273
status_str, headers_list, body_iter = response.as_wsgi()

webspark/constants.py

Lines changed: 4 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
101: "101 Switching Protocols",
2525
102: "102 Processing",
2626
103: "103 Early Hints",
27+
2728
# 2xx: Success
2829
200: "200 OK",
2930
201: "201 Created",
@@ -35,6 +36,7 @@
3536
207: "207 Multi-Status",
3637
208: "208 Already Reported",
3738
226: "226 IM Used",
39+
3840
# 3xx: Redirection
3941
300: "300 Multiple Choices",
4042
301: "301 Moved Permanently",
@@ -44,6 +46,7 @@
4446
305: "305 Use Proxy",
4547
307: "307 Temporary Redirect",
4648
308: "308 Permanent Redirect",
49+
4750
# 4xx: Client Error
4851
400: "400 Bad Request",
4952
401: "401 Unauthorized",
@@ -74,6 +77,7 @@
7477
429: "429 Too Many Requests",
7578
431: "431 Request Header Fields Too Large",
7679
451: "451 Unavailable For Legal Reasons",
80+
7781
# 5xx: Server Error
7882
500: "500 Internal Server Error",
7983
501: "501 Not Implemented",
@@ -87,84 +91,3 @@
8791
510: "510 Not Extended",
8892
511: "511 Network Authentication Required",
8993
}
90-
91-
ERROR_CONVENTIONS = {
92-
# 4xx — Client Errors
93-
400: {"code": "BAD_REQUEST", "message": "Invalid request."},
94-
401: {"code": "UNAUTHORIZED", "message": "Authentication required or invalid."},
95-
402: {"code": "PAYMENT_REQUIRED", "message": "Payment required."},
96-
403: {
97-
"code": "FORBIDDEN",
98-
"message": "You do not have permission to perform this action.",
99-
},
100-
404: {"code": "NOT_FOUND", "message": "Resource not found."},
101-
405: {
102-
"code": "METHOD_NOT_ALLOWED",
103-
"message": "HTTP method not allowed for this resource.",
104-
},
105-
406: {"code": "NOT_ACCEPTABLE", "message": "Response format not acceptable."},
106-
407: {
107-
"code": "PROXY_AUTH_REQUIRED",
108-
"message": "Proxy authentication is required.",
109-
},
110-
408: {"code": "REQUEST_TIMEOUT", "message": "Request timed out."},
111-
409: {
112-
"code": "CONFLICT",
113-
"message": "Conflict with the current state of the resource.",
114-
},
115-
410: {"code": "GONE", "message": "Resource is no longer available."},
116-
411: {"code": "LENGTH_REQUIRED", "message": "Content-Length header is required."},
117-
412: {"code": "PRECONDITION_FAILED", "message": "Preconditions not met."},
118-
413: {"code": "PAYLOAD_TOO_LARGE", "message": "Payload too large."},
119-
414: {"code": "URI_TOO_LONG", "message": "URI too long."},
120-
415: {"code": "UNSUPPORTED_MEDIA_TYPE", "message": "Unsupported media type."},
121-
416: {"code": "RANGE_NOT_SATISFIABLE", "message": "Byte range not satisfiable."},
122-
417: {
123-
"code": "EXPECTATION_FAILED",
124-
"message": "Expectation in header could not be met.",
125-
},
126-
418: {"code": "I_AM_A_TEAPOT", "message": "I am a teapot."},
127-
421: {
128-
"code": "MISDIRECTED_REQUEST",
129-
"message": "Request directed to the wrong server.",
130-
},
131-
422: {"code": "UNPROCESSABLE_ENTITY", "message": "Invalid semantic entity."},
132-
423: {"code": "LOCKED", "message": "Resource locked."},
133-
424: {"code": "FAILED_DEPENDENCY", "message": "Failed dependency."},
134-
425: {"code": "TOO_EARLY", "message": "Too early to process; try again."},
135-
426: {"code": "UPGRADE_REQUIRED", "message": "Protocol upgrade required."},
136-
428: {"code": "PRECONDITION_REQUIRED", "message": "Precondition required."},
137-
429: {
138-
"code": "TOO_MANY_REQUESTS",
139-
"message": "Too many requests; try again later.",
140-
},
141-
431: {
142-
"code": "REQUEST_HEADER_FIELDS_TOO_LARGE",
143-
"message": "Request header fields too large.",
144-
},
145-
451: {
146-
"code": "UNAVAILABLE_FOR_LEGAL_REASONS",
147-
"message": "Unavailable for legal reasons.",
148-
},
149-
# 5xx — Server Errors
150-
500: {"code": "INTERNAL_ERROR", "message": "Internal error. Please try again."},
151-
501: {"code": "NOT_IMPLEMENTED", "message": "Functionality not implemented."},
152-
502: {"code": "BAD_GATEWAY", "message": "Bad gateway."},
153-
503: {
154-
"code": "SERVICE_UNAVAILABLE",
155-
"message": "Service unavailable. Please try again later.",
156-
},
157-
504: {"code": "GATEWAY_TIMEOUT", "message": "Gateway timeout."},
158-
505: {
159-
"code": "HTTP_VERSION_NOT_SUPPORTED",
160-
"message": "HTTP version not supported.",
161-
},
162-
506: {"code": "VARIANT_ALSO_NEGOTIATES", "message": "Content negotiation failed."},
163-
507: {"code": "INSUFFICIENT_STORAGE", "message": "Insufficient storage."},
164-
508: {"code": "LOOP_DETECTED", "message": "Loop detected during processing."},
165-
510: {"code": "NOT_EXTENDED", "message": "Policy extensions required."},
166-
511: {
167-
"code": "NETWORK_AUTH_REQUIRED",
168-
"message": "Network authentication required.",
169-
},
170-
}

webspark/core/wsgi.py

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
from .plugin import Plugin
1010
from .router import path
1111

12-
from ..constants import ERROR_CONVENTIONS
1312
from ..http.request import Request
14-
from ..http.response import JsonResponse, Response
13+
from ..http.response import Response, TextResponse
1514
from ..utils import HTTPException
1615
from .router import Router
1716

@@ -206,41 +205,10 @@ def check_allowed_hosts(self, request: Request):
206205
raise HTTPException(f"Host '{host}' not allowed.", status_code=400)
207206

208207
def default_exception_handler(self, _: Request, exc: Exception):
209-
"""Default exception handler for unhandled exceptions.
210-
211-
This method generates a standardized JSON error response for any
212-
unhandled exceptions that occur during request processing. It uses
213-
the framework's error conventions to provide consistent error messages.
214-
215-
Args:
216-
_: Request object (unused).
217-
exc: Exception that occurred.
218-
219-
Returns:
220-
JsonResponse: Standardized error response.
221-
"""
222-
status_code = getattr(exc, "status_code", 500)
223-
details = getattr(exc, "details", None)
224-
225-
base = ERROR_CONVENTIONS.get(
226-
status_code,
227-
{"code": "UNKNOWN_ERROR", "message": "An unknown error occurred."},
228-
)
229-
230-
error = {"code": base["code"], "message": base["message"]}
231-
232-
if details:
233-
if isinstance(details, dict):
234-
error["details"] = details
235-
else:
236-
error["message"] = details
237-
238-
return JsonResponse(
239-
{
240-
"success": False,
241-
"error": error,
242-
},
243-
status=status_code,
208+
message = getattr(exc, "details", "Internal error. Please try again.")
209+
satus = getattr(exc, "status_code", 400)
210+
return TextResponse(
211+
str(message) if not isinstance(message, str) else message, status=satus
244212
)
245213

246214
def dispatch_request(self, request: Request):

webspark/http/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
RedirectResponse,
77
Response,
88
StreamResponse,
9-
SuccessResponse,
109
TextResponse,
1110
)
1211

@@ -17,7 +16,6 @@
1716
"HTMLResponse",
1817
"MultipartParser",
1918
"StreamResponse",
20-
"SuccessResponse",
2119
"TextResponse",
2220
"RedirectResponse",
2321
]

webspark/http/response.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -575,33 +575,3 @@ def as_wsgi(self):
575575
status_str = STATUS_CODE.get(self.status, f"{self.status} Unknown")
576576
headers_list = list(self.headers.items())
577577
return status_str, headers_list, self.body
578-
579-
580-
def SuccessResponse(
581-
data: Any, status: int = 200, headers: dict[str, str] | None = None
582-
) -> JsonResponse:
583-
"""Create a standardized success JSON response.
584-
585-
This utility function creates a JsonResponse with a standardized
586-
success format that includes a success flag and wrapped data.
587-
588-
Example:
589-
response = SuccessResponse({"user_id": 123, "username": "john_doe"})
590-
# Creates JSON: {"success": true, "data": {"user_id": 123, "username": "john_doe"}}
591-
592-
Args:
593-
data: The data to wrap in the success response.
594-
status: HTTP status code (default: 200).
595-
headers: Additional headers.
596-
597-
Returns:
598-
JsonResponse: A JSON response with success structure.
599-
"""
600-
return JsonResponse(
601-
{
602-
"success": True,
603-
"data": data,
604-
},
605-
status=status,
606-
headers=headers,
607-
)

0 commit comments

Comments
 (0)