diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/src/main/java/core_service/configuration/CacheConfig.java b/src/main/java/core_service/configuration/CacheConfig.java new file mode 100644 index 0000000..12ec458 --- /dev/null +++ b/src/main/java/core_service/configuration/CacheConfig.java @@ -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; + } +} diff --git a/src/main/java/core_service/configuration/security/JwtAuthenticationFilter.java b/src/main/java/core_service/configuration/security/JwtAuthenticationFilter.java index 32f3154..11a15b8 100644 --- a/src/main/java/core_service/configuration/security/JwtAuthenticationFilter.java +++ b/src/main/java/core_service/configuration/security/JwtAuthenticationFilter.java @@ -47,10 +47,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { try { final String userEmail = jwtService.extractEmail(jwt); final String userId = jwtService.extractUserId(jwt); + final Long steamId = jwtService.extractSteamId(jwt); 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( principal, null, diff --git a/src/main/java/core_service/configuration/security/JwtService.java b/src/main/java/core_service/configuration/security/JwtService.java index 9dbdb99..8bbc969 100644 --- a/src/main/java/core_service/configuration/security/JwtService.java +++ b/src/main/java/core_service/configuration/security/JwtService.java @@ -1,6 +1,5 @@ package core_service.configuration.security; -import core_service.shared.security.UserPrincipal; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Service; @@ -35,6 +34,15 @@ public class JwtService { .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) { try { Jwts.parser() diff --git a/src/main/java/core_service/modules/steam_adapter/api/SteamController.java b/src/main/java/core_service/modules/steam_adapter/api/SteamController.java new file mode 100644 index 0000000..6cc8602 --- /dev/null +++ b/src/main/java/core_service/modules/steam_adapter/api/SteamController.java @@ -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)); + } +} diff --git a/src/main/java/core_service/modules/steam_adapter/api/dto/response/SteamOwnedGamesResponse.java b/src/main/java/core_service/modules/steam_adapter/api/dto/response/SteamOwnedGamesResponse.java index e49ee95..68669ad 100644 --- a/src/main/java/core_service/modules/steam_adapter/api/dto/response/SteamOwnedGamesResponse.java +++ b/src/main/java/core_service/modules/steam_adapter/api/dto/response/SteamOwnedGamesResponse.java @@ -6,7 +6,7 @@ import java.util.List; public record SteamOwnedGamesResponse(OwnedGamesResponse response) { public record OwnedGamesResponse( - @JsonProperty("game_count") int gameCount, + @JsonProperty("game_count") Integer gameCount, List games ) {} diff --git a/src/main/java/core_service/modules/steam_adapter/service/SteamApiService.java b/src/main/java/core_service/modules/steam_adapter/service/SteamApiService.java index 3eee7b1..73f70ba 100644 --- a/src/main/java/core_service/modules/steam_adapter/service/SteamApiService.java +++ b/src/main/java/core_service/modules/steam_adapter/service/SteamApiService.java @@ -6,6 +6,7 @@ import core_service.modules.steam_adapter.api.dto.response.SteamProfileAndGamesR import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; @@ -23,6 +24,35 @@ public class SteamApiService { @Value("${app.steam.api-key}") 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 getFullPlayerProfile(String steamId) { var playerOpt = getPlayerSummary(steamId); if (playerOpt.isEmpty()) { @@ -47,7 +77,7 @@ public class SteamApiService { .body(SteamPlayerSummaryResponse.class); 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) { diff --git a/src/main/java/core_service/shared/security/UserPrincipal.java b/src/main/java/core_service/shared/security/UserPrincipal.java index 62025d7..4eabd73 100644 --- a/src/main/java/core_service/shared/security/UserPrincipal.java +++ b/src/main/java/core_service/shared/security/UserPrincipal.java @@ -4,7 +4,8 @@ import java.security.Principal; public record UserPrincipal( String id, - String email + String email, + Long steamId ) implements Principal { @Override diff --git a/src/test/java/core_service/modules/steam_adapter/service/SteamApiServiceTest.java b/src/test/java/core_service/modules/steam_adapter/service/SteamApiServiceTest.java index 62a69b4..2c674fb 100644 --- a/src/test/java/core_service/modules/steam_adapter/service/SteamApiServiceTest.java +++ b/src/test/java/core_service/modules/steam_adapter/service/SteamApiServiceTest.java @@ -32,6 +32,27 @@ class SteamApiServiceTest { 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 void getPlayerSummary_shouldReturnPlayer() { String steamId = "76561197960435530"; @@ -89,7 +110,7 @@ class SteamApiServiceTest { List result = steamApiService.getOwnedGames(steamId); assertThat(result).hasSize(1); - assertThat(result.get(0).name()).isEqualTo("Counter-Strike"); + assertThat(result.getFirst().name()).isEqualTo("Counter-Strike"); } @Test @@ -115,9 +136,9 @@ class SteamApiServiceTest { List result = steamApiService.getOwnedGames(steamId); assertThat(result).hasSize(1); - assertThat(result.get(0).name()).isEqualTo("Counter-Strike"); - assertThat(result.get(0).playtimeForever()).isNull(); - assertThat(result.get(0).hasCommunityVisibleStats()).isNull(); + assertThat(result.getFirst().name()).isEqualTo("Counter-Strike"); + assertThat(result.getFirst().playtimeForever()).isNull(); + assertThat(result.getFirst().hasCommunityVisibleStats()).isNull(); } @Test @@ -165,6 +186,6 @@ class SteamApiServiceTest { assertThat(result).isPresent(); assertThat(result.get().player().personaname()).isEqualTo("TestUser"); 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"); } }