1433 lines
38 KiB
Markdown
1433 lines
38 KiB
Markdown
# API Implementation Plan - Section 6: Testing and Documentation
|
|
|
|
**Date:** 2025-01-XX
|
|
**Status:** Planning
|
|
**Priority:** High
|
|
**Related Documents:** All previous sections (1-5)
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
This document outlines the implementation plan for **Section 6: Testing and Documentation** - the final phase of the API standardization project. The goal is to fully test all standardized endpoints, validate frontend compatibility, and update internal and external API documentation to reflect the new structure.
|
|
|
|
**Key Objectives:**
|
|
- Create comprehensive API test suite (manual + automated)
|
|
- Verify frontend integration works correctly
|
|
- Document unified API format for internal and external use
|
|
- Publish Swagger/OpenAPI documentation (optional)
|
|
- Add health check endpoint
|
|
- Finalize changelog and version bump
|
|
- Complete production readiness checklist
|
|
|
|
**Note:** This section should be executed after Sections 1-5 are complete.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Current State Analysis](#current-state-analysis)
|
|
2. [Implementation Tasks](#implementation-tasks)
|
|
3. [Task 1: Create API Test Suite](#task-1-create-api-test-suite)
|
|
4. [Task 2: Verify Frontend Integration](#task-2-verify-frontend-integration)
|
|
5. [Task 3: Document Unified API Format](#task-3-document-unified-api-format)
|
|
6. [Task 4: Publish Swagger/OpenAPI Docs (Optional)](#task-4-publish-swaggeropenapi-docs-optional)
|
|
7. [Task 5: Add Health Check Endpoint](#task-5-add-health-check-endpoint)
|
|
8. [Task 6: Finalize Changelog and Version Bump](#task-6-finalize-changelog-and-version-bump)
|
|
9. [Task 7: Production Readiness Checklist](#task-7-production-readiness-checklist)
|
|
10. [Testing Strategy](#testing-strategy)
|
|
11. [Rollout Plan](#rollout-plan)
|
|
12. [Success Criteria](#success-criteria)
|
|
|
|
---
|
|
|
|
## Current State Analysis
|
|
|
|
### Current Testing & Documentation Status
|
|
|
|
1. **Testing:**
|
|
- Unit tests exist but may not cover all standardized endpoints
|
|
- Integration tests may need updates for new format
|
|
- Manual testing done ad-hoc, not systematic
|
|
|
|
2. **Documentation:**
|
|
- `API-ENDPOINTS-ANALYSIS.md` exists but may be outdated
|
|
- No comprehensive API usage guide
|
|
- No Swagger/OpenAPI documentation
|
|
- Internal docs may not reflect new format
|
|
|
|
3. **Frontend Integration:**
|
|
- Frontend uses API extensively
|
|
- Need to verify all workflows work with new format
|
|
- Error handling may need updates
|
|
|
|
---
|
|
|
|
## Implementation Tasks
|
|
|
|
### Overview
|
|
|
|
| Task ID | Task Name | Priority | Estimated Effort | Dependencies |
|
|
|---------|-----------|----------|------------------|--------------|
|
|
| 1.1 | Create Postman collection | High | 4 hours | Sections 1-5 |
|
|
| 1.2 | Create automated test suite | High | 8 hours | Sections 1-5 |
|
|
| 2.1 | Frontend workflow testing | High | 8 hours | Sections 1-5 |
|
|
| 2.2 | Frontend error handling verification | High | 4 hours | Sections 1-5 |
|
|
| 3.1 | Update backend implementation docs | High | 4 hours | Sections 1-5 |
|
|
| 3.2 | Create API usage guide | High | 6 hours | Sections 1-5 |
|
|
| 4.1 | Set up Swagger/OpenAPI | Low | 6 hours | Sections 1-5 |
|
|
| 5.1 | Create health check endpoint | Medium | 2 hours | None |
|
|
| 6.1 | Finalize changelog | High | 2 hours | All sections |
|
|
| 6.2 | Version bump | High | 1 hour | 6.1 |
|
|
| 7.1 | Production readiness review | High | 4 hours | All tasks |
|
|
|
|
**Total Estimated Effort:** ~49 hours (~1.2 weeks)
|
|
|
|
---
|
|
|
|
## Task 1: Create API Test Suite
|
|
|
|
### Goal
|
|
Create comprehensive test suite covering all standardized endpoints with manual (Postman) and automated (Python) tests.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 1.1: Create Postman Collection
|
|
|
|
**File:** `unified-api/postman/API-Standardized-Tests.postman_collection.json`
|
|
|
|
**Collection Structure:**
|
|
```json
|
|
{
|
|
"info": {
|
|
"name": "IGNY8 API - Standardized Tests",
|
|
"description": "Comprehensive test suite for standardized API endpoints",
|
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
},
|
|
"item": [
|
|
{
|
|
"name": "Auth Module",
|
|
"item": [
|
|
{
|
|
"name": "Register",
|
|
"request": {
|
|
"method": "POST",
|
|
"url": "{{base_url}}/api/v1/auth/register/",
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"email\": \"test@example.com\",\n \"password\": \"testpass123\",\n \"password_confirm\": \"testpass123\"\n}"
|
|
}
|
|
},
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 201\", function () {",
|
|
" pm.response.to.have.status(201);",
|
|
"});",
|
|
"pm.test(\"Response has success field\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData).to.have.property('success');",
|
|
" pm.expect(jsonData.success).to.eql(true);",
|
|
"});",
|
|
"pm.test(\"Response has data field\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData).to.have.property('data');",
|
|
"});"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "Login",
|
|
"request": {
|
|
"method": "POST",
|
|
"url": "{{base_url}}/api/v1/auth/login/",
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"email\": \"test@example.com\",\n \"password\": \"testpass123\"\n}"
|
|
}
|
|
},
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 200\", function () {",
|
|
" pm.response.to.have.status(200);",
|
|
"});",
|
|
"pm.test(\"Response has success field\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData).to.have.property('success');",
|
|
" pm.expect(jsonData.success).to.eql(true);",
|
|
"});",
|
|
"pm.test(\"Response has tokens\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData.data).to.have.property('tokens');",
|
|
" pm.environment.set(\"access_token\", jsonData.data.tokens.access);",
|
|
"});"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "Planner Module",
|
|
"item": [
|
|
{
|
|
"name": "List Keywords",
|
|
"request": {
|
|
"method": "GET",
|
|
"url": "{{base_url}}/api/v1/planner/keywords/",
|
|
"header": [
|
|
{
|
|
"key": "Authorization",
|
|
"value": "Bearer {{access_token}}"
|
|
}
|
|
]
|
|
},
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 200\", function () {",
|
|
" pm.response.to.have.status(200);",
|
|
"});",
|
|
"pm.test(\"Response has success field\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData).to.have.property('success');",
|
|
" pm.expect(jsonData.success).to.eql(true);",
|
|
"});",
|
|
"pm.test(\"Response has pagination\", function () {",
|
|
" var jsonData = pm.response.json();",
|
|
" pm.expect(jsonData).to.have.property('count');",
|
|
" pm.expect(jsonData).to.have.property('results');",
|
|
"});"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Test Scenarios to Include:**
|
|
|
|
1. **Success Cases (200/201):**
|
|
- Valid requests return `success: true`
|
|
- Response has `data` field
|
|
- Optional `message` field present
|
|
|
|
2. **Validation Errors (400):**
|
|
- Invalid input returns `success: false`
|
|
- Response has `error` field
|
|
- Response has `errors` field with field-specific errors
|
|
|
|
3. **Authentication Errors (401):**
|
|
- Unauthenticated requests return 401
|
|
- Response has `success: false`
|
|
- Response has `error: "Authentication required"`
|
|
|
|
4. **Permission Errors (403):**
|
|
- Unauthorized requests return 403
|
|
- Response has `success: false`
|
|
- Response has `error: "Permission denied"`
|
|
|
|
5. **Not Found (404):**
|
|
- Non-existent resources return 404
|
|
- Response has `success: false`
|
|
- Response has `error: "Resource not found"`
|
|
|
|
6. **Rate Limiting (429):**
|
|
- Rate limit exceeded returns 429
|
|
- Response has throttle headers
|
|
- Response has `Retry-After` header
|
|
|
|
7. **Server Errors (500):**
|
|
- Server errors return 500
|
|
- Response has `success: false`
|
|
- Response has `error: "Internal server error"`
|
|
|
|
**Estimated Time:** 4 hours
|
|
|
|
#### Step 1.2: Create Automated Test Suite
|
|
|
|
**File:** `backend/igny8_core/api/tests/test_standardized_endpoints.py`
|
|
|
|
**Implementation:**
|
|
```python
|
|
"""
|
|
Comprehensive Test Suite for Standardized API Endpoints
|
|
|
|
Tests all endpoints to ensure they follow the unified interface design.
|
|
"""
|
|
|
|
from django.test import TestCase
|
|
from rest_framework.test import APIClient
|
|
from rest_framework import status
|
|
from django.contrib.auth import get_user_model
|
|
from igny8_core.auth.models import Account
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class StandardizedEndpointTestCase(TestCase):
|
|
"""
|
|
Base test case for standardized endpoint testing.
|
|
"""
|
|
|
|
def setUp(self):
|
|
self.client = APIClient()
|
|
self.account = Account.objects.create(name="Test Account")
|
|
self.user = User.objects.create_user(
|
|
email="test@example.com",
|
|
password="testpass123",
|
|
account=self.account,
|
|
role="owner"
|
|
)
|
|
self.client.force_authenticate(user=self.user)
|
|
|
|
def assert_unified_success_response(self, response, expected_status=status.HTTP_200_OK):
|
|
"""
|
|
Assert that response follows unified success format.
|
|
"""
|
|
self.assertEqual(response.status_code, expected_status)
|
|
data = response.json()
|
|
self.assertIn('success', data)
|
|
self.assertTrue(data['success'])
|
|
self.assertIn('data', data)
|
|
|
|
def assert_unified_error_response(self, response, expected_status=status.HTTP_400_BAD_REQUEST):
|
|
"""
|
|
Assert that response follows unified error format.
|
|
"""
|
|
self.assertEqual(response.status_code, expected_status)
|
|
data = response.json()
|
|
self.assertIn('success', data)
|
|
self.assertFalse(data['success'])
|
|
self.assertIn('error', data)
|
|
|
|
def assert_paginated_response(self, response):
|
|
"""
|
|
Assert that response follows unified paginated format.
|
|
"""
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
data = response.json()
|
|
self.assertIn('success', data)
|
|
self.assertTrue(data['success'])
|
|
self.assertIn('count', data)
|
|
self.assertIn('results', data)
|
|
self.assertIn('next', data)
|
|
self.assertIn('previous', data)
|
|
|
|
|
|
class AuthEndpointsTestCase(StandardizedEndpointTestCase):
|
|
"""Test Auth module endpoints"""
|
|
|
|
def test_register_endpoint(self):
|
|
"""Test register endpoint returns unified format"""
|
|
self.client.logout() # Ensure not authenticated
|
|
response = self.client.post('/api/v1/auth/register/', {
|
|
'email': 'newuser@example.com',
|
|
'password': 'newpass123',
|
|
'password_confirm': 'newpass123'
|
|
})
|
|
self.assert_unified_success_response(response, status.HTTP_201_CREATED)
|
|
|
|
def test_login_endpoint(self):
|
|
"""Test login endpoint returns unified format"""
|
|
self.client.logout()
|
|
response = self.client.post('/api/v1/auth/login/', {
|
|
'email': 'test@example.com',
|
|
'password': 'testpass123'
|
|
})
|
|
self.assert_unified_success_response(response)
|
|
data = response.json()
|
|
self.assertIn('tokens', data['data'])
|
|
|
|
def test_me_endpoint(self):
|
|
"""Test /me endpoint returns unified format"""
|
|
response = self.client.get('/api/v1/auth/me/')
|
|
self.assert_unified_success_response(response)
|
|
data = response.json()
|
|
self.assertIn('user', data['data'])
|
|
|
|
|
|
class PlannerEndpointsTestCase(StandardizedEndpointTestCase):
|
|
"""Test Planner module endpoints"""
|
|
|
|
def test_list_keywords(self):
|
|
"""Test keywords list endpoint returns unified paginated format"""
|
|
response = self.client.get('/api/v1/planner/keywords/')
|
|
self.assert_paginated_response(response)
|
|
|
|
def test_create_keyword(self):
|
|
"""Test keyword creation returns unified format"""
|
|
response = self.client.post('/api/v1/planner/keywords/', {
|
|
'keyword': 'test keyword',
|
|
'site_id': 1,
|
|
'sector_id': 1
|
|
})
|
|
self.assert_unified_success_response(response, status.HTTP_201_CREATED)
|
|
|
|
def test_validation_error(self):
|
|
"""Test validation error returns unified error format"""
|
|
response = self.client.post('/api/v1/planner/keywords/', {})
|
|
self.assert_unified_error_response(response, status.HTTP_400_BAD_REQUEST)
|
|
data = response.json()
|
|
self.assertIn('errors', data)
|
|
|
|
|
|
class WriterEndpointsTestCase(StandardizedEndpointTestCase):
|
|
"""Test Writer module endpoints"""
|
|
|
|
def test_list_tasks(self):
|
|
"""Test tasks list endpoint returns unified paginated format"""
|
|
response = self.client.get('/api/v1/writer/tasks/')
|
|
self.assert_paginated_response(response)
|
|
|
|
def test_rate_limiting(self):
|
|
"""Test that rate limiting works and returns 429"""
|
|
endpoint = '/api/v1/writer/tasks/auto_generate_content/'
|
|
|
|
# Make 10 requests (should succeed)
|
|
for i in range(10):
|
|
response = self.client.post(endpoint, {})
|
|
# May fail for other reasons, but not rate limiting
|
|
|
|
# 11th request should be rate limited
|
|
response = self.client.post(endpoint, {})
|
|
if response.status_code == status.HTTP_429_TOO_MANY_REQUESTS:
|
|
self.assert_unified_error_response(response, status.HTTP_429_TOO_MANY_REQUESTS)
|
|
self.assertIn('Retry-After', response.headers)
|
|
|
|
|
|
class ErrorHandlingTestCase(StandardizedEndpointTestCase):
|
|
"""Test error handling across endpoints"""
|
|
|
|
def test_401_unauthorized(self):
|
|
"""Test 401 returns unified error format"""
|
|
self.client.logout()
|
|
response = self.client.get('/api/v1/auth/me/')
|
|
self.assert_unified_error_response(response, status.HTTP_401_UNAUTHORIZED)
|
|
data = response.json()
|
|
self.assertEqual(data['error'], 'Authentication required')
|
|
|
|
def test_404_not_found(self):
|
|
"""Test 404 returns unified error format"""
|
|
response = self.client.get('/api/v1/planner/keywords/99999/')
|
|
self.assert_unified_error_response(response, status.HTTP_404_NOT_FOUND)
|
|
data = response.json()
|
|
self.assertEqual(data['error'], 'Resource not found')
|
|
|
|
def test_403_permission_denied(self):
|
|
"""Test 403 returns unified error format"""
|
|
# Create user with viewer role (limited permissions)
|
|
viewer_user = User.objects.create_user(
|
|
email='viewer@example.com',
|
|
password='viewerpass123',
|
|
account=self.account,
|
|
role='viewer'
|
|
)
|
|
self.client.force_authenticate(user=viewer_user)
|
|
|
|
# Try to delete user (requires admin/owner)
|
|
response = self.client.delete(f'/api/v1/auth/users/{self.user.id}/')
|
|
if response.status_code == status.HTTP_403_FORBIDDEN:
|
|
self.assert_unified_error_response(response, status.HTTP_403_FORBIDDEN)
|
|
data = response.json()
|
|
self.assertEqual(data['error'], 'Permission denied')
|
|
```
|
|
|
|
**Run Tests:**
|
|
```bash
|
|
# Run all standardized endpoint tests
|
|
python manage.py test igny8_core.api.tests.test_standardized_endpoints
|
|
|
|
# Run specific test class
|
|
python manage.py test igny8_core.api.tests.test_standardized_endpoints.AuthEndpointsTestCase
|
|
|
|
# Run with coverage
|
|
coverage run --source='.' manage.py test igny8_core.api.tests.test_standardized_endpoints
|
|
coverage report
|
|
```
|
|
|
|
**Estimated Time:** 8 hours
|
|
|
|
---
|
|
|
|
## Task 2: Verify Frontend Integration
|
|
|
|
### Goal
|
|
Ensure frontend application works correctly with standardized API endpoints.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 2.1: Frontend Workflow Testing
|
|
|
|
**Test Major Workflows:**
|
|
|
|
1. **Keyword → Cluster → Ideas → Tasks → Content → Images → Publish:**
|
|
- Open frontend: `http://localhost:5173`
|
|
- Navigate to Planner module
|
|
- Create keyword
|
|
- Create cluster
|
|
- Auto-generate ideas
|
|
- Queue ideas to writer (create tasks)
|
|
- Auto-generate content for task
|
|
- Generate images for content
|
|
- Verify all steps complete successfully
|
|
- Check browser console for errors
|
|
|
|
2. **Settings Flow:**
|
|
- Navigate to Settings
|
|
- Update AI prompts
|
|
- Update integration settings
|
|
- Test integration connections
|
|
- Update author profiles
|
|
- Verify changes saved
|
|
|
|
3. **Credits Flow:**
|
|
- Navigate to Billing
|
|
- View credit balance
|
|
- View usage logs
|
|
- View usage summary
|
|
- Verify data displays correctly
|
|
|
|
4. **Auth Flow:**
|
|
- Logout
|
|
- Register new user
|
|
- Login
|
|
- Change password
|
|
- View current user profile
|
|
- (As admin) Manage users
|
|
|
|
**Check Browser DevTools:**
|
|
- Network tab: Verify all API calls return expected format
|
|
- Console: Check for JavaScript errors
|
|
- Response preview: Verify `success`, `data`, `message` fields
|
|
|
|
**Estimated Time:** 8 hours
|
|
|
|
#### Step 2.2: Frontend Error Handling Verification
|
|
|
|
**Test Error Scenarios:**
|
|
|
|
1. **Validation Errors:**
|
|
- Submit form with invalid data
|
|
- Verify error message displays
|
|
- Verify field-specific errors show on form fields
|
|
|
|
2. **Authentication Errors:**
|
|
- Try to access protected endpoint without token
|
|
- Verify redirect to login
|
|
- Verify error message displays
|
|
|
|
3. **Permission Errors:**
|
|
- Try to access admin-only endpoint as regular user
|
|
- Verify error message displays
|
|
- Verify UI hides restricted actions
|
|
|
|
4. **Rate Limiting:**
|
|
- Trigger rate limit (make many requests quickly)
|
|
- Verify rate limit warning displays
|
|
- Verify UI disables buttons when rate limited
|
|
|
|
5. **Network Errors:**
|
|
- Simulate network failure
|
|
- Verify error message displays
|
|
- Verify retry mechanism works
|
|
|
|
**Files to Check:**
|
|
- `frontend/src/services/api.ts` - API client error handling
|
|
- `frontend/src/stores/*.ts` - Store error handling
|
|
- `frontend/src/components/*.tsx` - Component error display
|
|
|
|
**Estimated Time:** 4 hours
|
|
|
|
---
|
|
|
|
## Task 3: Document Unified API Format
|
|
|
|
### Goal
|
|
Create comprehensive documentation for internal and external API usage.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 3.1: Update Backend Implementation Docs
|
|
|
|
**File:** `docs/04-BACKEND-IMPLEMENTATION.md`
|
|
|
|
**Add Section: "Standardized API Response Structure"**
|
|
|
|
```markdown
|
|
## Standardized API Response Structure
|
|
|
|
All API endpoints follow a unified response format for consistency and predictability.
|
|
|
|
### Success Response Format
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
// Response data (object or array)
|
|
},
|
|
"message": "Optional success message"
|
|
}
|
|
```
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": 1,
|
|
"name": "Example Keyword",
|
|
"status": "active"
|
|
},
|
|
"message": "Keyword created successfully"
|
|
}
|
|
```
|
|
|
|
### Error Response Format
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Top-level error message",
|
|
"errors": {
|
|
"field_name": ["Field-specific error messages"]
|
|
},
|
|
"request_id": "uuid-for-error-tracking"
|
|
}
|
|
```
|
|
|
|
**Example:**
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Validation failed",
|
|
"errors": {
|
|
"email": ["Invalid email format"],
|
|
"password": ["Password must be at least 8 characters"]
|
|
},
|
|
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
|
}
|
|
```
|
|
|
|
### Paginated Response Format
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"count": 100,
|
|
"next": "http://api.igny8.com/api/v1/endpoint/?page=2",
|
|
"previous": null,
|
|
"results": [
|
|
// Array of 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
|
|
- `429 Too Many Requests` - Rate limit exceeded
|
|
- `500 Internal Server Error` - Server error
|
|
|
|
### Rate Limiting
|
|
|
|
Rate limits are enforced using scoped throttling. Check response headers:
|
|
|
|
- `X-Throttle-Limit`: Maximum requests allowed
|
|
- `X-Throttle-Remaining`: Requests remaining in current window
|
|
- `Retry-After`: Seconds to wait before retrying (when throttled)
|
|
|
|
**Rate Limit Scopes:**
|
|
- `ai_function`: 10 requests/minute
|
|
- `image_gen`: 15 requests/minute
|
|
- `content_write`: 30 requests/minute
|
|
- `auth`: 20 requests/minute
|
|
- `planner`: 60 requests/minute
|
|
- `writer`: 60 requests/minute
|
|
- `system`: 100 requests/minute
|
|
- `billing`: 30 requests/minute
|
|
|
|
### Authentication
|
|
|
|
Most endpoints require authentication via JWT token:
|
|
|
|
```
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
Public endpoints (no authentication required):
|
|
- `POST /api/v1/auth/register/`
|
|
- `POST /api/v1/auth/login/`
|
|
- `GET /api/v1/auth/plans/`
|
|
- `GET /api/v1/auth/industries/`
|
|
- `GET /api/v1/system/status/`
|
|
|
|
### Global Exception Handling
|
|
|
|
All exceptions are handled by a centralized exception handler that:
|
|
- Wraps errors in unified format
|
|
- Logs errors with request context
|
|
- Includes request ID for error tracking
|
|
- Provides debug information in development mode
|
|
|
|
### Sample ViewSet Format
|
|
|
|
```python
|
|
from igny8_core.api.viewsets import BaseTenantViewSet
|
|
from igny8_core.api.response import success_response, error_response
|
|
from igny8_core.api.permissions import IsAuthenticatedAndActive, HasTenantAccess
|
|
|
|
class MyViewSet(BaseTenantViewSet):
|
|
throttle_scope = 'planner'
|
|
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
|
|
queryset = MyModel.objects.all()
|
|
serializer_class = MySerializer
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def custom_action(self, request):
|
|
# Validation
|
|
if not request.data.get('field'):
|
|
return error_response(
|
|
error="Field is required",
|
|
errors={"field": ["This field is required"]},
|
|
status_code=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
# Success
|
|
return success_response(
|
|
data={"result": "..."},
|
|
message="Action completed successfully"
|
|
)
|
|
```
|
|
```
|
|
|
|
#### Step 3.2: Create API Usage Guide
|
|
|
|
**File:** `docs/API-USAGE-GUIDE.md`
|
|
|
|
**Implementation:**
|
|
```markdown
|
|
# IGNY8 API Usage Guide
|
|
|
|
Complete guide for using the IGNY8 API, including authentication, request/response formats, and examples.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Getting Started](#getting-started)
|
|
2. [Authentication](#authentication)
|
|
3. [Request Format](#request-format)
|
|
4. [Response Format](#response-format)
|
|
5. [Error Handling](#error-handling)
|
|
6. [Rate Limiting](#rate-limiting)
|
|
7. [Examples](#examples)
|
|
|
|
## Getting Started
|
|
|
|
### Base URL
|
|
|
|
```
|
|
Production: https://api.igny8.com/api/v1/
|
|
Development: http://localhost:8000/api/v1/
|
|
```
|
|
|
|
### API Version
|
|
|
|
Current version: `v1`
|
|
|
|
## Authentication
|
|
|
|
### Register New User
|
|
|
|
```bash
|
|
curl -X POST https://api.igny8.com/api/v1/auth/register/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "user@example.com",
|
|
"password": "securepassword123",
|
|
"password_confirm": "securepassword123"
|
|
}'
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": {
|
|
"id": 1,
|
|
"email": "user@example.com",
|
|
"role": "owner"
|
|
}
|
|
},
|
|
"message": "Registration successful"
|
|
}
|
|
```
|
|
|
|
### Login
|
|
|
|
```bash
|
|
curl -X POST https://api.igny8.com/api/v1/auth/login/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "user@example.com",
|
|
"password": "securepassword123"
|
|
}'
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": { ... },
|
|
"tokens": {
|
|
"access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
|
|
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
|
|
"access_expires_at": "2025-01-15T12:30:00Z",
|
|
"refresh_expires_at": "2025-01-22T12:00:00Z"
|
|
}
|
|
},
|
|
"message": "Login successful"
|
|
}
|
|
```
|
|
|
|
### Using Access Token
|
|
|
|
Include the access token in the Authorization header:
|
|
|
|
```bash
|
|
curl -X GET https://api.igny8.com/api/v1/auth/me/ \
|
|
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..."
|
|
```
|
|
|
|
## Request Format
|
|
|
|
### Content-Type
|
|
|
|
All requests should use `Content-Type: application/json` for JSON payloads.
|
|
|
|
### Pagination
|
|
|
|
List endpoints support pagination:
|
|
|
|
```
|
|
GET /api/v1/planner/keywords/?page=1&page_size=25
|
|
```
|
|
|
|
**Query Parameters:**
|
|
- `page`: Page number (default: 1)
|
|
- `page_size`: Items per page (default: 10, max: 100)
|
|
|
|
## Response Format
|
|
|
|
### Success Response
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
// Response data
|
|
},
|
|
"message": "Optional success message"
|
|
}
|
|
```
|
|
|
|
### Error Response
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Top-level error message",
|
|
"errors": {
|
|
"field_name": ["Field-specific errors"]
|
|
},
|
|
"request_id": "uuid-for-tracking"
|
|
}
|
|
```
|
|
|
|
### Paginated Response
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"count": 100,
|
|
"next": "http://api.igny8.com/api/v1/endpoint/?page=2",
|
|
"previous": null,
|
|
"results": [ ... ],
|
|
"message": "Optional message"
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Status Codes
|
|
|
|
- `200 OK` - Success
|
|
- `201 Created` - Resource created
|
|
- `400 Bad Request` - Validation error
|
|
- `401 Unauthorized` - Authentication required
|
|
- `403 Forbidden` - Permission denied
|
|
- `404 Not Found` - Resource not found
|
|
- `429 Too Many Requests` - Rate limit exceeded
|
|
- `500 Internal Server Error` - Server error
|
|
|
|
### Handling Errors
|
|
|
|
Always check the `success` field first:
|
|
|
|
```javascript
|
|
const response = await fetch('/api/v1/endpoint/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
// Handle success
|
|
console.log(result.data);
|
|
} else {
|
|
// Handle error
|
|
console.error(result.error);
|
|
if (result.errors) {
|
|
// Handle field-specific errors
|
|
Object.keys(result.errors).forEach(field => {
|
|
console.error(`${field}: ${result.errors[field].join(', ')}`);
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## Rate Limiting
|
|
|
|
Rate limits are enforced per endpoint scope. Check response headers:
|
|
|
|
- `X-Throttle-Limit`: Maximum requests
|
|
- `X-Throttle-Remaining`: Remaining requests
|
|
- `Retry-After`: Wait time when throttled (seconds)
|
|
|
|
**Example:**
|
|
```javascript
|
|
const throttleLimit = response.headers.get('X-Throttle-Limit');
|
|
const throttleRemaining = response.headers.get('X-Throttle-Remaining');
|
|
|
|
if (parseInt(throttleRemaining) < 5) {
|
|
console.warn('Approaching rate limit');
|
|
}
|
|
```
|
|
|
|
## Examples
|
|
|
|
### JavaScript (Fetch API)
|
|
|
|
```javascript
|
|
async function createKeyword(keywordData) {
|
|
const response = await fetch('https://api.igny8.com/api/v1/planner/keywords/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${accessToken}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(keywordData)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
return result.data;
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Python (Requests)
|
|
|
|
```python
|
|
import requests
|
|
|
|
def create_keyword(keyword_data, access_token):
|
|
url = 'https://api.igny8.com/api/v1/planner/keywords/'
|
|
headers = {
|
|
'Authorization': f'Bearer {access_token}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
|
|
response = requests.post(url, json=keyword_data, headers=headers)
|
|
result = response.json()
|
|
|
|
if result['success']:
|
|
return result['data']
|
|
else:
|
|
raise Exception(result['error'])
|
|
```
|
|
|
|
### cURL
|
|
|
|
```bash
|
|
# Create keyword
|
|
curl -X POST https://api.igny8.com/api/v1/planner/keywords/ \
|
|
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"keyword": "example keyword",
|
|
"site_id": 1,
|
|
"sector_id": 1
|
|
}'
|
|
|
|
# List keywords (paginated)
|
|
curl -X GET "https://api.igny8.com/api/v1/planner/keywords/?page=1&page_size=25" \
|
|
-H "Authorization: Bearer $ACCESS_TOKEN"
|
|
```
|
|
```
|
|
|
|
**Estimated Time:** 4 hours (backend docs) + 6 hours (usage guide) = 10 hours
|
|
|
|
---
|
|
|
|
## Task 4: Publish Swagger/OpenAPI Docs (Optional)
|
|
|
|
### Goal
|
|
Generate and publish interactive API documentation using Swagger/OpenAPI.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 4.1: Set Up drf-spectacular
|
|
|
|
**Install:**
|
|
```bash
|
|
pip install drf-spectacular
|
|
```
|
|
|
|
**File:** `backend/igny8_core/settings.py`
|
|
|
|
**Add to INSTALLED_APPS:**
|
|
```python
|
|
INSTALLED_APPS = [
|
|
# ... other apps ...
|
|
'drf_spectacular',
|
|
]
|
|
```
|
|
|
|
**Add to REST_FRAMEWORK:**
|
|
```python
|
|
REST_FRAMEWORK = {
|
|
# ... existing settings ...
|
|
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
|
}
|
|
```
|
|
|
|
**Add SPECTACULAR_SETTINGS:**
|
|
```python
|
|
SPECTACULAR_SETTINGS = {
|
|
'TITLE': 'IGNY8 API',
|
|
'DESCRIPTION': 'IGNY8 Content Planning and Generation API',
|
|
'VERSION': '1.0.0',
|
|
'SERVE_INCLUDE_SCHEMA': False,
|
|
'COMPONENT_SPLIT_REQUEST': True,
|
|
'SCHEMA_PATH_PREFIX': '/api/v1/',
|
|
}
|
|
```
|
|
|
|
#### Step 4.2: Add URL Routes
|
|
|
|
**File:** `backend/igny8_core/urls.py`
|
|
|
|
```python
|
|
from drf_spectacular.views import (
|
|
SpectacularAPIView,
|
|
SpectacularRedocView,
|
|
SpectacularSwaggerView,
|
|
)
|
|
|
|
urlpatterns = [
|
|
# ... existing URLs ...
|
|
|
|
# API Schema
|
|
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
|
|
|
|
# Swagger UI
|
|
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
|
|
|
# ReDoc
|
|
path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
|
|
]
|
|
```
|
|
|
|
#### Step 4.3: Customize Schema
|
|
|
|
**File:** `backend/igny8_core/api/schema.py`
|
|
|
|
```python
|
|
"""
|
|
Custom OpenAPI Schema Configuration
|
|
"""
|
|
|
|
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}
|
|
},
|
|
"required": ["success", "data"]
|
|
}
|
|
|
|
ERROR_RESPONSE_SCHEMA = {
|
|
"type": "object",
|
|
"properties": {
|
|
"success": {"type": "boolean", "example": False},
|
|
"error": {"type": "string"},
|
|
"errors": {"type": "object", "nullable": True},
|
|
"request_id": {"type": "string", "nullable": True}
|
|
},
|
|
"required": ["success", "error"]
|
|
}
|
|
```
|
|
|
|
**Estimated Time:** 6 hours (optional)
|
|
|
|
---
|
|
|
|
## Task 5: Add Health Check Endpoint
|
|
|
|
### Goal
|
|
Create a simple health check endpoint for API availability monitoring.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 5.1: Create Health Check View
|
|
|
|
**File:** `backend/igny8_core/api/views.py`
|
|
|
|
```python
|
|
"""
|
|
Health Check Endpoint
|
|
"""
|
|
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework import status
|
|
from django.utils import timezone
|
|
from igny8_core.api.response import success_response
|
|
|
|
|
|
class PingView(APIView):
|
|
"""
|
|
Health check endpoint for API availability.
|
|
|
|
Returns simple pong response to verify API is live.
|
|
"""
|
|
permission_classes = [] # Public endpoint
|
|
|
|
def get(self, request):
|
|
"""
|
|
Return health check response.
|
|
"""
|
|
return success_response(
|
|
data={
|
|
"pong": True,
|
|
"time": timezone.now().isoformat(),
|
|
"version": "1.0.0"
|
|
},
|
|
message="API is live"
|
|
)
|
|
```
|
|
|
|
#### Step 5.2: Add URL Route
|
|
|
|
**File:** `backend/igny8_core/urls.py`
|
|
|
|
```python
|
|
from igny8_core.api.views import PingView
|
|
|
|
urlpatterns = [
|
|
# ... existing URLs ...
|
|
path('api/ping/', PingView.as_view(), name='ping'),
|
|
]
|
|
```
|
|
|
|
**Test:**
|
|
```bash
|
|
curl http://localhost:8000/api/ping/
|
|
```
|
|
|
|
**Expected Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"pong": true,
|
|
"time": "2025-01-15T12:30:00Z",
|
|
"version": "1.0.0"
|
|
},
|
|
"message": "API is live"
|
|
}
|
|
```
|
|
|
|
**Estimated Time:** 2 hours
|
|
|
|
---
|
|
|
|
## Task 6: Finalize Changelog and Version Bump
|
|
|
|
### Goal
|
|
Complete changelog and update version numbers.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 6.1: Finalize Changelog
|
|
|
|
**File:** `CHANGELOG.md`
|
|
|
|
**Add Complete Entry:**
|
|
```markdown
|
|
## [1.1.0] - 2025-01-XX
|
|
|
|
### Changed
|
|
- **API Standardization:** Comprehensive API standardization across all modules
|
|
- Unified response format: All endpoints return `{ success, data, message, error, errors }`
|
|
- Centralized error handling with request ID tracking
|
|
- Standardized permission classes and authentication
|
|
- Scoped rate limiting for all endpoints
|
|
- Refactored all ViewSets to use BaseTenantViewSet
|
|
|
|
### Added
|
|
- **Response Format:** Unified success/error response format
|
|
- **Exception Handling:** Centralized exception handler with logging
|
|
- **Rate Limiting:** Scoped rate limiting with throttle headers
|
|
- **Permissions:** Standardized permission classes (IsAuthenticatedAndActive, HasTenantAccess, etc.)
|
|
- **Health Check:** `/api/ping/` endpoint for API availability
|
|
- **Documentation:** Comprehensive API usage guide and Swagger/OpenAPI docs
|
|
|
|
### Security
|
|
- All endpoints now require authentication (except public ones)
|
|
- Rate limiting prevents API abuse
|
|
- Tenant isolation enforced at permission level
|
|
- Request ID tracking for error correlation
|
|
|
|
### Affected Areas
|
|
- All API modules: Auth, Planner, Writer, System, Billing
|
|
- Frontend API client
|
|
- API documentation
|
|
|
|
### Migration Guide
|
|
See [API-USAGE-GUIDE.md](../docs/API-USAGE-GUIDE.md) for complete migration guide.
|
|
|
|
### Breaking Changes
|
|
- ⚠️ **Response Format:** All API responses now follow unified format
|
|
- Frontend must check `success` field before accessing `data`
|
|
- Error responses use `error` field instead of `message`
|
|
- Paginated responses include `success: true` field
|
|
|
|
- ⚠️ **Authentication:** Most endpoints now require authentication
|
|
- Public endpoints: register, login, plans, industries, status, ping
|
|
- All other endpoints require JWT token
|
|
|
|
- ⚠️ **Rate Limiting:** Rate limits are now enforced
|
|
- Check `X-Throttle-Remaining` header
|
|
- Handle 429 responses gracefully
|
|
|
|
### Documentation
|
|
- [API-ENDPOINTS-ANALYSIS.md](../unified-api/API-ENDPOINTS-ANALYSIS.md)
|
|
- [API-USAGE-GUIDE.md](../docs/API-USAGE-GUIDE.md)
|
|
- [API-REFACTOR-CHECKLIST.md](../docs/API-REFACTOR-CHECKLIST.md)
|
|
- Swagger UI: `/api/docs/`
|
|
- ReDoc: `/api/redoc/`
|
|
```
|
|
|
|
#### Step 6.2: Version Bump
|
|
|
|
**File:** `backend/igny8_core/settings.py`
|
|
|
|
```python
|
|
# API Version
|
|
API_VERSION = '1.1.0'
|
|
```
|
|
|
|
**File:** `package.json` (if exists)
|
|
|
|
```json
|
|
{
|
|
"version": "1.1.0"
|
|
}
|
|
```
|
|
|
|
**File:** `docker-compose.yml` (if exists)
|
|
|
|
```yaml
|
|
services:
|
|
backend:
|
|
environment:
|
|
- API_VERSION=1.1.0
|
|
```
|
|
|
|
**Estimated Time:** 2 hours (changelog) + 1 hour (version) = 3 hours
|
|
|
|
---
|
|
|
|
## Task 7: Production Readiness Checklist
|
|
|
|
### Goal
|
|
Complete final production readiness review.
|
|
|
|
### Implementation Steps
|
|
|
|
#### Step 7.1: Production Readiness Review
|
|
|
|
**Checklist:**
|
|
|
|
**API Response Structure:**
|
|
- [ ] All endpoints return unified format
|
|
- [ ] Success responses have `success: true`
|
|
- [ ] Error responses have `success: false`
|
|
- [ ] Paginated responses include `success: true`
|
|
|
|
**Security:**
|
|
- [ ] All ViewSets secured with proper permissions
|
|
- [ ] Tenant isolation enforced
|
|
- [ ] Rate limits applied and tested
|
|
- [ ] Authentication required for protected endpoints
|
|
|
|
**Error Handling:**
|
|
- [ ] All errors handled by centralized exception handler
|
|
- [ ] Error responses include request ID
|
|
- [ ] Error logging configured
|
|
- [ ] Debug information excluded in production
|
|
|
|
**Rate Limiting:**
|
|
- [ ] Rate limits configured for all endpoint types
|
|
- [ ] Throttle headers exposed
|
|
- [ ] Abuse detection logging implemented
|
|
- [ ] Frontend handles rate limits gracefully
|
|
|
|
**Testing:**
|
|
- [ ] Unit tests pass
|
|
- [ ] Integration tests pass
|
|
- [ ] Frontend integration tested
|
|
- [ ] Regression tests pass
|
|
- [ ] Rate limiting tests pass
|
|
|
|
**Documentation:**
|
|
- [ ] API usage guide complete
|
|
- [ ] Backend implementation docs updated
|
|
- [ ] Swagger/OpenAPI docs published (if implemented)
|
|
- [ ] Changelog complete
|
|
- [ ] Version bumped
|
|
|
|
**Frontend:**
|
|
- [ ] All workflows functional
|
|
- [ ] Error messages display correctly
|
|
- [ ] Rate limit warnings work
|
|
- [ ] No console errors
|
|
|
|
**Deployment:**
|
|
- [ ] Staging environment tested
|
|
- [ ] Production deployment plan ready
|
|
- [ ] Rollback plan documented
|
|
- [ ] Monitoring configured
|
|
|
|
**Estimated Time:** 4 hours
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
- All standardized endpoints
|
|
- Response format validation
|
|
- Error format validation
|
|
- Permission enforcement
|
|
- Rate limiting
|
|
|
|
### Integration Tests
|
|
- End-to-end workflows
|
|
- Frontend integration
|
|
- Error scenarios
|
|
- Rate limiting scenarios
|
|
|
|
### Manual Testing
|
|
- Postman collection
|
|
- Frontend workflows
|
|
- Browser DevTools verification
|
|
- Production readiness checklist
|
|
|
|
---
|
|
|
|
## Rollout Plan
|
|
|
|
### Phase 1: Test Suite Creation (Week 1)
|
|
- ✅ Task 1: Create test suite
|
|
- ✅ Task 2: Frontend integration testing
|
|
|
|
### Phase 2: Documentation (Week 2)
|
|
- ✅ Task 3: Document API format
|
|
- ✅ Task 4: Swagger/OpenAPI (optional)
|
|
- ✅ Task 5: Health check endpoint
|
|
|
|
### Phase 3: Finalization (Week 3)
|
|
- ✅ Task 6: Changelog and version
|
|
- ✅ Task 7: Production readiness
|
|
- ✅ Final review and deployment
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
### Definition of Done
|
|
|
|
1. ✅ Comprehensive test suite created
|
|
2. ✅ Frontend integration verified
|
|
3. ✅ API documentation complete
|
|
4. ✅ Swagger/OpenAPI published (if implemented)
|
|
5. ✅ Health check endpoint added
|
|
6. ✅ Changelog finalized
|
|
7. ✅ Version bumped
|
|
8. ✅ Production readiness checklist complete
|
|
9. ✅ All tests pass
|
|
10. ✅ Ready for production deployment
|
|
|
|
### Metrics
|
|
|
|
- **Test Coverage:** >90% for standardized endpoints
|
|
- **Documentation:** Complete API usage guide
|
|
- **Frontend Compatibility:** All workflows functional
|
|
- **Production Readiness:** All checklist items complete
|
|
|
|
---
|
|
|
|
**Document Status:** Implementation Plan
|
|
**Last Updated:** 2025-01-XX
|
|
**Next Review:** After Phase 1 completion
|
|
|