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

661 lines
10 KiB
Markdown

# 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.
```http
POST /api/moderation/submissions/{id}/approve/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"notes": "Optional approval notes"
}
```
**Response** (200 OK):
```json
{
"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.
```http
POST /api/moderation/submissions/{id}/reject/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"reason": "Required rejection reason"
}
```
**Response** (200 OK):
```json
{
"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.
```http
POST /api/moderation/submissions/{id}/escalate/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"reason": "Reason for escalation"
}
```
**Response** (200 OK):
```json
{
"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.
```http
POST /api/moderation/reports/{id}/start_review/
```
**Permissions**: MODERATOR or above
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"status": "UNDER_REVIEW",
"assigned_moderator": {
"id": 456,
"username": "moderator"
}
}
```
### Resolve Report
Mark a report as resolved.
```http
POST /api/moderation/reports/{id}/resolve/
```
**Permissions**: MODERATOR or above (must be assigned)
**Request Body**:
```json
{
"resolution_action": "CONTENT_REMOVED",
"resolution_notes": "Description of action taken"
}
```
**Response** (200 OK):
```json
{
"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.
```http
POST /api/moderation/reports/{id}/dismiss/
```
**Permissions**: MODERATOR or above (must be assigned)
**Request Body**:
```json
{
"resolution_notes": "Reason for dismissal"
}
```
**Response** (200 OK):
```json
{
"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.
```http
POST /api/parks/{slug}/close_temporarily/
```
**Permissions**: Authenticated user
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-park",
"name": "Example Park",
"status": "CLOSED_TEMP"
}
```
### Reopen
Reopen a temporarily closed park.
```http
POST /api/parks/{slug}/reopen/
```
**Permissions**: Authenticated user
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-park",
"name": "Example Park",
"status": "OPERATING"
}
```
### Close Permanently
Permanently close a park.
```http
POST /api/parks/{slug}/close_permanently/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"closing_date": "2025-12-31"
}
```
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-park",
"name": "Example Park",
"status": "CLOSED_PERM",
"closing_date": "2025-12-31"
}
```
### Demolish
Mark a park as demolished.
```http
POST /api/parks/{slug}/demolish/
```
**Permissions**: MODERATOR or above
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-park",
"name": "Example Park",
"status": "DEMOLISHED"
}
```
### Relocate
Mark a park as relocated.
```http
POST /api/parks/{slug}/relocate/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"new_location_notes": "Optional notes about new location"
}
```
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-park",
"name": "Example Park",
"status": "RELOCATED"
}
```
---
## Ride Transitions
### Open
Open a ride (from CLOSED_TEMP, SBNO, or UNDER_CONSTRUCTION).
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/open/
```
**Permissions**: Authenticated user (CLOSED_TEMP), MODERATOR+ (SBNO)
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "OPERATING"
}
```
### Close Temporarily
Temporarily close a ride.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/close_temporarily/
```
**Permissions**: Authenticated user
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "CLOSED_TEMP"
}
```
### Mark SBNO
Mark a ride as Standing But Not Operating.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/mark_sbno/
```
**Permissions**: MODERATOR or above
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "SBNO"
}
```
### Mark Closing
Mark a ride as scheduled for closure.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/mark_closing/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"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):
```json
{
"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.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/close_permanently/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"closing_date": "2025-12-31"
}
```
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "CLOSED_PERM",
"closing_date": "2025-12-31"
}
```
### Demolish
Mark a ride as demolished.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/demolish/
```
**Permissions**: MODERATOR or above
**Request Body**: None
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "DEMOLISHED"
}
```
### Relocate
Mark a ride as relocated.
```http
POST /api/parks/{park_slug}/rides/{ride_slug}/relocate/
```
**Permissions**: MODERATOR or above
**Request Body**:
```json
{
"new_park_slug": "destination-park",
"notes": "Optional relocation notes"
}
```
**Response** (200 OK):
```json
{
"id": 123,
"slug": "example-ride",
"name": "Example Coaster",
"status": "RELOCATED"
}
```
---
## Transition History
### Get Object History
Get the state transition history for any object.
```http
GET /api/moderation/history/{content_type}/{object_id}/
```
**Permissions**: MODERATOR or above
**Response** (200 OK):
```json
{
"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.
```http
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):
```json
{
"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:
```json
{
"error": "Error code",
"message": "Human-readable error message",
"details": {
"field_name": ["Specific field errors"]
}
}
```
### Example Error Responses
**Invalid Transition (400)**:
```json
{
"error": "TRANSITION_NOT_ALLOWED",
"message": "Cannot transition from APPROVED to REJECTED"
}
```
**Permission Denied (403)**:
```json
{
"error": "PERMISSION_DENIED",
"message": "This action requires moderator privileges"
}
```
**Validation Error (422)**:
```json
{
"error": "VALIDATION_ERROR",
"message": "Missing required fields",
"details": {
"post_closing_status": ["This field is required when marking as CLOSING"]
}
}