diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfiguration.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfiguration.java index 8e1376a46045..fd08cc653354 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfiguration.java @@ -42,15 +42,18 @@ AppConfigurationEventListener configListener(AppConfigurationRefresh appConfigur "org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties", "org.springframework.cloud.endpoint.RefreshEndpoint" }) + @Deprecated static class AppConfigurationPushRefreshConfiguration { @Bean + @Deprecated AppConfigurationRefreshEndpoint appConfigurationRefreshEndpoint(ContextRefresher contextRefresher, AppConfigurationProperties appConfiguration) { return new AppConfigurationRefreshEndpoint(contextRefresher, appConfiguration); } @Bean + @Deprecated AppConfigurationRefreshEventListener appConfigurationRefreshEventListener( AppConfigurationRefresh appConfigurationRefresh) { return new AppConfigurationRefreshEventListener(appConfigurationRefresh); @@ -63,15 +66,18 @@ AppConfigurationRefreshEventListener appConfigurationRefreshEventListener( "org.springframework.cloud.bus.BusProperties", "org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent", "org.springframework.cloud.endpoint.RefreshEndpoint" }) + @Deprecated static class AppConfigurationBusConfiguration { @Bean + @Deprecated AppConfigurationBusRefreshEndpoint appConfigurationBusRefreshEndpoint(ApplicationContext context, BusProperties bus, AppConfigurationProperties appConfiguration, Destination.Factory destinationFactory) { return new AppConfigurationBusRefreshEndpoint(context, bus.getId(), destinationFactory, appConfiguration); } @Bean + @Deprecated AppConfigurationBusRefreshEventListener appConfigurationBusRefreshEventListener( AppConfigurationRefresh appConfigurationRefresh) { return new AppConfigurationBusRefreshEventListener(appConfigurationRefresh); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pullrefresh/AppConfigurationEventListener.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pullrefresh/AppConfigurationEventListener.java index 1b00e0f55600..8771558968af 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pullrefresh/AppConfigurationEventListener.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pullrefresh/AppConfigurationEventListener.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; import org.springframework.web.context.support.ServletRequestHandledEvent; import com.azure.spring.cloud.appconfiguration.config.AppConfigurationRefresh; @@ -32,7 +33,7 @@ public AppConfigurationEventListener(AppConfigurationRefresh appConfigurationRef } @Override - public void onApplicationEvent(ServletRequestHandledEvent event) { + public void onApplicationEvent(@NonNull ServletRequestHandledEvent event) { try { if (!(event.getRequestUrl().equals(ACTUATOR + APPCONFIGURATION_REFRESH) || event.getRequestUrl().equals(ACTUATOR + APPCONFIGURATION_REFRESH_BUS))) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushbusrefresh/AppConfigurationBusRefreshEventListener.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushbusrefresh/AppConfigurationBusRefreshEventListener.java index bf846e6418df..296f554f51ec 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushbusrefresh/AppConfigurationBusRefreshEventListener.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushbusrefresh/AppConfigurationBusRefreshEventListener.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; import com.azure.spring.cloud.appconfiguration.config.AppConfigurationRefresh; @@ -31,7 +32,7 @@ public AppConfigurationBusRefreshEventListener(AppConfigurationRefresh appConfig * @param event Event Triggering refresh, contains valid config store endpoint. */ @Override - public void onApplicationEvent(AppConfigurationBusRefreshEvent event) { + public void onApplicationEvent(@NonNull AppConfigurationBusRefreshEvent event) { try { appConfigurationRefresh.expireRefreshInterval(event.getEndpoint(), event.getSyncToken()); } catch (Exception e) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEndpoint.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEndpoint.java index 139f2a18b433..1a97678e0cd9 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEndpoint.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEndpoint.java @@ -10,11 +10,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.http.HttpStatus; +import org.springframework.lang.NonNull; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -29,8 +30,7 @@ /** * Endpoint for requesting new configurations to be loaded. */ -@SuppressWarnings("removal") -@ControllerEndpoint(id = APPCONFIGURATION_REFRESH) +@Endpoint(id = APPCONFIGURATION_REFRESH) public class AppConfigurationRefreshEndpoint implements ApplicationEventPublisherAware { private static final Logger LOGGER = LoggerFactory.getLogger(AppConfigurationRefreshEndpoint.class); @@ -105,7 +105,7 @@ public String refresh(HttpServletRequest request, HttpServletResponse response, } @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEventListener.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEventListener.java index 187946abbf02..cfaf05050bc9 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEventListener.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/main/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/pushrefresh/AppConfigurationRefreshEventListener.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; import com.azure.spring.cloud.appconfiguration.config.AppConfigurationRefresh; @@ -32,7 +33,7 @@ public AppConfigurationRefreshEventListener(AppConfigurationRefresh appConfigura * @param event Event Triggering refresh, contains valid config store endpoint. */ @Override - public void onApplicationEvent(AppConfigurationRefreshEvent event) { + public void onApplicationEvent(@NonNull AppConfigurationRefreshEvent event) { try { appConfigurationRefresh.expireRefreshInterval(event.getEndpoint(), event.getSyncToken()); } catch (Exception e) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/test/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/test/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfigurationTest.java deleted file mode 100644 index 2f861d46ad5d..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config-web/src/test/java/com/azure/spring/cloud/appconfiguration/config/web/implementation/AppConfigurationWebAutoConfigurationTest.java +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.web.implementation; - -import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.CONN_STRING_PROP; -import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.STORE_ENDPOINT_PROP; -import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.TEST_CONN_STRING; -import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.TEST_STORE_NAME; -import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestUtils.propPair; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.FilteredClassLoader; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; -import org.springframework.cloud.bus.BusProperties; -import org.springframework.cloud.bus.event.PathDestinationFactory; -import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent; -import org.springframework.cloud.endpoint.RefreshEndpoint; - -import com.azure.spring.cloud.appconfiguration.config.AppConfigurationAutoConfiguration; -import com.azure.spring.cloud.appconfiguration.config.implementation.config.AppConfigurationBootstrapConfiguration; -import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; - -public class AppConfigurationWebAutoConfigurationTest { - - private static final ApplicationContextRunner CONTEXT_RUNNER = new ApplicationContextRunner() - .withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING), - propPair(STORE_ENDPOINT_PROP, TEST_STORE_NAME)) - .withConfiguration(AutoConfigurations.of(AppConfigurationBootstrapConfiguration.class, - AppConfigurationAutoConfiguration.class, AppConfigurationWebAutoConfiguration.class, - RefreshAutoConfiguration.class, PathDestinationFactory.class, AzureGlobalPropertiesAutoConfiguration.class)) - .withUserConfiguration(BusProperties.class); - - @Test - public void refreshMissing() { - CONTEXT_RUNNER - .withClassLoader(new FilteredClassLoader(WebEndpointProperties.class)) - .run(context -> { - assertThat(context) - .doesNotHaveBean("appConfigurationRefreshBusEndpoint"); - assertThat(context) - .doesNotHaveBean("appConfigurationRefreshEndpoint"); - assertThat(context) - .hasBean("configListener"); - }); - } - - @Test - public void busRefreshMissing() { - CONTEXT_RUNNER - .withClassLoader(new FilteredClassLoader(RefreshRemoteApplicationEvent.class)) - .run(context -> { - assertThat(context) - .doesNotHaveBean("appConfigurationBusRefreshEndpoint"); - assertThat(context) - .hasBean("appConfigurationRefreshEndpoint"); - assertThat(context) - .hasBean("configListener"); - }); - } - - @Test - public void pullRefreshListenerMissing() { - CONTEXT_RUNNER.withClassLoader(new FilteredClassLoader(RefreshEndpoint.class)) - .run(context -> assertThat(context) - .doesNotHaveBean("configListener")); - } - - @Test - public void pushRefresh() { - CONTEXT_RUNNER - .run(context -> { - assertThat(context) - .hasBean("appConfigurationRefreshEndpoint"); - }); - } - - @Test - public void busRefresh() { - CONTEXT_RUNNER - .run(context -> assertThat(context) - .hasBean("appConfigurationBusRefreshEndpoint")); - } - - @Test - public void fullRefresh() { - CONTEXT_RUNNER - .run(context -> { - assertThat(context) - .hasBean("configListener"); - assertThat(context) - .hasBean("appConfigurationRefreshEndpoint"); - assertThat(context) - .hasBean("appConfigurationBusRefreshEndpoint"); - }); - } -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml index ca003da589e4..b04d37285fc6 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml @@ -29,11 +29,6 @@ 3.4.1 true - - org.springframework.cloud - spring-cloud-starter-bootstrap - 4.2.0 - org.springframework.cloud spring-cloud-context @@ -45,6 +40,11 @@ 3.4.1 compile + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + com.azure @@ -177,13 +177,14 @@ - com.fasterxml.jackson.core:jackson-annotations:[2.18.2] - com.fasterxml.jackson.core:jackson-databind:[2.18.2] + jakarta.annotation:jakarta.annotation-api:[3.0.0] + com.fasterxml.jackson.core:jackson-annotations:[2.17.2] + com.fasterxml.jackson.core:jackson-databind:[2.17.2] org.springframework.boot:spring-boot-actuator:[3.4.1] org.springframework.boot:spring-boot-autoconfigure:[3.4.1] org.springframework.cloud:spring-cloud-context:[4.2.0] org.springframework.boot:spring-boot-configuration-processor:[3.4.1] - org.springframework.cloud:spring-cloud-starter-bootstrap:[4.2.0] + org.springframework.cloud:spring-cloud-starter-bootstrap:[4.1.4] diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationAutoConfiguration.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationWatchAutoConfiguration.java similarity index 55% rename from sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationAutoConfiguration.java rename to sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationWatchAutoConfiguration.java index dde0e852d65a..d2d7262e439e 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/AppConfigurationWatchAutoConfiguration.java @@ -2,12 +2,14 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config; +import org.springframework.boot.BootstrapContext; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.endpoint.RefreshEndpoint; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationPullRefresh; @@ -20,36 +22,28 @@ /** * Setup AppConfigurationRefresh when spring.cloud.azure.appconfiguration.enabled is enabled. */ -@Configuration @EnableAsync @ConditionalOnProperty(prefix = AppConfigurationProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true) -public class AppConfigurationAutoConfiguration { +@EnableConfigurationProperties({ AppConfigurationProperties.class, AppConfigurationProviderProperties.class }) +@AutoConfiguration +@ConditionalOnClass(RefreshEndpoint.class) +public class AppConfigurationWatchAutoConfiguration { /** - * Creates an instance of {@link AppConfigurationAutoConfiguration} + * Creates an instance of {@link AppConfigurationWatchAutoConfiguration} */ - public AppConfigurationAutoConfiguration() { + public AppConfigurationWatchAutoConfiguration() { } - /** - * Auto Watch - */ - @Configuration - @ConditionalOnClass(RefreshEndpoint.class) - public static class AppConfigurationWatchAutoConfiguration { - - /** - * Creates an instance of {@link AppConfigurationWatchAutoConfiguration} - */ - public AppConfigurationWatchAutoConfiguration() { - } + @Bean + @ConditionalOnMissingBean + AppConfigurationRefresh appConfigurationRefresh(AppConfigurationProperties properties, + AppConfigurationProviderProperties appProperties, BootstrapContext context) { + AppConfigurationReplicaClientFactory clientFactory = context + .get(AppConfigurationReplicaClientFactory.class); + ReplicaLookUp replicaLookUp = context.get(ReplicaLookUp.class); - @Bean - @ConditionalOnMissingBean - AppConfigurationRefresh appConfigurationRefresh(AppConfigurationProperties properties, - AppConfigurationProviderProperties appProperties, AppConfigurationReplicaClientFactory clientFactory, ReplicaLookUp replicaLookUp) { - return new AppConfigurationPullRefresh(clientFactory, properties.getRefreshInterval(), - appProperties.getDefaultMinBackoff(), replicaLookUp, new AppConfigurationRefreshUtil()); - } + return new AppConfigurationPullRefresh(clientFactory, properties.getRefreshInterval(), + appProperties.getDefaultMinBackoff(), replicaLookUp, new AppConfigurationRefreshUtil()); } } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySource.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySource.java index 5d410ab33151..f7c2a6c90f8b 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySource.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySource.java @@ -107,7 +107,7 @@ protected void processConfigurationSettings(List settings, * @return Key Vault Secret Value * @throws InvalidConfigurationPropertyValueException */ - protected void handleKeyVaultReference(String key, SecretReferenceConfigurationSetting secretReference) + private void handleKeyVaultReference(String key, SecretReferenceConfigurationSetting secretReference) throws InvalidConfigurationPropertyValueException { // Parsing Key Vault Reference for URI try { @@ -129,7 +129,7 @@ void handleFeatureFlag(String key, FeatureFlagConfigurationSetting setting, List handleJson(setting, trimStrings); } - void handleJson(ConfigurationSetting setting, List keyPrefixTrimValues) + private void handleJson(ConfigurationSetting setting, List keyPrefixTrimValues) throws InvalidConfigurationPropertyValueException { Map jsonSettings = JsonConfigurationParser.parseJsonSetting(setting); for (Entry jsonSetting : jsonSettings.entrySet()) { @@ -138,7 +138,7 @@ void handleJson(ConfigurationSetting setting, List keyPrefixTrimValues) } } - protected String trimKey(String key, List trimStrings) { + private String trimKey(String key, List trimStrings) { key = key.trim(); if (trimStrings != null) { for (String trim : trimStrings) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationConstants.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationConstants.java index 44a8db8df3cc..2daacc4bfd41 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationConstants.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationConstants.java @@ -12,16 +12,6 @@ public class AppConfigurationConstants { */ public static final String FEATURE_FLAG_CONTENT_TYPE = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; - /** - * App Configurations Key Vault Reference Content Type - */ - public static final String KEY_VAULT_CONTENT_TYPE = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; - - /** - * Feature Management Key Prefix - */ - public static final String FEATURE_MANAGEMENT_KEY = "feature-management."; - /** * Feature Flag Prefix */ @@ -47,16 +37,11 @@ public class AppConfigurationConstants { */ public static final String KEY_VAULT_CONFIGURED_TRACING = "UsesKeyVault"; - /** - * Constant for tracing for Replica Count - */ - public static final String REPLICA_COUNT = "ReplicaCount"; - /** * Http Header User Agent */ public static final String USER_AGENT_TYPE = "User-Agent"; - + /** * Http Header Correlation Context */ @@ -71,28 +56,10 @@ public class AppConfigurationConstants { public static final String TELEMETRY = "telemetry"; - public static final String USERS = "users"; - - public static final String USERS_CAPS = "Users"; - - public static final String AUDIENCE = "Audience"; - - public static final String GROUPS = "groups"; - - public static final String GROUPS_CAPS = "Groups"; - - public static final String TARGETING_FILTER = "targetingFilter"; - - public static final String DEFAULT_ROLLOUT_PERCENTAGE = "defaultRolloutPercentage"; - - public static final String DEFAULT_ROLLOUT_PERCENTAGE_CAPS = "DefaultRolloutPercentage"; - public static final String DEFAULT_REQUIREMENT_TYPE = "Any"; public static final String REQUIREMENT_TYPE_SERVICE = "requirement_type"; - public static final String REQUIREMENT_TYPE = "requirement-type"; - public static final String FEATURE_FLAG_ID = "FeatureFlagId"; public static final String FEATURE_FLAG_REFERENCE = "FeatureFlagReference"; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationKeyVaultClientFactory.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationKeyVaultClientFactory.java index 2cc9fbffe363..6a27f783285f 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationKeyVaultClientFactory.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationKeyVaultClientFactory.java @@ -10,7 +10,7 @@ import com.azure.spring.cloud.appconfiguration.config.implementation.stores.AppConfigurationSecretClientManager; import com.azure.spring.cloud.service.implementation.keyvault.secrets.SecretClientBuilderFactory; -public class AppConfigurationKeyVaultClientFactory { +class AppConfigurationKeyVaultClientFactory { private final Map keyVaultClients; @@ -26,7 +26,7 @@ public class AppConfigurationKeyVaultClientFactory { private final int timeout; - public AppConfigurationKeyVaultClientFactory(SecretClientCustomizer keyVaultClientProvider, + AppConfigurationKeyVaultClientFactory(SecretClientCustomizer keyVaultClientProvider, KeyVaultSecretProvider keyVaultSecretProvider, SecretClientBuilderFactory secretClientFactory, boolean credentialsConfigured, int timeout) { this.keyVaultClientProvider = keyVaultClientProvider; @@ -38,7 +38,7 @@ public AppConfigurationKeyVaultClientFactory(SecretClientCustomizer keyVaultClie this.timeout = timeout; } - public AppConfigurationSecretClientManager getClient(String host) { + AppConfigurationSecretClientManager getClient(String host) { // Check if we already have a client for this key vault, if not we will make // one if (!keyVaultClients.containsKey(host)) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySource.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySource.java index c31069689625..3a3c84907e22 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySource.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySource.java @@ -2,7 +2,6 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation; -import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -12,7 +11,6 @@ import org.springframework.core.env.EnumerablePropertySource; import com.azure.data.appconfiguration.ConfigurationClient; -import com.azure.data.appconfiguration.models.ConfigurationSetting; /** * Azure App Configuration PropertySource unique per Store Label(Profile) combo. @@ -26,8 +24,6 @@ abstract class AppConfigurationPropertySource extends EnumerablePropertySource properties = new LinkedHashMap<>(); - protected final List featureConfigurationSettings = new ArrayList<>(); - protected final AppConfigurationReplicaClient replicaClient; AppConfigurationPropertySource(String name, AppConfigurationReplicaClient replicaClient) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocator.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocator.java deleted file mode 100644 index 04de646b0631..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocator.java +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation; - -import static org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME; - -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.cloud.bootstrap.config.PropertySourceLocator; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.env.PropertySource; -import org.springframework.util.StringUtils; - -import com.azure.data.appconfiguration.models.ConfigurationSetting; -import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; -import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationKeyValueSelector; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagKeyValueSelector; - -/** - * Locates Azure App Configuration Property Sources. - */ -public final class AppConfigurationPropertySourceLocator implements PropertySourceLocator { - - private static final Logger LOGGER = LoggerFactory.getLogger(AppConfigurationPropertySourceLocator.class); - - private static final String PROPERTY_SOURCE_NAME = "azure-config-store"; - - private static final String REFRESH_ARGS_PROPERTY_SOURCE = "refreshArgs"; - - private final List configStores; - - private final AppConfigurationProviderProperties appProperties; - - private final AppConfigurationReplicaClientFactory clientFactory; - - private final AppConfigurationKeyVaultClientFactory keyVaultClientFactory; - - private final FeatureFlagClient featureFlagClient; - - private final ReplicaLookUp replicaLookUp; - - private Duration refreshInterval; - - static final AtomicBoolean STARTUP = new AtomicBoolean(true); - - /** - * Loads all Azure App Configuration Property Sources configured. - * - * @param properties Configurations for stores to be loaded. - * @param appProperties Configurations for the library. - * @param clientFactory factory for creating clients for connecting to Azure App Configuration. - * @param keyVaultClientFactory factory for creating clients for connecting to Azure Key Vault - * @param featureFlagLoader service for loadingFeatureFlags. - */ - public AppConfigurationPropertySourceLocator(AppConfigurationProviderProperties appProperties, - AppConfigurationReplicaClientFactory clientFactory, AppConfigurationKeyVaultClientFactory keyVaultClientFactory, - Duration refreshInterval, List configStores, ReplicaLookUp replicaLookUp, - FeatureFlagClient featureFlagLoader) { - this.refreshInterval = refreshInterval; - this.appProperties = appProperties; - this.configStores = configStores; - this.clientFactory = clientFactory; - this.keyVaultClientFactory = keyVaultClientFactory; - this.replicaLookUp = replicaLookUp; - this.featureFlagClient = featureFlagLoader; - - BackoffTimeCalculator.setDefaults(appProperties.getDefaultMaxBackoff(), appProperties.getDefaultMinBackoff()); - } - - @Override - public PropertySource locate(Environment environment) { - if (!(environment instanceof ConfigurableEnvironment)) { - return null; - } - replicaLookUp.updateAutoFailoverEndpoints(); - - ConfigurableEnvironment env = (ConfigurableEnvironment) environment; - boolean currentlyLoaded = env.getPropertySources().stream().anyMatch(source -> { - String storeName = configStores.get(0).getEndpoint(); - if (configStores.get(0).getSelects().size() == 0) { - return false; - } - AppConfigurationKeyValueSelector selectedKey = configStores.get(0).getSelects().get(0); - return source.getName() - .startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME + "-" + selectedKey.getKeyFilter() + storeName + "/"); - }); - if (currentlyLoaded && !env.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) { - return null; - } - - List profiles = Arrays.asList(env.getActiveProfiles()); - - CompositePropertySource composite = new CompositePropertySource(PROPERTY_SOURCE_NAME); - Collections.reverse(configStores); // Last store has the highest precedence - - StateHolder newState = new StateHolder(); - newState.setNextForcedRefresh(refreshInterval); - - // Feature Management needs to be set in the last config store. - for (ConfigStore configStore : configStores) { - boolean loadNewPropertySources = STARTUP.get() || StateHolder.getLoadState(configStore.getEndpoint()); - - if (configStore.isEnabled() && loadNewPropertySources) { - // There is only one Feature Set for all AppConfigurationPropertySources - - List clients = clientFactory - .getAvailableClients(configStore.getEndpoint(), true); - - boolean generatedPropertySources = false; - - List sourceList = new ArrayList<>(); - boolean reloadFailed = false; - - for (AppConfigurationReplicaClient client : clients) { - sourceList = new ArrayList<>(); - - if (!STARTUP.get() && reloadFailed && !AppConfigurationRefreshUtil - .checkStoreAfterRefreshFailed(client, clientFactory, configStore.getFeatureFlags())) { - // This store doesn't have any changes where to refresh store did. Skipping Checking next. - continue; - } - - // Reverse in order to add Profile specific properties earlier, and last profile comes first - try { - List sources = createSettings(client, configStore, profiles); - List featureFlags = createFeatureFlags(client, configStore, profiles); - sourceList.addAll(sources); - - LOGGER.debug("PropertySource context."); - setupMonitoring(configStore, client, sources, newState, featureFlags); - - generatedPropertySources = true; - } catch (AppConfigurationStatusException e) { - reloadFailed = true; - clientFactory.backoffClientClient(configStore.getEndpoint(), client.getEndpoint()); - } catch (Exception e) { - newState = failedToGeneratePropertySource(configStore, newState, e); - - // Not a retiable error - break; - } - if (generatedPropertySources) { - break; - } - } - - if (generatedPropertySources) { - // Updating list of propertySources - sourceList.forEach(composite::addPropertySource); - } else if (!STARTUP.get() || (configStore.isFailFast() && STARTUP.get())) { - String message = "Failed to generate property sources for " + configStore.getEndpoint(); - - // Refresh failed for a config store ending attempt - failedToGeneratePropertySource(configStore, newState, new RuntimeException(message)); - } - - if (featureFlagClient.getProperties().size() > 0) { - // This can be true if feature flags are enabled or if a Snapshot contained feature flags - AppConfigurationFeatureManagementPropertySource acfmps = new AppConfigurationFeatureManagementPropertySource( - featureFlagClient); - composite.addPropertySource(acfmps); - } - - } else if (!configStore.isEnabled() && loadNewPropertySources) { - LOGGER.info("Not loading configurations from {} as it is not enabled.", configStore.getEndpoint()); - } else { - LOGGER.warn("Not loading configurations from {} as it failed on startup.", configStore.getEndpoint()); - } - } - - StateHolder.updateState(newState); - STARTUP.set(false); - - return composite; - } - - private void setupMonitoring(ConfigStore configStore, AppConfigurationReplicaClient client, - List sources, StateHolder newState, List featureFlags) { - AppConfigurationStoreMonitoring monitoring = configStore.getMonitoring(); - - if (configStore.getFeatureFlags().getEnabled()) { - newState.setStateFeatureFlag(configStore.getEndpoint(), featureFlags, - monitoring.getFeatureFlagRefreshInterval()); - } - - if (monitoring.isEnabled()) { - // Setting new ETag values for Watch - List watchKeysSettings = monitoring.getTriggers().stream() - .map(trigger -> client.getWatchKey(trigger.getKey(), trigger.getLabel(), !STARTUP.get())).toList(); - - newState.setState(configStore.getEndpoint(), watchKeysSettings, monitoring.getRefreshInterval()); - } - newState.setLoadState(configStore.getEndpoint(), true, configStore.isFailFast()); - } - - private StateHolder failedToGeneratePropertySource(ConfigStore configStore, StateHolder newState, Exception e) { - String message = "Failed to generate property sources for " + configStore.getEndpoint(); - if (!STARTUP.get()) { - // Need to check for refresh first, or reset will never happen if fail fast is true. - LOGGER.error("Refreshing failed while reading configuration from Azure App Configuration store " - + configStore.getEndpoint() + "."); - - if (refreshInterval != null) { - // The next refresh will happen sooner if refresh interval is expired. - newState.updateNextRefreshTime(refreshInterval, appProperties.getDefaultMinBackoff()); - } - throw new RuntimeException(message, e); - } else if (configStore.isFailFast()) { - LOGGER.error("Fail fast is set and there was an error reading configuration from Azure App " - + "Configuration store " + configStore.getEndpoint() + "."); - delayException(); - throw new RuntimeException(message, e); - } else { - LOGGER.warn( - "Unable to load configuration from Azure AppConfiguration store " + configStore.getEndpoint() + ".", e); - newState.setLoadState(configStore.getEndpoint(), false, configStore.isFailFast()); - } - return newState; - } - - /** - * Creates a new set of AppConfigurationPropertySources, 1 per Label. - * - * @param client client for connecting to App Configuration - * @param store Config Store the PropertySource is being generated from - * @param profiles active profiles to be used as labels. it needs to be in the last one. - * @return a list of AppConfigurationPropertySources - * @throws Exception creating a property source failed - */ - private List createSettings(AppConfigurationReplicaClient client, ConfigStore store, - List profiles) throws Exception { - List sourceList = new ArrayList<>(); - List selects = store.getSelects(); - - for (AppConfigurationKeyValueSelector selectedKeys : selects) { - AppConfigurationPropertySource propertySource = null; - - if (StringUtils.hasText(selectedKeys.getSnapshotName())) { - propertySource = new AppConfigurationSnapshotPropertySource( - selectedKeys.getSnapshotName() + "/" + store.getEndpoint(), client, keyVaultClientFactory, - selectedKeys.getSnapshotName(), featureFlagClient); - } else { - propertySource = new AppConfigurationApplicationSettingPropertySource( - selectedKeys.getKeyFilter() + store.getEndpoint() + "/", client, keyVaultClientFactory, - selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles)); - } - propertySource.initProperties(store.getTrimKeyPrefix(), !STARTUP.get()); - sourceList.add(propertySource); - - } - - return sourceList; - } - - /** - * Creates a new set of AppConfigurationPropertySources, 1 per Label. - * - * @param client client for connecting to App Configuration - * @param store Config Store the PropertySource is being generated from - * @param profiles active profiles to be used as labels. it needs to be in the last one. - * @return a list of AppConfigurationPropertySources - * @throws Exception creating a property source failed - */ - private List createFeatureFlags(AppConfigurationReplicaClient client, ConfigStore store, - List profiles) throws Exception { - List featureFlagWatchKeys = new ArrayList<>(); - if (store.getFeatureFlags().getEnabled()) { - for (FeatureFlagKeyValueSelector selectedKeys : store.getFeatureFlags().getSelects()) { - List storesFeatureFlags = featureFlagClient.loadFeatureFlags(client, - selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles), !STARTUP.get()); - storesFeatureFlags.forEach(featureFlags -> featureFlags.setConfigStore(store)); - featureFlagWatchKeys.addAll(storesFeatureFlags); - } - } - return featureFlagWatchKeys; - } - - private void delayException() { - Instant currentDate = Instant.now(); - Instant preKillTIme = appProperties.getStartDate().plusSeconds(appProperties.getPrekillTime()); - if (currentDate.isBefore(preKillTIme)) { - long diffInMillies = Math.abs(preKillTIme.toEpochMilli() - currentDate.toEpochMilli()); - try { - Thread.sleep(diffInMillies); - } catch (InterruptedException e) { - LOGGER.error("Failed to wait before fast fail."); - } - } - } -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtil.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtil.java index ca1cc33c3905..94c1b9c8550f 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtil.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtil.java @@ -108,12 +108,6 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF return eventData; } - static boolean checkStoreAfterRefreshFailed(AppConfigurationReplicaClient client, - AppConfigurationReplicaClientFactory clientFactory, FeatureFlagStore featureStore) { - return refreshStoreCheck(client, clientFactory.findOriginForEndpoint(client.getEndpoint())) - || refreshStoreFeatureFlagCheck(featureStore, client); - } - /** * This is for a refresh fail only. * @@ -121,7 +115,7 @@ static boolean checkStoreAfterRefreshFailed(AppConfigurationReplicaClient client * @param originEndpoint config store origin endpoint * @return A refresh should be triggered. */ - private static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint) { + static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint) { RefreshEventData eventData = new RefreshEventData(); if (StateHolder.getLoadState(originEndpoint)) { refreshWithoutTime(client, StateHolder.getState(originEndpoint).getWatchKeys(), eventData); @@ -136,12 +130,12 @@ private static boolean refreshStoreCheck(AppConfigurationReplicaClient client, S * @param client Client checking for refresh * @return true if a refresh should be triggered. */ - private static boolean refreshStoreFeatureFlagCheck(FeatureFlagStore featureStore, + static boolean refreshStoreFeatureFlagCheck(Boolean featureStoreEnabled, AppConfigurationReplicaClient client) { RefreshEventData eventData = new RefreshEventData(); String endpoint = client.getEndpoint(); - if (featureStore.getEnabled() && StateHolder.getStateFeatureFlag(endpoint) != null) { + if (featureStoreEnabled && StateHolder.getStateFeatureFlag(endpoint) != null) { refreshWithoutTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(endpoint), eventData); } else { LOGGER.debug("Skipping feature flag refresh check for " + endpoint); @@ -197,7 +191,6 @@ private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient cl replicaLookUp.updateAutoFailoverEndpoints(); for (FeatureFlags featureFlags : state.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { String eventDataInfo = ".appconfig.featureflag/*"; @@ -219,7 +212,6 @@ private static void refreshWithoutTimeFeatureFlags(AppConfigurationReplicaClient RefreshEventData eventData) throws AppConfigurationStatusException { for (FeatureFlags featureFlags : watchKeys.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { String eventDataInfo = ".appconfig.featureflag/*"; @@ -273,7 +265,7 @@ RefreshEventData setMessage(String prefix) { return this; } - RefreshEventData setFullMessage(String message) { + private RefreshEventData setFullMessage(String message) { this.message = message; this.doRefresh = true; return this; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClient.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClient.java index 9c43522cafcb..5eea371940c2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClient.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClient.java @@ -22,7 +22,6 @@ import com.azure.data.appconfiguration.models.SettingSelector; import com.azure.data.appconfiguration.models.SnapshotComposition; import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; -import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.TracingInfo; import io.netty.handler.codec.http.HttpResponseStatus; @@ -39,19 +38,16 @@ class AppConfigurationReplicaClient { private int failedAttempts; - private final TracingInfo tracingInfo; - /** * Holds Configuration Client and info needed to manage backoff. * @param endpoint client endpoint * @param client Configuration Client to App Configuration store */ - AppConfigurationReplicaClient(String endpoint, ConfigurationClient client, TracingInfo tracingInfo) { + AppConfigurationReplicaClient(String endpoint, ConfigurationClient client) { this.endpoint = endpoint; this.client = client; this.backoffEndTime = Instant.now().minusMillis(1); this.failedAttempts = 0; - this.tracingInfo = tracingInfo; } /** @@ -92,7 +88,7 @@ String getEndpoint() { * @param label String value of the watch key, use \0 for null. * @return The first returned configuration. */ - ConfigurationSetting getWatchKey(String key, String label, Boolean isRefresh) + ConfigurationSetting getWatchKey(String key, String label, boolean isRefresh) throws HttpResponseException { try { Context context = new Context("refresh", isRefresh); @@ -115,7 +111,7 @@ ConfigurationSetting getWatchKey(String key, String label, Boolean isRefresh) * @param settingSelector Information on which setting to pull. i.e. number of results, key value... * @return List of Configuration Settings. */ - List listSettings(SettingSelector settingSelector, Boolean isRefresh) + List listSettings(SettingSelector settingSelector, boolean isRefresh) throws HttpResponseException { List configurationSettings = new ArrayList<>(); try { @@ -134,7 +130,7 @@ List listSettings(SettingSelector settingSelector, Boolean } } - FeatureFlags listFeatureFlags(SettingSelector settingSelector, Boolean isRefresh) throws HttpResponseException { + FeatureFlags listFeatureFlags(SettingSelector settingSelector, boolean isRefresh) throws HttpResponseException { List configurationSettings = new ArrayList<>(); List checks = new ArrayList<>(); try { @@ -159,10 +155,13 @@ FeatureFlags listFeatureFlags(SettingSelector settingSelector, Boolean isRefresh } } - List listSettingSnapshot(String snapshotName) { + List listSettingSnapshot(String snapshotName, boolean isRefresh) { List configurationSettings = new ArrayList<>(); try { - ConfigurationSnapshot snapshot = client.getSnapshot(snapshotName); + // Because Spring always refreshes all we still have to load snapshots on refresh to build the property + // sources. + Context context = new Context("refresh", isRefresh); + ConfigurationSnapshot snapshot = client.getSnapshotWithResponse(snapshotName, null, context).getValue(); if (!SnapshotComposition.KEY.equals(snapshot.getSnapshotComposition())) { throw new IllegalArgumentException("Snapshot " + snapshotName + " needs to be of type Key."); } @@ -178,8 +177,8 @@ List listSettingSnapshot(String snapshotName) { } } - Boolean checkWatchKeys(SettingSelector settingSelector, Boolean isRefresh) { - Context context = new Context("refresh", isRefresh); + boolean checkWatchKeys(SettingSelector settingSelector, boolean isRefresh) { + Context context = new Context("refresh", false); List> results = client.listConfigurationSettings(settingSelector, context) .streamByPage().filter(pagedResponse -> pagedResponse.getStatusCode() != 304).toList(); return results.size() > 0; @@ -208,8 +207,4 @@ private HttpResponseException hanndleHttpResponseException(HttpResponseException return e; } - TracingInfo getTracingInfo() { - return tracingInfo; - } - } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactory.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactory.java index 4f3fd987cc2f..1aef5729e617 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactory.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactory.java @@ -25,7 +25,7 @@ public class AppConfigurationReplicaClientFactory { * @param clientBuilder builder for app configuration replica clients * @param configStores configuration info for config stores */ - public AppConfigurationReplicaClientFactory(AppConfigurationReplicaClientsBuilder clientBuilder, + AppConfigurationReplicaClientFactory(AppConfigurationReplicaClientsBuilder clientBuilder, List configStores, ReplicaLookUp replicaLookUp) { this.configStores = configStores; if (CONNECTIONS.size() == 0) { @@ -99,23 +99,6 @@ String findOriginForEndpoint(String endpoint) { return endpoint; } - /** - * Checks if a given endpoint has any configured replicas. - * @param endpoint Endpoint to check for replicas - * @return true if at least one other unique endpoint connects to the same configuration store - */ - boolean hasReplicas(String endpoint) { - String originEndpoint = findOriginForEndpoint(endpoint); - for (ConfigStore store : configStores) { - if (store.getEndpoint().equals(originEndpoint)) { - if (store.getConnectionStrings().size() > 0 || store.getEndpoints().size() > 0) { - return true; - } - } - } - return false; - } - /** * Sets the replica as the currently used endpoint for connecting to the config store. * @param originEndpoint Origin Configuration Store diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientsBuilder.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientsBuilder.java index 3e225980e1fd..4f28d5297f28 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientsBuilder.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientsBuilder.java @@ -14,8 +14,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.convert.DurationStyle; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -25,7 +23,8 @@ import com.azure.core.util.Configuration; import com.azure.core.util.CoreUtils; import com.azure.data.appconfiguration.ConfigurationClientBuilder; -import com.azure.identity.ManagedIdentityCredentialBuilder; +import com.azure.identity.DefaultAzureCredential; +import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.spring.cloud.appconfiguration.config.ConfigurationClientCustomizer; import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.BaseAppConfigurationPolicy; import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.TracingInfo; @@ -37,22 +36,22 @@ import com.azure.spring.cloud.core.service.AzureServiceType.AppConfiguration; import com.azure.spring.cloud.service.implementation.appconfiguration.ConfigurationClientBuilderFactory; -public class AppConfigurationReplicaClientsBuilder implements EnvironmentAware { +public class AppConfigurationReplicaClientsBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(AppConfigurationReplicaClientsBuilder.class); /** * Invalid Connection String error message */ - public static final String NON_EMPTY_MSG = "%s property should not be null or empty in the connection string of Azure Config Service."; + private static final String NON_EMPTY_MSG = "%s property should not be null or empty in the connection string of Azure Config Service."; - public static final String RETRY_MODE_PROPERTY_NAME = "retry.mode"; + private static final String RETRY_MODE_PROPERTY_NAME = "retry.mode"; - public static final String MAX_RETRIES_PROPERTY_NAME = "retry.exponential.max-retries"; + private static final String MAX_RETRIES_PROPERTY_NAME = "retry.exponential.max-retries"; - public static final String BASE_DELAY_PROPERTY_NAME = "retry.exponential.base-delay"; + private static final String BASE_DELAY_PROPERTY_NAME = "retry.exponential.base-delay"; - public static final String MAX_DELAY_PROPERTY_NAME = "retry.exponential.max-delay"; + private static final String MAX_DELAY_PROPERTY_NAME = "retry.exponential.max-delay"; private static final Duration DEFAULT_MIN_RETRY_POLICY = Duration.ofMillis(800); @@ -66,30 +65,28 @@ public class AppConfigurationReplicaClientsBuilder implements EnvironmentAware { /** * Invalid Formatted Connection String Error message */ - public static final String ENDPOINT_ERR_MSG = String.format("Connection string does not follow format %s.", + private static final String ENDPOINT_ERR_MSG = String.format("Connection string does not follow format %s.", CONN_STRING_REGEXP); private static final Pattern CONN_STRING_PATTERN = Pattern.compile(CONN_STRING_REGEXP); - private ConfigurationClientCustomizer clientProvider; + private ConfigurationClientCustomizer clientCustomizer; private final ConfigurationClientBuilderFactory clientFactory; - private Environment env; - - private boolean isDev = false; - - private boolean isKeyVaultConfigured = false; + private boolean isKeyVaultConfigured; private final boolean credentialConfigured; private final int defaultMaxRetries; - public AppConfigurationReplicaClientsBuilder(int defaultMaxRetries, ConfigurationClientBuilderFactory clientFactory, - boolean credentialConfigured) { + AppConfigurationReplicaClientsBuilder(int defaultMaxRetries, ConfigurationClientBuilderFactory clientFactory, + ConfigurationClientCustomizer clientCustomizer, boolean credentialConfigured, boolean isKeyVaultConfigured) { this.defaultMaxRetries = defaultMaxRetries; - this.clientFactory = clientFactory; this.credentialConfigured = credentialConfigured; + this.clientFactory = clientFactory; + this.clientCustomizer = clientCustomizer; + this.isKeyVaultConfigured = isKeyVaultConfigured; } /** @@ -114,17 +111,6 @@ public static String getEndpointFromConnectionString(String connectionString) { return endpoint; } - /** - * @param clientProvider the clientProvider to set - */ - public void setClientProvider(ConfigurationClientCustomizer clientProvider) { - this.clientProvider = clientProvider; - } - - public void setIsKeyVaultConfigured(boolean isKeyVaultConfigured) { - this.isKeyVaultConfigured = isKeyVaultConfigured; - } - /** * Builds all the clients for a connection. * @@ -172,13 +158,11 @@ List buildClients(ConfigStore configStore) { clients.add(modifyAndBuildClient(builder, endpoint, connectionStrings.size() - 1)); } } else { + DefaultAzureCredential defautAzureCredential = new DefaultAzureCredentialBuilder().build(); for (String endpoint : endpoints) { ConfigurationClientBuilder builder = this.createBuilderInstance(); if (!credentialConfigured) { - // System Assigned Identity. Needs to be checked last as all of the above should - // have an Endpoint. - LOGGER.debug("Connecting to {} using Azure System Assigned Identity.", endpoint); - builder.credential(new ManagedIdentityCredentialBuilder().build()); + builder.credential(defautAzureCredential); } builder.endpoint(endpoint); @@ -189,7 +173,7 @@ List buildClients(ConfigStore configStore) { return clients; } - public AppConfigurationReplicaClient buildClient(String failoverEndpoint, ConfigStore configStore) { + AppConfigurationReplicaClient buildClient(String failoverEndpoint, ConfigStore configStore) { if (StringUtils.hasText(configStore.getConnectionString())) { ConnectionString connectionString = new ConnectionString(configStore.getConnectionString()); @@ -204,10 +188,7 @@ public AppConfigurationReplicaClient buildClient(String failoverEndpoint, Config } else { ConfigurationClientBuilder builder = createBuilderInstance(); if (!credentialConfigured) { - // System Assigned Identity. Needs to be checked last as all of the above should - // have an Endpoint. - LOGGER.debug("Connecting to {} using Azure System Assigned Identity.", failoverEndpoint); - builder.credential(new ManagedIdentityCredentialBuilder().build()); + builder.credential(new DefaultAzureCredentialBuilder().build()); } builder.endpoint(failoverEndpoint); return modifyAndBuildClient(builder, failoverEndpoint, 0); @@ -216,32 +197,22 @@ public AppConfigurationReplicaClient buildClient(String failoverEndpoint, Config private AppConfigurationReplicaClient modifyAndBuildClient(ConfigurationClientBuilder builder, String endpoint, Integer replicaCount) { - TracingInfo tracingInfo = new TracingInfo(isDev, isKeyVaultConfigured, replicaCount, + TracingInfo tracingInfo = new TracingInfo(isKeyVaultConfigured, replicaCount, Configuration.getGlobalConfiguration()); builder.addPolicy(new BaseAppConfigurationPolicy(tracingInfo)); - if (clientProvider != null) { - clientProvider.customize(builder, endpoint); - } - return new AppConfigurationReplicaClient(endpoint, builder.buildClient(), tracingInfo); - } - - @Override - public void setEnvironment(Environment environment) { - for (String profile : environment.getActiveProfiles()) { - if ("dev".equalsIgnoreCase(profile)) { - this.isDev = true; - break; - } + if (clientCustomizer != null) { + clientCustomizer.customize(builder, endpoint); } - this.env = environment; + return new AppConfigurationReplicaClient(endpoint, builder.buildClient()); } - protected ConfigurationClientBuilder createBuilderInstance() { + private ConfigurationClientBuilder createBuilderInstance() { RetryStrategy retryStatagy = null; - String mode = env.getProperty(AzureGlobalProperties.PREFIX + "." + RETRY_MODE_PROPERTY_NAME); - String modeService = env.getProperty(AzureAppConfigurationProperties.PREFIX + "." + RETRY_MODE_PROPERTY_NAME); + String mode = System.getProperty(AzureGlobalProperties.PREFIX + "." + RETRY_MODE_PROPERTY_NAME); + String modeService = System + .getProperty(AzureAppConfigurationProperties.PREFIX + "." + RETRY_MODE_PROPERTY_NAME); if ("exponential".equals(mode) || "exponential".equals(modeService) || (mode == null && modeService == null)) { Function checkPropertyInt = parameter -> (Integer.parseInt(parameter)); @@ -322,7 +293,6 @@ private static class ConnectionString { private final String secret; - @SuppressWarnings("deprecation") ConnectionString(String connectionString) { if (CoreUtils.isNullOrEmpty(connectionString)) { throw new IllegalArgumentException("'connectionString' cannot be null or empty."); @@ -358,7 +328,6 @@ private static class ConnectionString { } } - @SuppressWarnings("deprecation") protected ConnectionString setUri(String uri) { try { this.baseUri = new URL(uri); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationSnapshotPropertySource.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationSnapshotPropertySource.java index e4569b384150..853db7e8ab11 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationSnapshotPropertySource.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationSnapshotPropertySource.java @@ -42,10 +42,11 @@ final class AppConfigurationSnapshotPropertySource extends AppConfigurationAppli *

* * @param trim prefix to trim + * @param isRefresh true if a refresh triggered the loading of the Snapshot. * @throws InvalidConfigurationPropertyValueException thrown if fails to parse Json content type */ - public void initProperties(List trim) throws InvalidConfigurationPropertyValueException { - processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName), null, trim); + public void initProperties(List trim, boolean isRefresh) throws InvalidConfigurationPropertyValueException { + processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName, isRefresh), null, trim); FeatureFlags featureFlags = new FeatureFlags(null, featureFlagsList); featureFlagClient.proccessFeatureFlags(featureFlags, replicaClient.getEndpoint()); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigBoostrapRegistrar.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigBoostrapRegistrar.java new file mode 100644 index 000000000000..be7d90bb5e06 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigBoostrapRegistrar.java @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.appconfiguration.config.implementation; + +import org.springframework.boot.BootstrapRegistry.InstanceSupplier; +import org.springframework.boot.context.config.ConfigDataLocationResolverContext; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.util.StringUtils; + +import com.azure.data.appconfiguration.ConfigurationClientBuilder; +import com.azure.spring.cloud.appconfiguration.config.ConfigurationClientCustomizer; +import com.azure.spring.cloud.appconfiguration.config.KeyVaultSecretProvider; +import com.azure.spring.cloud.appconfiguration.config.SecretClientCustomizer; +import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; +import com.azure.spring.cloud.autoconfigure.implementation.appconfiguration.AzureAppConfigurationProperties; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.core.AbstractAzureHttpConfigurationProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.core.authentication.TokenCredentialConfigurationProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; +import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; +import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; +import com.azure.spring.cloud.service.implementation.appconfiguration.ConfigurationClientBuilderFactory; +import com.azure.spring.cloud.service.implementation.keyvault.secrets.SecretClientBuilderFactory; + +@EnableConfigurationProperties(AppConfigurationProviderProperties.class) +class AzureAppConfigurationBootstrapRegistrar { + + static void register(ConfigDataLocationResolverContext context, Binder binder, + AppConfigurationProperties properties, AppConfigurationProviderProperties appProperties, + ReplicaLookUp replicaLookup) { + + AzureGlobalProperties globalProperties = binder + .bind(AzureGlobalProperties.PREFIX, Bindable.of(AzureGlobalProperties.class)) + .orElseGet(AzureGlobalProperties::new); + AzureAppConfigurationProperties appConfigurationProperties = binder + .bind(AzureAppConfigurationProperties.PREFIX, Bindable.of(AzureAppConfigurationProperties.class)) + .orElseGet(AzureAppConfigurationProperties::new); + // the properties are used to custom the ConfigurationClientBuilder + AzureAppConfigurationProperties loadedProperties = AzureGlobalPropertiesUtils.loadProperties(globalProperties, + appConfigurationProperties); + + boolean isCredentialConfigured = isCredentialConfigured(loadedProperties); + + AppConfigurationKeyVaultClientFactory keyVaultClientFactory = appConfigurationKeyVaultClientFactory(context, + isCredentialConfigured, appProperties.getMaxRetryTime()); + AppConfigurationReplicaClientsBuilder replicaClientsBuilder = replicaClientBuilder(context, binder, + keyVaultClientFactory, loadedProperties, isCredentialConfigured, appProperties.getMaxRetries()); + + context.getBootstrapContext().registerIfAbsent(AppConfigurationKeyVaultClientFactory.class, + InstanceSupplier.from(() -> keyVaultClientFactory)); + context.getBootstrapContext().registerIfAbsent(AppConfigurationReplicaClientFactory.class, + InstanceSupplier.from(() -> buildClientFactory(replicaClientsBuilder, properties, replicaLookup))); + } + + private static AppConfigurationKeyVaultClientFactory appConfigurationKeyVaultClientFactory( + ConfigDataLocationResolverContext context, boolean isCredentialConfigured, Integer maxRetryTime) + throws IllegalArgumentException { + + SecretClientCustomizer customizer = context.getBootstrapContext().getOrElse(SecretClientCustomizer.class, null); + KeyVaultSecretProvider secretProvider = context.getBootstrapContext().getOrElse(KeyVaultSecretProvider.class, + null); + SecretClientBuilderFactory secretClientFactory = context.getBootstrapContext() + .getOrElse(SecretClientBuilderFactory.class, null); + + return new AppConfigurationKeyVaultClientFactory(customizer, secretProvider, secretClientFactory, + isCredentialConfigured, maxRetryTime); + } + + private static AppConfigurationReplicaClientFactory buildClientFactory( + AppConfigurationReplicaClientsBuilder clientBuilder, AppConfigurationProperties properties, + ReplicaLookUp replicaLookup) { + return new AppConfigurationReplicaClientFactory(clientBuilder, properties.getStores(), replicaLookup); + } + + @SuppressWarnings("unchecked") + private static AppConfigurationReplicaClientsBuilder replicaClientBuilder(ConfigDataLocationResolverContext context, + Binder binder, AppConfigurationKeyVaultClientFactory keyVaultClientFactory, + AzureAppConfigurationProperties properties, boolean isCredentialConfigured, Integer maxRetries) { + + InstanceSupplier> customizer = context + .getBootstrapContext() + .getRegisteredInstanceSupplier( + (Class>) (Class) AzureServiceClientBuilderCustomizer.class); + ConfigurationClientBuilderFactory clientFactory = context.getBootstrapContext() + .getOrElseSupply(ConfigurationClientBuilderFactory.class, () -> { + ConfigurationClientBuilderFactory factory = new ConfigurationClientBuilderFactory(properties); + factory.setSpringIdentifier(AzureSpringIdentifier.AZURE_SPRING_APP_CONFIG); + if (customizer != null) { + factory.addBuilderCustomizer(customizer.get(context.getBootstrapContext())); + } + return factory; + }); + if (customizer != null) { + clientFactory.addBuilderCustomizer(customizer.get(context.getBootstrapContext())); + } + + InstanceSupplier configurationClientCustomizer = context + .getBootstrapContext() + .getRegisteredInstanceSupplier( + (Class) (Class) ConfigurationClientCustomizer.class); + + ConfigurationClientCustomizer clientCustomizer = null; + if (configurationClientCustomizer != null) { + clientCustomizer = configurationClientCustomizer.get(context.getBootstrapContext()); + } + + return new AppConfigurationReplicaClientsBuilder(maxRetries, clientFactory, clientCustomizer, + isCredentialConfigured, keyVaultClientFactory.isConfigured()); + } + + private static boolean isCredentialConfigured(AbstractAzureHttpConfigurationProperties properties) { + if (properties.getCredential() != null) { + TokenCredentialConfigurationProperties tokenProps = properties.getCredential(); + if (StringUtils.hasText(tokenProps.getClientCertificatePassword())) { + return true; + } else if (StringUtils.hasText(tokenProps.getClientCertificatePath())) { + return true; + } else if (StringUtils.hasText(tokenProps.getClientId())) { + return true; + } else if (StringUtils.hasText(tokenProps.getClientSecret())) { + return true; + } else if (StringUtils.hasText(tokenProps.getUsername())) { + return true; + } else if (StringUtils.hasText(tokenProps.getPassword())) { + return true; + } + } + + return false; + } + +} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLoader.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLoader.java new file mode 100644 index 000000000000..72c1e03c3a3f --- /dev/null +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLoader.java @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.appconfiguration.config.implementation; + +import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.springframework.boot.BootstrapRegistry.InstanceSupplier; +import org.springframework.boot.context.config.ConfigData; +import org.springframework.boot.context.config.ConfigDataLoader; +import org.springframework.boot.context.config.ConfigDataLoaderContext; +import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.boot.logging.DeferredLogFactory; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.util.StringUtils; + +import com.azure.data.appconfiguration.models.ConfigurationSetting; +import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationKeyValueSelector; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagKeyValueSelector; + +@EnableConfigurationProperties(AppConfigurationProviderProperties.class) +public class AzureAppConfigDataLoader implements ConfigDataLoader { + + private static Log logger = new DeferredLog(); + + private AzureAppConfigDataResource resource; + + private AppConfigurationReplicaClientFactory replicaClientFactory; + + private AppConfigurationKeyVaultClientFactory keyVaultClientFactory; + + private StateHolder storeState = new StateHolder(); + + private FeatureFlagClient featureFlagClient; + + public AzureAppConfigDataLoader(DeferredLogFactory logFactory) { + logger = logFactory.getLog(getClass()); + } + + @Override + public ConfigData load(ConfigDataLoaderContext context, AzureAppConfigDataResource resource) + throws IOException, ConfigDataResourceNotFoundException { + this.resource = resource; + storeState.setNextForcedRefresh(resource.getRefreshInterval()); + + if (context.getBootstrapContext().isRegistered(FeatureFlagClient.class)) { + this.featureFlagClient = context.getBootstrapContext().get(FeatureFlagClient.class); + } else { + this.featureFlagClient = new FeatureFlagClient(); + context.getBootstrapContext().registerIfAbsent(FeatureFlagClient.class, + InstanceSupplier.from(() -> this.featureFlagClient)); + } + + List> sourceList = new ArrayList<>(); + + if (resource.isConfigStoreEnabled()) { + replicaClientFactory = context.getBootstrapContext() + .get(AppConfigurationReplicaClientFactory.class); + keyVaultClientFactory = context.getBootstrapContext() + .get(AppConfigurationKeyVaultClientFactory.class); + + List clients = replicaClientFactory + .getAvailableClients(resource.getEndpoint(), true); + + boolean reloadFailed = false; + + // Feature Management needs to be set in the last config store. + + for (AppConfigurationReplicaClient client : clients) { + sourceList = new ArrayList<>(); + + if (reloadFailed + && !AppConfigurationRefreshUtil.refreshStoreCheck(client, + replicaClientFactory.findOriginForEndpoint(client.getEndpoint()))) { + // This store doesn't have any changes where to refresh store did. Skipping Checking next. + continue; + } + + // Reverse in order to add Profile specific properties earlier, and last profile comes first + try { + sourceList.addAll(createSettings(client)); + List featureFlags = createFeatureFlags(client); + + logger.debug("PropertySource context."); + AppConfigurationStoreMonitoring monitoring = resource.getMonitoring(); + + storeState.setStateFeatureFlag(resource.getEndpoint(), featureFlags, + monitoring.getFeatureFlagRefreshInterval()); + + if (monitoring.isEnabled()) { + // Setting new ETag values for Watch + List watchKeysSettings = monitoring.getTriggers().stream() + .map(trigger -> client.getWatchKey(trigger.getKey(), trigger.getLabel(), + resource.isRefresh())) + .toList(); + + storeState.setState(resource.getEndpoint(), watchKeysSettings, monitoring.getRefreshInterval()); + } + storeState.setLoadState(resource.getEndpoint(), true); + } catch (AppConfigurationStatusException e) { + reloadFailed = true; + replicaClientFactory.backoffClientClient(resource.getEndpoint(), client.getEndpoint()); + } catch (Exception e) { + failedToGeneratePropertySource(e); + + // Not a retiable error + break; + } + if (sourceList.size() > 0) { + break; + } + } + } + + StateHolder.updateState(storeState); + sourceList.add(new AppConfigurationFeatureManagementPropertySource(featureFlagClient)); + return new ConfigData(sourceList); + } + + private void failedToGeneratePropertySource(Exception e) { + logger.error("Fail fast is set and there was an error reading configuration from Azure App " + + "Configuration store " + resource.getEndpoint() + "."); + delayException(); + throw new RuntimeException("Failed to generate property sources for " + resource.getEndpoint(), e); + } + + /** + * Creates a new set of AppConfigurationPropertySources, 1 per Label. + * + * @param client client for connecting to App Configuration + * @return a list of AppConfigurationPropertySources + * @throws Exception creating a property source failed + */ + private List createSettings(AppConfigurationReplicaClient client) + throws Exception { + List sourceList = new ArrayList<>(); + List selects = resource.getSelects(); + List profiles = resource.getProfiles().getActive(); + + for (AppConfigurationKeyValueSelector selectedKeys : selects) { + AppConfigurationPropertySource propertySource = null; + + if (StringUtils.hasText(selectedKeys.getSnapshotName())) { + propertySource = new AppConfigurationSnapshotPropertySource( + selectedKeys.getSnapshotName() + "/" + resource.getEndpoint(), client, keyVaultClientFactory, + selectedKeys.getSnapshotName(), featureFlagClient); + } else { + propertySource = new AppConfigurationApplicationSettingPropertySource( + selectedKeys.getKeyFilter() + resource.getEndpoint() + "/", client, keyVaultClientFactory, + selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles)); + } + propertySource.initProperties(resource.getTrimKeyPrefix(), resource.isRefresh()); + sourceList.add(propertySource); + } + return sourceList; + } + + /** + * Creates a new set of AppConfigurationPropertySources, 1 per Label. + * + * @param client client for connecting to App Configuration + * @return a list of AppConfigurationPropertySources + * @throws Exception creating a property source failed + */ + private List createFeatureFlags(AppConfigurationReplicaClient client) + throws Exception { + List featureFlagWatchKeys = new ArrayList<>(); + List profiles = resource.getProfiles().getActive(); + for (FeatureFlagKeyValueSelector selectedKeys : resource.getFeatureFlagSelects()) { + List storesFeatureFlags = featureFlagClient.loadFeatureFlags(client, + selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles), resource.isRefresh()); + featureFlagWatchKeys.addAll(storesFeatureFlags); + } + + return featureFlagWatchKeys; + } + + private void delayException() { + Instant currentDate = Instant.now(); + Instant preKillTIme = resource.getAppProperties().getStartDate() + .plusSeconds(resource.getAppProperties().getPrekillTime()); + if (currentDate.isBefore(preKillTIme)) { + long diffInMillies = Math.abs(preKillTIme.toEpochMilli() - currentDate.toEpochMilli()); + try { + Thread.sleep(diffInMillies); + } catch (InterruptedException e) { + logger.error("Failed to wait before fast fail."); + } + } + } +} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLocationResolver.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLocationResolver.java new file mode 100644 index 000000000000..77ec7057d854 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLocationResolver.java @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.appconfiguration.config.implementation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.naming.NamingException; + +import org.apache.commons.logging.Log; +import org.springframework.beans.BeanUtils; +import org.springframework.boot.BootstrapRegistry.InstanceSupplier; +import org.springframework.boot.context.config.ConfigDataLocation; +import org.springframework.boot.context.config.ConfigDataLocationNotFoundException; +import org.springframework.boot.context.config.ConfigDataLocationResolver; +import org.springframework.boot.context.config.ConfigDataLocationResolverContext; +import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; +import org.springframework.boot.context.config.Profiles; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.bind.BindHandler; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.util.StringUtils; + +import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; + +@EnableConfigurationProperties(AppConfigurationProviderProperties.class) +public class AzureAppConfigDataLocationResolver + implements ConfigDataLocationResolver { + + private static final Log LOGGER = new DeferredLog(); + + public static final String PREFIX = "azureAppConfiguration"; + + private static final AtomicBoolean START_UP = new AtomicBoolean(true); + + @Override + public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) { + if (!location.hasPrefix(PREFIX)) { + return false; + } + Boolean hasEndpoint = StringUtils.hasText(context.getBinder() + .bind(AppConfigurationProperties.CONFIG_PREFIX + ".stores[0].endpoint", String.class) + .orElse("")); + Boolean hasConnectionString = StringUtils.hasText(context.getBinder() + .bind(AppConfigurationProperties.CONFIG_PREFIX + ".stores[0].connection-string", String.class) + .orElse("")); + Boolean hasEndpoints = StringUtils.hasText(context.getBinder() + .bind(AppConfigurationProperties.CONFIG_PREFIX + ".stores[0].endpoints", String.class) + .orElse("")); + Boolean hasConnectionStrings = StringUtils.hasText(context.getBinder() + .bind(AppConfigurationProperties.CONFIG_PREFIX + ".stores[0].connection-strings", String.class) + .orElse("")); + + return (hasEndpoint || hasConnectionString || hasEndpoints || hasConnectionStrings); + } + + @Override + public List resolve(ConfigDataLocationResolverContext context, + ConfigDataLocation location) + throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException { + return Collections.emptyList(); + } + + @Override + public List resolveProfileSpecific( + ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location, Profiles profiles) + throws ConfigDataLocationNotFoundException { + + Holder holder = loadProperties(resolverContext); + List locations = new ArrayList<>(); + + for (ConfigStore store : holder.properties.getStores()) { + locations.add( + new AzureAppConfigDataResource(store, profiles, holder.appProperties, START_UP.get(), + holder.properties.getRefreshInterval())); + } + START_UP.set(false); + return locations; + } + + protected Holder loadProperties(ConfigDataLocationResolverContext context) { + Binder binder = context.getBinder(); + BindHandler bindHandler = getBindHandler(context); + AppConfigurationProperties properties; + AppConfigurationProviderProperties appProperties; + Holder holder = new Holder(); + + properties = binder.bind(AppConfigurationProperties.CONFIG_PREFIX, + Bindable.of(AppConfigurationProperties.class), bindHandler).get(); + + appProperties = binder.bind(AppConfigurationProviderProperties.CONFIG_PREFIX, + Bindable.of(AppConfigurationProviderProperties.class), bindHandler) + .orElseGet(AppConfigurationProviderProperties::new); + + properties.validateAndInit(); + ReplicaLookUp replicaLookup = null; + try { + replicaLookup = new ReplicaLookUp(properties); + context.getBootstrapContext().registerIfAbsent(ReplicaLookUp.class, InstanceSupplier.of(replicaLookup)); + } catch (NamingException e) { + LOGGER.info("Failed to find DNS Entry for config store while looking for replicas."); + } + + AzureAppConfigurationBootstrapRegistrar.register(context, binder, properties, appProperties, replicaLookup); + + holder.properties = properties; + holder.appProperties = appProperties; + + return holder; + } + + private BindHandler getBindHandler(ConfigDataLocationResolverContext context) { + return context.getBootstrapContext().getOrElse(BindHandler.class, null); + } + + private class Holder { + AppConfigurationProperties properties; + + AppConfigurationProviderProperties appProperties; + } + +} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataResource.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataResource.java new file mode 100644 index 000000000000..13f5f1a87ad8 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataResource.java @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.appconfiguration.config.implementation; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.boot.context.config.ConfigDataResource; +import org.springframework.boot.context.config.Profiles; + +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationKeyValueSelector; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagKeyValueSelector; + +public class AzureAppConfigDataResource extends ConfigDataResource { + + private final boolean configStoreEnabled; + + private final String endpoint; + + private List trimKeyPrefix; + + private final Profiles profiles; + + private List selects = new ArrayList<>(); + + private List featureFlagSelects = new ArrayList<>(); + + private final AppConfigurationStoreMonitoring monitoring; + + private final AppConfigurationProviderProperties appProperties; + + private final boolean isRefresh; + + private Duration refreshInterval; + + AzureAppConfigDataResource(ConfigStore configStore, Profiles profiles, + AppConfigurationProviderProperties appProperties, boolean isRefresh, Duration refreshInterval) { + this.configStoreEnabled = configStore.isEnabled(); + this.endpoint = configStore.getEndpoint(); + this.selects = configStore.getSelects(); + this.featureFlagSelects = configStore.getFeatureFlags().getSelects(); + this.trimKeyPrefix = configStore.getTrimKeyPrefix(); + this.monitoring = configStore.getMonitoring(); + this.profiles = profiles; + this.appProperties = appProperties; + this.isRefresh = isRefresh; + this.refreshInterval = refreshInterval; + } + + /** + * @return the selects + */ + public List getSelects() { + return selects; + } + + /** + * @param selects the selects to set + */ + public void setSelects(List selects) { + this.selects = selects; + } + + /** + * @return the selects for feature flags + */ + public List getFeatureFlagSelects() { + return featureFlagSelects; + } + + /** + * @param featureFlagSelects the selects to set + */ + public void setFeatureFlagSelects(List featureFlagSelects) { + this.featureFlagSelects = featureFlagSelects; + } + + /** + * @return the configStoreEnabled + */ + public boolean isConfigStoreEnabled() { + return configStoreEnabled; + } + + /** + * @return the endpoint + */ + public String getEndpoint() { + return endpoint; + } + + /** + * @return the monitoring + */ + public AppConfigurationStoreMonitoring getMonitoring() { + return monitoring; + } + + /** + * @return the trimKeyPrefix + */ + public List getTrimKeyPrefix() { + return trimKeyPrefix; + } + + /** + * @param trimKeyPrefix the trimKeyPrefix to set + */ + public void setTrimKeyPrefix(List trimKeyPrefix) { + this.trimKeyPrefix = trimKeyPrefix; + } + + /** + * @return the profiles + */ + public Profiles getProfiles() { + return profiles; + } + + /** + * @return the isRefresh + */ + public boolean isRefresh() { + return isRefresh; + } + + /** + * @return the appProperties + */ + public AppConfigurationProviderProperties getAppProperties() { + return appProperties; + } + + /** + * @return the refreshInterval + */ + public Duration getRefreshInterval() { + return refreshInterval; + } + + /** + * @param refreshInterval the refreshInterval to set + */ + public void setRefreshInterval(Duration refreshInterval) { + this.refreshInterval = refreshInterval; + } + +} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManager.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManager.java index 16c6d2d05fdc..17124ea72904 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManager.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManager.java @@ -7,7 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +20,7 @@ /** * Holds a set of connections to an app configuration store with zero to many geo-replications. */ -public class ConnectionManager { +class ConnectionManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class); @@ -143,13 +142,6 @@ List getAvailableClients(Boolean useCurrent) { return availableClients; } - List getAllEndpoints() { - List endpoints = clients.stream().map(AppConfigurationReplicaClient::getEndpoint) - .collect(Collectors.toList()); - endpoints.addAll(replicaLookUp.getAutoFailoverEndpoints(configStore.getEndpoint())); - return endpoints; - } - /** * Call when the current client failed * @param endpoint replica endpoint diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClient.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClient.java index 956454d616ca..474f0fda49f0 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClient.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClient.java @@ -14,6 +14,8 @@ import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.TELEMETRY; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -21,7 +23,6 @@ import java.util.List; import java.util.Map; -import org.bouncycastle.jcajce.provider.digest.SHA256; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -43,9 +44,9 @@ * take priority. */ @Component -public class FeatureFlagClient { +class FeatureFlagClient { - protected final Map properties = new LinkedHashMap<>(); + private final Map properties = new LinkedHashMap<>(); private static final ObjectMapper CASE_INSENSITIVE_MAPPER = JsonMapper.builder() .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); @@ -62,7 +63,7 @@ public class FeatureFlagClient { *

* */ - public List loadFeatureFlags(AppConfigurationReplicaClient replicaClient, String customKeyFilter, + List loadFeatureFlags(AppConfigurationReplicaClient replicaClient, String customKeyFilter, String[] labelFilter, boolean isRefresh) { List loadedFeatureFlags = new ArrayList<>(); @@ -84,7 +85,7 @@ public List loadFeatureFlags(AppConfigurationReplicaClient replica return loadedFeatureFlags; } - public List proccessFeatureFlags(FeatureFlags features, String endpoint) { + List proccessFeatureFlags(FeatureFlags features, String endpoint) { List loadedFeatureFlags = new ArrayList<>(); loadedFeatureFlags.add(features); @@ -151,11 +152,15 @@ protected static Feature createFeature(FeatureFlagConfigurationSetting item, Str */ private static String calculateFeatureFlagId(String key, String label) { final String data = String.format("%s\n%s", key, label.isEmpty() ? null : label); - final SHA256.Digest digest = new SHA256.Digest(); - final String beforeTrim = Base64URL.encode(digest.digest(data.getBytes(StandardCharsets.UTF_8))) - .toString().replace('+', '-').replace('/', '_'); - final int index = beforeTrim.indexOf('='); - return beforeTrim.substring(0, index > -1 ? index : beforeTrim.length()); + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + final String beforeTrim = Base64URL.encode(sha256.digest(data.getBytes(StandardCharsets.UTF_8))) + .toString().replace('+', '-').replace('/', '_'); + final int index = beforeTrim.indexOf('='); + return beforeTrim.substring(0, index > -1 ? index : beforeTrim.length()); + } catch (NoSuchAlgorithmException e) { + } + return ""; } /** diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/NormalizeNull.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/NormalizeNull.java index 8463930ba1c9..3ae7ac5f267b 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/NormalizeNull.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/NormalizeNull.java @@ -9,7 +9,7 @@ * configurations with any label. * */ -public class NormalizeNull { +class NormalizeNull { private static final String EMPTY_LABEL = "\0"; @@ -19,7 +19,7 @@ public class NormalizeNull { * @param setting ConfigurationSetting * @return ConfigurationSetting with label corrected from null to \0 */ - public static ConfigurationSetting normalizeNullLabel(ConfigurationSetting setting) { + static ConfigurationSetting normalizeNullLabel(ConfigurationSetting setting) { return setting.getLabel() == null ? setting.setLabel(EMPTY_LABEL) : setting; } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolder.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolder.java index 9de8f3d08228..817ee27d1c6c 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolder.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolder.java @@ -132,14 +132,9 @@ Map getLoadState() { /** * @param originEndpoint the configuration store connected to. * @param loaded true if the configuration store was loaded. - * @param failFast application started after it failed to load from a store. */ - void setLoadState(String originEndpoint, Boolean loaded, Boolean failFast) { - if (loaded || !failFast) { - loadState.put(originEndpoint, true); - } else { - loadState.put(originEndpoint, false); - } + void setLoadState(String originEndpoint, Boolean loaded) { + loadState.put(originEndpoint, loaded); } /** diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/autofailover/SRVRecord.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/autofailover/SRVRecord.java index 9646b83527d9..7b7e9c1225f2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/autofailover/SRVRecord.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/autofailover/SRVRecord.java @@ -2,7 +2,7 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation.autofailover; -public class SRVRecord { +class SRVRecord { private final int priority; @@ -14,7 +14,7 @@ public class SRVRecord { private static final String PROTOCAL = "https://"; - public SRVRecord(String[] record) { + SRVRecord(String[] record) { this.priority = Integer.valueOf(record[0]); this.weight = Integer.valueOf(record[1]); this.port = Integer.valueOf(record[2]); @@ -41,7 +41,7 @@ public String getEndpoint() { return PROTOCAL + target; } - public int compareTo(SRVRecord record) { + int compareTo(SRVRecord record) { if (priority > record.getPriority()) { return 1; } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfiguration.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfiguration.java deleted file mode 100644 index 2573d3abf766..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfiguration.java +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation.config; - -import javax.naming.NamingException; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.core.env.Environment; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.util.StringUtils; - -import com.azure.data.appconfiguration.ConfigurationClientBuilder; -import com.azure.spring.cloud.appconfiguration.config.ConfigurationClientCustomizer; -import com.azure.spring.cloud.appconfiguration.config.KeyVaultSecretProvider; -import com.azure.spring.cloud.appconfiguration.config.SecretClientCustomizer; -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationKeyVaultClientFactory; -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationPropertySourceLocator; -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientFactory; -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientsBuilder; -import com.azure.spring.cloud.appconfiguration.config.implementation.FeatureFlagClient; -import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; -import com.azure.spring.cloud.autoconfigure.implementation.appconfiguration.AzureAppConfigurationProperties; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; -import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultSecretProperties; -import com.azure.spring.cloud.autoconfigure.implementation.properties.core.AbstractAzureHttpConfigurationProperties; -import com.azure.spring.cloud.autoconfigure.implementation.properties.core.authentication.TokenCredentialConfigurationProperties; -import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; -import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; -import com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils; -import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; -import com.azure.spring.cloud.service.implementation.appconfiguration.ConfigurationClientBuilderFactory; -import com.azure.spring.cloud.service.implementation.keyvault.secrets.SecretClientBuilderFactory; - -/** - * Setup ConnectionPool, AppConfigurationPropertySourceLocator, and ClientStore when - * spring.cloud.azure.appconfiguration.enabled is enabled. - */ -@Configuration -@PropertySource("classpath:appConfiguration.properties") -@EnableConfigurationProperties({ AppConfigurationProperties.class, AppConfigurationProviderProperties.class }) -@ConditionalOnClass(AppConfigurationPropertySourceLocator.class) -@ConditionalOnProperty(prefix = AppConfigurationProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true) -@EnableAsync -public class AppConfigurationBootstrapConfiguration { - - @Autowired - private transient ApplicationContext context; - - @Bean - AppConfigurationPropertySourceLocator sourceLocator(AppConfigurationProperties properties, - AppConfigurationProviderProperties appProperties, AppConfigurationReplicaClientFactory clientFactory, - AppConfigurationKeyVaultClientFactory keyVaultClientFactory, ReplicaLookUp replicaLookUp, - FeatureFlagClient featureFlagLoader) - throws IllegalArgumentException { - - return new AppConfigurationPropertySourceLocator(appProperties, clientFactory, keyVaultClientFactory, - properties.getRefreshInterval(), properties.getStores(), replicaLookUp, featureFlagLoader); - } - - @Bean - AppConfigurationKeyVaultClientFactory appConfigurationKeyVaultClientFactory(Environment environment, - AppConfigurationProviderProperties appProperties) - throws IllegalArgumentException { - AzureGlobalProperties globalSource = Binder.get(environment).bindOrCreate(AzureGlobalProperties.PREFIX, - AzureGlobalProperties.class); - AzureGlobalProperties serviceSource = Binder.get(environment).bindOrCreate(AzureKeyVaultSecretProperties.PREFIX, - AzureGlobalProperties.class); - - AzureKeyVaultSecretProperties globalProperties = AzureGlobalPropertiesUtils.loadProperties( - globalSource, - new AzureKeyVaultSecretProperties()); - AzureKeyVaultSecretProperties clientProperties = AzureGlobalPropertiesUtils.loadProperties(serviceSource, - new AzureKeyVaultSecretProperties()); - - AzurePropertiesUtils.copyAzureCommonPropertiesIgnoreNull(globalProperties, clientProperties); - - SecretClientCustomizer keyVaultClientProvider = context.getBeanProvider(SecretClientCustomizer.class) - .getIfAvailable(); - KeyVaultSecretProvider keyVaultSecretProvider = context.getBeanProvider(KeyVaultSecretProvider.class) - .getIfAvailable(); - - SecretClientBuilderFactory secretClientBuilderFactory = new SecretClientBuilderFactory(clientProperties); - - boolean credentialConfigured = isCredentialConfigured(clientProperties); - - return new AppConfigurationKeyVaultClientFactory(keyVaultClientProvider, keyVaultSecretProvider, - secretClientBuilderFactory, credentialConfigured, appProperties.getMaxRetryTime()); - } - - /** - * Factory for working with App Configuration Clients - * - * @param clientBuilder Builder for configuration clients - * @param properties Client configurations for setting up connections to each config store. - * @return AppConfigurationReplicaClientFactory - */ - @Bean - @ConditionalOnMissingBean - AppConfigurationReplicaClientFactory buildClientFactory(AppConfigurationReplicaClientsBuilder clientBuilder, - AppConfigurationProperties properties, ReplicaLookUp replicaLookUp) { - return new AppConfigurationReplicaClientFactory(clientBuilder, properties.getStores(), replicaLookUp); - } - - /** - * Loader for all feature flags. Enables de-duplicating of feature flags when multiple feature flags with the same - * name are loaded. - * @return {@link FeatureFlagClient} - */ - @Bean - @ConditionalOnMissingBean - FeatureFlagClient featureFlagLoader() { - return new FeatureFlagClient(); - } - - /** - * Builder for clients connecting to App Configuration. - * - * @param clientProperties AzureAppConfigurationProperties Spring Cloud Azure global properties. - * @param appProperties Library configurations for setting up connections to each config store. - * @param keyVaultClientFactory used for tracing info for if key vault has been configured - * @param customizers Client Customizers for connecting to Azure App Configuration - * @return ClientStore - */ - @Bean - @ConditionalOnMissingBean - AppConfigurationReplicaClientsBuilder replicaClientBuilder(Environment environment, - AppConfigurationProviderProperties appProperties, AppConfigurationKeyVaultClientFactory keyVaultClientFactory, - ObjectProvider> customizers) { - AzureGlobalProperties globalSource = Binder.get(environment).bindOrCreate(AzureGlobalProperties.PREFIX, - AzureGlobalProperties.class); - AzureGlobalProperties serviceSource = Binder.get(environment).bindOrCreate( - AzureAppConfigurationProperties.PREFIX, - AzureGlobalProperties.class); - - AzureGlobalProperties globalProperties = AzureGlobalPropertiesUtils.loadProperties(globalSource, - new AzureGlobalProperties()); - AzureAppConfigurationProperties clientProperties = AzureGlobalPropertiesUtils.loadProperties(serviceSource, - new AzureAppConfigurationProperties()); - - AzurePropertiesUtils.copyAzureCommonPropertiesIgnoreNull(globalProperties, clientProperties); - - ConfigurationClientBuilderFactory clientFactory = new ConfigurationClientBuilderFactory(clientProperties); - - clientFactory.setSpringIdentifier(AzureSpringIdentifier.AZURE_SPRING_APP_CONFIG); - customizers.orderedStream().forEach(clientFactory::addBuilderCustomizer); - - boolean credentialConfigured = isCredentialConfigured(clientProperties); - - AppConfigurationReplicaClientsBuilder clientBuilder = new AppConfigurationReplicaClientsBuilder( - appProperties.getMaxRetries(), clientFactory, credentialConfigured); - - clientBuilder - .setClientProvider(context.getBeanProvider(ConfigurationClientCustomizer.class) - .getIfAvailable()); - - clientBuilder.setIsKeyVaultConfigured(keyVaultClientFactory.isConfigured()); - - return clientBuilder; - } - - @Bean - ReplicaLookUp replicaLookUp(AppConfigurationProperties properties) throws NamingException { - return new ReplicaLookUp(properties); - } - - private boolean isCredentialConfigured(AbstractAzureHttpConfigurationProperties properties) { - if (properties.getCredential() != null) { - TokenCredentialConfigurationProperties tokenProps = properties.getCredential(); - if (StringUtils.hasText(tokenProps.getClientCertificatePassword())) { - return true; - } else if (StringUtils.hasText(tokenProps.getClientCertificatePath())) { - return true; - } else if (StringUtils.hasText(tokenProps.getClientId())) { - return true; - } else if (StringUtils.hasText(tokenProps.getClientSecret())) { - return true; - } else if (StringUtils.hasText(tokenProps.getUsername())) { - return true; - } else if (StringUtils.hasText(tokenProps.getPassword())) { - return true; - } - } - - return false; - } - -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/FeatureFlags.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/FeatureFlags.java index dc725a309000..2a1380a3d907 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/FeatureFlags.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/FeatureFlags.java @@ -6,7 +6,6 @@ import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.SettingSelector; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; public class FeatureFlags { @@ -14,27 +13,11 @@ public class FeatureFlags { private List featureFlags; - private ConfigStore configStore; - public FeatureFlags(SettingSelector settingSelector, List featureFlags) { this.settingSelector = settingSelector; this.featureFlags = featureFlags; } - /** - * @return the configStore - */ - public ConfigStore getConfigStore() { - return configStore; - } - - /** - * @param configStore the configStore to set - */ - public void setConfigStore(ConfigStore configStore) { - this.configStore = configStore; - } - /** * @return the settingSelector */ diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/Conditions.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/Conditions.java index d882c0399021..e010edfe7e8f 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/Conditions.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/Conditions.java @@ -20,7 +20,7 @@ public class Conditions { @JsonProperty("requirement_type") private String requirementType = DEFAULT_REQUIREMENT_TYPE; - public Conditions(List featureFilters, String requirementType) { + Conditions(List featureFilters, String requirementType) { clientFilters = new ArrayList<>(); clientFilters.addAll(featureFilters); this.requirementType = requirementType; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureFilterEvaluationContext.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureFilterEvaluationContext.java deleted file mode 100644 index 760284fc57bd..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureFilterEvaluationContext.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation.feature.entity; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Context passed into Feature Filters used for evaluation. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public final class FeatureFilterEvaluationContext { - - /** - * Creates an instance of {@link FeatureFilterEvaluationContext} - */ - public FeatureFilterEvaluationContext() { - } - - private String name; - - private Map parameters; - - /** - * Return the name - * @return the name - */ - public String getName() { - return name; - } - - /** - * Set the name - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * Return the parameters - * @return the parameters - */ - public Map getParameters() { - Map params = new HashMap(); - if (parameters != null) { - params.putAll(parameters); - } - return params; - } - - /** - * Set the parameters - * @param parameters the parameters to set - */ - public void setParameters(Map parameters) { - this.parameters = parameters; - } - -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureSet.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureSet.java deleted file mode 100644 index 5259f28a3b11..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/feature/entity/FeatureSet.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation.feature.entity; - -import java.util.HashMap; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Set of Feature Flag Key pairs. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public final class FeatureSet { - - @JsonProperty("FeatureManagement") - private HashMap featureManagement; - - /** - * Returns Map of Feature Flags. - * - * @return the featureFlags - */ - public HashMap getFeatureManagement() { - return featureManagement; - } - - /** - * Adds a new Feature Flag. - * - * @param key Name of the Feature Flag. - * @param feature true/false, for on/off feature Flag. {@code Feature} if Feature Filter. - */ - public void addFeature(String key, Object feature) { - if (featureManagement == null) { - featureManagement = new HashMap<>(); - } - if (feature != null) { - featureManagement.put(key, feature); - } - } -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicy.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicy.java index 0f5db1c2ee38..bd72467aa3f9 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicy.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicy.java @@ -27,10 +27,10 @@ public final class BaseAppConfigurationPolicy implements HttpPipelinePolicy { /** * Format of User Agent */ - public static final String USER_AGENT = String.format("%s/%s", StringUtils.replace(PACKAGE_NAME, " ", ""), + private static final String USER_AGENT = String.format("%s/%s", StringUtils.replace(PACKAGE_NAME, " ", ""), BaseAppConfigurationPolicy.class.getPackage().getImplementationVersion()); - final TracingInfo tracingInfo; + private final TracingInfo tracingInfo; /** * App Configuration Http Pipeline Policy @@ -40,6 +40,7 @@ public BaseAppConfigurationPolicy(TracingInfo tracingInfo) { this.tracingInfo = tracingInfo; } + @Override public Mono process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) { Boolean watchRequests = (Boolean) context.getData("refresh").orElse(false); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/FeatureFlagTracing.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/FeatureFlagTracing.java index 8a325a4614ce..24b62193cc55 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/FeatureFlagTracing.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/FeatureFlagTracing.java @@ -5,7 +5,7 @@ import java.util.Arrays; import java.util.List; -public class FeatureFlagTracing { +class FeatureFlagTracing { private static final String CUSTOM_FILTER = "CSTM"; @@ -34,18 +34,18 @@ public class FeatureFlagTracing { private Boolean usesTargetingFilter = false; - public boolean usesAnyFilter() { + boolean usesAnyFilter() { return usesCustomFilter || usesPercentageFilter || usesTimeWindowFilter || usesTargetingFilter; } - public void resetFeatureFilterTelemetry() { + void resetFeatureFilterTelemetry() { usesCustomFilter = false; usesPercentageFilter = false; usesTimeWindowFilter = false; usesTargetingFilter = false; } - public void updateFeatureFilterTelemetry(String filterName) { + void updateFeatureFilterTelemetry(String filterName) { if (PERCENTAGE_FILTER_NAMES.stream().anyMatch(name -> name.equalsIgnoreCase(filterName))) { usesPercentageFilter = true; } else if (TIME_WINDOW_FILTER_NAMES.stream().anyMatch(name -> name.equalsIgnoreCase(filterName))) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfo.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfo.java index e9608eb72db2..5f57b8e9ab78 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfo.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfo.java @@ -2,7 +2,6 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation.http.policy; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.DEV_ENV_TRACING; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.KEY_VAULT_CONFIGURED_TRACING; import org.springframework.util.StringUtils; @@ -14,8 +13,6 @@ public class TracingInfo { - private boolean isDev = false; - private boolean isKeyVaultConfigured = false; private int replicaCount; @@ -24,17 +21,15 @@ public class TracingInfo { private final Configuration configuration; - public TracingInfo(boolean isDev, boolean isKeyVaultConfigured, int replicaCount, Configuration configuration) { - this.isDev = isDev; + public TracingInfo(boolean isKeyVaultConfigured, int replicaCount, Configuration configuration) { this.isKeyVaultConfigured = isKeyVaultConfigured; this.replicaCount = replicaCount; this.featureFlagTracing = new FeatureFlagTracing(); this.configuration = configuration; } - public String getValue(boolean watchRequests) { - String track = configuration - .get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); + String getValue(boolean watchRequests) { + String track = configuration.get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); if (track != null && Boolean.valueOf(track)) { return ""; } @@ -52,10 +47,6 @@ public String getValue(boolean watchRequests) { if (!hostType.isEmpty()) { sb.append(",").append(RequestTracingConstants.HOST_TYPE_KEY).append("=").append(hostType); } - - if (isDev) { - sb.append(",Env=").append(DEV_ENV_TRACING); - } if (isKeyVaultConfigured) { sb.append(",").append(KEY_VAULT_CONFIGURED_TRACING); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationKeyValueSelector.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationKeyValueSelector.java index d6d4baed1efd..3b210621899d 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationKeyValueSelector.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationKeyValueSelector.java @@ -30,7 +30,7 @@ public final class AppConfigurationKeyValueSelector { /** * Separator for multiple labels */ - public static final String LABEL_SEPARATOR = ","; + private static final String LABEL_SEPARATOR = ","; @NotNull /** @@ -129,7 +129,7 @@ public void setSnapshotName(String snapshotName) { * Validates key-filter and label-filter are valid. */ @PostConstruct - public void validateAndInit() { + void validateAndInit() { Assert.isTrue(!keyFilter.contains("*"), "KeyFilter must not contain asterisk(*)"); if (labelFilter != null) { Assert.isTrue(!labelFilter.contains("*"), "LabelFilter must not contain asterisk(*)"); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreMonitoring.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreMonitoring.java index 22db1862d354..f90a3a573f12 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreMonitoring.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreMonitoring.java @@ -118,7 +118,7 @@ public void setPushNotification(PushNotification pushNotification) { * Validates refreshIntervals are at least 1 second, and if enabled triggers are valid. */ @PostConstruct - public void validateAndInit() { + void validateAndInit() { if (enabled) { Assert.notEmpty(triggers, "Triggers need to be set if refresh is enabled."); for (AppConfigurationStoreTrigger trigger : triggers) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreTrigger.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreTrigger.java index ec69376e1bd7..97499d8b3f23 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreTrigger.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationStoreTrigger.java @@ -58,7 +58,7 @@ public void setLabel(String label) { * Validates key isn't null */ @PostConstruct - public void validateAndInit() { + void validateAndInit() { Assert.notNull(key, "All Triggers need a key value set."); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/ConfigStore.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/ConfigStore.java index 7444d739770f..0f344b1637a2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/ConfigStore.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/ConfigStore.java @@ -53,15 +53,6 @@ public final class ConfigStore { */ private List selects = new ArrayList<>(); - /** - * If true, the application will fail to start if the Config Store cannot be - * reached. If false, the application will start without the Config Store. - */ - private boolean failFast = true; - - /** - * Options for retrieving Feature Flags from the Azure Config Service. - */ private FeatureFlagStore featureFlags = new FeatureFlagStore(); /** @@ -147,20 +138,6 @@ public void setConnectionStrings(List connectionStrings) { this.connectionStrings = connectionStrings; } - /** - * @return the failFast - */ - public boolean isFailFast() { - return failFast; - } - - /** - * @param failFast the failFast to set - */ - public void setFailFast(boolean failFast) { - this.failFast = failFast; - } - /** * @return the enabled */ diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagKeyValueSelector.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagKeyValueSelector.java index b3280178e200..4d1de6b90b11 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagKeyValueSelector.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagKeyValueSelector.java @@ -105,7 +105,7 @@ public FeatureFlagKeyValueSelector setLabelFilter(String labelFilter) { * Validates key-filter and label-filter are valid. */ @PostConstruct - public void validateAndInit() { + void validateAndInit() { if (labelFilter != null) { Assert.isTrue(!labelFilter.contains("*"), "LabelFilter must not contain asterisk(*)"); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagStore.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagStore.java index c7b5e89646f7..18ee7c2f28b3 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagStore.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/FeatureFlagStore.java @@ -48,7 +48,7 @@ public void setSelects(List selects) { } @PostConstruct - public void validateAndInit() { + void validateAndInit() { if (enabled && selects.size() == 0) { selects.add(new FeatureFlagKeyValueSelector()); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring.factories index 9a76c2325a3c..fb0f411d86c2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring.factories @@ -1,3 +1,5 @@ -org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -com.azure.spring.cloud.appconfiguration.config.implementation.config.AppConfigurationBootstrapConfiguration +# ConfigData Location Resolvers + org.springframework.boot.context.config.ConfigDataLocationResolver= com.azure.spring.cloud.appconfiguration.config.implementation.AzureAppConfigDataLocationResolver + # ConfigData Loaders + org.springframework.boot.context.config.ConfigDataLoader= com.azure.spring.cloud.appconfiguration.config.implementation.AzureAppConfigDataLoader diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 1b36b907ed1b..0472d30fc7d2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1 @@ -com.azure.spring.cloud.appconfiguration.config.AppConfigurationAutoConfiguration -com.azure.spring.cloud.appconfiguration.config.implementation.config.AppConfigurationBootstrapConfiguration \ No newline at end of file +com.azure.spring.cloud.appconfiguration.config.AppConfigurationWatchAutoConfiguration diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/appConfiguration.properties b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/appConfiguration.properties deleted file mode 100644 index 9b90ce63ee85..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/resources/appConfiguration.properties +++ /dev/null @@ -1,6 +0,0 @@ -spring.cloud.appconfiguration.version=1.0 -spring.cloud.appconfiguration.maxRetries=2 -spring.cloud.appconfiguration.maxRetryTime=60 -spring.cloud.appconfiguration.preKillTime=5 -spring.cloud.appconfiguration.defaultMinBackoff=30 -spring.cloud.appconfiguration.defaultmaxBackoff=600 diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceSnapshotTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceSnapshotTest.java index bd48c729a109..7ef8af30b518 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceSnapshotTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceSnapshotTest.java @@ -128,10 +128,10 @@ public void cleanup() throws Exception { @Test public void testPropCanBeInitAndQueried() throws IOException { when(configurationListMock.iterator()).thenReturn(testItems.iterator()).thenReturn(testItems.iterator()); - when(clientMock.listSettingSnapshot(Mockito.any())).thenReturn(configurationListMock) + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(TRIM); + propertySource.initProperties(TRIM, false); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = testItems.stream().map(t -> { @@ -155,10 +155,10 @@ public void testPropertyNameSlashConvertedToDots() throws IOException { List settings = new ArrayList<>(); settings.add(slashedProp); when(configurationListMock.iterator()).thenReturn(settings.iterator()).thenReturn(Collections.emptyIterator()); - when(clientMock.listSettingSnapshot(Mockito.any())).thenReturn(configurationListMock) + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(TRIM); + propertySource.initProperties(TRIM, false); String expectedKeyName = TEST_SLASH_KEY.replace('/', '.'); String[] actualKeyNames = propertySource.getPropertyNames(); @@ -174,9 +174,9 @@ public void initNullValidContentTypeTest() throws IOException { List items = new ArrayList<>(); items.add(ITEM_NULL); when(configurationListMock.iterator()).thenReturn(items.iterator()).thenReturn(Collections.emptyIterator()); - when(clientMock.listSettingSnapshot(Mockito.any())).thenReturn(configurationListMock); + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock); - propertySource.initProperties(TRIM); + propertySource.initProperties(TRIM, false); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceKeyVaultTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceKeyVaultTest.java index 2833cc92970c..0bc44e5c546c 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceKeyVaultTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceKeyVaultTest.java @@ -2,7 +2,7 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.KEY_VAULT_CONTENT_TYPE; +import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.KEY_VAULT_CONTENT_TYPE; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_CONN_STRING; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_KEY_VAULT_1; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_LABEL_VAULT_1; @@ -146,8 +146,7 @@ public void invalidKeyVaultReferenceParseErrorTest() { .thenReturn(clientManagerMock); when(clientManagerMock.getSecret(Mockito.any())).thenThrow(new RuntimeException("Parse Failed")); - RuntimeException exception = assertThrows(RuntimeException.class, - () -> propertySource.initProperties(null, false)); + RuntimeException exception = assertThrows(RuntimeException.class, () -> propertySource.initProperties(null, false)); assertEquals("Parse Failed", exception.getMessage()); } } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocatorTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocatorTest.java deleted file mode 100644 index a68ac60e28e6..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPropertySourceLocatorTest.java +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation; - -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.EMPTY_LABEL; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_CONN_STRING; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_CONN_STRING_2; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_STORE_NAME; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_STORE_NAME_2; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; - -import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; -import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; -import com.azure.spring.cloud.appconfiguration.config.implementation.feature.entity.Feature; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationKeyValueSelector; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProviderProperties; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreTrigger; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; -import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagStore; - -@TestMethodOrder(MethodOrderer.MethodName.class) -public class AppConfigurationPropertySourceLocatorTest { - - private static final String PROFILE_NAME_1 = "dev"; - - private static final String PROFILE_NAME_2 = "prod"; - - private static final String KEY_FILTER = "/foo/"; - - @Mock - private ConfigurableEnvironment emptyEnvironment; - - @Mock - private ConfigurableEnvironment devEnvironment; - - @Mock - private ConfigurableEnvironment multiEnvironment; - - @Mock - private AppConfigurationReplicaClientFactory clientFactoryMock; - - @Mock - private AppConfigurationKeyVaultClientFactory keyVaultClientFactory; - - @Mock - private AppConfigurationReplicaClient replicaClientMock; - - @Mock - private FeatureFlagStore featureFlagStoreMock; - - @Mock - private ConfigStore configStoreMockError; - - @Mock - private AppConfigurationProviderProperties appPropertiesMock; - - @Mock - private ReplicaLookUp replicaLookUpMock; - - @Mock - private FeatureFlagClient featureFlagClientMock; - - @Mock - private ConfigStore configStoreMock; - - private AppConfigurationPropertySourceLocator locator; - - private AppConfigurationProperties properties; - - private AppConfigurationProviderProperties appProperties; - - private List stores; - - private AppConfigurationStoreMonitoring monitoring; - - private MutablePropertySources sources = new MutablePropertySources(); - - private MockitoSession session; - - @BeforeEach - public void setup() { - session = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); - MockitoAnnotations.openMocks(this); - - sources.addFirst(new PropertySource("refreshArgs") { - - @Override - public Object getProperty(String name) { - return null; - } - }); - - properties = new AppConfigurationProperties(); - properties.setEnabled(true); - properties.setRefreshInterval(null); - - TestUtils.addStore(properties, TEST_STORE_NAME, TEST_CONN_STRING, KEY_FILTER); - - monitoring = new AppConfigurationStoreMonitoring(); - monitoring.setEnabled(false); - AppConfigurationStoreTrigger trigger = new AppConfigurationStoreTrigger(); - trigger.setKey("test"); - - monitoring.setTriggers(List.of(trigger)); - - appProperties = new AppConfigurationProviderProperties(); - appProperties.setVersion("1.0"); - appProperties.setMaxRetries(12); - appProperties.setMaxRetryTime(0); - appProperties.setDefaultMaxBackoff((long) 600); - appProperties.setDefaultMinBackoff((long) 30); - - properties.getStores().get(0).setFeatureFlags(featureFlagStoreMock); - properties.getStores().get(0).setMonitoring(monitoring); - stores = properties.getStores(); - } - - @AfterEach - public void cleanup() throws Exception { - MockitoAnnotations.openMocks(this).close(); - session.finishMocking(); - AppConfigurationPropertySourceLocator.STARTUP.set(true); - } - - @Test - public void compositeSourceIsCreated() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, - keyVaultClientFactory, null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void compositeSourceIsCreatedWithMonitoring() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - String watchKey = "wk1"; - AppConfigurationStoreMonitoring monitoring = new AppConfigurationStoreMonitoring(); - monitoring.setEnabled(true); - - AppConfigurationStoreTrigger trigger = new AppConfigurationStoreTrigger(); - trigger.setKey(watchKey); - trigger.setLabel(EMPTY_LABEL); - monitoring.setTriggers(List.of(trigger)); - - properties.getStores().get(0).setMonitoring(monitoring); - - when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(TestUtils.createItem("", watchKey, "0", EMPTY_LABEL, "")); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), - Mockito.anyBoolean()); - } - } - - @Test - public void compositeSourceIsCreatedWithMonitoringWatchKeyDoesNotExist() { - // The listed Watch Key doesn't have a value in app config. When one is added will cause a refresh. - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - String watchKey = "wk1"; - AppConfigurationStoreMonitoring monitoring = new AppConfigurationStoreMonitoring(); - monitoring.setEnabled(true); - - AppConfigurationStoreTrigger trigger = new AppConfigurationStoreTrigger(); - trigger.setKey(watchKey); - trigger.setLabel(EMPTY_LABEL); - monitoring.setTriggers(List.of(trigger)); - - properties.getStores().get(0).setMonitoring(monitoring); - - when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(null); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), - Mockito.anyBoolean()); - } - } - - @Test - public void devSourceIsCreated() { - when(devEnvironment.getActiveProfiles()).thenReturn(new String[] { PROFILE_NAME_1 }); - when(devEnvironment.getPropertySources()).thenReturn(sources); - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(devEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/dev" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void multiSourceIsCreated() { - when(multiEnvironment.getActiveProfiles()).thenReturn(new String[] { PROFILE_NAME_1, PROFILE_NAME_2 }); - when(multiEnvironment.getPropertySources()).thenReturn(sources); - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(multiEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/prod,dev" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void storeCreatedWithFeatureFlags() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of("fake_features", new Feature())); - - FeatureFlagStore featureFlagStore = new FeatureFlagStore(); - featureFlagStore.setEnabled(true); - featureFlagStore.validateAndInit(); - - FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("Alpha", false); - featureFlag.setValue(""); - - properties.getStores().get(0).setFeatureFlags(featureFlagStore); - - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of(featureFlag)); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0", - "feature_management" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void storeCreatedWithFeatureFlagsWithMonitoring() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of("fake_features", new Feature())); - - AppConfigurationStoreMonitoring monitoring = new AppConfigurationStoreMonitoring(); - monitoring.setEnabled(true); - FeatureFlagStore featureFlagStore = new FeatureFlagStore(); - featureFlagStore.setEnabled(true); - featureFlagStore.validateAndInit(); - - FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("Alpha", false); - featureFlag.setValue(""); - - properties.getStores().get(0).setFeatureFlags(featureFlagStore); - properties.getStores().get(0).setMonitoring(monitoring); - - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of(featureFlag)); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0", - "feature_management" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void watchedKeyCheck() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - String[] expectedSourceNames = new String[] { - KEY_FILTER + "store1/\0" - }; - assertEquals(expectedSourceNames.length, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void defaultFailFastThrowException() { - when(emptyEnvironment.getActiveProfiles()).thenReturn(new String[] {}); - when(emptyEnvironment.getPropertySources()).thenReturn(sources); - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(configStoreMock.getEndpoint()).thenReturn(TEST_STORE_NAME); - when(configStoreMock.isEnabled()).thenReturn(true); - when(configStoreMock.getSelects()).thenReturn(List.of()); - when(configStoreMock.getFeatureFlags()).thenReturn(featureFlagStoreMock); - when(configStoreMock.isFailFast()).thenReturn(true); - - AppConfigurationStoreTrigger trigger = new AppConfigurationStoreTrigger(); - AppConfigurationStoreMonitoring monitor = new AppConfigurationStoreMonitoring(); - monitor.setEnabled(true); - monitor.setTriggers(List.of(trigger)); - - when(configStoreMock.getMonitoring()).thenReturn(monitor); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, List.of(configStoreMock), replicaLookUpMock, featureFlagClientMock); - - when(replicaClientMock.getWatchKey(Mockito.any(), Mockito.anyString(), Mockito.anyBoolean())).thenThrow(new RuntimeException()); - RuntimeException e = assertThrows(RuntimeException.class, () -> locator.locate(emptyEnvironment)); - assertEquals("Failed to generate property sources for " + TEST_STORE_NAME, e.getMessage()); - verify(configStoreMock, times(1)).isFailFast(); - } - - @Test - public void refreshThrowException() throws IllegalArgumentException { - setupEmptyEnvironment(); - when(replicaClientMock.listSettings(any(), Mockito.anyBoolean())).thenThrow(new RuntimeException()); - - AppConfigurationPropertySourceLocator.STARTUP.set(false); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.getLoadState(Mockito.anyString())).thenReturn(true); - RuntimeException e = assertThrows(RuntimeException.class, () -> locator.locate(emptyEnvironment)); - assertEquals("Failed to generate property sources for store1", e.getMessage()); - } - } - - @Test - public void notFailFastShouldPass() { - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - when(emptyEnvironment.getActiveProfiles()).thenReturn(new String[] {}); - when(emptyEnvironment.getPropertySources()).thenReturn(sources); - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(configStoreMock.getEndpoint()).thenReturn(TEST_STORE_NAME); - when(configStoreMock.isEnabled()).thenReturn(true); - when(configStoreMock.getSelects()).thenReturn(List.of()); - when(configStoreMock.getFeatureFlags()).thenReturn(featureFlagStoreMock); - when(configStoreMock.isFailFast()).thenReturn(false); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, List.of(configStoreMock), replicaLookUpMock, featureFlagClientMock); - - properties.getStores().get(0).setFailFast(false); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - // Once a store fails it should stop attempting to load - verify(configStoreMock, times(3)).isFailFast(); - } - } - - @Test - public void multiplePropertySourcesExistForMultiStores() { - setupEmptyEnvironment(); - when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); - TestUtils.addStore(properties, TEST_STORE_NAME_2, TEST_CONN_STRING_2, KEY_FILTER); - - locator = new AppConfigurationPropertySourceLocator(appProperties, - clientFactoryMock, keyVaultClientFactory, null, properties.getStores(), replicaLookUpMock, - featureFlagClientMock); - - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - String[] expectedSourceNames = new String[] { KEY_FILTER + TEST_STORE_NAME_2 + "/\0", - KEY_FILTER + TEST_STORE_NAME + "/\0" }; - assertEquals(2, sources.size()); - assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - } - } - - @Test - public void awaitOnError() { - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); - when(appPropertiesMock.getPrekillTime()).thenReturn(5); - - ConfigurableEnvironment env = Mockito.mock(ConfigurableEnvironment.class); - MutablePropertySources sources = new MutablePropertySources(); - - sources.addFirst(new PropertySource("refreshArgs") { - - @Override - public Object getProperty(String name) { - return null; - } - }); - - when(env.getPropertySources()).thenReturn(sources); - - String[] array = {}; - when(env.getActiveProfiles()).thenReturn(array); - AppConfigurationKeyValueSelector selectedKeys = new AppConfigurationKeyValueSelector() - .setKeyFilter("/application/"); - List selects = new ArrayList<>(); - selects.add(selectedKeys); - when(configStoreMockError.getSelects()).thenReturn(selects); - when(configStoreMockError.isEnabled()).thenReturn(true); - when(configStoreMockError.isFailFast()).thenReturn(true); - when(configStoreMockError.getEndpoint()).thenReturn(""); - - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenThrow(new NullPointerException("")); - when(appPropertiesMock.getPrekillTime()).thenReturn(-60); - when(appPropertiesMock.getStartDate()).thenReturn(Instant.now()); - - locator = new AppConfigurationPropertySourceLocator(appPropertiesMock, clientFactoryMock, keyVaultClientFactory, - null, List.of(configStoreMockError), replicaLookUpMock, featureFlagClientMock); - - assertThrows(RuntimeException.class, () -> locator.locate(env)); - verify(appPropertiesMock, times(1)).getPrekillTime(); - } - - @Test - public void storeDisabled() { - when(emptyEnvironment.getActiveProfiles()).thenReturn(new String[] {}); - when(emptyEnvironment.getPropertySources()).thenReturn(sources); - properties.getStores().get(0).setEnabled(false); - properties.getStores().get(0).setMonitoring(monitoring); - - locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, - null, stores, replicaLookUpMock, featureFlagClientMock); - try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - stateHolderMock.when(() -> StateHolder.updateState(Mockito.any())).thenReturn(null); - PropertySource source = locator.locate(emptyEnvironment); - assertTrue(source instanceof CompositePropertySource); - - Collection> sources = ((CompositePropertySource) source).getPropertySources(); - - assertEquals(0, sources.size()); - } - } - - private void setupEmptyEnvironment() { - when(emptyEnvironment.getActiveProfiles()).thenReturn(new String[] {}); - when(emptyEnvironment.getPropertySources()).thenReturn(sources); - when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); - } -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtilTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtilTest.java index ba6b62292b8c..161681f4740b 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtilTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtilTest.java @@ -101,13 +101,12 @@ public void cleanup() throws Exception { public void refreshWithoutTimeWatchKeyConfigStoreNotLoaded(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getLoadState(endpoint)).thenReturn(false); - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); } } @@ -115,20 +114,18 @@ public void refreshWithoutTimeWatchKeyConfigStoreNotLoaded(TestInfo testInfo) { public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNotReturned(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); List watchKeys = generateWatchKeys(); State newState = new State(watchKeys, Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); // Config Store doesn't return a watch key change. - when(clientMock.getWatchKey(Mockito.eq(KEY_FILTER), Mockito.eq(EMPTY_LABEL), Mockito.anyBoolean())) - .thenReturn(watchKeys.get(0)); + when(clientMock.getWatchKey(Mockito.eq(KEY_FILTER), Mockito.eq(EMPTY_LABEL), Mockito.anyBoolean())).thenReturn(watchKeys.get(0)); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getLoadState(endpoint)).thenReturn(true); stateHolderMock.when(() -> StateHolder.getState(endpoint)).thenReturn(newState); - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); } } @@ -136,7 +133,6 @@ public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNotReturned(TestInfo te public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNoChange(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); FeatureFlagState newState = new FeatureFlagState( List.of(new FeatureFlags(new SettingSelector().setKeyFilter(KEY_FILTER).setLabelFilter(EMPTY_LABEL), null)), @@ -147,38 +143,36 @@ public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNoChange(TestInfo testI try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); } } - @SuppressWarnings("try") @Test public void refreshWithoutTimeFeatureFlagDisabled(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); configStore.getFeatureFlags().setEnabled(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + stateHolderMock.verify(() -> StateHolder.getLoadState(Mockito.anyString()), times(1)); } } - @SuppressWarnings("try") @Test public void refreshWithoutTimeFeatureFlagNotLoaded(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); configStore.getFeatureFlags().setEnabled(true); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + stateHolderMock.verify(() -> StateHolder.getLoadState(Mockito.anyString()), times(1)); } } @@ -186,8 +180,7 @@ public void refreshWithoutTimeFeatureFlagNotLoaded(TestInfo testInfo) { public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); - + FeatureFlagState newState = new FeatureFlagState( List.of(new FeatureFlags(new SettingSelector().setKeyFilter(KEY_FILTER).setLabelFilter(EMPTY_LABEL), null)), Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); @@ -197,8 +190,8 @@ public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertFalse( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); } } @@ -207,8 +200,7 @@ public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { public void refreshWithoutTimeFeatureFlagEtagChanged(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - when(clientFactoryMock.findOriginForEndpoint(Mockito.eq(endpoint))).thenReturn(endpoint); - + FeatureFlags featureFlags = new FeatureFlags(new SettingSelector(), watchKeysFeatureFlags); FeatureFlagState newState = new FeatureFlagState(List.of(featureFlags), Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); @@ -218,8 +210,8 @@ public void refreshWithoutTimeFeatureFlagEtagChanged(TestInfo testInfo) { try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertTrue( - AppConfigurationRefreshUtil.checkStoreAfterRefreshFailed(clientMock, clientFactoryMock, featureStore)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); + assertTrue(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); } } @@ -241,8 +233,7 @@ public void refreshStoresCheckSettingsTestNotEnabled(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -261,8 +252,7 @@ public void refreshStoresCheckSettingsTestNotLoaded(TestInfo testInfo) { Duration.ofMinutes(10), (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -282,8 +272,7 @@ public void refreshStoresCheckSettingsTestNotRefreshTime(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -305,8 +294,7 @@ public void refreshStoresCheckSettingsTestFailedRequest(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); assertEquals(newState, StateHolder.getState(endpoint)); } } @@ -332,8 +320,7 @@ public void refreshStoresCheckSettingsTestRefreshTimeNoChange(TestInfo testInfo) assertEquals(newState, StateHolder.getState(endpoint)); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -349,8 +336,7 @@ public void refreshStoresCheckSettingsTestTriggerRefresh(TestInfo testInfo) { ConfigurationSetting refreshKey = new ConfigurationSetting().setKey(KEY_FILTER).setLabel(EMPTY_LABEL) .setETag("new"); - when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(refreshKey); + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())).thenReturn(refreshKey); State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); @@ -364,8 +350,7 @@ public void refreshStoresCheckSettingsTestTriggerRefresh(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertTrue(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); verify(currentStateMock, times(1)).updateStateRefresh(Mockito.any(), Mockito.any()); } } @@ -388,8 +373,7 @@ public void refreshStoresCheckFeatureFlagTestNotLoaded(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -411,8 +395,7 @@ public void refreshStoresCheckFeatureFlagTestNotRefreshTime(TestInfo testInfo) { (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } @@ -440,8 +423,7 @@ public void refreshStoresCheckFeatureFlagTestNoChange(TestInfo testInfo) { Duration.ofMinutes(10), (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); verify(currentStateMock, times(1)).updateFeatureFlagStateRefresh(Mockito.any(), Mockito.any()); } @@ -468,8 +450,7 @@ public void refreshStoresCheckFeatureFlagTestTriggerRefresh(TestInfo testInfo) { Duration.ofMinutes(10), (long) 60, replicaLookUpMock); assertTrue(eventData.getDoRefresh()); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), - Mockito.anyBoolean()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()); } } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientBuilderTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientBuilderTest.java index 3dda6cd72a43..c35010596f36 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientBuilderTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientBuilderTest.java @@ -51,7 +51,7 @@ public class AppConfigurationReplicaClientBuilderTest { @Mock private ConfigurationClientBuilderFactory clientFactoryMock; - + @Mock private Environment envMock; @@ -68,7 +68,6 @@ public void setup() { configStore.validateAndInit(); clientBuilder = null; - when(envMock.getActiveProfiles()).thenReturn(new String[0]); } @AfterEach @@ -79,8 +78,7 @@ public void cleanup() throws Exception { @Test public void buildClientFromEndpointTest() { - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); when(clientFactoryMock.build()).thenReturn(builderMock); @@ -102,8 +100,7 @@ public void buildClientFromConnectionStringTest() { configStore.setConnectionString(TEST_CONN_STRING); configStore.validateAndInit(); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); when(clientFactoryMock.build()).thenReturn(builderMock); @@ -120,9 +117,7 @@ public void buildClientFromConnectionStringTest() { @Test public void modifyClientTest() { - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setClientProvider(modifierMock); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, modifierMock, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); @@ -153,8 +148,7 @@ public void buildClientsFromMultipleEndpointsTest() { configStore.validateAndInit(); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); @@ -180,8 +174,7 @@ public void buildClientsFromMultipleConnectionStringsTest() { configStore.validateAndInit(); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); @@ -205,8 +198,7 @@ public void endpointAndConnectionString() { configStore.setConnectionString(TEST_CONN_STRING); configStore.validateAndInit(); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); String message = assertThrows(IllegalArgumentException.class, () -> clientBuilder.buildClients(configStore).get(0)).getMessage(); @@ -216,8 +208,7 @@ public void endpointAndConnectionString() { @Test public void buildClientTest() { - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); when(clientFactoryMock.build()).thenReturn(builderMock); @@ -236,8 +227,7 @@ public void buildClientTest() { @Test public void buildClientConnectionStringTest() { configStore.setConnectionString(TEST_CONN_STRING); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); when(clientFactoryMock.build()).thenReturn(builderMock); @@ -255,8 +245,7 @@ public void buildClientConnectionStringTest() { @Test public void buildClientConnectionStringsTest() { configStore.setConnectionStrings(List.of(TEST_CONN_STRING, TEST_CONN_STRING_GEO)); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); when(builderMock.addPolicy(Mockito.any())).thenReturn(builderMock); @@ -274,8 +263,7 @@ public void buildClientConnectionStringsTest() { @Test public void buildClientConnectionStringInvalidTest() { configStore.setConnectionString(TEST_CONN_STRING); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, @@ -287,8 +275,7 @@ public void buildClientConnectionStringInvalidTest() { @Test public void buildClientConnectionStringInvalid2Test() { configStore.setConnectionString("Not A Connection String"); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, @@ -300,8 +287,7 @@ public void buildClientConnectionStringInvalid2Test() { @Test public void buildClientConnectionStringInvalid3Test() { configStore.setConnectionString("Not;A;Connection String"); - clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, false); - clientBuilder.setEnvironment(envMock); + clientBuilder = new AppConfigurationReplicaClientsBuilder(0, clientFactoryMock, null, false, false); AppConfigurationReplicaClientsBuilder spy = Mockito.spy(clientBuilder); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactoryTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactoryTest.java index 3be2260be0f4..186a6ee8db94 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientFactoryTest.java @@ -3,8 +3,6 @@ package com.azure.spring.cloud.appconfiguration.config.implementation; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; @@ -79,12 +77,4 @@ public void findOriginTest() { assertEquals(invalidReplica, clientFactory.findOriginForEndpoint(invalidReplica)); } - @Test - public void hasReplicasTest() { - assertTrue(clientFactory.hasReplicas(originEndpoint)); - assertTrue(clientFactory.hasReplicas(replica1)); - assertFalse(clientFactory.hasReplicas(invalidReplica)); - assertFalse(clientFactory.hasReplicas(noReplicaEndpoint)); - } - } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientTest.java index 99c71ea169a0..9e328cffd751 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientTest.java @@ -37,7 +37,6 @@ import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.PagedResponseBase; import com.azure.core.http.rest.Response; -import com.azure.core.util.Configuration; import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.ConfigurationSnapshot; @@ -45,7 +44,6 @@ import com.azure.data.appconfiguration.models.SettingSelector; import com.azure.data.appconfiguration.models.SnapshotComposition; import com.azure.identity.CredentialUnavailableException; -import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.TracingInfo; import reactor.core.publisher.Mono; @@ -67,7 +65,10 @@ public class AppConfigurationReplicaClientTest { private Supplier>> supplierMock; @Mock - private Response mockResponse; + private Response configurationSettingResponse; + + @Mock + private Response snapshotResponseMock; private final String endpoint = "clientTest.azconfig.io"; @@ -87,20 +88,17 @@ public void cleanup() throws Exception { @Test public void getWatchKeyTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); ConfigurationSetting watchKey = new ConfigurationSetting().setKey("watch").setLabel("\0"); - when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), - Mockito.any())).thenReturn(mockResponse); - when(mockResponse.getValue()).thenReturn(watchKey); + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), + Mockito.any())).thenReturn(configurationSettingResponse); + when(configurationSettingResponse.getValue()).thenReturn(watchKey); - //assertEquals(watchKey, client.getWatchKey("watch", "\0", false)); + assertEquals(watchKey, client.getWatchKey("watch", "\0", false)); - when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), - Mockito.any())).thenReturn(mockResponse); - when(mockResponse.getValue()).thenThrow(exceptionMock); + when(configurationSettingResponse.getValue()).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); @@ -114,15 +112,14 @@ public void getWatchKeyTest() { when(responseMock.getStatusCode()).thenReturn(499); assertThrows(HttpResponseException.class, () -> client.getWatchKey("watch", "\0", false)); - when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.any())).thenThrow(new UncheckedIOException(new UnknownHostException())); assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); } @Test public void listSettingsTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); ConfigurationSetting configurationSetting = new ConfigurationSetting().setKey("test-key"); List configurations = List.of(configurationSetting); @@ -154,8 +151,7 @@ public void listSettingsTest() { @Test public void listFeatureFlagsTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("Alpha", false); List configurations = List.of(featureFlag); @@ -192,8 +188,7 @@ public void listFeatureFlagsTest() { @Test public void listSettingsUnknownHostTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new UncheckedIOException(new UnknownHostException())); @@ -202,8 +197,7 @@ public void listSettingsUnknownHostTest() { @Test public void listSettingsNoCredentialTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new CredentialUnavailableException("No Credential")); @@ -213,12 +207,9 @@ public void listSettingsNoCredentialTest() { @Test public void getWatchNoCredentialTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); - when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), - Mockito.any())).thenReturn(mockResponse); - when(mockResponse.getValue()) + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.any())) .thenThrow(new CredentialUnavailableException("No Credential")); assertThrows(CredentialUnavailableException.class, () -> client.getWatchKey("key", "label", false)); @@ -226,8 +217,7 @@ public void getWatchNoCredentialTest() { @Test public void backoffTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); // Setups in the past and with no errors. assertTrue(client.getBackoffEndTime().isBefore(Instant.now())); @@ -255,55 +245,57 @@ public void backoffTest() { @Test public void listSettingSnapshotTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); List configurations = new ArrayList<>(); ConfigurationSnapshot snapshot = new ConfigurationSnapshot(null); snapshot.setSnapshotComposition(SnapshotComposition.KEY); - when(clientMock.getSnapshot(Mockito.any())).thenReturn(snapshot); + when(clientMock.getSnapshotWithResponse(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(snapshotResponseMock); + when(snapshotResponseMock.getValue()).thenReturn(snapshot); when(clientMock.listConfigurationSettingsForSnapshot(Mockito.any())).thenReturn(settingsMock); - assertEquals(configurations, client.listSettingSnapshot("SnapshotName")); + assertEquals(configurations, client.listSettingSnapshot("SnapshotName", false)); when(clientMock.listConfigurationSettingsForSnapshot(Mockito.any())).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName")); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName")); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName")); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listSettingSnapshot("SnapshotName")); + assertThrows(HttpResponseException.class, () -> client.listSettingSnapshot("SnapshotName", false)); - when(clientMock.getSnapshot(Mockito.any())).thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName")); + when(clientMock.getSnapshotWithResponse(Mockito.any(), Mockito.any(), Mockito.any())) + .thenThrow(new UncheckedIOException(new UnknownHostException())); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); } @Test public void listSettingSnapshotInvalidCompositionTypeTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); ConfigurationSnapshot snapshot = new ConfigurationSnapshot(null); snapshot.setSnapshotComposition(SnapshotComposition.KEY_LABEL); - when(clientMock.getSnapshot(Mockito.any())).thenReturn(snapshot); + when(clientMock.getSnapshotWithResponse(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(snapshotResponseMock); + when(snapshotResponseMock.getValue()).thenReturn(snapshot); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, - () -> client.listSettingSnapshot("SnapshotName")); + () -> client.listSettingSnapshot("SnapshotName", false)); assertEquals("Snapshot SnapshotName needs to be of type Key.", e.getMessage()); } @Test public void updateSyncTokenTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); String fakeToken = "fake_sync_token"; client.updateSyncToken(fakeToken); @@ -316,8 +308,7 @@ public void updateSyncTokenTest() { @Test public void checkWatchKeysTest() { - AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock); FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("Alpha", false); List configurations = List.of(featureFlag); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManagerTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManagerTest.java index ff6a30fed45b..bdaa51cecaf7 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManagerTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/ConnectionManagerTest.java @@ -4,7 +4,6 @@ import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_ENDPOINT; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -118,8 +117,6 @@ public void backoffTest() { expectedEndpoints.add(replicaEndpoint); assertEquals(2, connectionManager.getAvailableClients().size()); - assertEquals(2, connectionManager.getAllEndpoints().size()); - assertTrue(connectionManager.getAllEndpoints().containsAll(expectedEndpoints)); assertEquals(AppConfigurationStoreHealth.UP, connectionManager.getHealth()); connectionManager.backoffClient(originEndpoint); @@ -129,15 +126,11 @@ public void backoffTest() { when(replicaClient1.getBackoffEndTime()).thenReturn(Instant.now().plusSeconds(1000)); assertEquals(1, connectionManager.getAvailableClients().size()); - assertEquals(2, connectionManager.getAllEndpoints().size()); - assertTrue(connectionManager.getAllEndpoints().containsAll(expectedEndpoints)); assertEquals(AppConfigurationStoreHealth.UP, connectionManager.getHealth()); connectionManager.backoffClient(originEndpoint); assertEquals(1, connectionManager.getAvailableClients().size()); - assertEquals(2, connectionManager.getAllEndpoints().size()); - assertTrue(connectionManager.getAllEndpoints().containsAll(expectedEndpoints)); assertEquals(AppConfigurationStoreHealth.UP, connectionManager.getHealth()); connectionManager.backoffClient(replicaEndpoint); @@ -146,8 +139,6 @@ public void backoffTest() { when(replicaClient2.getBackoffEndTime()).thenReturn(Instant.now().plusSeconds(1000)); assertEquals(0, connectionManager.getAvailableClients().size()); - assertEquals(2, connectionManager.getAllEndpoints().size()); - assertTrue(connectionManager.getAllEndpoints().containsAll(expectedEndpoints)); assertEquals(AppConfigurationStoreHealth.DOWN, connectionManager.getHealth()); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClientTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClientTest.java index f473388bd8c3..07c2e5a56fa2 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClientTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/FeatureFlagClientTest.java @@ -2,18 +2,18 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.DEFAULT_ROLLOUT_PERCENTAGE; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.E_TAG; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.FEATURE_FLAG_CONTENT_TYPE; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.FEATURE_FLAG_ID; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.FEATURE_FLAG_REFERENCE; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.GROUPS; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.USERS; +import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.DEFAULT_ROLLOUT_PERCENTAGE; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FEATURE_LABEL; +import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FEATURE_VALUE_ALL; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FEATURE_VALUE_TELEMETRY; +import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.GROUPS; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_ENDPOINT; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_E_TAG; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FEATURE_VALUE_ALL; +import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.USERS; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestUtils.createItemFeatureFlag; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -78,8 +78,7 @@ public void loadFeatureFlagsTestNoFeatureFlags() { FeatureFlags featureFlags = new FeatureFlags(null, settings); when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, - false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals("FakeKey", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -93,8 +92,7 @@ public void loadFeatureFlagsTestFeatureFlags() { FeatureFlags featureFlags = new FeatureFlags(null, settings); when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, - false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/Alpha", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -109,8 +107,7 @@ public void loadFeatureFlagsTestMultipleLoads() { FeatureFlags featureFlags = new FeatureFlags(null, settings); when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, - false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/Alpha", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -168,8 +165,7 @@ public void loadFeatureFlagsTestTargetingFilter() { FeatureFlags featureFlags = new FeatureFlags(null, settings); when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, - false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/TargetingTest", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolderTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolderTest.java index 184a33374bb7..3b10d736f8ec 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolderTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/StateHolderTest.java @@ -180,7 +180,7 @@ private void updateNextRefreshBackoffCalcTest(TestInfo testInfo) { private void loadStateTest(TestInfo testInfo) { String endpoint = testInfo.getDisplayName() + "updateRefreshTimeBackoffCalc" + ".azconfig.io"; StateHolder testStateHolder = new StateHolder(); - testStateHolder.setLoadState(endpoint, true, false); + testStateHolder.setLoadState(endpoint, true); StateHolder.updateState(testStateHolder); assertEquals(testStateHolder.getLoadState().get(endpoint), StateHolder.getLoadState(endpoint)); assertEquals(testStateHolder, StateHolder.getCurrentState()); diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/TestConstants.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/TestConstants.java index 63903d1ef5ec..48e379747e3b 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/TestConstants.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/TestConstants.java @@ -6,6 +6,22 @@ * Test constants which can be shared across different test classes */ public final class TestConstants { + + /** + * Http Header Correlation Context + */ + public static final String CORRELATION_CONTEXT = "Correlation-Context"; + + /** + * App Configurations Key Vault Reference Content Type + */ + public static final String KEY_VAULT_CONTENT_TYPE = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; + + public static final String USERS = "users"; + + public static final String GROUPS = "groups"; + + public static final String DEFAULT_ROLLOUT_PERCENTAGE = "defaultRolloutPercentage"; // Store specific configuration public static final String TEST_STORE_NAME = "store1"; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfigurationTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfigurationTest.java deleted file mode 100644 index 93202173f069..000000000000 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/config/AppConfigurationBootstrapConfigurationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.appconfiguration.config.implementation.config; - -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.CONN_STRING_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FAIL_FAST_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.STORE_ENDPOINT_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_CONN_STRING; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_STORE_NAME; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestUtils.propPair; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; - -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationPropertySourceLocator; -import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientFactory; -import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; - -public class AppConfigurationBootstrapConfigurationTest { - - private static final ApplicationContextRunner CONTEXT_RUNNER = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AppConfigurationBootstrapConfiguration.class, - AzureGlobalPropertiesAutoConfiguration.class)) - .withPropertyValues(propPair("spring.cloud.azure.appconfiguration.enabled", "true")); - - @Test - public void iniConnectionStringSystemAssigned() { - CONTEXT_RUNNER - .withPropertyValues(propPair(STORE_ENDPOINT_PROP, TEST_STORE_NAME), propPair(FAIL_FAST_PROP, "false")) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class)); - } - - @Test - public void iniConnectionStringUserAssigned() { - CONTEXT_RUNNER - .withPropertyValues(propPair(STORE_ENDPOINT_PROP, TEST_STORE_NAME), propPair(FAIL_FAST_PROP, "false"), - propPair("spring.cloud.azure.appconfiguration.managed-identity.client-id", "client-id")) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class)); - } - - @Test - public void propertySourceLocatorBeanCreated() { - CONTEXT_RUNNER - .withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING), propPair(FAIL_FAST_PROP, "false")) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class)); - } - - @Test - public void clientsBeanCreated() { - CONTEXT_RUNNER - .withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING)) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationReplicaClientFactory.class)); - } -} diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicyTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicyTest.java index c08c29975ffa..d3bfdea27cef 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicyTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/BaseAppConfigurationPolicyTest.java @@ -2,10 +2,7 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation.http.policy; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.CORRELATION_CONTEXT; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.DEV_ENV_TRACING; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.KEY_VAULT_CONFIGURED_TRACING; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.USER_AGENT_TYPE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @@ -24,11 +21,13 @@ import org.mockito.MockitoAnnotations; import org.mockito.junit.jupiter.MockitoExtension; +import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpMethod; import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; import com.azure.core.http.HttpRequest; import com.azure.core.util.Configuration; +import com.azure.spring.cloud.appconfiguration.config.implementation.RequestTracingConstants; // This test class needs to be isolated and ran sequential as it uses BaseAppConfigurationPolicy.setWatchRequests // which mutates a global static and can result in race condition failures. @@ -55,27 +54,28 @@ public void cleanup() throws Exception { MockitoAnnotations.openMocks(this).close(); } - @SuppressWarnings("deprecation") @Test public void startupThenWatchUpdateTest() throws MalformedURLException { URL url = new URL("https://www.test.url/kv"); HttpRequest request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); + request.setHeader(HttpHeaderName.USER_AGENT, "PreExistingUserAgent"); BaseAppConfigurationPolicy policy = new BaseAppConfigurationPolicy( - new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); + new TracingInfo(false, 0, Configuration.getGlobalConfiguration())); when(contextMock.getHttpRequest()).thenReturn(request); policy.process(contextMock, nextMock); - String userAgent = contextMock.getHttpRequest().getHeaders().get(USER_AGENT_TYPE).getValue(); + String userAgent = contextMock.getHttpRequest().getHeaders().get(HttpHeaderName.USER_AGENT).getValue(); assertEquals("null/null " + PRE_USER_AGENT, userAgent); assertEquals("RequestType=Startup", - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); + contextMock.getHttpRequest().getHeaders() + .get(HttpHeaderName.fromString(RequestTracingConstants.CORRELATION_CONTEXT_HEADER.toString())) + .getValue()); request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); + request.setHeader(HttpHeaderName.USER_AGENT, "PreExistingUserAgent"); when(contextMock.getHttpRequest()).thenReturn(request); when(contextMock.getData("refresh")).thenReturn(Optional.of(true)); @@ -85,10 +85,12 @@ public void startupThenWatchUpdateTest() throws MalformedURLException { assertEquals("null/null " + PRE_USER_AGENT, userAgent); assertEquals("RequestType=Watch", - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); + contextMock.getHttpRequest().getHeaders() + .get(HttpHeaderName.fromString(RequestTracingConstants.CORRELATION_CONTEXT_HEADER.toString())) + .getValue()); request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); + request.setHeader(HttpHeaderName.USER_AGENT, "PreExistingUserAgent"); when(contextMock.getHttpRequest()).thenReturn(request); @@ -96,55 +98,26 @@ public void startupThenWatchUpdateTest() throws MalformedURLException { assertEquals("null/null " + PRE_USER_AGENT, userAgent); assertEquals("RequestType=Watch", - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); + contextMock.getHttpRequest().getHeaders() + .get(HttpHeaderName.fromString(RequestTracingConstants.CORRELATION_CONTEXT_HEADER.toString())) + .getValue()); } - @SuppressWarnings("deprecation") - @Test - public void devIsConfigured() throws MalformedURLException { - BaseAppConfigurationPolicy policy = new BaseAppConfigurationPolicy( - new TracingInfo(true, false, 0, Configuration.getGlobalConfiguration())); - - URL url = new URL("https://www.test.url/kv"); - HttpRequest request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); - when(contextMock.getHttpRequest()).thenReturn(request); - - policy.process(contextMock, nextMock); - assertEquals("RequestType=Startup,Env=" + DEV_ENV_TRACING, - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); - } - - @SuppressWarnings("deprecation") @Test public void keyVaultIsConfigured() throws MalformedURLException { BaseAppConfigurationPolicy policy = new BaseAppConfigurationPolicy( - new TracingInfo(false, true, 0, Configuration.getGlobalConfiguration())); + new TracingInfo(true, 0, Configuration.getGlobalConfiguration())); URL url = new URL("https://www.test.url/kv"); HttpRequest request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); + request.setHeader(HttpHeaderName.USER_AGENT, "PreExistingUserAgent"); when(contextMock.getHttpRequest()).thenReturn(request); policy.process(contextMock, nextMock); assertEquals("RequestType=Startup," + KEY_VAULT_CONFIGURED_TRACING, - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); - } - - @SuppressWarnings("deprecation") - @Test - public void devAndKeyVaultAreConfigured() throws MalformedURLException { - BaseAppConfigurationPolicy policy = new BaseAppConfigurationPolicy( - new TracingInfo(true, true, 0, Configuration.getGlobalConfiguration())); - - URL url = new URL("https://www.test.url/kv"); - HttpRequest request = new HttpRequest(HttpMethod.GET, url); - request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); - when(contextMock.getHttpRequest()).thenReturn(request); - - policy.process(contextMock, nextMock); - assertEquals("RequestType=Startup,Env=" + DEV_ENV_TRACING + "," + KEY_VAULT_CONFIGURED_TRACING, - contextMock.getHttpRequest().getHeaders().get(CORRELATION_CONTEXT).getValue()); + contextMock.getHttpRequest().getHeaders() + .get(HttpHeaderName.fromString(RequestTracingConstants.CORRELATION_CONTEXT_HEADER.toString())) + .getValue()); } } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfoTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfoTest.java index 67c3aba02153..2bdcdd238590 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfoTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/http/policy/TracingInfoTest.java @@ -23,20 +23,17 @@ public class TracingInfoTest { public void getValueTest() { Configuration configuration = getConfiguration("false"); - TracingInfo tracingInfo = new TracingInfo(false, false, 0, configuration); + TracingInfo tracingInfo = new TracingInfo(false, 0, configuration); assertEquals("RequestType=Startup", tracingInfo.getValue(false)); assertEquals("RequestType=Watch", tracingInfo.getValue(true)); - tracingInfo = new TracingInfo(true, false, 0, configuration); - assertEquals("RequestType=Startup,Env=Dev", tracingInfo.getValue(false)); - - tracingInfo = new TracingInfo(false, true, 0, configuration); + tracingInfo = new TracingInfo(true, 0, configuration); assertEquals("RequestType=Startup,UsesKeyVault", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 1, configuration); + tracingInfo = new TracingInfo(false, 1, configuration); assertEquals("RequestType=Startup,ReplicaCount=1", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 0, configuration); + tracingInfo = new TracingInfo(false, 0, configuration); tracingInfo.getFeatureFlagTracing().updateFeatureFilterTelemetry("Random"); assertEquals("RequestType=Startup,Filter=CSTM", tracingInfo.getValue(false)); @@ -44,19 +41,19 @@ public void getValueTest() { @Test public void disableTracingTest() { - TracingInfo tracingInfo = new TracingInfo(false, false, 0, getConfiguration(null)); + TracingInfo tracingInfo = new TracingInfo(false, 0, getConfiguration(null)); assertNotEquals("", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 0, getConfiguration("")); + tracingInfo = new TracingInfo(false, 0, getConfiguration("")); assertNotEquals("", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 0, getConfiguration("true")); + tracingInfo = new TracingInfo(false, 0, getConfiguration("true")); assertEquals("", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 0, getConfiguration("false")); + tracingInfo = new TracingInfo(false, 0, getConfiguration("false")); assertNotEquals("", tracingInfo.getValue(false)); - tracingInfo = new TracingInfo(false, false, 0, getConfiguration("random string")); + tracingInfo = new TracingInfo(false, 0, getConfiguration("random string")); assertNotEquals("", tracingInfo.getValue(false)); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationPropertiesTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationPropertiesTest.java index e8d839e22faa..6641e1ee7108 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationPropertiesTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationPropertiesTest.java @@ -2,35 +2,18 @@ // Licensed under the MIT License. package com.azure.spring.cloud.appconfiguration.config.implementation.properties; -import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientsBuilder.ENDPOINT_ERR_MSG; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.CONN_STRING_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.CONN_STRING_PROP_NEW; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.FAIL_FAST_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.KEY_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.LABEL_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.REFRESH_INTERVAL_PROP; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.STORE_ENDPOINT_PROP; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_CONN_STRING; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_ENDPOINT; import static com.azure.spring.cloud.appconfiguration.config.implementation.TestConstants.TEST_ENDPOINT_GEO; -import static com.azure.spring.cloud.appconfiguration.config.implementation.TestUtils.propPair; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.time.Duration; import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockitoAnnotations; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; - -import com.azure.spring.cloud.appconfiguration.config.implementation.config.AppConfigurationBootstrapConfiguration; -import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; public class AppConfigurationPropertiesTest { @@ -43,29 +26,21 @@ public class AppConfigurationPropertiesTest { private static final String VALID_KEY = "/application/"; private static final String ILLEGAL_LABELS = "*,my-label"; + + private AppConfigurationProperties properties; - @InjectMocks - private ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AppConfigurationBootstrapConfiguration.class, - AzureGlobalPropertiesAutoConfiguration.class)) - .withPropertyValues("spring.cloud.azure.appconfiguration.endpoint=https://test-appconfig.azconfig.io"); @BeforeEach public void setup() { - MockitoAnnotations.openMocks(this); - } - - @AfterEach - public void cleanup() throws Exception { - MockitoAnnotations.openMocks(this).close(); + properties = new AppConfigurationProperties(); + properties.setStores(List.of(new ConfigStore())); } @Test public void validInputShouldCreatePropertiesBean() { - this.contextRunner - .withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING)) - .withPropertyValues(propPair(FAIL_FAST_PROP, "false")) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationProperties.class)); + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(TEST_CONN_STRING); + store.validateAndInit(); } @Test @@ -84,55 +59,54 @@ public void secretMustExistInConnectionString() { } private void testConnStringFields(String connString) { - this.contextRunner - .withPropertyValues(propPair(CONN_STRING_PROP, connString)) - .run(context -> assertThat(context).getFailure().hasStackTraceContaining(ENDPOINT_ERR_MSG)); + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(connString); + IllegalStateException e = assertThrows(IllegalStateException.class, () -> properties.validateAndInit()); + assertEquals("Connection string does not follow format Endpoint=([^;]+);Id=([^;]+);Secret=([^;]+).", e.getMessage()); } @Test public void asteriskShouldNotBeIncludedInTheLabels() { - this.contextRunner - .withPropertyValues( - propPair(CONN_STRING_PROP, TEST_CONN_STRING), - propPair(KEY_PROP, VALID_KEY), - propPair(LABEL_PROP, ILLEGAL_LABELS)) - .run(context -> assertThat(context) - .getFailure() - .hasStackTraceContaining("LabelFilter must not contain asterisk(*)")); + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(TEST_CONN_STRING); + AppConfigurationKeyValueSelector select = new AppConfigurationKeyValueSelector(); + select.setKeyFilter(VALID_KEY); + select.setLabelFilter(ILLEGAL_LABELS); + store.setSelects(List.of(select)); + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> properties.validateAndInit()); + assertEquals("LabelFilter must not contain asterisk(*)", e.getMessage()); } @Test public void storeNameCanBeInitIfConnectionStringConfigured() { - this.contextRunner - .withPropertyValues( - propPair(CONN_STRING_PROP, TEST_CONN_STRING), - propPair(STORE_ENDPOINT_PROP, "")) - .withPropertyValues(propPair(FAIL_FAST_PROP, "false")) - .run(context -> { - AppConfigurationProperties properties = context.getBean(AppConfigurationProperties.class); - assertThat(properties.getStores()).isNotNull(); - assertThat(properties.getStores().size()).isEqualTo(1); - assertThat(properties.getStores().get(0).getEndpoint()).isEqualTo("https://fake.test.config.io"); - }); + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(TEST_CONN_STRING); + store.setEndpoint(""); + store.validateAndInit(); + assertEquals(1, properties.getStores().size()); + assertEquals("https://fake.test.config.io", properties.getStores().get(0).getEndpoint()); } @Test public void duplicateConnectionStringIsNotAllowed() { - this.contextRunner - .withPropertyValues( - propPair(CONN_STRING_PROP, TEST_CONN_STRING), - propPair(CONN_STRING_PROP_NEW, TEST_CONN_STRING)) - .run(context -> assertThat(context) - .getFailure() - .hasStackTraceContaining("Duplicate store name exists")); + properties = new AppConfigurationProperties(); + properties.setStores(List.of(new ConfigStore(), new ConfigStore())); + + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(TEST_CONN_STRING); + ConfigStore newStore = properties.getStores().get(1); + newStore.setConnectionString(TEST_CONN_STRING); + + java.lang.IllegalArgumentException e = assertThrows(java.lang.IllegalArgumentException.class, () -> properties.validateAndInit()); + assertEquals("Duplicate store name exists.", e.getMessage()); } @Test public void minValidWatchTime() { - this.contextRunner - .withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING)) - .withPropertyValues(propPair(REFRESH_INTERVAL_PROP, "1s")) - .run(context -> assertThat(context).hasSingleBean(AppConfigurationProperties.class)); + ConfigStore store = properties.getStores().get(0); + store.setConnectionString(TEST_CONN_STRING); + properties.setRefreshInterval(Duration.ofSeconds(1)); + properties.validateAndInit(); } @Test diff --git a/sdk/spring/spring-cloud-azure-feature-management/src/test/java/com/azure/spring/cloud/feature/management/ValidationsTest.java b/sdk/spring/spring-cloud-azure-feature-management/src/test/java/com/azure/spring/cloud/feature/management/ValidationsTest.java deleted file mode 100644 index 35d43967f85e..000000000000 --- a/sdk/spring/spring-cloud-azure-feature-management/src/test/java/com/azure/spring/cloud/feature/management/ValidationsTest.java +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.spring.cloud.feature.management; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.stream.Stream; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.azure.spring.cloud.feature.management.filters.TargetingFilter; -import com.azure.spring.cloud.feature.management.filters.TargetingFilterTestContextAccessor; -import com.azure.spring.cloud.feature.management.filters.TimeWindowFilter; -import com.azure.spring.cloud.feature.management.implementation.FeatureManagementConfigProperties; -import com.azure.spring.cloud.feature.management.implementation.FeatureManagementProperties; -import com.azure.spring.cloud.feature.management.validationstests.models.ValidationTestCase; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.type.CollectionType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -@ExtendWith(SpringExtension.class) -public class ValidationsTest { - @Mock - private ApplicationContext context; - - @Mock - private FeatureManagementConfigProperties configProperties; - - private static final Logger LOGGER = LoggerFactory.getLogger(ValidationsTest.class); - - private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder() - .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); - - private static final String TEST_CASE_FOLDER_PATH = "validations-tests"; - - private final String inputsUser = "user"; - - private final String inputsGroups = "groups"; - - private static final String SAMPLE_FILE_NAME_FILTER = "sample"; - - private static final String TESTS_FILE_NAME_FILTER = "tests"; - - @BeforeEach - public void setup() { - MockitoAnnotations.openMocks(this); - when(configProperties.isFailFast()).thenReturn(true); - when(context.getBean(Mockito.contains("TimeWindow"))).thenReturn(new TimeWindowFilter()); - } - - @AfterEach - public void cleanup() throws Exception { - MockitoAnnotations.openMocks(this).close(); - } - - private boolean hasException(ValidationTestCase testCase) { - final String exceptionStr = testCase.getIsEnabled().getException(); - return exceptionStr != null && !exceptionStr.isEmpty(); - } - - private boolean hasInput(ValidationTestCase testCase) { - final LinkedHashMap inputsMap = testCase.getInputs(); - return inputsMap != null && !inputsMap.isEmpty(); - } - - private static File[] getFileList(String fileNameFilter) { - final URL folderUrl = Thread.currentThread().getContextClassLoader().getResource(TEST_CASE_FOLDER_PATH); - assert folderUrl != null; - - final File folderFile = new File(folderUrl.getFile()); - final File[] filteredFiles = folderFile - .listFiles(pathname -> pathname.getName().toLowerCase().contains(fileNameFilter)); - assert filteredFiles != null; - - Arrays.sort(filteredFiles, Comparator.comparing(File::getName)); - return filteredFiles; - } - - private List readTestcasesFromFile(File testFile) throws IOException { - final String jsonString = Files.readString(testFile.toPath()); - final CollectionType typeReference = TypeFactory.defaultInstance().constructCollectionType(List.class, - ValidationTestCase.class); - return OBJECT_MAPPER.readValue(jsonString, typeReference); - } - - @SuppressWarnings("unchecked") - private static LinkedHashMap readConfigurationFromFile(File sampleFile) throws IOException { - final String jsonString = Files.readString(sampleFile.toPath()); - final LinkedHashMap configurations = OBJECT_MAPPER.readValue(jsonString, new TypeReference<>() { - }); - final Object featureManagementSection = configurations.get("feature_management"); - if (featureManagementSection.getClass().isAssignableFrom(LinkedHashMap.class)) { - return (LinkedHashMap) featureManagementSection; - } - throw new IllegalArgumentException("feature_management part is not a map"); - } - - static Stream testProvider() throws IOException { - List arguments = new ArrayList<>(); - File[] files = getFileList(TESTS_FILE_NAME_FILTER); - - final File[] sampleFiles = getFileList(SAMPLE_FILE_NAME_FILTER); - List properties = new ArrayList<>(); - for (File sampleFile : sampleFiles) { - final FeatureManagementProperties managementProperties = new FeatureManagementProperties(); - managementProperties.putAll(readConfigurationFromFile(sampleFile)); - properties.add(managementProperties); - } - - for (int i = 0; i < files.length; i++) { - if (files[i].getName().contains(("TargetingFilter"))) { - continue; // TODO(mametcal). Not run the test case until we release the little endian fix - } - arguments.add(Arguments.of(files[i].getName(), files[i], properties.get(i))); - } - - return arguments.stream(); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("testProvider") - void validationTest(String name, File testsFile, FeatureManagementProperties managementProperties) - throws IOException { - LOGGER.debug("Running test case from file: " + name); - final FeatureManager featureManager = new FeatureManager(context, managementProperties, configProperties); - List testCases = readTestcasesFromFile(testsFile); - for (ValidationTestCase testCase : testCases) { - LOGGER.debug("Test case : " + testCase.getDescription()); - if (hasException(testCase)) { // TODO(mametcal). Currently we didn't throw the exception when parameter is - // invalid - assertNull(managementProperties.getOnOff().get(testCase.getFeatureFlagName())); - continue; - } - if (hasInput(testCase)) { // Set inputs - final Object userObj = testCase.getInputs().get(inputsUser); - final Object groupsObj = testCase.getInputs().get(inputsGroups); - final String user = userObj != null ? userObj.toString() : null; - @SuppressWarnings("unchecked") - final List groups = groupsObj != null ? (List) groupsObj : null; - when(context.getBean(Mockito.contains("Targeting"))) - .thenReturn(new TargetingFilter(new TargetingFilterTestContextAccessor(user, groups))); - } - - final Boolean result = featureManager.isEnabled(testCase.getFeatureFlagName()); - assertEquals(result.toString(), testCase.getIsEnabled().getResult()); - } - } -}