backup for restore later
This commit is contained in:
365
backup-api-standard-v1/docs/MIGRATION-GUIDE.md
Normal file
365
backup-api-standard-v1/docs/MIGRATION-GUIDE.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# API Migration Guide
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Last Updated**: 2025-11-16
|
||||
|
||||
Guide for migrating existing API consumers to IGNY8 API Standard v1.0.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The IGNY8 API v1.0 introduces a unified response format that standardizes all API responses. This guide helps you migrate existing code to work with the new format.
|
||||
|
||||
---
|
||||
|
||||
## What Changed
|
||||
|
||||
### Before (Legacy Format)
|
||||
|
||||
**Success Response**:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Keyword",
|
||||
"status": "active"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Response**:
|
||||
```json
|
||||
{
|
||||
"detail": "Not found."
|
||||
}
|
||||
```
|
||||
|
||||
### After (Unified Format v1.0)
|
||||
|
||||
**Success Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "Keyword",
|
||||
"status": "active"
|
||||
},
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Response**:
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Resource not found",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Update Response Parsing
|
||||
|
||||
#### Before
|
||||
|
||||
```python
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
# Direct access
|
||||
keyword_id = data['id']
|
||||
keyword_name = data['name']
|
||||
```
|
||||
|
||||
#### After
|
||||
|
||||
```python
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
# Check success first
|
||||
if data['success']:
|
||||
# Extract data from unified format
|
||||
keyword_data = data['data'] # or data['results'] for lists
|
||||
keyword_id = keyword_data['id']
|
||||
keyword_name = keyword_data['name']
|
||||
else:
|
||||
# Handle error
|
||||
error_message = data['error']
|
||||
raise Exception(error_message)
|
||||
```
|
||||
|
||||
### Step 2: Update Error Handling
|
||||
|
||||
#### Before
|
||||
|
||||
```python
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
except requests.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
print("Not found")
|
||||
elif e.response.status_code == 400:
|
||||
print("Bad request")
|
||||
```
|
||||
|
||||
#### After
|
||||
|
||||
```python
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
if not data['success']:
|
||||
# Unified error format
|
||||
error_message = data['error']
|
||||
field_errors = data.get('errors', {})
|
||||
|
||||
if response.status_code == 404:
|
||||
print(f"Not found: {error_message}")
|
||||
elif response.status_code == 400:
|
||||
print(f"Validation error: {error_message}")
|
||||
for field, errors in field_errors.items():
|
||||
print(f" {field}: {', '.join(errors)}")
|
||||
```
|
||||
|
||||
### Step 3: Update Pagination Handling
|
||||
|
||||
#### Before
|
||||
|
||||
```python
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
results = data['results']
|
||||
next_page = data['next']
|
||||
count = data['count']
|
||||
```
|
||||
|
||||
#### After
|
||||
|
||||
```python
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
|
||||
if data['success']:
|
||||
# Paginated response format
|
||||
results = data['results'] # Same field name
|
||||
next_page = data['next'] # Same field name
|
||||
count = data['count'] # Same field name
|
||||
else:
|
||||
# Handle error
|
||||
raise Exception(data['error'])
|
||||
```
|
||||
|
||||
### Step 4: Update Frontend Code
|
||||
|
||||
#### Before (JavaScript)
|
||||
|
||||
```javascript
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
// Direct access
|
||||
const keywordId = data.id;
|
||||
const keywordName = data.name;
|
||||
```
|
||||
|
||||
#### After (JavaScript)
|
||||
|
||||
```javascript
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
// Check success first
|
||||
if (data.success) {
|
||||
// Extract data from unified format
|
||||
const keywordData = data.data || data.results;
|
||||
const keywordId = keywordData.id;
|
||||
const keywordName = keywordData.name;
|
||||
} else {
|
||||
// Handle error
|
||||
console.error('Error:', data.error);
|
||||
if (data.errors) {
|
||||
// Handle field-specific errors
|
||||
Object.entries(data.errors).forEach(([field, errors]) => {
|
||||
console.error(`${field}: ${errors.join(', ')}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### Python Helper
|
||||
|
||||
```python
|
||||
def parse_api_response(response):
|
||||
"""Parse unified API response format"""
|
||||
data = response.json()
|
||||
|
||||
if data.get('success'):
|
||||
# Return data or results
|
||||
return data.get('data') or data.get('results')
|
||||
else:
|
||||
# Raise exception with error details
|
||||
error_msg = data.get('error', 'Unknown error')
|
||||
errors = data.get('errors', {})
|
||||
|
||||
if errors:
|
||||
error_msg += f": {errors}"
|
||||
|
||||
raise Exception(error_msg)
|
||||
|
||||
# Usage
|
||||
response = requests.get(url, headers=headers)
|
||||
keyword_data = parse_api_response(response)
|
||||
```
|
||||
|
||||
### JavaScript Helper
|
||||
|
||||
```javascript
|
||||
function parseApiResponse(data) {
|
||||
if (data.success) {
|
||||
return data.data || data.results;
|
||||
} else {
|
||||
const error = new Error(data.error);
|
||||
error.errors = data.errors || {};
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
try {
|
||||
const keywordData = parseApiResponse(data);
|
||||
} catch (error) {
|
||||
console.error('API Error:', error.message);
|
||||
if (error.errors) {
|
||||
// Handle field-specific errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### 1. Response Structure
|
||||
|
||||
**Breaking**: All responses now include `success` field and wrap data in `data` or `results`.
|
||||
|
||||
**Migration**: Update all response parsing code to check `success` and extract `data`/`results`.
|
||||
|
||||
### 2. Error Format
|
||||
|
||||
**Breaking**: Error responses now use unified format with `error` and `errors` fields.
|
||||
|
||||
**Migration**: Update error handling to use new format.
|
||||
|
||||
### 3. Request ID
|
||||
|
||||
**New**: All responses include `request_id` for debugging.
|
||||
|
||||
**Migration**: Optional - can be used for support requests.
|
||||
|
||||
---
|
||||
|
||||
## Non-Breaking Changes
|
||||
|
||||
### 1. Pagination
|
||||
|
||||
**Status**: Compatible - same field names (`count`, `next`, `previous`, `results`)
|
||||
|
||||
**Migration**: No changes needed, but wrap in success check.
|
||||
|
||||
### 2. Authentication
|
||||
|
||||
**Status**: Compatible - same JWT Bearer token format
|
||||
|
||||
**Migration**: No changes needed.
|
||||
|
||||
### 3. Endpoint URLs
|
||||
|
||||
**Status**: Compatible - same endpoint paths
|
||||
|
||||
**Migration**: No changes needed.
|
||||
|
||||
---
|
||||
|
||||
## Testing Migration
|
||||
|
||||
### 1. Update Test Code
|
||||
|
||||
```python
|
||||
# Before
|
||||
def test_get_keyword():
|
||||
response = client.get('/api/v1/planner/keywords/1/')
|
||||
assert response.status_code == 200
|
||||
assert response.json()['id'] == 1
|
||||
|
||||
# After
|
||||
def test_get_keyword():
|
||||
response = client.get('/api/v1/planner/keywords/1/')
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data['success'] == True
|
||||
assert data['data']['id'] == 1
|
||||
```
|
||||
|
||||
### 2. Test Error Handling
|
||||
|
||||
```python
|
||||
def test_not_found():
|
||||
response = client.get('/api/v1/planner/keywords/99999/')
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
assert data['success'] == False
|
||||
assert data['error'] == "Resource not found"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Update response parsing to check `success` field
|
||||
- [ ] Extract data from `data` or `results` field
|
||||
- [ ] Update error handling to use unified format
|
||||
- [ ] Update pagination handling (wrap in success check)
|
||||
- [ ] Update frontend code (if applicable)
|
||||
- [ ] Update test code
|
||||
- [ ] Test all endpoints
|
||||
- [ ] Update documentation
|
||||
- [ ] Deploy and monitor
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise during migration:
|
||||
|
||||
1. **Temporary Compatibility Layer**: Add wrapper to convert unified format back to legacy format
|
||||
2. **Feature Flag**: Use feature flag to toggle between formats
|
||||
3. **Gradual Migration**: Migrate endpoints one module at a time
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For migration support:
|
||||
- Review [API Documentation](API-DOCUMENTATION.md)
|
||||
- Check [Error Codes Reference](ERROR-CODES.md)
|
||||
- Contact support with `request_id` from failed requests
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-16
|
||||
**API Version**: 1.0.0
|
||||
|
||||
Reference in New Issue
Block a user