Public Access
feat(library): auditor catalog search and time to beat correction
GET /api/library/catalog finds any game a user has, by name, for
auditors; GET /api/library/catalog/{appid} loads it for the correction
dialog. New correction field TIME_TO_BEAT (normal + completionist,
1..60000 min, completionist >= normal) that survives the IGDB refresh.
Every correction and reset writes GAME_CORRECT / GAME_CORRECTION_RESET
to admin_audit.
This commit is contained in:
1 parent
a2ea1e45d3
commit
44af19d32e
16 files changed
+596
-13
No files matched your search
@@ -136,10 +136,12 @@ ArchUnit role rules (`RoleAnnotationRules`) and `src/test/resources/contract/aut
|
||||
| `PUT` | `/api/rewards/equipped/{slot}` (`{item}`, null = take off; `200` wallet, `409 NOT_OWNED`, `400 WRONG_SLOT`) | JWT |
|
||||
| `GET` | `/api/library/games` (`hideDuplicates=true` leaves out the lesser editions of a linked set), `/api/library/games/{appid}` | JWT |
|
||||
| `PATCH` | `/api/library/games/{appid}/backlog` (only the sent fields; `null` clears one) | JWT |
|
||||
| `PUT`, `DELETE` | `/api/library/games/{appid}/corrections/{field}` (`{value}`; `name`, `image`, `speedrun`, `moods`, `endless`, `genres`; `204`, `400 INVALID_CORRECTION`) | JWT, AUDITOR |
|
||||
| `PUT`, `DELETE` | `/api/library/games/{appid}/corrections/{field}` (`{value}`; `name`, `image`, `speedrun`, `moods`, `endless`, `genres`, `time_to_beat` = `{normallyMinutes, completelyMinutes}`; `204`, `400 INVALID_CORRECTION`) | JWT, AUDITOR |
|
||||
| `GET` | `/api/library/catalog?q=&page=&size=` (every game some user still has, by name part, `PageDto` of `{appid, name, imageUrl, platforms}`; `q` at most 100 characters; size 20, 40 or 80) | JWT, AUDITOR |
|
||||
| `GET` | `/api/library/catalog/{appid}` (what the correction dialog reads: name, image, speedrun, moods, endless, genres, timeToBeat, corrections; `404 GAME_NOT_FOUND`) | JWT, AUDITOR |
|
||||
| `POST` | `/api/library/games/{appid}/merge` (`{otherAppid}`; `200 {appid}` = the key that stays, `400 SAME_GAME` / `STEAM_GAMES`, `404 GAME_NOT_FOUND`) | JWT, AUDITOR |
|
||||
| `POST`, `DELETE` | `/api/library/games/{appid}/link` (POST `{otherAppid}`, `204`, `400 SAME_GAME`, `404 GAME_NOT_FOUND`, `409 ALREADY_LINKED`; DELETE takes the game out of its set, `204`, `404 NOT_LINKED`) | JWT, AUDITOR |
|
||||
| `GET` | `/api/library/audit-log?action=&page=&size=` (the newest `admin_audit` rows, `PageDto` of `{id, actorUserId, actorName, action, appid, gameName, targetAppid, targetGameName, detail, createdAt}`; `action` is `GAME_MERGE`, `GAME_LINK`, `GAME_UNLINK` or `GAME_LINK_DISMISS`; size 20, 40 or 80) | JWT, AUDITOR |
|
||||
| `GET` | `/api/library/audit-log?action=&page=&size=` (the newest `admin_audit` rows, `PageDto` of `{id, actorUserId, actorName, action, appid, gameName, targetAppid, targetGameName, detail, createdAt}`; `action` is `GAME_MERGE`, `GAME_LINK`, `GAME_UNLINK`, `GAME_LINK_DISMISS`, `GAME_CORRECT` or `GAME_CORRECTION_RESET`; size 20, 40 or 80) | JWT, AUDITOR |
|
||||
| `GET` | `/api/library/link-queue?page=&size=` (`PageDto` of `{a, b, igdb, requests, createdAt}`: both games, most requested first; size 20, 40 or 80) | JWT, AUDITOR |
|
||||
| `DELETE` | `/api/library/link-queue/{appid}/{otherAppid}` (dismiss; `204`, `404 PAIR_NOT_OPEN`) | JWT, AUDITOR |
|
||||
| `POST` | `/api/library/games/{appid}/link-requests` (`{otherAppid}`, both in the caller's library; `204`, `400 SAME_GAME`, `404 GAME_NOT_FOUND`, `409 ALREADY_LINKED` / `PAIR_DISMISSED`, `429 TOO_MANY_REQUESTS`) | JWT |
|
||||
@@ -311,13 +313,17 @@ a restart.
|
||||
|
||||
### Corrections by an auditor
|
||||
|
||||
An AUDITOR (active role) corrects a game's name, header image, speedrun link, moods, endless flag and genres for
|
||||
An AUDITOR (active role) corrects a game's name, header image, speedrun link, moods, endless flag, genres and time to beat for
|
||||
every user (`game_correction`, V20). The value is also written into the cached row (`steam_game`, `game_speedrun`,
|
||||
`game_mood`), so no read joins the corrections. The refresh writers skip a corrected field: the Steam sync (name),
|
||||
the image repair (image), the IGDB add (name, image), the mood and speedrun upserts (SQL). `SteamGame` has no
|
||||
the image repair (image), the IGDB add (name, image), the mood, speedrun and time to beat upserts (SQL). `SteamGame` has no
|
||||
setter for name and image: `applyCorrection` is the only Java writer, a refresh uses the guarded updates in
|
||||
`SteamGameRepository`. "Back to source" (`DELETE`) writes back the source value from the first correction and
|
||||
lets the next refresh ask IGDB or speedrun.com again. An image must be on a host of the frontend's CSP.
|
||||
The time to beat is one field for both times (`normallyMinutes`, `completelyMinutes`, each empty or 1 to 60000,
|
||||
completely not below normally) written into `game_time_to_beat`, so pile hours and stats use it; the IGDB `hastily`
|
||||
time is not correctable. Every correction and every reset writes `admin_audit` (`GAME_CORRECT`,
|
||||
`GAME_CORRECTION_RESET`). `GET /api/library/catalog` lets an auditor find and correct a game they do not own.
|
||||
Known limit: a mood or speedrun refresh that waits on the row lock while an auditor saves can still write the
|
||||
source value once (its guard sees the state from before the save); the auditor saves again. `save(new SteamGame(...))` on a
|
||||
stored key also overwrites a corrected name; the sync and the IGDB add do it only after `findById` found no row.
|
||||
|
||||
Reference in new issue
Block a user