Files
thrilltrack-explorer/django-backend/API_HISTORY_ENDPOINTS.md

647 lines
16 KiB
Markdown

# 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