# API Implementation Plan - Section 1: Unified Response Format **Date:** 2025-01-XX **Status:** Planning **Priority:** High **Related Document:** `API-ENDPOINTS-ANALYSIS.md` --- ## Executive Summary This document outlines the implementation plan for **Section 1: Define a Unified Response Format** across the IGNY8 API layer. The goal is to ensure every API endpoint in all modules responds with a consistent JSON structure, improving API predictability, frontend integration, and developer experience. **Target Format:** ```json { "success": true | false, "data": {...} | [...], "message": "Human-readable success/failure message", "error": "Top-level error message (if any)", "errors": { "field": ["Error list"] } (optional, for validation failures) } ``` --- ## Table of Contents 1. [Current State Analysis](#current-state-analysis) 2. [Implementation Tasks](#implementation-tasks) 3. [Task 1: Define Global Response Wrapper Functions](#task-1-define-global-response-wrapper-functions) 4. [Task 2: Refactor All API Views and ViewSets](#task-2-refactor-all-api-views-and-viewsets) 5. [Task 3: Patch DRF Auto-generated Responses (Optional)](#task-3-patch-drf-auto-generated-responses-optional) 6. [Task 4: Update Exception Handlers](#task-4-update-exception-handlers) 7. [Task 5: Validation via Postman/Frontend Logs](#task-5-validation-via-postmanfrontend-logs) 8. [Task 6: Documentation Update](#task-6-documentation-update) 9. [Task 7: Changelog Entry](#task-7-changelog-entry) 10. [Testing Strategy](#testing-strategy) 11. [Rollout Plan](#rollout-plan) 12. [Success Criteria](#success-criteria) --- ## Current State Analysis ### Current Response Format Issues Based on `API-ENDPOINTS-ANALYSIS.md`, the following inconsistencies exist: 1. **Mixed Response Formats:** - Some endpoints return `{ success: true, data: ... }` - Some endpoints return DRF standard format `{ count, next, previous, results: [...] }` - Some endpoints return custom formats - Error responses vary: `{ error: "..." }` vs `{ success: false, message: "..." }` 2. **Affected Modules:** - **Auth Module**: 15+ endpoints (register, login, user management, etc.) - **Planner Module**: 20+ endpoints (keywords, clusters, ideas) - **Writer Module**: 20+ endpoints (tasks, content, images) - **System Module**: 30+ endpoints (settings, prompts, integrations) - **Billing Module**: 10+ endpoints (credits, transactions, usage) 3. **Base Classes to Update:** - `AccountModelViewSet` - Used by many ViewSets - `SiteSectorModelViewSet` - Used by Planner/Writer modules - Custom ViewSets with overridden `create`, `update`, `destroy` methods --- ## Implementation Tasks ### Overview | Task ID | Task Name | Priority | Estimated Effort | Dependencies | |---------|-----------|----------|------------------|--------------| | 1.1 | Create response wrapper functions | High | 2 hours | None | | 1.2 | Update exception handlers | High | 3 hours | 1.1 | | 2.1 | Audit all ViewSets | Medium | 4 hours | None | | 2.2 | Refactor Auth module ViewSets | High | 6 hours | 1.1, 1.2 | | 2.3 | Refactor Planner module ViewSets | High | 8 hours | 1.1, 1.2 | | 2.4 | Refactor Writer module ViewSets | High | 8 hours | 1.1, 1.2 | | 2.5 | Refactor System module ViewSets | High | 10 hours | 1.1, 1.2 | | 2.6 | Refactor Billing module ViewSets | High | 4 hours | 1.1, 1.2 | | 3.1 | Patch DRF auto-generated responses (optional) | Low | 4 hours | 1.1 | | 5.1 | Manual testing | High | 8 hours | 2.2-2.6 | | 6.1 | Update documentation | Medium | 4 hours | 5.1 | **Total Estimated Effort:** ~61 hours --- ## Task 1: Define Global Response Wrapper Functions ### Goal Create reusable helper functions that standardize all API responses. ### Implementation Steps #### Step 1.1: Create Response Utility Module **File:** `backend/igny8_core/api/response.py` **Implementation:** ```python """ Unified API Response Format Utilities This module provides helper functions to ensure all API endpoints return a consistent response format. """ from rest_framework.response import Response from rest_framework import status def success_response(data=None, message=None, status_code=status.HTTP_200_OK): """ Create a standardized success response. Args: data: Response data (dict, list, or any serializable object) message: Optional human-readable success message status_code: HTTP status code (default: 200) Returns: Response: DRF Response object with unified format Example: return success_response( data={"id": 1, "name": "Example"}, message="Resource created successfully" ) """ response_data = { "success": True, "data": data, } if message: response_data["message"] = message return Response(response_data, status=status_code) def error_response( error, errors=None, status_code=status.HTTP_400_BAD_REQUEST, message=None ): """ Create a standardized error response. Args: error: Top-level error message (string) errors: Optional field-specific validation errors (dict) status_code: HTTP status code (default: 400) message: Optional additional message (deprecated, use error) Returns: Response: DRF Response object with unified format Example: return error_response( error="Validation failed", errors={"email": ["Invalid email format"]}, status_code=status.HTTP_400_BAD_REQUEST ) """ response_data = { "success": False, "error": error, } if errors: response_data["errors"] = errors # Backward compatibility: if message is provided, use it as error if message and not error: response_data["error"] = message return Response(response_data, status=status_code) def paginated_response(paginated_data, message=None): """ Create a standardized paginated response. This wraps DRF's pagination response to include success flag and optional message while preserving pagination metadata. Args: paginated_data: DRF paginated response data (dict with count, next, previous, results) message: Optional human-readable message Returns: Response: DRF Response object with unified format Example: paginator = CustomPageNumberPagination() page = paginator.paginate_queryset(queryset, request) serializer = MySerializer(page, many=True) paginated_data = paginator.get_paginated_response(serializer.data).data return paginated_response(paginated_data, message="Keywords retrieved successfully") """ response_data = { "success": True, **paginated_data # Unpack count, next, previous, results } if message: response_data["message"] = message return Response(response_data) ``` #### Step 1.2: Update `__init__.py` for Easy Import **File:** `backend/igny8_core/api/__init__.py` **Add:** ```python from .response import success_response, error_response, paginated_response __all__ = ['success_response', 'error_response', 'paginated_response'] ``` #### Step 1.3: Create Unit Tests **File:** `backend/igny8_core/api/tests/test_response.py` **Test Cases:** - Test `success_response` with data only - Test `success_response` with data and message - Test `success_response` with custom status code (201, 204) - Test `error_response` with error only - Test `error_response` with error and errors dict - Test `error_response` with custom status codes (400, 403, 404, 500) - Test `paginated_response` with standard pagination data - Test `paginated_response` with message **Estimated Time:** 2 hours --- ## Task 2: Refactor All API Views and ViewSets ### Goal Update all ViewSets to use the new response wrapper functions. ### Implementation Strategy #### Step 2.1: Audit All ViewSets **Action Items:** 1. List all ViewSets in each module 2. Identify custom `create`, `update`, `destroy`, `list`, `retrieve` methods 3. Identify custom action methods (`@action` decorator) 4. Document current response formats 5. Create refactoring checklist **Files to Audit:** **Auth Module:** - `backend/auth/api/views.py` - UsersViewSet, AccountsViewSet, SiteViewSet, etc. - `backend/auth/api/viewsets.py` - Custom authentication views **Planner Module:** - `backend/planner/api/viewsets.py` - KeywordViewSet, ClusterViewSet, ContentIdeasViewSet **Writer Module:** - `backend/writer/api/viewsets.py` - TasksViewSet, ContentViewSet, ImagesViewSet **System Module:** - `backend/system/api/viewsets.py` - AIPromptViewSet, AuthorProfileViewSet, StrategyViewSet - `backend/system/api/viewsets.py` - IntegrationSettingsViewSet, SystemSettingsViewSet, etc. **Billing Module:** - `backend/billing/api/viewsets.py` - CreditBalanceViewSet, CreditUsageViewSet, CreditTransactionViewSet **Estimated Time:** 4 hours #### Step 2.2: Refactor Auth Module ViewSets **Priority Endpoints:** 1. `POST /api/v1/auth/register/` - Registration endpoint 2. `POST /api/v1/auth/login/` - Login endpoint 3. `POST /api/v1/auth/change-password/` - Password change 4. `GET /api/v1/auth/me/` - Current user info 5. All ViewSet CRUD operations 6. Custom actions (invite, activate, etc.) **Example Refactoring:** **Before:** ```python class RegisterView(APIView): def post(self, request): # ... validation logic ... return Response({ "success": True, "message": "Registration successful", "user": user_data }, status=201) ``` **After:** ```python from igny8_core.api.response import success_response class RegisterView(APIView): def post(self, request): # ... validation logic ... return success_response( data={"user": user_data}, message="Registration successful", status_code=status.HTTP_201_CREATED ) ``` **Estimated Time:** 6 hours #### Step 2.3: Refactor Planner Module ViewSets **Priority Endpoints:** 1. KeywordViewSet - All CRUD + custom actions (bulk_delete, auto_cluster, etc.) 2. ClusterViewSet - All CRUD + custom actions (auto_generate_ideas) 3. ContentIdeasViewSet - All CRUD + custom actions (bulk_queue_to_writer) **Example Refactoring:** **Before:** ```python class KeywordViewSet(SiteSectorModelViewSet): @action(detail=False, methods=['post']) def bulk_delete(self, request): ids = request.data.get('ids', []) deleted_count = Keyword.objects.filter(id__in=ids).delete()[0] return Response({"deleted_count": deleted_count}) ``` **After:** ```python from igny8_core.api.response import success_response, error_response class KeywordViewSet(SiteSectorModelViewSet): @action(detail=False, methods=['post']) def bulk_delete(self, request): ids = request.data.get('ids', []) if not ids: return error_response( error="No IDs provided", status_code=status.HTTP_400_BAD_REQUEST ) deleted_count = Keyword.objects.filter(id__in=ids).delete()[0] return success_response( data={"deleted_count": deleted_count}, message=f"Successfully deleted {deleted_count} keywords" ) ``` **Estimated Time:** 8 hours #### Step 2.4: Refactor Writer Module ViewSets **Priority Endpoints:** 1. TasksViewSet - All CRUD + custom actions (auto_generate_content, bulk_update) 2. ContentViewSet - All CRUD + custom actions (generate_image_prompts) 3. ImagesViewSet - All CRUD + custom actions (generate_images, bulk_update) **Estimated Time:** 8 hours #### Step 2.5: Refactor System Module ViewSets **Priority Endpoints:** 1. AIPromptViewSet - All CRUD + custom actions (save, reset, by_type) 2. IntegrationSettingsViewSet - Custom URL patterns (save, test, generate) 3. All Settings ViewSets (SystemSettings, AccountSettings, UserSettings, etc.) **Special Considerations:** - IntegrationSettingsViewSet uses custom URL patterns (not standard ViewSet routes) - Task progress endpoint returns Celery task status (may need special handling) **Estimated Time:** 10 hours #### Step 2.6: Refactor Billing Module ViewSets **Priority Endpoints:** 1. CreditBalanceViewSet - Custom action (balance) 2. CreditUsageViewSet - Read-only + custom actions (summary, limits) 3. CreditTransactionViewSet - Read-only **Estimated Time:** 4 hours ### Refactoring Checklist Template For each ViewSet, check: - [ ] Import response wrapper functions - [ ] Update `create` method (if overridden) - [ ] Update `update` method (if overridden) - [ ] Update `destroy` method (if overridden) - [ ] Update `list` method (if overridden) - Use `paginated_response` for paginated lists - [ ] Update `retrieve` method (if overridden) - [ ] Update all custom `@action` methods - [ ] Update error handling to use `error_response` - [ ] Test each endpoint manually - [ ] Verify response format matches specification --- ## Task 3: Patch DRF Auto-generated Responses (Optional) ### Goal Ensure DRF's auto-generated responses (from ModelViewSet default methods) also follow the unified format. ### Implementation Steps #### Step 3.1: Create Base API View Class **File:** `backend/igny8_core/api/views.py` **Implementation:** ```python """ Base API View Classes with Unified Response Format """ from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from rest_framework import status from .response import success_response, error_response, paginated_response class CustomAPIView(APIView): """ Base APIView that automatically wraps responses in unified format. Override finalize_response to ensure all responses follow the format. """ def finalize_response(self, request, response, *args, **kwargs): """ Override to wrap DRF responses in unified format. This is called for all responses, including exceptions. """ # If response is already in unified format, return as-is if isinstance(response.data, dict) and 'success' in response.data: return super().finalize_response(request, response, *args, **kwargs) # Wrap standard DRF responses if response.status_code < 400: # Success response if isinstance(response.data, dict): # Check if it's paginated response if 'results' in response.data and 'count' in response.data: # Paginated response return super().finalize_response( request, paginated_response(response.data), *args, **kwargs ) else: # Regular success response return super().finalize_response( request, success_response(response.data), *args, **kwargs ) else: # List or other data return super().finalize_response( request, success_response(response.data), *args, **kwargs ) else: # Error response error_msg = str(response.data) if response.data else "An error occurred" errors = response.data if isinstance(response.data, dict) else None return super().finalize_response( request, error_response( error=error_msg, errors=errors, status_code=response.status_code ), *args, **kwargs ) class CustomModelViewSet(ModelViewSet): """ Base ModelViewSet that automatically wraps responses in unified format. Inherit from this instead of ModelViewSet to get automatic response formatting. """ def finalize_response(self, request, response, *args, **kwargs): """ Override to wrap DRF responses in unified format. """ # Same logic as CustomAPIView if isinstance(response.data, dict) and 'success' in response.data: return super().finalize_response(request, response, *args, **kwargs) if response.status_code < 400: if isinstance(response.data, dict) and 'results' in response.data: return super().finalize_response( request, paginated_response(response.data), *args, **kwargs ) else: return super().finalize_response( request, success_response(response.data), *args, **kwargs ) else: error_msg = str(response.data) if response.data else "An error occurred" errors = response.data if isinstance(response.data, dict) else None return super().finalize_response( request, error_response( error=error_msg, errors=errors, status_code=response.status_code ), *args, **kwargs ) ``` #### Step 3.2: Update Base ViewSets (Optional) **Decision Point:** - **Option A:** Update `AccountModelViewSet` and `SiteSectorModelViewSet` to inherit from `CustomModelViewSet` - **Option B:** Keep current inheritance, manually wrap responses in each ViewSet **Recommendation:** Option B (manual wrapping) for more control and explicit behavior. **Estimated Time:** 4 hours (if implemented) --- ## Task 4: Update Exception Handlers ### Goal Ensure all exceptions (400, 403, 404, 500) are wrapped in the unified error format. ### Implementation Steps #### Step 4.1: Create Custom Exception Handler **File:** `backend/igny8_core/api/exception_handlers.py` **Implementation:** ```python """ Custom Exception Handlers for Unified Response Format """ from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from .response import error_response def custom_exception_handler(exc, context): """ Custom exception handler that wraps all exceptions in unified format. This handler is called for all exceptions raised in API views. It ensures all error responses follow the unified format. Args: exc: The exception instance context: Dictionary containing request, view, and args Returns: Response: Error response in unified format, or None to use default """ # Call DRF's default exception handler first response = drf_exception_handler(exc, context) if response is not None: # Extract error message and details error_message = "An error occurred" errors = None status_code = response.status_code # Handle different exception types if hasattr(exc, 'detail'): # DRF exceptions have 'detail' attribute if isinstance(exc.detail, dict): # Validation errors - use as errors dict errors = exc.detail # Extract first error message as top-level error if errors: first_key = list(errors.keys())[0] first_error = errors[first_key] if isinstance(first_error, list) and first_error: error_message = f"{first_key}: {first_error[0]}" else: error_message = f"{first_key}: {first_error}" else: error_message = "Validation failed" elif isinstance(exc.detail, list): # List of errors error_message = exc.detail[0] if exc.detail else "An error occurred" errors = {"non_field_errors": exc.detail} else: # String error message error_message = str(exc.detail) else: # Generic exception error_message = str(exc) if exc else "An error occurred" # Map status codes to appropriate error messages if status_code == status.HTTP_400_BAD_REQUEST: if not error_message or error_message == "An error occurred": error_message = "Bad request" elif status_code == status.HTTP_401_UNAUTHORIZED: error_message = "Authentication required" if error_message == "An error occurred" else error_message elif status_code == status.HTTP_403_FORBIDDEN: error_message = "Permission denied" if error_message == "An error occurred" else error_message elif status_code == status.HTTP_404_NOT_FOUND: error_message = "Resource not found" if error_message == "An error occurred" else error_message elif status_code == status.HTTP_500_INTERNAL_SERVER_ERROR: error_message = "Internal server error" if error_message == "An error occurred" else error_message # Return unified error response return error_response( error=error_message, errors=errors, status_code=status_code ) # Return None to use default exception handling for unhandled exceptions return None ``` #### Step 4.2: Register Exception Handler in Settings **File:** `backend/igny8_core/settings.py` **Update REST_FRAMEWORK configuration:** ```python REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'igny8_core.api.authentication.JWTAuthentication', 'igny8_core.api.authentication.CSRFExemptSessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', ], 'EXCEPTION_HANDLER': 'igny8_core.api.exception_handlers.custom_exception_handler', # Add this # ... other settings ... } ``` #### Step 4.3: Test Exception Handling **Test Cases:** 1. ValidationError (400) - Should return `{ success: false, error: "...", errors: {...} }` 2. PermissionDenied (403) - Should return `{ success: false, error: "Permission denied" }` 3. NotFound (404) - Should return `{ success: false, error: "Resource not found" }` 4. Generic Exception (500) - Should return `{ success: false, error: "Internal server error" }` **Estimated Time:** 3 hours --- ## Task 5: Validation via Postman/Frontend Logs ### Goal Manually test endpoints to ensure responses match the unified format. ### Implementation Steps #### Step 5.1: Create Test Checklist **Test Endpoints by Module:** **Auth Module (5 endpoints):** - [ ] `POST /api/v1/auth/register/` - Success and error cases - [ ] `POST /api/v1/auth/login/` - Success and error cases - [ ] `POST /api/v1/auth/change-password/` - Success and error cases - [ ] `GET /api/v1/auth/me/` - Success and unauthorized cases - [ ] `GET /api/v1/auth/users/` - List with pagination **Planner Module (6 endpoints):** - [ ] `GET /api/v1/planner/keywords/` - List with pagination - [ ] `POST /api/v1/planner/keywords/` - Create success and validation error - [ ] `POST /api/v1/planner/keywords/bulk_delete/` - Success and error - [ ] `POST /api/v1/planner/keywords/auto_cluster/` - Success response - [ ] `GET /api/v1/planner/clusters/` - List with pagination - [ ] `POST /api/v1/planner/ideas/bulk_queue_to_writer/` - Success response **Writer Module (6 endpoints):** - [ ] `GET /api/v1/writer/tasks/` - List with pagination - [ ] `POST /api/v1/writer/tasks/` - Create success and validation error - [ ] `POST /api/v1/writer/tasks/auto_generate_content/` - Success response - [ ] `GET /api/v1/writer/content/` - List with pagination - [ ] `POST /api/v1/writer/images/generate_images/` - Success response - [ ] `GET /api/v1/writer/images/{id}/file/` - Success response **System Module (5 endpoints):** - [ ] `GET /api/v1/system/prompts/` - List with pagination - [ ] `POST /api/v1/system/prompts/save/` - Success and error - [ ] `GET /api/v1/system/settings/integrations/{pk}/` - Success response - [ ] `POST /api/v1/system/settings/integrations/{pk}/test/` - Success and error - [ ] `GET /api/v1/system/status/` - Success response **Billing Module (3 endpoints):** - [ ] `GET /api/v1/billing/credits/balance/balance/` - Success response - [ ] `GET /api/v1/billing/credits/usage/` - List with pagination - [ ] `GET /api/v1/billing/credits/usage/summary/` - Success response #### Step 5.2: Create Postman Collection **File:** `unified-api/postman/API-Response-Format-Tests.postman_collection.json` **Include:** - All test endpoints from checklist - Success case tests - Error case tests (validation, 404, 403, 500) - Response format validation (JSON schema) #### Step 5.3: Test Frontend Integration **Action Items:** 1. Check browser console for API response logs 2. Verify frontend code handles new response format 3. Test error handling in frontend 4. Verify pagination still works correctly 5. Check for any breaking changes in response structure **Files to Check:** - `frontend/src/services/api.ts` - API client - Frontend stores (Zustand) - Response parsing - Error handling components **Estimated Time:** 8 hours --- ## Task 6: Documentation Update ### Goal Update all relevant documentation to reflect the new unified response format. ### Implementation Steps #### Step 6.1: Update Backend Implementation Docs **File:** `docs/04-BACKEND-IMPLEMENTATION.md` **Add Section: "Standard Response Format"** ```markdown ## Standard Response Format All API endpoints return responses in a unified JSON format: ### Success Response ```json { "success": true, "data": { // Response data (object or array) }, "message": "Optional success message" } ``` ### Error Response ```json { "success": false, "error": "Top-level error message", "errors": { "field_name": ["Field-specific error messages"] } } ``` ### Paginated Response ```json { "success": true, "count": 100, "next": "http://api.igny8.com/api/v1/endpoint/?page=2", "previous": null, "results": [ ... ], "message": "Optional message" } ``` ### Status Codes - `200 OK` - Success - `201 Created` - Resource created successfully - `400 Bad Request` - Validation error or bad request - `401 Unauthorized` - Authentication required - `403 Forbidden` - Permission denied - `404 Not Found` - Resource not found - `500 Internal Server Error` - Server error ``` #### Step 6.2: Update API Endpoints Analysis **File:** `unified-api/API-ENDPOINTS-ANALYSIS.md` **Update Section:** "Response Format Standards" - Mark as "Implemented" or "Standardized" - Add examples using new format - Update any outdated examples #### Step 6.3: Update API Schema (if using Swagger/OpenAPI) **If using drf-spectacular or similar:** **File:** `backend/igny8_core/api/schema.py` (if exists) **Add response schema:** ```python from drf_spectacular.utils import extend_schema, OpenApiResponse from drf_spectacular.types import OpenApiTypes # Define unified response schemas SUCCESS_RESPONSE_SCHEMA = { "type": "object", "properties": { "success": {"type": "boolean", "example": True}, "data": {"type": "object"}, "message": {"type": "string", "nullable": True} } } ERROR_RESPONSE_SCHEMA = { "type": "object", "properties": { "success": {"type": "boolean", "example": False}, "error": {"type": "string"}, "errors": {"type": "object", "nullable": True} } } ``` #### Step 6.4: Create API Design Guide Section **File:** `docs/API-DESIGN-GUIDE.md` (create if doesn't exist) **Add:** - Response format policy - When to use `success_response` vs `error_response` - Pagination guidelines - Error message best practices - Status code guidelines **Estimated Time:** 4 hours --- ## Task 7: Changelog Entry ### Goal Document the changes in the project changelog. ### Implementation Steps #### Step 7.1: Update CHANGELOG.md **File:** `CHANGELOG.md` (or similar) **Add Entry:** ```markdown ## [Unreleased] - 2025-01-XX ### Changed - **API Response Format**: Unified all API responses under standardized success/error format - All endpoints now return `{ success, data, message, error, errors }` format - Created `igny8_core.api.response` module with `success_response()`, `error_response()`, and `paginated_response()` helpers - Updated custom exception handler to wrap all errors in unified format - Refactored all ViewSets across Auth, Planner, Writer, System, and Billing modules - Updated documentation with response format examples ### Affected Areas - API Layer (`igny8_core/api/`) - Auth Module (`auth/api/`) - Planner Module (`planner/api/`) - Writer Module (`writer/api/`) - System Module (`system/api/`) - Billing Module (`billing/api/`) ### Breaking Changes - ⚠️ **Response Format Change**: All API responses now follow unified format - Frontend code may need updates if it directly accesses response fields - Paginated responses now include `success: true` and optional `message` field - Error responses now use `error` field instead of `message` for top-level errors ### Migration Guide 1. Update frontend API client to handle new response format 2. Update error handling to check `success` field 3. Update pagination handling to account for `success` and `message` fields in paginated responses ``` **Estimated Time:** 1 hour --- ## Testing Strategy ### Unit Tests **File:** `backend/igny8_core/api/tests/test_response.py` **Test Cases:** - Response wrapper functions - Exception handler - Edge cases (None data, empty lists, etc.) ### Integration Tests **Test Cases:** - End-to-end API calls - Error scenarios (validation, 404, 500) - Pagination with unified format - Authentication errors ### Manual Testing **Checklist:** - [ ] All endpoints return unified format - [ ] Success responses have `success: true` - [ ] Error responses have `success: false` - [ ] Pagination includes `success: true` - [ ] Error messages are human-readable - [ ] Validation errors include field-specific errors --- ## Rollout Plan ### Phase 1: Foundation (Week 1) - ✅ Task 1: Create response wrapper functions - ✅ Task 4: Update exception handlers - ✅ Unit tests for response utilities ### Phase 2: Module Refactoring (Week 2-3) - ✅ Task 2.2: Refactor Auth module - ✅ Task 2.3: Refactor Planner module - ✅ Task 2.4: Refactor Writer module - ✅ Task 2.5: Refactor System module - ✅ Task 2.6: Refactor Billing module ### Phase 3: Testing & Validation (Week 4) - ✅ Task 5: Manual testing - ✅ Frontend integration testing - ✅ Bug fixes and adjustments ### Phase 4: Documentation & Release (Week 5) - ✅ Task 6: Documentation updates - ✅ Task 7: Changelog entry - ✅ Release to staging - ✅ Production deployment --- ## Success Criteria ### Definition of Done 1. ✅ All API endpoints return unified response format 2. ✅ Response wrapper functions are unit tested 3. ✅ Exception handler wraps all errors correctly 4. ✅ All ViewSets use response wrappers 5. ✅ Documentation is updated 6. ✅ Frontend integration tested and working 7. ✅ No breaking changes for critical endpoints (or migration guide provided) 8. ✅ Postman collection updated with new format 9. ✅ Changelog entry created ### Metrics - **Coverage:** 100% of API endpoints use unified format - **Test Coverage:** >90% for response utilities - **Documentation:** All modules documented - **Breaking Changes:** Documented and migration guide provided --- ## Risk Assessment ### Risks 1. **Breaking Changes:** Frontend may break if it expects old format - **Mitigation:** Provide migration guide, test frontend integration early 2. **Performance Impact:** Wrapping responses may add overhead - **Mitigation:** Minimal overhead, test performance if concerned 3. **Incomplete Migration:** Some endpoints may be missed - **Mitigation:** Comprehensive audit checklist, automated tests 4. **Exception Handler Conflicts:** Custom exception handling may conflict - **Mitigation:** Test thoroughly, handle edge cases ### Rollback Plan If issues arise: 1. Revert exception handler changes (keep in settings but disable) 2. Keep response wrapper functions (non-breaking) 3. Gradually roll back ViewSet changes if needed 4. Document issues for future fixes --- ## Appendix ### Response Format Examples #### Success with Data ```json { "success": true, "data": { "id": 1, "name": "Example Keyword", "status": "active" }, "message": "Keyword created successfully" } ``` #### Success with List ```json { "success": true, "data": [ {"id": 1, "name": "Keyword 1"}, {"id": 2, "name": "Keyword 2"} ], "message": "Keywords retrieved successfully" } ``` #### Error with Validation ```json { "success": false, "error": "Validation failed", "errors": { "email": ["Invalid email format"], "password": ["Password must be at least 8 characters"] } } ``` #### Error without Details ```json { "success": false, "error": "Resource not found" } ``` #### Paginated Response ```json { "success": true, "count": 150, "next": "http://api.igny8.com/api/v1/planner/keywords/?page=2", "previous": null, "results": [ {"id": 1, "name": "Keyword 1"}, {"id": 2, "name": "Keyword 2"} ], "message": "Keywords retrieved successfully" } ``` --- **Document Status:** Implementation Plan **Last Updated:** 2025-01-XX **Next Review:** After Phase 1 completion