Public Access
fix(steam): sync owned apps Steam calls unvetted
GetOwnedGames leaves out "unvetted" apps unless skip_unvetted_apps=0 is set. So a released game owned through a developer license was missing from the library. Such an app can come without a name. The sync now keeps the stored name, or uses "App <appid>" for a new game, so the NOT NULL name column does not fail the whole sync.
This commit is contained in:
1 parent
b350de1c79
commit
3224257325
4 files changed
+54
-2
No files matched your search
@@ -170,10 +170,12 @@ public class LibrarySyncWorker {
|
||||
private void upsertGame(SteamOwnedGamesResponse.Game g, Instant now) {
|
||||
Long appid = g.appid().longValue();
|
||||
SteamGame existing = steamGameRepository.findById(appid).orElse(null);
|
||||
// An unvetted app can come without a name, and the name column is NOT NULL.
|
||||
boolean hasName = g.name() != null && !g.name().isBlank();
|
||||
if (existing == null) {
|
||||
steamGameRepository.save(new SteamGame(appid, g.name(), g.imgIconUrl(), now));
|
||||
steamGameRepository.save(new SteamGame(appid, hasName ? g.name() : "App " + appid, g.imgIconUrl(), now));
|
||||
} else {
|
||||
existing.setName(g.name());
|
||||
if (hasName) existing.setName(g.name());
|
||||
existing.setImgIconUrl(g.imgIconUrl());
|
||||
existing.setLastSeenAt(now);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public class SteamApiService {
|
||||
.queryParam("steamid", steamId)
|
||||
.queryParam("include_appinfo", "true")
|
||||
.queryParam("include_played_free_games", "true")
|
||||
.queryParam("skip_unvetted_apps", 0)
|
||||
.queryParam("format", "json")
|
||||
.build())
|
||||
.retrieve()
|
||||
@@ -145,6 +146,8 @@ public class SteamApiService {
|
||||
.queryParam("steamid", steamId)
|
||||
.queryParam("include_appinfo", 1)
|
||||
.queryParam("include_played_free_games", 1)
|
||||
// Else Steam hides some owned apps, e.g. a game from a developer license.
|
||||
.queryParam("skip_unvetted_apps", 0)
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(SteamOwnedGamesResponse.class);
|
||||
|
||||
@@ -2,6 +2,7 @@ package core_service.modules.library.service;
|
||||
|
||||
import core_service.modules.library.domain.GameSource;
|
||||
import core_service.modules.library.domain.GameStatus;
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.domain.UserGameId;
|
||||
import core_service.modules.library.domain.UserSteamSync;
|
||||
@@ -34,10 +35,12 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -415,4 +418,30 @@ class LibrarySyncWorkerTest extends AbstractUnitTest {
|
||||
verify(timeToBeatService).refresh(List.of(220L, -1025L));
|
||||
verify(moodService).refresh(List.of(220L, -1025L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runSync_GameWithoutName_GetsTheAppidAsName() {
|
||||
when(userSteamSyncRepository.tryClaimRun(eq(USER_ID), eq(STEAM_ID), any())).thenReturn(1);
|
||||
when(steamApiService.getOwnedGames(STEAM_ID)).thenReturn(List.of(
|
||||
new SteamOwnedGamesResponse.Game(2000001, null, 12, null, null, null),
|
||||
game(90, 0)));
|
||||
|
||||
worker.runSync(USER_ID, STEAM_ID);
|
||||
|
||||
verify(steamGameRepository).save(argThat((SteamGame g) -> g.getAppid() == 2000001L && "App 2000001".equals(g.getName())));
|
||||
verify(steamGameRepository).save(argThat((SteamGame g) -> g.getAppid() == 220L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runSync_GameWithoutName_KeepsTheStoredName() {
|
||||
SteamGame stored = new SteamGame(2000001L, "El Mucho", null, Instant.EPOCH);
|
||||
when(userSteamSyncRepository.tryClaimRun(eq(USER_ID), eq(STEAM_ID), any())).thenReturn(1);
|
||||
when(steamGameRepository.findById(2000001L)).thenReturn(Optional.of(stored));
|
||||
when(steamApiService.getOwnedGames(STEAM_ID)).thenReturn(List.of(
|
||||
new SteamOwnedGamesResponse.Game(2000001, " ", 12, null, null, null)));
|
||||
|
||||
worker.runSync(USER_ID, STEAM_ID);
|
||||
|
||||
assertThat(stored.getName()).isEqualTo("El Mucho");
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,24 @@ class SteamApiServiceTest {
|
||||
assertThat(result.getFirst().name()).isEqualTo("Counter-Strike");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOwnedGames_shouldIncludeUnvettedApps() {
|
||||
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
|
||||
.andExpect(queryParam("skip_unvetted_apps", "0"))
|
||||
.andRespond(withSuccess("{\"response\":{\"games\":[{\"appid\":10,\"name\":\"El Mucho\"}]}}", MediaType.APPLICATION_JSON));
|
||||
|
||||
assertThat(steamApiService.getOwnedGames("76561197960435530")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOwnedGamesCount_shouldCountUnvettedApps() {
|
||||
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
|
||||
.andExpect(queryParam("skip_unvetted_apps", "0"))
|
||||
.andRespond(withSuccess("{\"response\":{\"game_count\":1}}", MediaType.APPLICATION_JSON));
|
||||
|
||||
assertThat(steamApiService.getOwnedGamesCount("76561197960435530")).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOwnedGames_shouldReturnGamesEvenWithNullValues() {
|
||||
String steamId = "76561197960435530";
|
||||
|
||||
Reference in new issue
Block a user