- Enhanced the `error_response` function to support backward compatibility by normalizing arguments when positional arguments are misused. - Updated various views to pass `None` for the `errors` parameter in `error_response` calls, ensuring consistent response formatting. - Adjusted logging in `ContentSyncService` and `WordPressClient` to use debug level for expected 401 errors, improving log clarity. - Removed deprecated fields from serializers and views, streamlining content management processes.
177 lines
5.9 KiB
Python
177 lines
5.9 KiB
Python
"""
|
|
Unified API Response Helpers
|
|
Provides consistent response format across all endpoints
|
|
"""
|
|
from rest_framework.response import Response
|
|
from rest_framework import status
|
|
import uuid
|
|
from typing import Any
|
|
from django.http import HttpRequest
|
|
|
|
|
|
def get_request_id(request):
|
|
"""Get request ID from request object (set by middleware) or headers, or generate new one"""
|
|
if not request:
|
|
return None
|
|
|
|
# First check if middleware set request_id on request object
|
|
if hasattr(request, 'request_id') and request.request_id:
|
|
return request.request_id
|
|
|
|
# Fallback to headers
|
|
if hasattr(request, 'META'):
|
|
request_id = request.META.get('HTTP_X_REQUEST_ID') or request.META.get('X-Request-ID')
|
|
if request_id:
|
|
return request_id
|
|
|
|
# Generate new request ID if none found
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
def success_response(data=None, message=None, status_code=status.HTTP_200_OK, request=None):
|
|
"""
|
|
Create a standardized success response
|
|
|
|
Args:
|
|
data: Response data (dict, list, or None)
|
|
message: Optional success message
|
|
status_code: HTTP status code (default: 200)
|
|
request: Request object (optional, for request_id)
|
|
|
|
Returns:
|
|
Response object with unified format
|
|
"""
|
|
response_data = {
|
|
'success': True,
|
|
}
|
|
|
|
if data is not None:
|
|
response_data['data'] = data
|
|
|
|
if message:
|
|
response_data['message'] = message
|
|
|
|
# Add request_id if request is provided
|
|
if request:
|
|
response_data['request_id'] = get_request_id(request)
|
|
|
|
return Response(response_data, status=status_code)
|
|
|
|
|
|
def error_response(error=None, errors=None, status_code=status.HTTP_400_BAD_REQUEST, request=None, debug_info=None):
|
|
"""
|
|
Create a standardized error response
|
|
|
|
Args:
|
|
error: Top-level error message
|
|
errors: Field-specific errors (dict of field -> list of errors)
|
|
status_code: HTTP status code (default: 400)
|
|
request: Request object (optional, for request_id)
|
|
debug_info: Debug information (only in DEBUG mode)
|
|
|
|
Returns:
|
|
Response object with unified error format
|
|
"""
|
|
response_data = {
|
|
'success': False,
|
|
}
|
|
|
|
# Backwards compatibility: some callers used positional args in the order
|
|
# (error, status_code, request) which maps to (error, errors, status_code=request)
|
|
# causing `status_code` to be a Request object and raising TypeError.
|
|
# Detect this misuse and normalize arguments:
|
|
try:
|
|
if request is None and status_code is not None:
|
|
# If status_code appears to be a Request object, shift arguments
|
|
if isinstance(status_code, HttpRequest) or hasattr(status_code, 'META'):
|
|
# original call looked like: error_response(msg, status.HTTP_400_BAD_REQUEST, request)
|
|
# which resulted in: errors = status.HTTP_400..., status_code = request
|
|
request = status_code
|
|
# If `errors` holds an int-like HTTP status, use it as status_code
|
|
if isinstance(errors, int):
|
|
status_code = errors
|
|
errors = None
|
|
else:
|
|
# fallback to default 400
|
|
status_code = status.HTTP_400_BAD_REQUEST
|
|
except Exception:
|
|
# Defensive: if introspection fails, continue with provided args
|
|
pass
|
|
|
|
if error:
|
|
response_data['error'] = error
|
|
elif status_code == status.HTTP_400_BAD_REQUEST:
|
|
response_data['error'] = 'Bad request'
|
|
elif status_code == status.HTTP_401_UNAUTHORIZED:
|
|
response_data['error'] = 'Authentication required'
|
|
elif status_code == status.HTTP_403_FORBIDDEN:
|
|
response_data['error'] = 'Permission denied'
|
|
elif status_code == status.HTTP_404_NOT_FOUND:
|
|
response_data['error'] = 'Resource not found'
|
|
elif status_code == status.HTTP_409_CONFLICT:
|
|
response_data['error'] = 'Conflict'
|
|
elif status_code == status.HTTP_422_UNPROCESSABLE_ENTITY:
|
|
response_data['error'] = 'Validation failed'
|
|
elif status_code == status.HTTP_429_TOO_MANY_REQUESTS:
|
|
response_data['error'] = 'Rate limit exceeded'
|
|
elif status_code >= 500:
|
|
response_data['error'] = 'Internal server error'
|
|
else:
|
|
response_data['error'] = 'An error occurred'
|
|
|
|
if errors:
|
|
response_data['errors'] = errors
|
|
|
|
# Add request_id if request is provided
|
|
if request:
|
|
response_data['request_id'] = get_request_id(request)
|
|
|
|
# Add debug info in DEBUG mode
|
|
if debug_info:
|
|
response_data['debug'] = debug_info
|
|
|
|
return Response(response_data, status=status_code)
|
|
|
|
|
|
def paginated_response(paginated_data, message=None, request=None):
|
|
"""
|
|
Create a standardized paginated response
|
|
|
|
Args:
|
|
paginated_data: Paginated data dict from DRF paginator (contains count, next, previous, results)
|
|
message: Optional success message
|
|
request: Request object (optional, for request_id)
|
|
|
|
Returns:
|
|
Response object with unified paginated format
|
|
"""
|
|
response_data = {
|
|
'success': True,
|
|
}
|
|
|
|
# Copy pagination fields from DRF paginator
|
|
if isinstance(paginated_data, dict):
|
|
response_data.update({
|
|
'count': paginated_data.get('count', 0),
|
|
'next': paginated_data.get('next'),
|
|
'previous': paginated_data.get('previous'),
|
|
'results': paginated_data.get('results', [])
|
|
})
|
|
else:
|
|
# Fallback if paginated_data is not a dict
|
|
response_data['count'] = 0
|
|
response_data['next'] = None
|
|
response_data['previous'] = None
|
|
response_data['results'] = []
|
|
|
|
if message:
|
|
response_data['message'] = message
|
|
|
|
# Add request_id if request is provided
|
|
if request:
|
|
response_data['request_id'] = get_request_id(request)
|
|
|
|
return Response(response_data, status=status.HTTP_200_OK)
|
|
|
|
|