diff --git a/src/main/java/org/zalando/baigan/proxy/handler/ContextAwareConfigurationMethodInvocationHandler.java b/src/main/java/org/zalando/baigan/proxy/handler/ContextAwareConfigurationMethodInvocationHandler.java index 58b9421..19e18f2 100644 --- a/src/main/java/org/zalando/baigan/proxy/handler/ContextAwareConfigurationMethodInvocationHandler.java +++ b/src/main/java/org/zalando/baigan/proxy/handler/ContextAwareConfigurationMethodInvocationHandler.java @@ -11,13 +11,18 @@ import org.zalando.baigan.context.ContextProviderRetriever; import org.zalando.baigan.model.Configuration; import org.zalando.baigan.context.ContextProvider; +import org.zalando.baigan.repository.ConfigurationParser; import org.zalando.baigan.repository.ConfigurationRepository; +import javax.annotation.Nullable; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Suppliers.memoize; @@ -57,7 +62,13 @@ public void setBeanFactory(final BeanFactory beanFactory) throws BeansException @Override protected Object handleInvocation(Object proxy, Method method, Object[] args) { final String key = createKey(getClass(proxy), method); - final Object result = getConfig(key); + + final List contextProviders = Arrays.stream(args) + .filter(ContextProvider.class::isInstance) + .map(ContextProvider.class::cast) + .collect(Collectors.toList()); + + final Object result = getConfig(key, contextProviders); if (result == null) { LOG.warn("No configuration found for key [{}] in configuration source, falling back to null.", key); return null; @@ -76,7 +87,7 @@ private Class getClass(final Object proxy) { return interfaces[0]; } - private Object getConfig(final String key) { + private Object getConfig(final String key, final List contextProviders) { final Optional optional = configurationRepository.get().get(key); if (!optional.isPresent()) { @@ -95,6 +106,19 @@ private Object getConfig(final String key) { context.put(param, provider.getContextParam(param)); } + if (!CollectionUtils.isEmpty(contextProviders)) { + contextProviders.forEach(contextProvider -> { + contextProvider + .getProvidedContexts() + .forEach(contextParam -> { + if(context.containsKey(contextParam)){ + throw new RuntimeException("Cannot have more than one context provider for the same context key "+contextParam); + } + context.put(contextParam, contextProvider.getContextParam(contextParam)); + }); + }); + } + return conditionsProcessor.get().process(optional.get(), context); } diff --git a/src/test/java/org/zalando/baigan/e2e/configs/SomeConfiguration.java b/src/test/java/org/zalando/baigan/e2e/configs/SomeConfiguration.java index 24a57de..f50f6d1 100644 --- a/src/test/java/org/zalando/baigan/e2e/configs/SomeConfiguration.java +++ b/src/test/java/org/zalando/baigan/e2e/configs/SomeConfiguration.java @@ -1,6 +1,7 @@ package org.zalando.baigan.e2e.configs; import org.zalando.baigan.annotation.BaiganConfig; +import org.zalando.baigan.e2e.filerepo.CustomContextProvider; import java.util.List; import java.util.Map; diff --git a/src/test/java/org/zalando/baigan/e2e/configs/TestContextConfiguration.java b/src/test/java/org/zalando/baigan/e2e/configs/TestContextConfiguration.java new file mode 100644 index 0000000..e291f8a --- /dev/null +++ b/src/test/java/org/zalando/baigan/e2e/configs/TestContextConfiguration.java @@ -0,0 +1,15 @@ +package org.zalando.baigan.e2e.configs; + +import org.zalando.baigan.annotation.BaiganConfig; +import org.zalando.baigan.e2e.filerepo.CustomContextProvider; + +@BaiganConfig +public interface TestContextConfiguration { + + String someValue(); + + Boolean isThisTrue(CustomContextProvider customContextProvider); + + Boolean toggleFlag(CustomContextProvider customContextProvider, CustomContextProvider secondProvider); + +} \ No newline at end of file diff --git a/src/test/java/org/zalando/baigan/e2e/filerepo/CustomContextProvider.java b/src/test/java/org/zalando/baigan/e2e/filerepo/CustomContextProvider.java new file mode 100644 index 0000000..08831d2 --- /dev/null +++ b/src/test/java/org/zalando/baigan/e2e/filerepo/CustomContextProvider.java @@ -0,0 +1,27 @@ +package org.zalando.baigan.e2e.filerepo; + +import org.jetbrains.annotations.NotNull; +import org.zalando.baigan.context.ContextProvider; + +import java.util.Set; + +public class CustomContextProvider implements ContextProvider { + + private final Set PARAMS = Set.of("appdomain"); + + private final String appDomain; + + public CustomContextProvider(String appDomain) { + this.appDomain = appDomain; + } + + @Override + public String getContextParam(@NotNull final String name) { + return appDomain; + } + + @Override + public Set getProvidedContexts() { + return PARAMS; + } +} diff --git a/src/test/java/org/zalando/baigan/e2e/filerepo/FileSystemConfigurationContextProviderEnd2EndTest.java b/src/test/java/org/zalando/baigan/e2e/filerepo/FileSystemConfigurationContextProviderEnd2EndTest.java new file mode 100644 index 0000000..ad68a38 --- /dev/null +++ b/src/test/java/org/zalando/baigan/e2e/filerepo/FileSystemConfigurationContextProviderEnd2EndTest.java @@ -0,0 +1,61 @@ +package org.zalando.baigan.e2e.filerepo; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.zalando.baigan.BaiganSpringContext; +import org.zalando.baigan.annotation.ConfigurationServiceScan; +import org.zalando.baigan.e2e.configs.TestContextConfiguration; +import org.zalando.baigan.repository.FileSystemConfigurationRepository; +import org.zalando.baigan.repository.RepositoryFactory; + +import java.time.Duration; + +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {FileSystemConfigurationContextProviderEnd2EndTest.RepoConfig.class}) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class FileSystemConfigurationContextProviderEnd2EndTest { + + @Autowired + private TestContextConfiguration testContextConfiguration; + + private static final Duration CONFIG_REFRESH_INTERVAL = Duration.ofMillis(100); + + @Test + public void testConfigurationsWithMultipleContextsHavingTheSameKeyShouldFail() { + assertThrows(RuntimeException.class, () -> testContextConfiguration.toggleFlag(new CustomContextProvider("1"), new CustomContextProvider("3"))); + } + + @Test + public void testConfigurationsWithMultipleContexts() { + assertThat(testContextConfiguration.isThisTrue(new CustomContextProvider("1")), equalTo(true)); + assertThat(testContextConfiguration.someValue(), equalTo("some value")); + } + + @ConfigurationServiceScan(basePackageClasses = TestContextConfiguration.class) + @Testcontainers + @ComponentScan(basePackageClasses = {BaiganSpringContext.class}) + static class RepoConfig { + + @Bean + FileSystemConfigurationRepository configurationRepository(RepositoryFactory repositoryFactory) { + return repositoryFactory.fileSystemConfigurationRepository() + .fileName(FileSystemConfigurationContextProviderEnd2EndTest.class.getClassLoader().getResource("test-config.json").getPath()) + .refreshInterval(CONFIG_REFRESH_INTERVAL) + .objectMapper(new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false)) + .build(); + } + } +} diff --git a/src/test/resources/test-config.json b/src/test/resources/test-config.json new file mode 100644 index 0000000..661c376 --- /dev/null +++ b/src/test/resources/test-config.json @@ -0,0 +1,34 @@ +[ + { + "alias": "test.context.configuration.is.this.true", + "defaultValue": false, + "conditions": [ + { + "value": true, + "conditionType": { + "onValue": "1", + "type": "Equals" + }, + "paramName": "appdomain" + } + ] + }, + { + "alias": "test.context.configuration.some.value", + "defaultValue": "some value" + }, + { + "alias": "test.context.configuration.toggle.flag", + "defaultValue": false, + "conditions": [ + { + "value": true, + "conditionType": { + "onValue": "1", + "type": "Equals" + }, + "paramName": "appdomain" + } + ] + } +] \ No newline at end of file