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

16 KiB

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

{
  "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:

# 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

{
  "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:

# 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

{
  "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:

# 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

{
  "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:

# 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:

{
  "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

{
  "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:

# 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

{
  "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:

# 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

{
  "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:

# 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

{
  "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:

# 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

{
  "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

{
  "error": "Entity not found"
}

400 Bad Request

{
  "error": "Invalid date format. Use YYYY-MM-DD"
}

403 Forbidden

{
  "error": "Only moderators and administrators can perform rollbacks"
}

401 Unauthorized

{
  "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)

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)

// 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

# 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:

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