mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 08:51:13 -05:00
647 lines
16 KiB
Markdown
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
|