# 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