- 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.
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 OperatingCLOSED_PERM- Will be permanently closedDEMOLISHED- Will be demolishedRELOCATED- 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
- Trailing Slashes: All endpoints MUST include trailing forward slashes
- HTTP Methods: All transitions use POST
- Authentication: All endpoints require valid JWT token
- 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"]
}
}