docs: update manual team action list API

This commit is contained in:
Daniel Schädler 2026-04-23 22:48:11 +02:00
parent d69e08a25b
commit d3589235d9
2 changed files with 91 additions and 27 deletions

View File

@ -176,6 +176,7 @@ Practical rule:
For team action lists specifically:
- create lists explicitly through owner-facing HTTP API; do not auto-generate them from stage transitions unless product requirements say so
- subscribe by tournament id
- channel name is `TournamentTeamActionListsChannel`
- broadcast full current list snapshot for that tournament

View File

@ -2,13 +2,19 @@
## Purpose
Team action lists let the backend expose checklist-style workflows for teams inside a tournament.
Team action lists let tournament owners create checklist-style workflows for teams inside a tournament.
Current example:
Examples:
- `group_stage_survivor_playoff_tokens`
- collect playoff tokens
- track team check-in
- hand out medals
That list contains all teams that advanced from the group stage and tracks whether each team already collected a playoff token.
Important:
- lists are not auto-generated by backend tournament events
- owner creates a list explicitly through API
- backend materializes the initial team items from the chosen `source`
## Read Flow
@ -26,7 +32,7 @@ Example response shape:
{
"id": 9,
"name": "Playoff token collection",
"key": "group_stage_survivor_playoff_tokens",
"key": "playoff_token_collection",
"action_name": "Collect playoff token",
"source": "group_stage_survivors",
"team_action_items": [
@ -45,6 +51,50 @@ Example response shape:
}
```
## Create Flow
Only tournament owner may create lists.
Follower tournaments in `read_only_mode` reject creation with `423`.
Endpoint:
- `POST /tournaments/:tournament_id/team_action_lists`
Request body:
```json
{
"name": "Playoff token collection",
"key": "playoff_token_collection",
"action_name": "Collect playoff token",
"source": "group_stage_survivors"
}
```
Response:
- `201 Created`
- response body is the created `TeamActionList` including `team_action_items`
Current supported sources:
- `all_tournament_teams`
- `group_stage_survivors`
Current source behavior:
- `all_tournament_teams` uses all teams currently in tournament
- `group_stage_survivors` uses teams currently advancing from a finished group stage
Creation is snapshot-style:
- backend resolves teams at create time
- backend creates one item per resolved team
- backend does not auto-create lists later
- backend does not auto-refresh existing list membership later
If selected source currently resolves to no teams, backend returns `422`.
## Write Flow
Only tournament owner may change item status.
@ -79,7 +129,7 @@ Endpoint:
Example:
- `PATCH /tournaments/123/team_action_lists/group_stage_survivor_playoff_tokens/teams/55`
- `PATCH /tournaments/123/team_action_lists/playoff_token_collection/teams/55`
Request body:
@ -93,7 +143,12 @@ Response body for both write endpoints is the updated `TeamActionItem`.
## Idempotency Rules
These endpoints are designed to be idempotent if frontend sends explicit target state.
Create endpoint is idempotent by unique business key per tournament:
- backend enforces unique `(tournament_id, key)`
- repeated create with same key returns `422`, not a second list
Item update endpoints are idempotent if frontend sends explicit target state.
Good:
@ -103,12 +158,12 @@ Good:
Avoid:
- toggle-style client behavior such as "invert current state"
- whole-list writeback from stale client state
Why this is idempotent:
Why item updates are idempotent:
- sending `completed=true` multiple times leaves item in same final state
- sending `completed=false` multiple times leaves item in same final state
- backend does not create duplicate items
- backend stores one unique item per `(team_action_list_id, team_id)`
`completed_at` behavior:
@ -122,10 +177,10 @@ Why this is idempotent:
Recommended frontend flow:
1. Load tournament via `GET /tournaments/:id`
2. Find desired list by stable `key`
3. Render `team_action_items`
4. On user action, send explicit target state
5. Update local UI from response or refetch tournament
2. Render current `team_action_lists`
3. If owner wants a new checklist, send `POST /tournaments/:id/team_action_lists`
4. For item changes, send explicit target state for one item only
5. Update local UI from response or websocket snapshot
Recommended frontend identifiers:
@ -135,6 +190,7 @@ Recommended frontend identifiers:
Recommended optimistic UI:
- create list only once per intended key
- set checkbox immediately to requested target state
- revert if API returns error
- do not compute next state from stale cached data if multiple clients may edit
@ -143,11 +199,13 @@ Recommended optimistic UI:
Expected statuses:
- `200` update accepted
- `201` list created
- `200` item update accepted
- `401` unauthenticated
- `403` authenticated but not tournament owner
- `404` tournament, list, team, or item not found
- `422` invalid create payload, duplicate key, unsupported source, or source with no teams
- `423` follower tournament is read-only
- `401` unauthenticated
## Sync / Follower Behavior
@ -155,10 +213,12 @@ Team action lists are included in tournament sync snapshots.
That means:
- source tournament owner updates item state
- source owner creates list on source tournament
- backend enqueues tournament sync
- follower tournament receives updated list state
- follower users can view but not mutate item state while read-only
- follower tournament receives created list and all items
- source owner updates item state
- follower tournament receives updated completion state
- follower users can view but not mutate list state while read-only
## Websocket / Live Update Behavior
@ -168,7 +228,7 @@ This is a websocket flow, not a webhook flow.
- backend pushes updates to subscribed clients over websocket
- frontend does not register callback URLs
- writes still happen over normal HTTP `PATCH` requests
- writes still happen over normal HTTP `POST` and `PATCH` requests
Endpoint:
@ -190,11 +250,12 @@ Subscription identifier example:
Behavior:
- subscription immediately receives current full team-action-list snapshot for that tournament
- later list/item changes broadcast updated full snapshot again
- later list creation and item changes broadcast updated full snapshot again
- same broadcast path is used for:
- direct source updates
- list creation
- direct item updates
- follower sync imports
- both write endpoints trigger the same backend update path before broadcast:
- both item update endpoints trigger the same backend update path before broadcast:
- `PATCH /team_action_items/:id`
- `PATCH /tournaments/:tournament_id/team_action_lists/:key/teams/:team_id`
@ -208,7 +269,7 @@ Payload shape:
{
"id": 9,
"name": "Playoff token collection",
"key": "group_stage_survivor_playoff_tokens",
"key": "playoff_token_collection",
"action_name": "Collect playoff token",
"source": "group_stage_survivors",
"team_action_items": [
@ -239,20 +300,22 @@ Practical frontend pattern:
1. load initial tournament state over HTTP
2. open websocket subscription for same tournament id
3. render incoming websocket payload as latest live snapshot
4. when user checks or unchecks one team, send one idempotent HTTP `PATCH` for that item only
5. let websocket keep other viewers in sync
4. if owner submits create form, send one HTTP `POST` to create the list
5. when user checks or unchecks one team, send one idempotent HTTP `PATCH` for that item only
6. let websocket keep other viewers in sync
This avoids whole-list writebacks and reduces race-condition risk between multiple clients.
## Future Extension Guidance
When new lists are added, frontend should not hardcode backend internals beyond:
When new sources or list types are added, frontend should not hardcode backend internals beyond:
- stable list `key`
- user-facing `name`
- user-facing `action_name`
- selected `source`
New list types should automatically fit the same frontend rendering model:
New list types should still fit same frontend rendering model:
- list metadata
- list items