docs: update manual team action list API
This commit is contained in:
parent
d69e08a25b
commit
d3589235d9
|
|
@ -176,6 +176,7 @@ Practical rule:
|
||||||
|
|
||||||
For team action lists specifically:
|
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
|
- subscribe by tournament id
|
||||||
- channel name is `TournamentTeamActionListsChannel`
|
- channel name is `TournamentTeamActionListsChannel`
|
||||||
- broadcast full current list snapshot for that tournament
|
- broadcast full current list snapshot for that tournament
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,19 @@
|
||||||
|
|
||||||
## Purpose
|
## 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
|
## Read Flow
|
||||||
|
|
||||||
|
|
@ -26,7 +32,7 @@ Example response shape:
|
||||||
{
|
{
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"name": "Playoff token collection",
|
"name": "Playoff token collection",
|
||||||
"key": "group_stage_survivor_playoff_tokens",
|
"key": "playoff_token_collection",
|
||||||
"action_name": "Collect playoff token",
|
"action_name": "Collect playoff token",
|
||||||
"source": "group_stage_survivors",
|
"source": "group_stage_survivors",
|
||||||
"team_action_items": [
|
"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
|
## Write Flow
|
||||||
|
|
||||||
Only tournament owner may change item status.
|
Only tournament owner may change item status.
|
||||||
|
|
@ -79,7 +129,7 @@ Endpoint:
|
||||||
|
|
||||||
Example:
|
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:
|
Request body:
|
||||||
|
|
||||||
|
|
@ -93,7 +143,12 @@ Response body for both write endpoints is the updated `TeamActionItem`.
|
||||||
|
|
||||||
## Idempotency Rules
|
## 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:
|
Good:
|
||||||
|
|
||||||
|
|
@ -103,12 +158,12 @@ Good:
|
||||||
Avoid:
|
Avoid:
|
||||||
|
|
||||||
- toggle-style client behavior such as "invert current state"
|
- 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=true` multiple times leaves item in same final state
|
||||||
- sending `completed=false` 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)`
|
- backend stores one unique item per `(team_action_list_id, team_id)`
|
||||||
|
|
||||||
`completed_at` behavior:
|
`completed_at` behavior:
|
||||||
|
|
@ -122,10 +177,10 @@ Why this is idempotent:
|
||||||
Recommended frontend flow:
|
Recommended frontend flow:
|
||||||
|
|
||||||
1. Load tournament via `GET /tournaments/:id`
|
1. Load tournament via `GET /tournaments/:id`
|
||||||
2. Find desired list by stable `key`
|
2. Render current `team_action_lists`
|
||||||
3. Render `team_action_items`
|
3. If owner wants a new checklist, send `POST /tournaments/:id/team_action_lists`
|
||||||
4. On user action, send explicit target state
|
4. For item changes, send explicit target state for one item only
|
||||||
5. Update local UI from response or refetch tournament
|
5. Update local UI from response or websocket snapshot
|
||||||
|
|
||||||
Recommended frontend identifiers:
|
Recommended frontend identifiers:
|
||||||
|
|
||||||
|
|
@ -135,6 +190,7 @@ Recommended frontend identifiers:
|
||||||
|
|
||||||
Recommended optimistic UI:
|
Recommended optimistic UI:
|
||||||
|
|
||||||
|
- create list only once per intended key
|
||||||
- set checkbox immediately to requested target state
|
- set checkbox immediately to requested target state
|
||||||
- revert if API returns error
|
- revert if API returns error
|
||||||
- do not compute next state from stale cached data if multiple clients may edit
|
- do not compute next state from stale cached data if multiple clients may edit
|
||||||
|
|
@ -143,11 +199,13 @@ Recommended optimistic UI:
|
||||||
|
|
||||||
Expected statuses:
|
Expected statuses:
|
||||||
|
|
||||||
- `200` update accepted
|
- `201` list created
|
||||||
|
- `200` item update accepted
|
||||||
|
- `401` unauthenticated
|
||||||
- `403` authenticated but not tournament owner
|
- `403` authenticated but not tournament owner
|
||||||
- `404` tournament, list, team, or item not found
|
- `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
|
- `423` follower tournament is read-only
|
||||||
- `401` unauthenticated
|
|
||||||
|
|
||||||
## Sync / Follower Behavior
|
## Sync / Follower Behavior
|
||||||
|
|
||||||
|
|
@ -155,10 +213,12 @@ Team action lists are included in tournament sync snapshots.
|
||||||
|
|
||||||
That means:
|
That means:
|
||||||
|
|
||||||
- source tournament owner updates item state
|
- source owner creates list on source tournament
|
||||||
- backend enqueues tournament sync
|
- backend enqueues tournament sync
|
||||||
- follower tournament receives updated list state
|
- follower tournament receives created list and all items
|
||||||
- follower users can view but not mutate item state while read-only
|
- 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
|
## 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
|
- backend pushes updates to subscribed clients over websocket
|
||||||
- frontend does not register callback URLs
|
- 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:
|
Endpoint:
|
||||||
|
|
||||||
|
|
@ -190,11 +250,12 @@ Subscription identifier example:
|
||||||
Behavior:
|
Behavior:
|
||||||
|
|
||||||
- subscription immediately receives current full team-action-list snapshot for that tournament
|
- 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:
|
- same broadcast path is used for:
|
||||||
- direct source updates
|
- list creation
|
||||||
|
- direct item updates
|
||||||
- follower sync imports
|
- 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 /team_action_items/:id`
|
||||||
- `PATCH /tournaments/:tournament_id/team_action_lists/:key/teams/:team_id`
|
- `PATCH /tournaments/:tournament_id/team_action_lists/:key/teams/:team_id`
|
||||||
|
|
||||||
|
|
@ -208,7 +269,7 @@ Payload shape:
|
||||||
{
|
{
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"name": "Playoff token collection",
|
"name": "Playoff token collection",
|
||||||
"key": "group_stage_survivor_playoff_tokens",
|
"key": "playoff_token_collection",
|
||||||
"action_name": "Collect playoff token",
|
"action_name": "Collect playoff token",
|
||||||
"source": "group_stage_survivors",
|
"source": "group_stage_survivors",
|
||||||
"team_action_items": [
|
"team_action_items": [
|
||||||
|
|
@ -239,20 +300,22 @@ Practical frontend pattern:
|
||||||
1. load initial tournament state over HTTP
|
1. load initial tournament state over HTTP
|
||||||
2. open websocket subscription for same tournament id
|
2. open websocket subscription for same tournament id
|
||||||
3. render incoming websocket payload as latest live snapshot
|
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
|
4. if owner submits create form, send one HTTP `POST` to create the list
|
||||||
5. let websocket keep other viewers in sync
|
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.
|
This avoids whole-list writebacks and reduces race-condition risk between multiple clients.
|
||||||
|
|
||||||
## Future Extension Guidance
|
## 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`
|
- stable list `key`
|
||||||
- user-facing `name`
|
- user-facing `name`
|
||||||
- user-facing `action_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 metadata
|
||||||
- list items
|
- list items
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue