diff --git a/backend/src/main/java/de/grimsi/gameyfin/GameyfinApplication.java b/backend/src/main/java/de/grimsi/gameyfin/GameyfinApplication.java index f17d349..b04a980 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/GameyfinApplication.java +++ b/backend/src/main/java/de/grimsi/gameyfin/GameyfinApplication.java @@ -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) { diff --git a/backend/src/main/java/de/grimsi/gameyfin/config/GameyfinFolderConfig.java b/backend/src/main/java/de/grimsi/gameyfin/config/GameyfinFolderConfig.java index 9ec685d..5a2a2c1 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/config/GameyfinFolderConfig.java +++ b/backend/src/main/java/de/grimsi/gameyfin/config/GameyfinFolderConfig.java @@ -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: diff --git a/backend/src/main/java/de/grimsi/gameyfin/config/properties/GameyfinProperties.java b/backend/src/main/java/de/grimsi/gameyfin/config/properties/GameyfinProperties.java new file mode 100644 index 0000000..7444993 --- /dev/null +++ b/backend/src/main/java/de/grimsi/gameyfin/config/properties/GameyfinProperties.java @@ -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 fileExtensions, + List fileSuffixes, + igdb igdb) { + + public record folders(String data) {} + + + public record igdb(config config) { + public record config(List preferredPlatforms) {} + } +} + diff --git a/backend/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java b/backend/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java index d52dca6..ef3990c 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java +++ b/backend/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java @@ -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 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 searchForGameByTitle(String searchTerm, Collection 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, diff --git a/backend/src/main/java/de/grimsi/gameyfin/util/FilenameUtil.java b/backend/src/main/java/de/grimsi/gameyfin/util/FilenameUtil.java index 308e544..6e75f29 100644 --- a/backend/src/main/java/de/grimsi/gameyfin/util/FilenameUtil.java +++ b/backend/src/main/java/de/grimsi/gameyfin/util/FilenameUtil.java @@ -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 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 possibleGameFileSuffixes) { // Sort in descending length, so for example "windows" gets checked before "win" diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 37e7822..a9cd889 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -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 \ No newline at end of file diff --git a/backend/src/test/java/de/grimsi/gameyfin/igdb/IgdbWrapperTest.java b/backend/src/test/java/de/grimsi/gameyfin/igdb/IgdbWrapperTest.java index 2c64f52..f0b84f1 100644 --- a/backend/src/test/java/de/grimsi/gameyfin/igdb/IgdbWrapperTest.java +++ b/backend/src/test/java/de/grimsi/gameyfin/igdb/IgdbWrapperTest.java @@ -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())); diff --git a/backend/src/test/java/de/grimsi/gameyfin/util/FilenameUtilTest.java b/backend/src/test/java/de/grimsi/gameyfin/util/FilenameUtilTest.java index fd6be48..e2abfab 100644 --- a/backend/src/test/java/de/grimsi/gameyfin/util/FilenameUtilTest.java +++ b/backend/src/test/java/de/grimsi/gameyfin/util/FilenameUtilTest.java @@ -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