Try to fetch steam game count if requested and cache it
Build, Test & Push Core Service / quality-check (push) Successful in 44s
Build, Test & Push Core Service / docker (push) Successful in 1m48s

This commit is contained in:
Andreas Dahm committed 2026-04-28 15:43:02 +02:00
1 parent 2e3896b00d
commit 697fdcb208
9 files changed
+125 -10

No files matched your search

Vendored Regular → Executable
View File
File mode changed.
@@ -0,0 +1,24 @@
package core_service.configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("steamGameCount");
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(1000));
return cacheManager;
}
}
@@ -47,10 +47,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
try { try {
final String userEmail = jwtService.extractEmail(jwt); final String userEmail = jwtService.extractEmail(jwt);
final String userId = jwtService.extractUserId(jwt); final String userId = jwtService.extractUserId(jwt);
final Long steamId = jwtService.extractSteamId(jwt);
if (userEmail != null && userId != null && SecurityContextHolder.getContext().getAuthentication() == null) { if (userEmail != null && userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserPrincipal principal = new UserPrincipal(userId, userEmail); UserPrincipal principal = new UserPrincipal(userId, userEmail, steamId);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
principal, principal,
null, null,
@@ -1,6 +1,5 @@
package core_service.configuration.security; package core_service.configuration.security;
import core_service.shared.security.UserPrincipal;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -35,6 +34,15 @@ public class JwtService {
.get("userId", String.class); .get("userId", String.class);
} }
public Long extractSteamId(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload()
.get("steamId", Long.class);
}
public boolean isTokenValid(String token) { public boolean isTokenValid(String token) {
try { try {
Jwts.parser() Jwts.parser()
@@ -0,0 +1,30 @@
package core_service.modules.steam_adapter.api;
import core_service.modules.steam_adapter.service.SteamApiService;
import core_service.shared.security.UserPrincipal;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api/steam")
@RequiredArgsConstructor
public class SteamController {
private final SteamApiService steamApiService;
@GetMapping("/games-count")
public ResponseEntity<?> getGamesCount(@AuthenticationPrincipal UserPrincipal principal) {
if (principal.steamId() == null) {
return ResponseEntity.ok(Map.of("count", 0, "hasSteamId", false));
}
int count = steamApiService.getOwnedGamesCount(String.valueOf(principal.steamId()));
return ResponseEntity.ok(Map.of("count", count, "hasSteamId", true));
}
}
@@ -6,7 +6,7 @@ import java.util.List;
public record SteamOwnedGamesResponse(OwnedGamesResponse response) { public record SteamOwnedGamesResponse(OwnedGamesResponse response) {
public record OwnedGamesResponse( public record OwnedGamesResponse(
@JsonProperty("game_count") int gameCount, @JsonProperty("game_count") Integer gameCount,
List<Game> games List<Game> games
) {} ) {}
@@ -6,6 +6,7 @@ import core_service.modules.steam_adapter.api.dto.response.SteamProfileAndGamesR
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
@@ -23,6 +24,35 @@ public class SteamApiService {
@Value("${app.steam.api-key}") @Value("${app.steam.api-key}")
private String apiKey; private String apiKey;
@Cacheable(value = "steamGameCount", key = "#steamId")
public int getOwnedGamesCount(String steamId) {
try {
log.info("Fetching owned games count for SteamID: {}", steamId);
SteamOwnedGamesResponse response = steamRestClient.get()
.uri(uriBuilder -> uriBuilder
.path("/IPlayerService/GetOwnedGames/v0001/")
.queryParam("key", apiKey)
.queryParam("steamid", steamId)
.queryParam("include_appinfo", 0)
.queryParam("include_played_free_games", 1)
.build())
.retrieve()
.body(SteamOwnedGamesResponse.class);
log.info("SteamOwnedGamesResponse: {}", response);
if (response != null && response.response() != null) {
return response.response().gameCount() != null ? response.response().gameCount() : 0;
}
} catch (Exception e) {
log.error("Error communicating with Steam API for game count of SteamID: {}", steamId, e);
}
return 0;
}
public Optional<SteamProfileAndGamesResponse> getFullPlayerProfile(String steamId) { public Optional<SteamProfileAndGamesResponse> getFullPlayerProfile(String steamId) {
var playerOpt = getPlayerSummary(steamId); var playerOpt = getPlayerSummary(steamId);
if (playerOpt.isEmpty()) { if (playerOpt.isEmpty()) {
@@ -47,7 +77,7 @@ public class SteamApiService {
.body(SteamPlayerSummaryResponse.class); .body(SteamPlayerSummaryResponse.class);
if (response != null && response.response() != null && !response.response().players().isEmpty()) { if (response != null && response.response() != null && !response.response().players().isEmpty()) {
return Optional.of(response.response().players().get(0)); return Optional.of(response.response().players().getFirst());
} }
} catch (Exception e) { } catch (Exception e) {
@@ -4,7 +4,8 @@ import java.security.Principal;
public record UserPrincipal( public record UserPrincipal(
String id, String id,
String email String email,
Long steamId
) implements Principal { ) implements Principal {
@Override @Override
@@ -32,6 +32,27 @@ class SteamApiServiceTest {
ReflectionTestUtils.setField(steamApiService, "apiKey", "test-api-key"); ReflectionTestUtils.setField(steamApiService, "apiKey", "test-api-key");
} }
@Test
void getOwnedGamesCount_shouldReturnCount() {
String steamId = "76561197960435530";
String jsonResponse = """
{
"response": {
"game_count": 42
}
}
""";
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v0001/")))
.andExpect(queryParam("steamid", steamId))
.andExpect(queryParam("key", "test-api-key"))
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
int result = steamApiService.getOwnedGamesCount(steamId);
assertThat(result).isEqualTo(42);
}
@Test @Test
void getPlayerSummary_shouldReturnPlayer() { void getPlayerSummary_shouldReturnPlayer() {
String steamId = "76561197960435530"; String steamId = "76561197960435530";
@@ -89,7 +110,7 @@ class SteamApiServiceTest {
List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId); List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId);
assertThat(result).hasSize(1); assertThat(result).hasSize(1);
assertThat(result.get(0).name()).isEqualTo("Counter-Strike"); assertThat(result.getFirst().name()).isEqualTo("Counter-Strike");
} }
@Test @Test
@@ -115,9 +136,9 @@ class SteamApiServiceTest {
List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId); List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId);
assertThat(result).hasSize(1); assertThat(result).hasSize(1);
assertThat(result.get(0).name()).isEqualTo("Counter-Strike"); assertThat(result.getFirst().name()).isEqualTo("Counter-Strike");
assertThat(result.get(0).playtimeForever()).isNull(); assertThat(result.getFirst().playtimeForever()).isNull();
assertThat(result.get(0).hasCommunityVisibleStats()).isNull(); assertThat(result.getFirst().hasCommunityVisibleStats()).isNull();
} }
@Test @Test
@@ -165,6 +186,6 @@ class SteamApiServiceTest {
assertThat(result).isPresent(); assertThat(result).isPresent();
assertThat(result.get().player().personaname()).isEqualTo("TestUser"); assertThat(result.get().player().personaname()).isEqualTo("TestUser");
assertThat(result.get().games()).hasSize(1); assertThat(result.get().games()).hasSize(1);
assertThat(result.get().games().get(0).name()).isEqualTo("Counter-Strike"); assertThat(result.get().games().getFirst().name()).isEqualTo("Counter-Strike");
} }
} }