Files
thrillwiki_django_no_react/docs/api/state_transitions.md
pacnpal b508434574 Add state machine diagrams and code examples for ThrillWiki
- Created a comprehensive documentation file for state machine diagrams, detailing various states and transitions for models such as EditSubmission, ModerationReport, and Park Status.
- Included transition matrices for each state machine to clarify role requirements and guards.
- Developed a new document providing code examples for implementing state machines, including adding new state machines to models, defining custom guards, implementing callbacks, and testing state machines.
- Added examples for document approval workflows, custom guards, email notifications, and cache invalidation callbacks.
- Implemented a test suite for document workflows, covering various scenarios including approval, rejection, and transition logging.
2025-12-21 20:21:54 -05:00

10 KiB

State Transition API Endpoints

This document describes the API endpoints for performing state transitions on various models in ThrillWiki.

Overview

State transitions are performed via POST requests to specific action endpoints. All transition endpoints:

  • Require authentication
  • Return the updated object on success
  • Return appropriate error codes on failure
  • Log the transition in the StateLog

Authentication

All endpoints require a valid JWT token in the Authorization header:

Authorization: Bearer <token>

Error Responses

Status Code Meaning
400 Invalid transition (state not allowed)
401 Not authenticated
403 Permission denied (insufficient role)
404 Object not found
422 Validation error (missing required fields)

EditSubmission Transitions

Approve Submission

Approve an edit submission and apply changes to the target object.

POST /api/moderation/submissions/{id}/approve/

Permissions: MODERATOR or above

Request Body:

{
  "notes": "Optional approval notes"
}

Response (200 OK):

{
  "id": 123,
  "status": "APPROVED",
  "handled_by": {
    "id": 456,
    "username": "moderator"
  },
  "handled_at": "2025-01-15T10:30:00Z",
  "notes": "Looks good, approved"
}

Reject Submission

Reject an edit submission with a reason.

POST /api/moderation/submissions/{id}/reject/

Permissions: MODERATOR or above

Request Body:

{
  "reason": "Required rejection reason"
}

Response (200 OK):

{
  "id": 123,
  "status": "REJECTED",
  "handled_by": {
    "id": 456,
    "username": "moderator"
  },
  "handled_at": "2025-01-15T10:30:00Z",
  "notes": "Rejected: Insufficient evidence"
}

Escalate Submission

Escalate a submission to admin review.

POST /api/moderation/submissions/{id}/escalate/

Permissions: MODERATOR or above

Request Body:

{
  "reason": "Reason for escalation"
}

Response (200 OK):

{
  "id": 123,
  "status": "ESCALATED",
  "handled_by": {
    "id": 456,
    "username": "moderator"
  },
  "handled_at": "2025-01-15T10:30:00Z",
  "notes": "Escalated: Needs admin approval for sensitive change"
}

ModerationReport Transitions

Start Review

Claim a report and start reviewing it.

POST /api/moderation/reports/{id}/start_review/

Permissions: MODERATOR or above

Request Body: None

Response (200 OK):

{
  "id": 123,
  "status": "UNDER_REVIEW",
  "assigned_moderator": {
    "id": 456,
    "username": "moderator"
  }
}

Resolve Report

Mark a report as resolved.

POST /api/moderation/reports/{id}/resolve/

Permissions: MODERATOR or above (must be assigned)

Request Body:

{
  "resolution_action": "CONTENT_REMOVED",
  "resolution_notes": "Description of action taken"
}

Response (200 OK):

{
  "id": 123,
  "status": "RESOLVED",
  "resolution_action": "CONTENT_REMOVED",
  "resolution_notes": "Description of action taken",
  "resolved_at": "2025-01-15T10:30:00Z"
}

Dismiss Report

Dismiss a report as invalid.

POST /api/moderation/reports/{id}/dismiss/

Permissions: MODERATOR or above (must be assigned)

Request Body:

{
  "resolution_notes": "Reason for dismissal"
}

Response (200 OK):

{
  "id": 123,
  "status": "DISMISSED",
  "resolution_notes": "Report did not violate guidelines",
  "resolved_at": "2025-01-15T10:30:00Z"
}

Park Transitions

Close Temporarily

Temporarily close a park.

POST /api/parks/{slug}/close_temporarily/

Permissions: Authenticated user

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-park",
  "name": "Example Park",
  "status": "CLOSED_TEMP"
}

Reopen

Reopen a temporarily closed park.

POST /api/parks/{slug}/reopen/

Permissions: Authenticated user

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-park",
  "name": "Example Park",
  "status": "OPERATING"
}

Close Permanently

Permanently close a park.

POST /api/parks/{slug}/close_permanently/

Permissions: MODERATOR or above

Request Body:

{
  "closing_date": "2025-12-31"
}

Response (200 OK):

{
  "id": 123,
  "slug": "example-park",
  "name": "Example Park",
  "status": "CLOSED_PERM",
  "closing_date": "2025-12-31"
}

Demolish

Mark a park as demolished.

POST /api/parks/{slug}/demolish/

Permissions: MODERATOR or above

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-park",
  "name": "Example Park",
  "status": "DEMOLISHED"
}

Relocate

Mark a park as relocated.

POST /api/parks/{slug}/relocate/

Permissions: MODERATOR or above

Request Body:

{
  "new_location_notes": "Optional notes about new location"
}

Response (200 OK):

{
  "id": 123,
  "slug": "example-park",
  "name": "Example Park",
  "status": "RELOCATED"
}

Ride Transitions

Open

Open a ride (from CLOSED_TEMP, SBNO, or UNDER_CONSTRUCTION).

POST /api/parks/{park_slug}/rides/{ride_slug}/open/

Permissions: Authenticated user (CLOSED_TEMP), MODERATOR+ (SBNO)

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "OPERATING"
}

Close Temporarily

Temporarily close a ride.

POST /api/parks/{park_slug}/rides/{ride_slug}/close_temporarily/

Permissions: Authenticated user

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "CLOSED_TEMP"
}

Mark SBNO

Mark a ride as Standing But Not Operating.

POST /api/parks/{park_slug}/rides/{ride_slug}/mark_sbno/

Permissions: MODERATOR or above

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "SBNO"
}

Mark Closing

Mark a ride as scheduled for closure.

POST /api/parks/{park_slug}/rides/{ride_slug}/mark_closing/

Permissions: MODERATOR or above

Request Body:

{
  "closing_date": "2025-12-31",
  "post_closing_status": "DEMOLISHED"
}

Valid post_closing_status values:

  • SBNO - Will become Standing But Not Operating
  • CLOSED_PERM - Will be permanently closed
  • DEMOLISHED - Will be demolished
  • RELOCATED - Will be relocated

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "CLOSING",
  "closing_date": "2025-12-31",
  "post_closing_status": "DEMOLISHED"
}

Close Permanently

Permanently close a ride.

POST /api/parks/{park_slug}/rides/{ride_slug}/close_permanently/

Permissions: MODERATOR or above

Request Body:

{
  "closing_date": "2025-12-31"
}

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "CLOSED_PERM",
  "closing_date": "2025-12-31"
}

Demolish

Mark a ride as demolished.

POST /api/parks/{park_slug}/rides/{ride_slug}/demolish/

Permissions: MODERATOR or above

Request Body: None

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "DEMOLISHED"
}

Relocate

Mark a ride as relocated.

POST /api/parks/{park_slug}/rides/{ride_slug}/relocate/

Permissions: MODERATOR or above

Request Body:

{
  "new_park_slug": "destination-park",
  "notes": "Optional relocation notes"
}

Response (200 OK):

{
  "id": 123,
  "slug": "example-ride",
  "name": "Example Coaster",
  "status": "RELOCATED"
}

Transition History

Get Object History

Get the state transition history for any object.

GET /api/moderation/history/{content_type}/{object_id}/

Permissions: MODERATOR or above

Response (200 OK):

{
  "count": 3,
  "results": [
    {
      "id": 1,
      "timestamp": "2025-01-10T08:00:00Z",
      "source_state": "PENDING",
      "state": "ESCALATED",
      "transition": "transition_to_escalated",
      "by": {
        "id": 456,
        "username": "moderator"
      },
      "description": null
    },
    {
      "id": 2,
      "timestamp": "2025-01-15T10:30:00Z",
      "source_state": "ESCALATED",
      "state": "APPROVED",
      "transition": "transition_to_approved",
      "by": {
        "id": 789,
        "username": "admin"
      },
      "description": "Approved after escalation review"
    }
  ]
}

Get All History (Admin)

Get all recent transition history across all models.

GET /api/moderation/reports/all_history/

Permissions: ADMIN or above

Query Parameters:

  • page - Page number (default: 1)
  • page_size - Items per page (default: 20)
  • model - Filter by model name (optional)
  • user - Filter by user ID (optional)
  • from_date - Filter from date (optional)
  • to_date - Filter to date (optional)

Response (200 OK):

{
  "count": 100,
  "next": "/api/moderation/reports/all_history/?page=2",
  "previous": null,
  "results": [
    {
      "id": 1,
      "content_type": "moderation.editsubmission",
      "object_id": 123,
      "timestamp": "2025-01-15T10:30:00Z",
      "source_state": "PENDING",
      "state": "APPROVED",
      "transition": "transition_to_approved",
      "by": {
        "id": 456,
        "username": "moderator"
      }
    }
  ]
}

Mandatory API Rules

  1. Trailing Slashes: All endpoints MUST include trailing forward slashes
  2. HTTP Methods: All transitions use POST
  3. Authentication: All endpoints require valid JWT token
  4. Content-Type: Request bodies must be application/json

Error Response Format

All error responses follow this format:

{
  "error": "Error code",
  "message": "Human-readable error message",
  "details": {
    "field_name": ["Specific field errors"]
  }
}

Example Error Responses

Invalid Transition (400):

{
  "error": "TRANSITION_NOT_ALLOWED",
  "message": "Cannot transition from APPROVED to REJECTED"
}

Permission Denied (403):

{
  "error": "PERMISSION_DENIED",
  "message": "This action requires moderator privileges"
}

Validation Error (422):

{
  "error": "VALIDATION_ERROR",
  "message": "Missing required fields",
  "details": {
    "post_closing_status": ["This field is required when marking as CLOSING"]
  }
}