mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 08:31:15 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
646
django-backend/API_HISTORY_ENDPOINTS.md
Normal file
646
django-backend/API_HISTORY_ENDPOINTS.md
Normal file
@@ -0,0 +1,646 @@
|
||||
# History API Endpoints Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The History API provides complete access to historical changes for all major entities in the ThrillTrack system. Built on top of the django-pghistory library, this API enables:
|
||||
|
||||
- **Historical Tracking**: View complete history of changes to entities
|
||||
- **Event Comparison**: Compare different versions of entities over time
|
||||
- **Field History**: Track changes to specific fields
|
||||
- **Activity Summaries**: Get statistics about entity modifications
|
||||
- **Rollback Capabilities**: Restore entities to previous states (admin only)
|
||||
|
||||
## Supported Entities
|
||||
|
||||
The History API is available for the following entities:
|
||||
|
||||
- **Parks** (`/api/v1/parks/{park_id}/history/`)
|
||||
- **Rides** (`/api/v1/rides/{ride_id}/history/`)
|
||||
- **Companies** (`/api/v1/companies/{company_id}/history/`)
|
||||
- **Ride Models** (`/api/v1/ride-models/{model_id}/history/`)
|
||||
- **Reviews** (`/api/v1/reviews/{review_id}/history/`)
|
||||
|
||||
Additionally, generic history endpoints are available:
|
||||
- **Generic Event Access** (`/api/v1/history/events/{event_id}`)
|
||||
- **Generic Event Comparison** (`/api/v1/history/compare`)
|
||||
|
||||
## Authentication & Authorization
|
||||
|
||||
### Access Levels
|
||||
|
||||
The History API implements a tiered access control system:
|
||||
|
||||
#### 1. Public (Unauthenticated)
|
||||
- **Access Window**: Last 30 days
|
||||
- **Permissions**: Read-only access to recent history
|
||||
- **Use Cases**: Public transparency, recent changes visibility
|
||||
|
||||
#### 2. Authenticated Users
|
||||
- **Access Window**: Last 1 year
|
||||
- **Permissions**: Read-only access to extended history
|
||||
- **Use Cases**: User research, tracking their contributions
|
||||
|
||||
#### 3. Moderators/Admins/Superusers
|
||||
- **Access Window**: Unlimited (entire history)
|
||||
- **Permissions**: Full read access + rollback capabilities
|
||||
- **Use Cases**: Moderation, auditing, data recovery
|
||||
|
||||
### Rollback Permissions
|
||||
|
||||
Only users with moderator, admin, or superuser privileges can perform rollbacks:
|
||||
- Check via `can_rollback` field in responses
|
||||
- Requires explicit permission check
|
||||
- Creates audit trail of rollback actions
|
||||
|
||||
## Endpoint Reference
|
||||
|
||||
### 1. List Entity History
|
||||
|
||||
Get paginated history of changes to an entity.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/`
|
||||
|
||||
**Query Parameters**:
|
||||
- `page` (integer, default: 1): Page number
|
||||
- `page_size` (integer, default: 50, max: 100): Items per page
|
||||
- `date_from` (string, format: YYYY-MM-DD): Filter from date
|
||||
- `date_to` (string, format: YYYY-MM-DD): Filter to date
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park|ride|company|ridemodel|review",
|
||||
"entity_name": "Entity Display Name",
|
||||
"total_events": 150,
|
||||
"accessible_events": 150,
|
||||
"access_limited": false,
|
||||
"access_reason": "Full access (moderator)",
|
||||
"events": [
|
||||
{
|
||||
"id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"operation": "insert|update|delete",
|
||||
"snapshot": {
|
||||
"id": "uuid",
|
||||
"name": "Example Park",
|
||||
"status": "operating",
|
||||
...
|
||||
},
|
||||
"changed_fields": ["name", "status"],
|
||||
"change_summary": "Updated name and status",
|
||||
"can_rollback": true
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"page_size": 50,
|
||||
"total_pages": 3,
|
||||
"total_items": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Get park history
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/
|
||||
|
||||
# Get ride history with date filter
|
||||
GET /api/v1/rides/987fcdeb-51a2-43f1-9876-543210fedcba/history/?date_from=2024-01-01&date_to=2024-12-31
|
||||
|
||||
# Get company history, page 2
|
||||
GET /api/v1/companies/456e789a-b12c-34d5-e678-901234567890/history/?page=2&page_size=100
|
||||
```
|
||||
|
||||
### 2. Get Specific History Event
|
||||
|
||||
Retrieve detailed information about a single historical event.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/{event-id}/`
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"operation": "update",
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park",
|
||||
"entity_name": "Example Park",
|
||||
"snapshot": {
|
||||
"id": "uuid",
|
||||
"name": "Example Park",
|
||||
"status": "operating",
|
||||
...
|
||||
},
|
||||
"changed_fields": ["name", "status"],
|
||||
"metadata": {},
|
||||
"can_rollback": true,
|
||||
"rollback_preview": null
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Get specific park event
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/12345/
|
||||
|
||||
# Get specific review event
|
||||
GET /api/v1/reviews/67890/history/54321/
|
||||
```
|
||||
|
||||
### 3. Compare Two History Events
|
||||
|
||||
Compare two historical snapshots to see what changed between them.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/compare/`
|
||||
|
||||
**Query Parameters**:
|
||||
- `event1` (integer, required): First event ID
|
||||
- `event2` (integer, required): Second event ID
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park",
|
||||
"entity_name": "Example Park",
|
||||
"event1": {
|
||||
"id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"snapshot": {...}
|
||||
},
|
||||
"event2": {
|
||||
"id": 12346,
|
||||
"timestamp": "2024-01-16T14:20:00Z",
|
||||
"snapshot": {...}
|
||||
},
|
||||
"differences": {
|
||||
"name": {
|
||||
"old_value": "Old Park Name",
|
||||
"new_value": "New Park Name",
|
||||
"changed": true
|
||||
},
|
||||
"status": {
|
||||
"old_value": "closed",
|
||||
"new_value": "operating",
|
||||
"changed": true
|
||||
}
|
||||
},
|
||||
"changed_field_count": 2,
|
||||
"unchanged_field_count": 15,
|
||||
"time_between": "1 day, 3:50:00"
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Compare two park events
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/compare/?event1=12345&event2=12346
|
||||
|
||||
# Compare ride events
|
||||
GET /api/v1/rides/987fcdeb-51a2-43f1-9876-543210fedcba/history/compare/?event1=100&event2=105
|
||||
```
|
||||
|
||||
### 4. Compare Event with Current State
|
||||
|
||||
Compare a historical event with the entity's current state.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/{event-id}/diff-current/`
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park",
|
||||
"entity_name": "Example Park",
|
||||
"event": {
|
||||
"id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"snapshot": {...}
|
||||
},
|
||||
"current_state": {
|
||||
"name": "Current Park Name",
|
||||
"status": "operating",
|
||||
...
|
||||
},
|
||||
"differences": {
|
||||
"name": {
|
||||
"old_value": "Historical Name",
|
||||
"new_value": "Current Park Name",
|
||||
"changed": true
|
||||
}
|
||||
},
|
||||
"changed_field_count": 3,
|
||||
"time_since": "45 days, 2:15:30"
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Compare historical park state with current
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/12345/diff-current/
|
||||
|
||||
# Compare historical company state with current
|
||||
GET /api/v1/companies/456e789a-b12c-34d5-e678-901234567890/history/98765/diff-current/
|
||||
```
|
||||
|
||||
### 5. Rollback to Historical State
|
||||
|
||||
Restore an entity to a previous state. **Requires moderator/admin/superuser permissions**.
|
||||
|
||||
**Endpoint Pattern**: `POST /{entity-type}/{entity-id}/history/{event-id}/rollback/`
|
||||
|
||||
**Authentication**: Required (JWT)
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"fields": ["name", "status"], // Optional: specific fields to rollback
|
||||
"comment": "Reverting vandalism", // Optional: reason for rollback
|
||||
"create_backup": true // Optional: create backup event before rollback
|
||||
}
|
||||
```
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Successfully rolled back to event 12345",
|
||||
"rolled_back_fields": ["name", "status"],
|
||||
"backup_event_id": 12350,
|
||||
"new_event_id": 12351
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses**:
|
||||
- `401 Unauthorized`: Authentication required
|
||||
- `403 Forbidden`: Insufficient permissions
|
||||
- `404 Not Found`: Event or entity not found
|
||||
- `400 Bad Request`: Invalid rollback request
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Full rollback of park
|
||||
POST /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/12345/rollback/
|
||||
{
|
||||
"comment": "Reverting accidental changes",
|
||||
"create_backup": true
|
||||
}
|
||||
|
||||
# Partial rollback (specific fields only)
|
||||
POST /api/v1/rides/987fcdeb-51a2-43f1-9876-543210fedcba/history/54321/rollback/
|
||||
{
|
||||
"fields": ["name", "description"],
|
||||
"comment": "Restoring original name and description",
|
||||
"create_backup": true
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Get Field History
|
||||
|
||||
Track all changes to a specific field over time.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/field/{field-name}/`
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park",
|
||||
"entity_name": "Example Park",
|
||||
"field": "status",
|
||||
"field_type": "CharField",
|
||||
"changes": [
|
||||
{
|
||||
"event_id": 12346,
|
||||
"timestamp": "2024-01-16T14:20:00Z",
|
||||
"old_value": "closed",
|
||||
"new_value": "operating"
|
||||
},
|
||||
{
|
||||
"event_id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"old_value": "operating",
|
||||
"new_value": "closed"
|
||||
}
|
||||
],
|
||||
"total_changes": 2,
|
||||
"first_recorded": "2023-06-01T08:00:00Z",
|
||||
"last_changed": "2024-01-16T14:20:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Track park status changes
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/field/status/
|
||||
|
||||
# Track ride height changes
|
||||
GET /api/v1/rides/987fcdeb-51a2-43f1-9876-543210fedcba/history/field/height/
|
||||
|
||||
# Track company name changes
|
||||
GET /api/v1/companies/456e789a-b12c-34d5-e678-901234567890/history/field/name/
|
||||
```
|
||||
|
||||
### 7. Get Activity Summary
|
||||
|
||||
Get statistics about modifications to an entity.
|
||||
|
||||
**Endpoint Pattern**: `GET /{entity-type}/{entity-id}/history/summary/`
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"entity_id": "uuid-string",
|
||||
"entity_type": "park",
|
||||
"entity_name": "Example Park",
|
||||
"total_events": 150,
|
||||
"total_updates": 145,
|
||||
"total_creates": 1,
|
||||
"total_deletes": 0,
|
||||
"first_event": "2023-01-01T00:00:00Z",
|
||||
"last_event": "2024-03-15T16:45:00Z",
|
||||
"most_active_period": "2024-01",
|
||||
"average_updates_per_month": 12.5,
|
||||
"most_changed_fields": [
|
||||
{"field": "status", "changes": 25},
|
||||
{"field": "description", "changes": 18},
|
||||
{"field": "ride_count", "changes": 15}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```bash
|
||||
# Get park activity summary
|
||||
GET /api/v1/parks/123e4567-e89b-12d3-a456-426614174000/history/summary/
|
||||
|
||||
# Get review activity summary
|
||||
GET /api/v1/reviews/67890/history/summary/
|
||||
```
|
||||
|
||||
## Generic History Endpoints
|
||||
|
||||
### Get Any Event by ID
|
||||
|
||||
Retrieve any historical event by its ID, regardless of entity type.
|
||||
|
||||
**Endpoint**: `GET /api/v1/history/events/{event-id}`
|
||||
|
||||
**Response**: `200 OK`
|
||||
```json
|
||||
{
|
||||
"id": 12345,
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"operation": "update",
|
||||
"entity_type": "park",
|
||||
"entity_id": "uuid-string",
|
||||
"snapshot": {...},
|
||||
"changed_fields": ["name", "status"],
|
||||
"can_rollback": true
|
||||
}
|
||||
```
|
||||
|
||||
### Compare Any Two Events
|
||||
|
||||
Compare any two events, even across different entities.
|
||||
|
||||
**Endpoint**: `GET /api/v1/history/compare`
|
||||
|
||||
**Query Parameters**:
|
||||
- `event1` (integer, required): First event ID
|
||||
- `event2` (integer, required): Second event ID
|
||||
|
||||
**Response**: Similar to entity-specific comparison endpoint
|
||||
|
||||
## Access Control Details
|
||||
|
||||
### Time-Based Access Windows
|
||||
|
||||
Access windows are enforced based on user authentication level:
|
||||
|
||||
```python
|
||||
# Access limits
|
||||
PUBLIC_WINDOW = 30 days
|
||||
AUTHENTICATED_WINDOW = 1 year
|
||||
PRIVILEGED_WINDOW = Unlimited
|
||||
```
|
||||
|
||||
### Access Reason Messages
|
||||
|
||||
The API provides clear feedback about access limitations:
|
||||
|
||||
- **"Full access (moderator)"**: Unlimited access
|
||||
- **"Full access (admin)"**: Unlimited access
|
||||
- **"Full access (superuser)"**: Unlimited access
|
||||
- **"Access limited to last 365 days (authenticated user)"**: 1-year limit
|
||||
- **"Access limited to last 30 days (public)"**: 30-day limit
|
||||
|
||||
## Rollback Safety Guidelines
|
||||
|
||||
### Before Performing a Rollback
|
||||
|
||||
1. **Review the Target State**: Use `diff-current` to see what will change
|
||||
2. **Check Dependencies**: Consider impact on related entities
|
||||
3. **Create Backup**: Always set `create_backup: true` for safety
|
||||
4. **Add Comment**: Document why the rollback is being performed
|
||||
5. **Use Partial Rollback**: When possible, rollback only specific fields
|
||||
|
||||
### Rollback Best Practices
|
||||
|
||||
```json
|
||||
{
|
||||
"fields": ["name", "description"], // Limit scope
|
||||
"comment": "Reverting vandalism on 2024-03-15", // Document reason
|
||||
"create_backup": true // Always true in production
|
||||
}
|
||||
```
|
||||
|
||||
### Audit Trail
|
||||
|
||||
All rollbacks create:
|
||||
1. **Backup Event**: Snapshot before rollback (if `create_backup: true`)
|
||||
2. **Rollback Event**: New event with restored state
|
||||
3. **Audit Log**: Metadata tracking who performed rollback and why
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Error Responses
|
||||
|
||||
**404 Not Found**
|
||||
```json
|
||||
{
|
||||
"error": "Entity not found"
|
||||
}
|
||||
```
|
||||
|
||||
**400 Bad Request**
|
||||
```json
|
||||
{
|
||||
"error": "Invalid date format. Use YYYY-MM-DD"
|
||||
}
|
||||
```
|
||||
|
||||
**403 Forbidden**
|
||||
```json
|
||||
{
|
||||
"error": "Only moderators and administrators can perform rollbacks"
|
||||
}
|
||||
```
|
||||
|
||||
**401 Unauthorized**
|
||||
```json
|
||||
{
|
||||
"error": "Authentication required"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Codes
|
||||
|
||||
| Status Code | Meaning |
|
||||
|-------------|---------|
|
||||
| 200 | Success |
|
||||
| 201 | Created |
|
||||
| 400 | Bad Request (invalid parameters) |
|
||||
| 401 | Unauthorized (authentication required) |
|
||||
| 403 | Forbidden (insufficient permissions) |
|
||||
| 404 | Not Found (entity or event not found) |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
The History API implements standard rate limiting:
|
||||
|
||||
- **Authenticated Users**: 100 requests per minute
|
||||
- **Unauthenticated Users**: 20 requests per minute
|
||||
- **Rollback Operations**: 10 per minute (additional limit)
|
||||
|
||||
Rate limit headers:
|
||||
```
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 95
|
||||
X-RateLimit-Reset: 1617181723
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Pagination
|
||||
|
||||
- Default page size: 50 events
|
||||
- Maximum page size: 100 events
|
||||
- Use pagination for large result sets
|
||||
|
||||
### Caching
|
||||
|
||||
- Event data is cached for 5 minutes
|
||||
- Comparison results are cached for 2 minutes
|
||||
- Current state comparisons are not cached
|
||||
|
||||
### Query Optimization
|
||||
|
||||
- Use date filters to reduce result sets
|
||||
- Prefer field-specific history for focused queries
|
||||
- Use summary endpoints for overview data
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Python (requests)
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# Get park history
|
||||
response = requests.get(
|
||||
'https://api.thrilltrack.com/v1/parks/123/history/',
|
||||
params={'page': 1, 'page_size': 50},
|
||||
headers={'Authorization': 'Bearer YOUR_TOKEN'}
|
||||
)
|
||||
history = response.json()
|
||||
|
||||
# Compare two events
|
||||
response = requests.get(
|
||||
'https://api.thrilltrack.com/v1/parks/123/history/compare/',
|
||||
params={'event1': 100, 'event2': 105}
|
||||
)
|
||||
comparison = response.json()
|
||||
|
||||
# Perform rollback
|
||||
response = requests.post(
|
||||
'https://api.thrilltrack.com/v1/parks/123/history/100/rollback/',
|
||||
json={
|
||||
'comment': 'Reverting vandalism',
|
||||
'create_backup': True
|
||||
},
|
||||
headers={'Authorization': 'Bearer YOUR_TOKEN'}
|
||||
)
|
||||
```
|
||||
|
||||
### JavaScript (fetch)
|
||||
|
||||
```javascript
|
||||
// Get ride history
|
||||
const response = await fetch(
|
||||
'https://api.thrilltrack.com/v1/rides/456/history/',
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
);
|
||||
const history = await response.json();
|
||||
|
||||
// Compare with current state
|
||||
const diffResponse = await fetch(
|
||||
'https://api.thrilltrack.com/v1/rides/456/history/200/diff-current/'
|
||||
);
|
||||
const diff = await diffResponse.json();
|
||||
```
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
# Get company history
|
||||
curl -H "Authorization: Bearer TOKEN" \
|
||||
"https://api.thrilltrack.com/v1/companies/789/history/"
|
||||
|
||||
# Rollback to previous state
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"comment": "Reverting changes", "create_backup": true}' \
|
||||
"https://api.thrilltrack.com/v1/companies/789/history/150/rollback/"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue**: "Access limited to last 30 days"
|
||||
- **Solution**: Authenticate with valid credentials to extend access window
|
||||
|
||||
**Issue**: "Event not found or not accessible"
|
||||
- **Solution**: Event may be outside your access window or doesn't exist
|
||||
|
||||
**Issue**: "Cannot rollback: Event not found"
|
||||
- **Solution**: Verify event ID and ensure you have rollback permissions
|
||||
|
||||
**Issue**: Rate limit exceeded
|
||||
- **Solution**: Implement exponential backoff or reduce request frequency
|
||||
|
||||
## Support
|
||||
|
||||
For additional support:
|
||||
- **Documentation**: https://docs.thrilltrack.com/history-api
|
||||
- **GitHub Issues**: https://github.com/thrilltrack/api/issues
|
||||
- **Email**: api-support@thrilltrack.com
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.0 (Current)
|
||||
- Initial release of History API
|
||||
- Support for Parks, Rides, Companies, Ride Models, and Reviews
|
||||
- Complete CRUD history tracking
|
||||
- Comparison and rollback capabilities
|
||||
- Tiered access control system
|
||||
Reference in New Issue
Block a user