From 484f0d0039b1c6eb3a3cb8500713d83f4d5c5ead Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 16:56:23 +0200 Subject: [PATCH 1/6] Added DotEnvConditionChecker to detect the presence of a dotenv file and automatically create a bean accordingly. --- .../config/dotenv/DotEnvConditionChecker.java | 13 +++++++++++++ .../greencity/config/{ => dotenv}/DotenvConfig.java | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java rename service/src/main/java/greencity/config/{ => dotenv}/DotenvConfig.java (78%) diff --git a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java new file mode 100644 index 0000000000..09700d7827 --- /dev/null +++ b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java @@ -0,0 +1,13 @@ +package greencity.config.dotenv; + +import greencity.constant.AppConstant; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class DotEnvConditionChecker { + public static boolean isEnabled() { + return Files.exists(Paths.get(System.getProperty("user.dir") + File.separator + AppConstant.DOTENV_FILENAME)); + } +} diff --git a/service/src/main/java/greencity/config/DotenvConfig.java b/service/src/main/java/greencity/config/dotenv/DotenvConfig.java similarity index 78% rename from service/src/main/java/greencity/config/DotenvConfig.java rename to service/src/main/java/greencity/config/dotenv/DotenvConfig.java index 660495c512..b728ce3377 100644 --- a/service/src/main/java/greencity/config/DotenvConfig.java +++ b/service/src/main/java/greencity/config/dotenv/DotenvConfig.java @@ -1,25 +1,29 @@ -package greencity.config; +package greencity.config.dotenv; import greencity.constant.AppConstant; import greencity.constant.ErrorMessage; import greencity.exception.exceptions.FunctionalityNotAvailableException; import io.github.cdimascio.dotenv.Dotenv; import io.github.cdimascio.dotenv.DotenvException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; @Configuration @Lazy public class DotenvConfig { @Bean + @ConditionalOnMissingBean(Dotenv.class) public Dotenv fallbackDotenv() { return Dotenv.configure().ignoreIfMissing().load(); } @Bean - @ConditionalOnMissingBean(Dotenv.class) + @ConditionalOnExpression("#{T(greencity.config.dotenv.DotEnvConditionChecker).isEnabled()}") + @Primary Dotenv dotenv() { try { return Dotenv.configure() From 6ba65d631a902ef66a96a395b89d433fc929af6e Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 17:01:49 +0200 Subject: [PATCH 2/6] Fixed checkstyle --- .../java/greencity/config/dotenv/DotEnvConditionChecker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java index 09700d7827..ac469a67d1 100644 --- a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java +++ b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java @@ -1,7 +1,6 @@ package greencity.config.dotenv; import greencity.constant.AppConstant; - import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; From 40ac95b63284ebd4de2882bfb86c6568fed2bb9c Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 18:19:24 +0200 Subject: [PATCH 3/6] Added caching and tests --- .../config/dotenv/DotEnvConditionChecker.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java index ac469a67d1..5ad209a3af 100644 --- a/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java +++ b/service/src/main/java/greencity/config/dotenv/DotEnvConditionChecker.java @@ -6,7 +6,17 @@ import java.nio.file.Paths; public class DotEnvConditionChecker { + private static Boolean cachedValue = null; + public static boolean isEnabled() { - return Files.exists(Paths.get(System.getProperty("user.dir") + File.separator + AppConstant.DOTENV_FILENAME)); + if (cachedValue == null) { + try { + cachedValue = Files + .exists(Paths.get(System.getProperty("user.dir") + File.separator + AppConstant.DOTENV_FILENAME)); + } catch (SecurityException e) { + cachedValue = false; + } + } + return cachedValue; } } From fe17f744edefc6b8a7c07414da22e65e2c4e92e6 Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 18:19:45 +0200 Subject: [PATCH 4/6] Added caching and tests --- .../dotenv/DotEnvConditionCheckerTest.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java diff --git a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java new file mode 100644 index 0000000000..fdbf1fa8aa --- /dev/null +++ b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java @@ -0,0 +1,87 @@ +package greencity.utils.dotenv; + +import greencity.config.dotenv.DotEnvConditionChecker; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; + +class DotEnvConditionCheckerTest { + private static MockedStatic mockedPaths; + private static MockedStatic mockedFiles; + + @BeforeEach + void setUp() throws NoSuchFieldException, IllegalAccessException { + Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); + cachedValueField.setAccessible(true); + cachedValueField.set(null, null); + } + + @BeforeAll + public static void init() { + mockedFiles = Mockito.mockStatic(Files.class); + mockedPaths = Mockito.mockStatic(Paths.class); + } + + @AfterAll + public static void cleanup() { + mockedPaths.close(); + mockedFiles.close(); + } + + @Test + public void checkIsEnabledReturnsTrueIfPathIsValid() { + Path paths = Mockito.mock(Path.class); + + mockedPaths.when(() -> Paths.get(anyString())).thenReturn(paths); + mockedFiles.when(() -> Files.exists(paths)).thenReturn(true); + + assertTrue(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(paths), times(1)); + + } + + @Test + public void checkIsEnabledReturnsFalseIfPathNotExistsTest() { + Path paths = Mockito.mock(Path.class); + + mockedPaths.when(() -> Paths.get(anyString())).thenReturn(paths); + mockedFiles.when(() -> Files.exists(paths)).thenReturn(false); + + assertFalse(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(paths), times(1)); + } + + @Test + public void checkIsFalseIfPathIsProtectedTest() { + mockedPaths.when(() -> Paths.get(anyString())).thenThrow(SecurityException.class); + + assertFalse(DotEnvConditionChecker.isEnabled()); + mockedPaths.verify(() -> Paths.get(anyString()), times(1)); + mockedFiles.verify(() -> Files.exists(any(Path.class)), times(0)); + } + + @Test + public void checkIsNoInvocationIfAlreadyCalculatedTest() throws NoSuchFieldException, IllegalAccessException { + Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); + cachedValueField.setAccessible(true); + cachedValueField.set(null, true); + + mockedPaths.verify(() -> Paths.get(anyString()), times(0)); + mockedFiles.verify(() -> Files.exists(any(Path.class)), times(0)); + } +} From d9b34ca5ad4a348a0987d38613324a7069023948 Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 18:54:47 +0200 Subject: [PATCH 5/6] Added caching and tests --- .../java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java index fdbf1fa8aa..0a2e360cb7 100644 --- a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java +++ b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java @@ -27,6 +27,8 @@ void setUp() throws NoSuchFieldException, IllegalAccessException { Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); cachedValueField.setAccessible(true); cachedValueField.set(null, null); + mockedPaths.clearInvocations(); + mockedFiles.clearInvocations(); } @BeforeAll From fc48c5ff2091c7e896eeb45ce0408c3707486805 Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 26 Mar 2025 19:00:32 +0200 Subject: [PATCH 6/6] Fixed test --- .../greencity/utils/dotenv/DotEnvConditionCheckerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java index 0a2e360cb7..154676e3d0 100644 --- a/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java +++ b/service/src/test/java/greencity/utils/dotenv/DotEnvConditionCheckerTest.java @@ -81,8 +81,9 @@ public void checkIsFalseIfPathIsProtectedTest() { public void checkIsNoInvocationIfAlreadyCalculatedTest() throws NoSuchFieldException, IllegalAccessException { Field cachedValueField = DotEnvConditionChecker.class.getDeclaredField("cachedValue"); cachedValueField.setAccessible(true); - cachedValueField.set(null, true); + cachedValueField.set(null, false); + assertFalse(DotEnvConditionChecker.isEnabled()); mockedPaths.verify(() -> Paths.get(anyString()), times(0)); mockedFiles.verify(() -> Files.exists(any(Path.class)), times(0)); }