Fixed complex property handling

This commit is contained in:
Simon Grimme
2023-10-10 23:08:06 +02:00
parent 7aeb876463
commit 3eeec47b68
8 changed files with 56 additions and 15 deletions
@@ -2,8 +2,10 @@ package de.grimsi.gameyfin;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
public class GameyfinApplication {
public static void main(String[] args) {
@@ -24,7 +24,9 @@ import java.util.stream.StreamSupport;
@RequiredArgsConstructor
public class GameyfinFolderConfig {
private static final String INTERNAL_FOLDER_NAME = ".gameyfin";
@Value("${gameyfin.internal-folder}")
private String INTERNAL_FOLDER_NAME;
/**
* The following SpEL expression will:
@@ -0,0 +1,22 @@
package de.grimsi.gameyfin.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
// see https://stackoverflow.com/questions/26699385/spring-boot-yaml-configuration-for-a-list-of-strings
@ConfigurationProperties("gameyfin")
public record GameyfinProperties(
folders folders,
List<String> fileExtensions,
List<String> fileSuffixes,
igdb igdb) {
public record folders(String data) {}
public record igdb(config config) {
public record config(List<Integer> preferredPlatforms) {}
}
}
@@ -2,6 +2,7 @@ package de.grimsi.gameyfin.igdb;
import com.igdb.proto.Igdb;
import de.grimsi.gameyfin.config.WebClientConfig;
import de.grimsi.gameyfin.config.properties.GameyfinProperties;
import de.grimsi.gameyfin.dto.AutocompleteSuggestionDto;
import de.grimsi.gameyfin.entities.Platform;
import de.grimsi.gameyfin.igdb.IgdbApiQueryBuilder.*;
@@ -38,12 +39,12 @@ public class IgdbWrapper {
private final WebClient.Builder webclientBuilder;
private final WebClientConfig webClientConfig;
private final GameMapper gameMapper;
private final GameyfinProperties gameyfinProperties;
@Value("${gameyfin.igdb.api.client-id}")
private String clientId;
@Value("${gameyfin.igdb.api.client-secret}")
private String clientSecret;
@Value("${gameyfin.igdb.config.preferred-platforms:6}")
private List<Integer> preferredPlatforms;
@Value("${gameyfin.igdb.api.endpoints.base}")
private String igdbApiBaseUrl;
@Value("${gameyfin.igdb.api.endpoints.auth}")
@@ -119,7 +120,7 @@ public class IgdbWrapper {
IgdbApiProperties.ENDPOINT_GAMES_PROTOBUF,
queryBuilder.search(searchTerm)
.fields("slug,name,first_release_date,platforms.name")
.where(in("platforms", preferredPlatforms))
.where(in("platforms", gameyfinProperties.igdb().config().preferredPlatforms()))
.limit(limit)
.build(),
Igdb.GameResult.class
@@ -137,8 +138,8 @@ public class IgdbWrapper {
public Optional<Igdb.Game> searchForGameByTitle(String searchTerm, Collection<String> platformSlugs) {
IgdbApiQueryBuilder queryBuilder = new IgdbApiQueryBuilder();
Condition platforms = isNotEmpty(platformSlugs) ?
and(in("platforms", preferredPlatforms), in("platforms.slug", platformSlugs)) :
in("platforms", preferredPlatforms);
and(in("platforms", gameyfinProperties.igdb().config().preferredPlatforms()), in("platforms.slug", platformSlugs)) :
in("platforms", gameyfinProperties.igdb().config().preferredPlatforms());
Igdb.GameResult gameResult = queryIgdbApi(
IgdbApiProperties.ENDPOINT_GAMES_PROTOBUF,
@@ -1,5 +1,6 @@
package de.grimsi.gameyfin.util;
import de.grimsi.gameyfin.config.properties.GameyfinProperties;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -20,11 +21,14 @@ public class FilenameUtil {
private static final Pattern trailingNoisePattern = Pattern.compile("( |\\(\\)|\\[\\]|[-_.])+$");
private static final Pattern headingNoisePattern = Pattern.compile("^( |\\(\\)|\\[\\]|[-_.])+");
@Value("${gameyfin.file-extensions}")
public void setPossibleGameFileExtensions(List<String> possibleGameFileExtensions) {
FilenameUtil.possibleGameFileExtensions = possibleGameFileExtensions;
public FilenameUtil(GameyfinProperties gameyfinProperties) {
possibleGameFileExtensions = gameyfinProperties.fileExtensions();
// Sort in descending length, so for example "windows" gets checked before "win"
FilenameUtil.possibleGameFileSuffixes = gameyfinProperties.fileSuffixes();
possibleGameFileSuffixes.sort((s1,s2) -> Integer.compare(s2.length(), s1.length()));
}
@Value("${gameyfin.file-suffixes}")
public void setPossibleGameFileSuffixes(List<String> possibleGameFileSuffixes) {
// Sort in descending length, so for example "windows" gets checked before "win"
@@ -6,5 +6,6 @@ logging:
level:
de.grimsi: debug
# org.springframework.web.reactive.function.client.ExchangeFunctions: debug
org.apache.catalina.core.ContainerBase: info
spring.mvc.log-request-details: true
@@ -5,6 +5,7 @@ import com.google.protobuf.Message;
import com.google.protobuf.Timestamp;
import com.igdb.proto.Igdb;
import de.grimsi.gameyfin.config.WebClientConfig;
import de.grimsi.gameyfin.config.properties.GameyfinProperties;
import de.grimsi.gameyfin.dto.AutocompleteSuggestionDto;
import de.grimsi.gameyfin.entities.Platform;
import de.grimsi.gameyfin.igdb.dto.TwitchOAuthTokenDto;
@@ -22,6 +23,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.reactive.function.client.WebClient;
@@ -52,8 +54,9 @@ class IgdbWrapperTest {
static void setup() throws IOException, InterruptedException {
WebClientConfig webClientConfigMock = mock(WebClientConfig.class);
GameMapper gameMapperMock = mock(GameMapper.class);
GameyfinProperties gameyfinPropertiesMock = mock(GameyfinProperties.class, Mockito.RETURNS_DEEP_STUBS);
target = new IgdbWrapper(WebClient.builder(), webClientConfigMock, gameMapperMock);
target = new IgdbWrapper(WebClient.builder(), webClientConfigMock, gameMapperMock, gameyfinPropertiesMock);
igdbApiMock.start();
twitchApiMock.start();
@@ -62,7 +65,8 @@ class IgdbWrapperTest {
ReflectionTestUtils.setField(target, "clientSecret", "client_secret_value");
ReflectionTestUtils.setField(target, "igdbApiBaseUrl", "http://localhost:%s".formatted(igdbApiMock.getPort()));
ReflectionTestUtils.setField(target, "twitchAuthUrl", "http://localhost:%s/oauth2/token".formatted(twitchApiMock.getPort()));
ReflectionTestUtils.setField(target, "preferredPlatforms", List.of(6));
when(gameyfinPropertiesMock.igdb().config().preferredPlatforms()).thenReturn(List.of(6));
when(webClientConfigMock.getIgdbConcurrencyLimiter()).thenReturn(Bulkhead.of("test_bulkhead", BulkheadConfig.ofDefaults()));
when(webClientConfigMock.getIgdbRateLimiter()).thenReturn(RateLimiter.of("test_ratelimiter", RateLimiterConfig.ofDefaults()));
@@ -4,6 +4,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.FileSystem;
@@ -13,6 +15,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import de.grimsi.gameyfin.config.properties.GameyfinProperties;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
@@ -24,6 +27,7 @@ import com.google.common.jimfs.Jimfs;
class FilenameUtilTest {
private static final GameyfinProperties gameyfinPropertiesMock = mock(GameyfinProperties.class);
private static final FileSystem unixFS = Jimfs.newFileSystem(Configuration.unix());
private static final FileSystem osxFS = Jimfs.newFileSystem(Configuration.osX());
private static final FileSystem winFS = Jimfs.newFileSystem(Configuration.windows());
@@ -33,9 +37,10 @@ class FilenameUtilTest {
@BeforeAll
static void init() {
FilenameUtil filenameUtil = new FilenameUtil();
filenameUtil.setPossibleGameFileExtensions(gameFileExtensions);
filenameUtil.setPossibleGameFileSuffixes(possibleGameFileSuffixes);
when(gameyfinPropertiesMock.fileExtensions()).thenReturn(gameFileExtensions);
when(gameyfinPropertiesMock.fileSuffixes()).thenReturn(possibleGameFileSuffixes);
FilenameUtil filenameUtil = new FilenameUtil(gameyfinPropertiesMock);
}
@AfterAll