Public Access
refactor(library): auditor fields only through applyCorrection
This commit is contained in:
1 parent
79d7bf17f2
commit
1abc03d2c1
7 files changed
+30
-18
No files matched your search
@@ -232,11 +232,13 @@ At 01:30 `ImageRepairService` repairs the pictures reported through `POST /api/l
|
||||
An AUDITOR (active role) corrects a game's name, header image, speedrun link, moods, endless flag and genres 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). **A new writer of these
|
||||
fields must do the same.** "Back to source" (`DELETE`) writes back the source value from the first correction and
|
||||
the image repair (image), the IGDB add (name, image), the mood and speedrun 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.
|
||||
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.
|
||||
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.
|
||||
|
||||
### Merging duplicates
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.time.Instant;
|
||||
@DynamicUpdate
|
||||
@Table(name = "steam_game")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public class SteamGame {
|
||||
|
||||
@@ -28,9 +27,11 @@ public class SteamGame {
|
||||
@Column(name = "name", nullable = false, length = 512)
|
||||
private String name;
|
||||
|
||||
@Setter
|
||||
@Column(name = "img_icon_url", length = 512)
|
||||
private String imgIconUrl;
|
||||
|
||||
@Setter
|
||||
@Column(name = "last_seen_at", nullable = false)
|
||||
private Instant lastSeenAt;
|
||||
|
||||
@@ -45,9 +46,26 @@ public class SteamGame {
|
||||
}
|
||||
|
||||
public SteamGame(Long appid, String name, String imgIconUrl, Instant lastSeenAt) {
|
||||
this(appid, name, imgIconUrl, null, lastSeenAt);
|
||||
}
|
||||
|
||||
public SteamGame(Long appid, String name, String imgIconUrl, String imageUrl, Instant lastSeenAt) {
|
||||
this.appid = appid;
|
||||
this.name = name;
|
||||
this.imgIconUrl = imgIconUrl;
|
||||
this.imageUrl = imageUrl;
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only Java writer of name and image on a stored game. Refreshes from a source use the guarded updates in
|
||||
* {@code SteamGameRepository}, so they never overwrite a correction.
|
||||
*/
|
||||
public void applyCorrection(CorrectionField field, String value) {
|
||||
switch (field) {
|
||||
case NAME -> name = value;
|
||||
case IMAGE -> imageUrl = value;
|
||||
default -> throw new IllegalArgumentException(field + " is not a column of steam_game");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,8 +115,7 @@ public class GameCorrectionService {
|
||||
|
||||
private void write(CorrectionField field, SteamGame game, Object value) {
|
||||
switch (field) {
|
||||
case NAME -> game.setName((String) value);
|
||||
case IMAGE -> game.setImageUrl((String) value);
|
||||
case NAME, IMAGE -> game.applyCorrection(field, (String) value);
|
||||
// A new row has only the corrected link, so the next check would ask speedrun.com for nothing.
|
||||
case SPEEDRUN -> gameSpeedrunRepository.writeWeblink(game.getAppid(), (String) value, Instant.now());
|
||||
case MOODS -> gameMoodsRepository.writeMoods(game.getAppid(), strings(value), LONG_AGO);
|
||||
|
||||
@@ -290,11 +290,8 @@ public class ManualGameService {
|
||||
private void saveCatalogEntry(long appid, IgdbApiService.Game game, Instant now) {
|
||||
Optional<SteamGame> existing = steamGameRepository.findById(appid);
|
||||
if (existing.isEmpty()) {
|
||||
SteamGame created = new SteamGame(appid, game.name(), null, now);
|
||||
if (!GameKeys.isSteam(appid)) {
|
||||
created.setImageUrl(game.imageUrl());
|
||||
}
|
||||
steamGameRepository.save(created);
|
||||
String imageUrl = GameKeys.isSteam(appid) ? null : game.imageUrl();
|
||||
steamGameRepository.save(new SteamGame(appid, game.name(), null, imageUrl, now));
|
||||
} else if (appid == GameKeys.ofIgdb(game.igdbId())) {
|
||||
steamGameRepository.setSourceName(appid, game.name());
|
||||
steamGameRepository.setImageUrl(appid, game.imageUrl());
|
||||
|
||||
+1
-3
@@ -107,9 +107,7 @@ class LibraryControllerAchievementsTest extends AbstractIntegrationTest {
|
||||
game(70L, 8, null, true); // unlocks never fetched
|
||||
game(80L, 10, 12, true); // schema lost achievements, still perfect
|
||||
game(90L, 6, 0, true); // not started
|
||||
SteamGame repaired = steamGameRepository.findById(20L).orElseThrow();
|
||||
repaired.setImageUrl(REPAIRED_COVER);
|
||||
steamGameRepository.save(repaired);
|
||||
steamGameRepository.setImageUrl(20L, REPAIRED_COVER);
|
||||
steamGameRepository.save(new SteamGame(95L, "Game 95", null, Instant.now()));
|
||||
UserGame removed = new UserGame(USER_ID, 95L, 60, Instant.now(), Instant.now());
|
||||
removed.setRemovedAt(Instant.now());
|
||||
|
||||
@@ -53,8 +53,7 @@ class LibraryControllerActivityTest extends AbstractIntegrationTest {
|
||||
void seed() {
|
||||
userGameRepository.deleteAll();
|
||||
jdbc.update("delete from user_game_playtime_day");
|
||||
SteamGame repairedPortal = new SteamGame(620L, "Portal 2", null, Instant.now());
|
||||
repairedPortal.setImageUrl(REPAIRED_COVER);
|
||||
SteamGame repairedPortal = new SteamGame(620L, "Portal 2", null, REPAIRED_COVER, Instant.now());
|
||||
steamGameRepository.saveAll(List.of(
|
||||
new SteamGame(220L, "Half-Life 2", null, Instant.now()),
|
||||
repairedPortal,
|
||||
|
||||
@@ -136,8 +136,7 @@ class ImageRepairServiceTest extends AbstractUnitTest {
|
||||
@Test
|
||||
void repairNightly_ChecksTheStoredRepairFirst() {
|
||||
due(row(620L, ImageKind.COVER, Instant.now()));
|
||||
SteamGame game = new SteamGame(620L, "Portal 2", null, Instant.now());
|
||||
game.setImageUrl(HASHED_620);
|
||||
SteamGame game = new SteamGame(620L, "Portal 2", null, HASHED_620, Instant.now());
|
||||
when(steamGameRepository.findAllById(List.of(620L))).thenReturn(List.of(game));
|
||||
when(steamCdnService.isAvailable(HASHED_620)).thenReturn(true);
|
||||
|
||||
|
||||
Reference in new issue
Block a user