From 147b2ed1788a8bdb9c31d7e9a4d67a72d237ed4e Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 21 Aug 2024 10:27:53 -0700 Subject: [PATCH 1/4] Updating correlation context to use pipeline context --- ...ationApplicationSettingPropertySource.java | 4 +- .../AppConfigurationPropertySource.java | 2 +- ...AppConfigurationPropertySourceLocator.java | 6 +- .../AppConfigurationPullRefresh.java | 2 - .../AppConfigurationRefreshUtil.java | 8 +- .../AppConfigurationReplicaClient.java | 23 +++-- .../implementation/FeatureFlagClient.java | 4 +- .../config/implementation/HostType.java | 7 +- .../RequestTracingConstants.java | 5 + .../policy/BaseAppConfigurationPolicy.java | 25 ++--- .../http/policy/TracingInfo.java | 20 +++- ...nApplicationSettingPropertySourceTest.java | 19 ++-- ...nfigurationPropertySourceKeyVaultTest.java | 19 ++-- ...onfigurationPropertySourceLocatorTest.java | 28 +++--- .../AppConfigurationRefreshUtilTest.java | 48 ++++++---- .../AppConfigurationReplicaClientTest.java | 92 +++++++++++-------- .../implementation/FeatureFlagClientTest.java | 24 +++-- .../BaseAppConfigurationPolicyTest.java | 4 +- 18 files changed, 200 insertions(+), 140 deletions(-) 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 c6fbf1b5e18b..5d410ab33151 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 @@ -60,7 +60,7 @@ class AppConfigurationApplicationSettingPropertySource extends AppConfigurationP * @param keyPrefixTrimValues prefixs to trim from key values * @throws InvalidConfigurationPropertyValueException thrown if fails to parse Json content type */ - public void initProperties(List keyPrefixTrimValues) throws InvalidConfigurationPropertyValueException { + public void initProperties(List keyPrefixTrimValues, boolean isRefresh) throws InvalidConfigurationPropertyValueException { List labels = Arrays.asList(labelFilters); // Reverse labels so they have the right priority order. @@ -70,7 +70,7 @@ public void initProperties(List keyPrefixTrimValues) throws InvalidConfi SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter + "*").setLabelFilter(label); // * for wildcard match - processConfigurationSettings(replicaClient.listSettings(settingSelector), settingSelector.getKeyFilter(), + processConfigurationSettings(replicaClient.listSettings(settingSelector, isRefresh), settingSelector.getKeyFilter(), keyPrefixTrimValues); } } 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 6877a09a014b..c31069689625 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 @@ -55,5 +55,5 @@ protected static String getLabelName(String[] labelFilters) { return String.join(",", labelFilters); } - protected abstract void initProperties(List trim) throws InvalidConfigurationPropertyValueException; + protected abstract void initProperties(List trim, boolean isRefresh) throws InvalidConfigurationPropertyValueException; } 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 index 824d3017cfa6..04de646b0631 100644 --- 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 @@ -200,7 +200,7 @@ private void setupMonitoring(ConfigStore configStore, AppConfigurationReplicaCli if (monitoring.isEnabled()) { // Setting new ETag values for Watch List watchKeysSettings = monitoring.getTriggers().stream() - .map(trigger -> client.getWatchKey(trigger.getKey(), trigger.getLabel())).toList(); + .map(trigger -> client.getWatchKey(trigger.getKey(), trigger.getLabel(), !STARTUP.get())).toList(); newState.setState(configStore.getEndpoint(), watchKeysSettings, monitoring.getRefreshInterval()); } @@ -258,7 +258,7 @@ private List createSettings(AppConfigurationRepl selectedKeys.getKeyFilter() + store.getEndpoint() + "/", client, keyVaultClientFactory, selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles)); } - propertySource.initProperties(store.getTrimKeyPrefix()); + propertySource.initProperties(store.getTrimKeyPrefix(), !STARTUP.get()); sourceList.add(propertySource); } @@ -281,7 +281,7 @@ private List createFeatureFlags(AppConfigurationReplicaClient clie if (store.getFeatureFlags().getEnabled()) { for (FeatureFlagKeyValueSelector selectedKeys : store.getFeatureFlags().getSelects()) { List storesFeatureFlags = featureFlagClient.loadFeatureFlags(client, - selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles)); + selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles), !STARTUP.get()); storesFeatureFlags.forEach(featureFlags -> featureFlags.setConfigStore(store)); featureFlagWatchKeys.addAll(storesFeatureFlags); } diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPullRefresh.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPullRefresh.java index e346eaf1e633..ac8965d6e901 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPullRefresh.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationPullRefresh.java @@ -16,7 +16,6 @@ import com.azure.spring.cloud.appconfiguration.config.AppConfigurationStoreHealth; import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationRefreshUtil.RefreshEventData; import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; -import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.BaseAppConfigurationPolicy; import reactor.core.publisher.Mono; @@ -110,7 +109,6 @@ public void expireRefreshInterval(String endpoint, String syncToken) { */ private boolean refreshStores() { if (running.compareAndSet(false, true)) { - BaseAppConfigurationPolicy.setWatchRequests(true); try { RefreshEventData eventData = refreshUtils.refreshStoresCheck(clientFactory, refreshInterval, defaultMinBackoff, replicaLookUp); 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 61e6b2eddc58..ca1cc33c3905 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 @@ -15,7 +15,6 @@ import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp; import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlagState; import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; -import com.azure.spring.cloud.appconfiguration.config.implementation.http.policy.BaseAppConfigurationPolicy; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagStore; @@ -32,7 +31,6 @@ public class AppConfigurationRefreshUtil { RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientFactory, Duration refreshInterval, Long defaultMinBackoff, ReplicaLookUp replicaLookUp) { RefreshEventData eventData = new RefreshEventData(); - BaseAppConfigurationPolicy.setWatchRequests(true); try { if (refreshInterval != null && StateHolder.getNextForcedRefresh() != null @@ -178,7 +176,7 @@ private static void refreshWithTime(AppConfigurationReplicaClient client, State private static void refreshWithoutTime(AppConfigurationReplicaClient client, List watchKeys, RefreshEventData eventData) throws AppConfigurationStatusException { for (ConfigurationSetting watchKey : watchKeys) { - ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel()); + ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel(), true); // If there is no result, etag will be considered empty. // A refresh will trigger once the selector returns a value. @@ -200,7 +198,7 @@ private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient cl for (FeatureFlags featureFlags : state.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector())) { + if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { String eventDataInfo = ".appconfig.featureflag/*"; // Only one refresh Event needs to be call to update all of the @@ -222,7 +220,7 @@ private static void refreshWithoutTimeFeatureFlags(AppConfigurationReplicaClient for (FeatureFlags featureFlags : watchKeys.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector())) { + if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { String eventDataInfo = ".appconfig.featureflag/*"; // Only one refresh Event needs to be call to update all of the 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 7e55d4b6622c..9c43522cafcb 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 @@ -14,6 +14,7 @@ import com.azure.core.http.MatchConditions; import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.PagedResponse; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.ConfigurationSnapshot; @@ -91,11 +92,14 @@ 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) + ConfigurationSetting getWatchKey(String key, String label, Boolean isRefresh) throws HttpResponseException { try { + Context context = new Context("refresh", isRefresh); + ConfigurationSetting selector = new ConfigurationSetting().setKey(key).setLabel(label); ConfigurationSetting watchKey = NormalizeNull - .normalizeNullLabel(client.getConfigurationSetting(key, label)); + .normalizeNullLabel( + client.getConfigurationSettingWithResponse(selector, null, false, context).getValue()); this.failedAttempts = 0; return watchKey; } catch (HttpResponseException e) { @@ -111,11 +115,12 @@ ConfigurationSetting getWatchKey(String key, String label) * @param settingSelector Information on which setting to pull. i.e. number of results, key value... * @return List of Configuration Settings. */ - List listSettings(SettingSelector settingSelector) + List listSettings(SettingSelector settingSelector, Boolean isRefresh) throws HttpResponseException { List configurationSettings = new ArrayList<>(); try { - PagedIterable settings = client.listConfigurationSettings(settingSelector); + Context context = new Context("refresh", isRefresh); + PagedIterable settings = client.listConfigurationSettings(settingSelector, context); settings.forEach(setting -> { configurationSettings.add(NormalizeNull.normalizeNullLabel(setting)); }); @@ -129,11 +134,12 @@ List listSettings(SettingSelector settingSelector) } } - FeatureFlags listFeatureFlags(SettingSelector settingSelector) throws HttpResponseException { + FeatureFlags listFeatureFlags(SettingSelector settingSelector, Boolean isRefresh) throws HttpResponseException { List configurationSettings = new ArrayList<>(); List checks = new ArrayList<>(); try { - client.listConfigurationSettings(settingSelector).streamByPage().forEach(pagedResponse -> { + Context context = new Context("refresh", isRefresh); + client.listConfigurationSettings(settingSelector, context).streamByPage().forEach(pagedResponse -> { checks.add( new MatchConditions().setIfNoneMatch(pagedResponse.getHeaders().getValue(HttpHeaderName.ETAG))); for (ConfigurationSetting featureFlag : pagedResponse.getValue()) { @@ -172,8 +178,9 @@ List listSettingSnapshot(String snapshotName) { } } - Boolean checkWatchKeys(SettingSelector settingSelector) { - List> results = client.listConfigurationSettings(settingSelector) + Boolean checkWatchKeys(SettingSelector settingSelector, Boolean isRefresh) { + Context context = new Context("refresh", isRefresh); + List> results = client.listConfigurationSettings(settingSelector, context) .streamByPage().filter(pagedResponse -> pagedResponse.getStatusCode() != 304).toList(); return results.size() > 0; } 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 4a4fb7ee4219..956454d616ca 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 @@ -63,7 +63,7 @@ public class FeatureFlagClient { * */ public List loadFeatureFlags(AppConfigurationReplicaClient replicaClient, String customKeyFilter, - String[] labelFilter) { + String[] labelFilter, boolean isRefresh) { List loadedFeatureFlags = new ArrayList<>(); String keyFilter = SELECT_ALL_FEATURE_FLAGS; @@ -78,7 +78,7 @@ public List loadFeatureFlags(AppConfigurationReplicaClient replica for (String label : labels) { SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter).setLabelFilter(label); - FeatureFlags features = replicaClient.listFeatureFlags(settingSelector); + FeatureFlags features = replicaClient.listFeatureFlags(settingSelector, isRefresh); loadedFeatureFlags.addAll(proccessFeatureFlags(features, keyFilter)); } return loadedFeatureFlags; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/HostType.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/HostType.java index 6465186dbecc..e438a3c2fc15 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/HostType.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/HostType.java @@ -30,7 +30,12 @@ public enum HostType { /** * Host is Container App */ - CONTAINER_APP("ContainerApp"); + CONTAINER_APP("ContainerApp"), + + /** + * Host is Service Fabric + */ + SERVICE_FABRIC("ServiceFabric"); private final String text; diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/RequestTracingConstants.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/RequestTracingConstants.java index bd0cbff3aa08..e5884a0f4753 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/RequestTracingConstants.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/RequestTracingConstants.java @@ -32,6 +32,11 @@ public enum RequestTracingConstants { */ CONTAINER_APP_ENVIRONMENT_VARIABLE("CONTAINER_APP_NAME"), + /** + * Constant for checking Service Fabric + */ + SERVICE_FABRIC_ENVIRONMENT_VARIABLE("Fabric_NodeName"), + /** * Constant for tracing the type of request */ 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 49c72ba082fd..0f5db1c2ee38 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 @@ -2,17 +2,18 @@ // 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.USER_AGENT_TYPE; - import org.springframework.util.StringUtils; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; import com.azure.core.http.HttpResponse; import com.azure.core.http.policy.HttpPipelinePolicy; -import com.azure.spring.cloud.appconfiguration.config.implementation.RequestTracingConstants; +import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants; import reactor.core.publisher.Mono; + /** * HttpPipelinePolicy for connecting to Azure App Configuration. */ @@ -29,8 +30,6 @@ public final class BaseAppConfigurationPolicy implements HttpPipelinePolicy { public static final String USER_AGENT = String.format("%s/%s", StringUtils.replace(PACKAGE_NAME, " ", ""), BaseAppConfigurationPolicy.class.getPackage().getImplementationVersion()); - static Boolean watchRequests = false; - final TracingInfo tracingInfo; /** @@ -41,22 +40,16 @@ public BaseAppConfigurationPolicy(TracingInfo tracingInfo) { this.tracingInfo = tracingInfo; } - @SuppressWarnings("deprecation") @Override public Mono process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) { - String sdkUserAgent = context.getHttpRequest().getHeaders().get(USER_AGENT_TYPE).getValue(); - context.getHttpRequest().getHeaders().set(USER_AGENT_TYPE, USER_AGENT + " " + sdkUserAgent); - context.getHttpRequest().getHeaders().set(RequestTracingConstants.CORRELATION_CONTEXT_HEADER.toString(), + Boolean watchRequests = (Boolean) context.getData("refresh").orElse(false); + HttpHeaders headers = context.getHttpRequest().getHeaders(); + String sdkUserAgent = headers.get(HttpHeaderName.USER_AGENT).getValue(); + headers.set(HttpHeaderName.USER_AGENT, USER_AGENT + " " + sdkUserAgent); + headers.set(HttpHeaderName.fromString(AppConfigurationConstants.CORRELATION_CONTEXT), tracingInfo.getValue(watchRequests)); return next.process(); } - /** - * @param watchRequests the watchRequests to set - */ - public static void setWatchRequests(Boolean watchRequests) { - BaseAppConfigurationPolicy.watchRequests = watchRequests; - } - } 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 3299dbd5ab14..e9608eb72db2 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 @@ -5,6 +5,8 @@ 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; + import com.azure.core.util.Configuration; import com.azure.spring.cloud.appconfiguration.config.implementation.HostType; import com.azure.spring.cloud.appconfiguration.config.implementation.RequestTracingConstants; @@ -19,7 +21,7 @@ public class TracingInfo { private int replicaCount; private final FeatureFlagTracing featureFlagTracing; - + private final Configuration configuration; public TracingInfo(boolean isDev, boolean isKeyVaultConfigured, int replicaCount, Configuration configuration) { @@ -31,7 +33,8 @@ public TracingInfo(boolean isDev, boolean isKeyVaultConfigured, int replicaCount } public String getValue(boolean watchRequests) { - String track = configuration.get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); + String track = configuration + .get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); if (track != null && Boolean.valueOf(track)) { return ""; } @@ -60,6 +63,8 @@ public String getValue(boolean watchRequests) { if (replicaCount > 0) { sb.append(",").append(RequestTracingConstants.REPLICA_COUNT).append("=").append(replicaCount); } + + sb = getFeatureManagementUsage(sb); return sb.toString(); } @@ -80,10 +85,21 @@ private static String getHostType() { hostType = HostType.KUBERNETES; } else if (System.getenv(RequestTracingConstants.CONTAINER_APP_ENVIRONMENT_VARIABLE.toString()) != null) { hostType = HostType.CONTAINER_APP; + } else if (System.getenv(RequestTracingConstants.SERVICE_FABRIC_ENVIRONMENT_VARIABLE.toString()) != null) { + hostType = HostType.SERVICE_FABRIC; } return hostType.toString(); } + + private static StringBuilder getFeatureManagementUsage(StringBuilder sb) { + ClassLoader loader = ClassLoader.getSystemClassLoader(); + Package ff = loader.getDefinedPackage("com.azure.spring.cloud.feature.management.models"); + if (ff != null && StringUtils.hasText(ff.getImplementationVersion())) { + sb.append(",FMSpVer=").append(ff.getImplementationVersion()); + } + return sb; + } /** * @return the featureFlagTracing diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceTest.java index 9836dbed373d..1f7fe5ce9760 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceTest.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceTest.java @@ -63,7 +63,8 @@ public class AppConfigurationApplicationSettingPropertySourceTest { private static final ConfigurationSetting ITEM_NULL = createItem(KEY_FILTER, TEST_KEY_3, TEST_VALUE_3, TEST_LABEL_3, null); - private static final ConfigurationSetting ITEM_INVALID_JSON = createItem(KEY_FILTER, TEST_KEY_3, TEST_VALUE_3, TEST_LABEL_3, + private static final ConfigurationSetting ITEM_INVALID_JSON = createItem(KEY_FILTER, TEST_KEY_3, TEST_VALUE_3, + TEST_LABEL_3, JSON_CONTENT_TYPE); private static final ObjectMapper MAPPER = new ObjectMapper(); @@ -115,10 +116,10 @@ public void cleanup() throws Exception { @Test public void testPropCanBeInitAndQueried() throws IOException { when(configurationListMock.iterator()).thenReturn(testItems.iterator()); - when(clientMock.listSettings(Mockito.any())).thenReturn(configurationListMock) + when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(null); + propertySource.initProperties(null, false); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = testItems.stream() @@ -139,10 +140,10 @@ public void testPropertyNameSlashConvertedToDots() throws IOException { settings.add(slashedProp); when(configurationListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any())).thenReturn(configurationListMock) + when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(null); + propertySource.initProperties(null, false); String expectedKeyName = TEST_SLASH_KEY.replace('/', '.'); String[] actualKeyNames = propertySource.getPropertyNames(); @@ -159,9 +160,9 @@ public void initNullValidContentTypeTest() throws IOException { items.add(ITEM_NULL); when(configurationListMock.iterator()).thenReturn(items.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any())).thenReturn(configurationListMock); + when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock); - propertySource.initProperties(null); + propertySource.initProperties(null, false); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = items.stream() @@ -176,9 +177,9 @@ public void jsonContentTypeWithInvalidJsonValueTest() { items.add(ITEM_INVALID_JSON); when(configurationListMock.iterator()).thenReturn(items.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any())).thenReturn(configurationListMock); + when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock); - assertThatThrownBy(() -> propertySource.initProperties(null)) + assertThatThrownBy(() -> propertySource.initProperties(null, false)) .isInstanceOf(InvalidConfigurationPropertyValueException.class) .hasMessageNotContaining(ITEM_INVALID_JSON.getValue()); } 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 8ed112b31f8e..2833cc92970c 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 @@ -98,15 +98,16 @@ public void testKeyVaultTest() { List settings = List.of(KEY_VAULT_ITEM); when(keyVaultSecretListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); KeyVaultSecret secret = new KeyVaultSecret("mySecret", "mySecretValue"); - when(keyVaultClientFactoryMock.getClient(Mockito.eq("https://test.key.vault.com"))).thenReturn(clientManagerMock); + when(keyVaultClientFactoryMock.getClient(Mockito.eq("https://test.key.vault.com"))) + .thenReturn(clientManagerMock); when(clientManagerMock.getSecret(Mockito.any(URI.class))).thenReturn(secret); try { - propertySource.initProperties(null); + propertySource.initProperties(null, false); } catch (InvalidConfigurationPropertyValueException e) { fail("Failed Reading in Feature Flags"); } @@ -124,11 +125,11 @@ public void invalidKeyVaultReferenceInvalidURITest() { List settings = List.of(KEY_VAULT_ITEM_INVALID_URI); when(keyVaultSecretListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); InvalidConfigurationPropertyValueException exception = assertThrows( - InvalidConfigurationPropertyValueException.class, () -> propertySource.initProperties(null)); + InvalidConfigurationPropertyValueException.class, () -> propertySource.initProperties(null, false)); assertEquals("test_key_vault_1", exception.getName()); assertEquals("", exception.getValue()); assertEquals("Invalid URI found in JSON property field 'uri' unable to parse.", exception.getReason()); @@ -139,12 +140,14 @@ public void invalidKeyVaultReferenceParseErrorTest() { List settings = List.of(KEY_VAULT_ITEM); when(keyVaultSecretListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); - when(keyVaultClientFactoryMock.getClient(Mockito.eq("https://test.key.vault.com"))).thenReturn(clientManagerMock); + when(keyVaultClientFactoryMock.getClient(Mockito.eq("https://test.key.vault.com"))) + .thenReturn(clientManagerMock); when(clientManagerMock.getSecret(Mockito.any())).thenThrow(new RuntimeException("Parse Failed")); - RuntimeException exception = assertThrows(RuntimeException.class, () -> propertySource.initProperties(null)); + 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 index 09b9c7b36caf..a68ac60e28e6 100644 --- 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 @@ -194,7 +194,7 @@ public void compositeSourceIsCreatedWithMonitoring() { properties.getStores().get(0).setMonitoring(monitoring); - when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString())) + when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), Mockito.anyBoolean())) .thenReturn(TestUtils.createItem("", watchKey, "0", EMPTY_LABEL, "")); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, @@ -213,7 +213,8 @@ public void compositeSourceIsCreatedWithMonitoring() { }; assertEquals(expectedSourceNames.length, sources.size()); assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString()); + verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -234,7 +235,7 @@ public void compositeSourceIsCreatedWithMonitoringWatchKeyDoesNotExist() { properties.getStores().get(0).setMonitoring(monitoring); - when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString())) + when(replicaClientMock.getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), Mockito.anyBoolean())) .thenReturn(null); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, @@ -253,7 +254,8 @@ public void compositeSourceIsCreatedWithMonitoringWatchKeyDoesNotExist() { }; assertEquals(expectedSourceNames.length, sources.size()); assertArrayEquals((Object[]) expectedSourceNames, sources.stream().map(PropertySource::getName).toArray()); - verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString()); + verify(replicaClientMock, times(1)).getWatchKey(Mockito.eq(watchKey), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -262,7 +264,7 @@ 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())).thenReturn(List.of()); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, @@ -288,7 +290,7 @@ 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())).thenReturn(List.of()); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); when(featureFlagClientMock.getProperties()).thenReturn(Map.of()); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, @@ -323,7 +325,7 @@ public void storeCreatedWithFeatureFlags() { properties.getStores().get(0).setFeatureFlags(featureFlagStore); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(List.of(featureFlag)); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of(featureFlag)); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, null, stores, replicaLookUpMock, featureFlagClientMock); @@ -360,7 +362,7 @@ public void storeCreatedWithFeatureFlagsWithMonitoring() { properties.getStores().get(0).setFeatureFlags(featureFlagStore); properties.getStores().get(0).setMonitoring(monitoring); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(List.of(featureFlag)); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of(featureFlag)); locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, null, stores, replicaLookUpMock, featureFlagClientMock); @@ -423,7 +425,7 @@ public void defaultFailFastThrowException() { locator = new AppConfigurationPropertySourceLocator(appProperties, clientFactoryMock, keyVaultClientFactory, null, List.of(configStoreMock), replicaLookUpMock, featureFlagClientMock); - when(replicaClientMock.getWatchKey(Mockito.any(), Mockito.anyString())).thenThrow(new RuntimeException()); + 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(); @@ -432,7 +434,7 @@ public void defaultFailFastThrowException() { @Test public void refreshThrowException() throws IllegalArgumentException { setupEmptyEnvironment(); - when(replicaClientMock.listSettings(any())).thenThrow(new RuntimeException()); + when(replicaClientMock.listSettings(any(), Mockito.anyBoolean())).thenThrow(new RuntimeException()); AppConfigurationPropertySourceLocator.STARTUP.set(false); @@ -499,7 +501,7 @@ public void multiplePropertySourcesExistForMultiStores() { @Test public void awaitOnError() { when(clientFactoryMock.getAvailableClients(Mockito.anyString(), Mockito.eq(true))).thenReturn(Arrays.asList(replicaClientMock)); - when(replicaClientMock.listSettings(Mockito.any())).thenReturn(List.of()); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(List.of()); when(appPropertiesMock.getPrekillTime()).thenReturn(5); ConfigurableEnvironment env = Mockito.mock(ConfigurableEnvironment.class); @@ -526,7 +528,7 @@ public Object getProperty(String name) { when(configStoreMockError.isFailFast()).thenReturn(true); when(configStoreMockError.getEndpoint()).thenReturn(""); - when(replicaClientMock.listSettings(Mockito.any())).thenThrow(new NullPointerException("")); + when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenThrow(new NullPointerException("")); when(appPropertiesMock.getPrekillTime()).thenReturn(-60); when(appPropertiesMock.getStartDate()).thenReturn(Instant.now()); @@ -561,6 +563,6 @@ 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())).thenReturn(List.of()); + 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 30104ca62524..ba6b62292b8c 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 @@ -121,7 +121,8 @@ public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNotReturned(TestInfo te 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))).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); @@ -142,7 +143,7 @@ public void refreshWithoutTimeWatchKeyConfigStoreWatchKeyNoChange(TestInfo testI Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); // Config Store does return a watch key change. - when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class))).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.anyBoolean())).thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -192,7 +193,7 @@ public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); // Config Store doesn't return a watch key change. - when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class))).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.anyBoolean())).thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -213,7 +214,7 @@ public void refreshWithoutTimeFeatureFlagEtagChanged(TestInfo testInfo) { Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); // Config Store does return a watch key change. - when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class))).thenReturn(true); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.anyBoolean())).thenReturn(true); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -240,7 +241,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -259,7 +261,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -279,7 +282,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -301,7 +305,8 @@ 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()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); assertEquals(newState, StateHolder.getState(endpoint)); } } @@ -311,7 +316,7 @@ public void refreshStoresCheckSettingsTestRefreshTimeNoChange(TestInfo testInfo) endpoint = testInfo.getDisplayName() + ".azconfig.io"; setupFeatureFlagLoad(); - when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString())) + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())) .thenReturn(generateWatchKeys().get(0)); State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); @@ -327,7 +332,8 @@ 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()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -343,7 +349,8 @@ public void refreshStoresCheckSettingsTestTriggerRefresh(TestInfo testInfo) { ConfigurationSetting refreshKey = new ConfigurationSetting().setKey(KEY_FILTER).setLabel(EMPTY_LABEL) .setETag("new"); - when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString())).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); @@ -357,7 +364,8 @@ 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()); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); verify(currentStateMock, times(1)).updateStateRefresh(Mockito.any(), Mockito.any()); } } @@ -380,7 +388,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -402,7 +411,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); } } @@ -414,7 +424,7 @@ public void refreshStoresCheckFeatureFlagTestNoChange(TestInfo testInfo) { configStore.setMonitoring(monitoring); setupFeatureFlagLoad(); - when(clientOriginMock.checkWatchKeys(Mockito.any())).thenReturn(false); + when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.anyBoolean())).thenReturn(false); FeatureFlagState newState = new FeatureFlagState( List.of(new FeatureFlags(new SettingSelector().setKeyFilter(KEY_FILTER).setLabelFilter(EMPTY_LABEL), null)), @@ -430,7 +440,8 @@ 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()); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.anyBoolean()); verify(currentStateMock, times(1)).updateFeatureFlagStateRefresh(Mockito.any(), Mockito.any()); } @@ -440,7 +451,7 @@ public void refreshStoresCheckFeatureFlagTestNoChange(TestInfo testInfo) { public void refreshStoresCheckFeatureFlagTestTriggerRefresh(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; setupFeatureFlagLoad(); - when(clientOriginMock.checkWatchKeys(Mockito.any())).thenReturn(true); + when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.anyBoolean())).thenReturn(true); FeatureFlags featureFlags = new FeatureFlags(new SettingSelector(), watchKeysFeatureFlags); @@ -457,7 +468,8 @@ 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()); + 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/AppConfigurationReplicaClientTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationReplicaClientTest.java index 68d57642399e..99c71ea169a0 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 @@ -36,6 +36,7 @@ import com.azure.core.http.rest.PagedIterable; 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; @@ -65,6 +66,9 @@ public class AppConfigurationReplicaClientTest { @Mock private Supplier>> supplierMock; + @Mock + private Response mockResponse; + private final String endpoint = "clientTest.azconfig.io"; private MockitoSession session; @@ -77,8 +81,8 @@ public void setup() { @AfterEach public void cleanup() throws Exception { - MockitoAnnotations.openMocks(this).close(); session.finishMocking(); + MockitoAnnotations.openMocks(this).close(); } @Test @@ -88,28 +92,31 @@ public void getWatchKeyTest() { ConfigurationSetting watchKey = new ConfigurationSetting().setKey("watch").setLabel("\0"); - when(clientMock.getConfigurationSetting(Mockito.anyString(), Mockito.anyString())).thenReturn(watchKey); + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), + Mockito.any())).thenReturn(mockResponse); + when(mockResponse.getValue()).thenReturn(watchKey); - assertEquals(watchKey, client.getWatchKey("watch", "\0")); + //assertEquals(watchKey, client.getWatchKey("watch", "\0", false)); - when(clientMock.getConfigurationSetting(Mockito.anyString(), Mockito.anyString())) - .thenThrow(exceptionMock); + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), + Mockito.any())).thenReturn(mockResponse); + when(mockResponse.getValue()).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0")); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0")); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0")); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.getWatchKey("watch", "\0")); + assertThrows(HttpResponseException.class, () -> client.getWatchKey("watch", "\0", false)); - when(clientMock.getConfigurationSetting(Mockito.any(), Mockito.any())) - .thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0")); + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), + Mockito.any())).thenThrow(new UncheckedIOException(new UnknownHostException())); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); } @Test @@ -125,23 +132,24 @@ public void listSettingsTest() { 200, null, configurations, null, null); when(supplierMock.get()).thenReturn(Mono.just(pagedResponse)); - when(clientMock.listConfigurationSettings(Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) + .thenReturn(new PagedIterable<>(pagedFlux)); - assertEquals(configurations, client.listSettings(new SettingSelector())); + assertEquals(configurations, client.listSettings(new SettingSelector(), false)); - when(clientMock.listConfigurationSettings(Mockito.any())).thenThrow(exceptionMock); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(HttpResponseException.class, () -> client.listSettings(new SettingSelector(), false)); } @Test @@ -159,23 +167,27 @@ public void listFeatureFlagsTest() { when(supplierMock.get()).thenReturn(Mono.just(pagedResponse)); - when(clientMock.listConfigurationSettings(Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) + .thenReturn(new PagedIterable<>(pagedFlux)); - assertEquals(configurations, client.listFeatureFlags(new SettingSelector()).getFeatureFlags()); + assertEquals(configurations, client.listFeatureFlags(new SettingSelector(), false).getFeatureFlags()); - when(clientMock.listConfigurationSettings(Mockito.any())).thenThrow(exceptionMock); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.listFeatureFlags(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, + () -> client.listFeatureFlags(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.listFeatureFlags(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, + () -> client.listFeatureFlags(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.listFeatureFlags(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, + () -> client.listFeatureFlags(new SettingSelector(), false)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listFeatureFlags(new SettingSelector())); + assertThrows(HttpResponseException.class, () -> client.listFeatureFlags(new SettingSelector(), false)); } @Test @@ -183,9 +195,9 @@ public void listSettingsUnknownHostTest() { AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); - when(clientMock.listConfigurationSettings(Mockito.any())) + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); } @Test @@ -193,10 +205,10 @@ public void listSettingsNoCredentialTest() { AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); - when(clientMock.listConfigurationSettings(Mockito.any())) + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new CredentialUnavailableException("No Credential")); - assertThrows(CredentialUnavailableException.class, () -> client.listSettings(new SettingSelector())); + assertThrows(CredentialUnavailableException.class, () -> client.listSettings(new SettingSelector(), false)); } @Test @@ -204,10 +216,12 @@ public void getWatchNoCredentialTest() { AppConfigurationReplicaClient client = new AppConfigurationReplicaClient(endpoint, clientMock, new TracingInfo(false, false, 0, Configuration.getGlobalConfiguration())); - when(clientMock.getConfigurationSetting(Mockito.anyString(), Mockito.anyString())) + when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.isNull(), Mockito.anyBoolean(), + Mockito.any())).thenReturn(mockResponse); + when(mockResponse.getValue()) .thenThrow(new CredentialUnavailableException("No Credential")); - assertThrows(CredentialUnavailableException.class, () -> client.getWatchKey("key", "label")); + assertThrows(CredentialUnavailableException.class, () -> client.getWatchKey("key", "label", false)); } @Test @@ -231,9 +245,10 @@ public void backoffTest() { assertEquals(2, client.getFailedAttempts()); // Success in a list request results in a reset of failed attemtps - when(clientMock.listConfigurationSettings(Mockito.any(SettingSelector.class))).thenReturn(settingsMock); + when(clientMock.listConfigurationSettings(Mockito.any(SettingSelector.class), Mockito.any())) + .thenReturn(settingsMock); - client.listSettings(new SettingSelector()); + client.listSettings(new SettingSelector(), false); assertTrue(client.getBackoffEndTime().isBefore(Instant.now())); assertEquals(0, client.getFailedAttempts()); } @@ -315,9 +330,10 @@ public void checkWatchKeysTest() { when(supplierMock.get()).thenReturn(Mono.just(pagedResponse)); - when(clientMock.listConfigurationSettings(Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) + .thenReturn(new PagedIterable<>(pagedFlux)); - assertTrue(client.checkWatchKeys(new SettingSelector())); + assertTrue(client.checkWatchKeys(new SettingSelector(), false)); pagedResponse.close(); } catch (IOException e) { e.printStackTrace(); @@ -329,9 +345,9 @@ public void checkWatchKeysTest() { when(supplierMock.get()).thenReturn(Mono.just(pagedResponse)); - when(clientMock.listConfigurationSettings(Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); + when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); - assertFalse(client.checkWatchKeys(new SettingSelector())); + assertFalse(client.checkWatchKeys(new SettingSelector(), false)); pagedResponse.close(); } catch (IOException e) { e.printStackTrace(); 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 6f5bcf70e29c..f473388bd8c3 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 @@ -76,9 +76,10 @@ public void cleanup() throws Exception { public void loadFeatureFlagsTestNoFeatureFlags() { List settings = List.of(new ConfigurationSetting().setKey("FakeKey")); FeatureFlags featureFlags = new FeatureFlags(null, settings); - when(clientMock.listFeatureFlags(Mockito.any())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList); + 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()); @@ -90,9 +91,10 @@ public void loadFeatureFlagsTestFeatureFlags() { List settings = List.of(new FeatureFlagConfigurationSetting("Alpha", false), new FeatureFlagConfigurationSetting("Beta", true)); FeatureFlags featureFlags = new FeatureFlags(null, settings); - when(clientMock.listFeatureFlags(Mockito.any())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList); + 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()); @@ -105,9 +107,10 @@ public void loadFeatureFlagsTestMultipleLoads() { List settings = List.of(new FeatureFlagConfigurationSetting("Alpha", false), new FeatureFlagConfigurationSetting("Beta", true)); FeatureFlags featureFlags = new FeatureFlags(null, settings); - when(clientMock.listFeatureFlags(Mockito.any())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList); + 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()); @@ -117,9 +120,9 @@ public void loadFeatureFlagsTestMultipleLoads() { List settings2 = List.of(new FeatureFlagConfigurationSetting("Alpha", true), new FeatureFlagConfigurationSetting("Gamma", false)); featureFlags = new FeatureFlags(null, settings2); - when(clientMock.listFeatureFlags(Mockito.any())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList); + 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()); @@ -163,9 +166,10 @@ public void loadFeatureFlagsTestTargetingFilter() { targetingFlag.addClientFilter(targetingFilter); List settings = List.of(targetingFlag); FeatureFlags featureFlags = new FeatureFlags(null, settings); - when(clientMock.listFeatureFlags(Mockito.any())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList); + 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/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 89e339f0b6cc..c08c29975ffa 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 @@ -11,6 +11,7 @@ import java.net.MalformedURLException; import java.net.URL; +import java.util.Optional; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -47,7 +48,6 @@ public class BaseAppConfigurationPolicyTest { @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); - BaseAppConfigurationPolicy.setWatchRequests(false); } @AfterEach @@ -78,7 +78,7 @@ public void startupThenWatchUpdateTest() throws MalformedURLException { request.setHeader(USER_AGENT_TYPE, "PreExistingUserAgent"); when(contextMock.getHttpRequest()).thenReturn(request); - BaseAppConfigurationPolicy.setWatchRequests(true); + when(contextMock.getData("refresh")).thenReturn(Optional.of(true)); policy.process(contextMock, nextMock); From 526e9ee905dc6889c84d894137d78f66f5bbea47 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Thu, 22 Aug 2024 09:57:12 -0700 Subject: [PATCH 2/4] Fixing yaml hints --- .../spring-cloud-azure-appconfiguration-config/pom.xml | 6 ++++++ .../properties/AppConfigurationProperties.java | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml index eb278628d4a3..7232ecaf67ca 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml @@ -23,6 +23,12 @@ spring-boot-autoconfigure 3.3.2 + + org.springframework.boot + spring-boot-configuration-processor + 3.3.2 + false + org.springframework.cloud spring-cloud-starter-bootstrap diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java index 981f487518cc..38fd248ea04c 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java @@ -9,6 +9,8 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -17,8 +19,9 @@ /** * Properties for all Azure App Configuration stores that are loaded. */ +@Configuration @ConfigurationProperties(prefix = AppConfigurationProperties.CONFIG_PREFIX) -public final class AppConfigurationProperties { +public class AppConfigurationProperties { /** * Prefix for client configurations for connecting to configuration stores. From ecb4782c1114ac2839b47d250c8b95ab0ebfee8b Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Thu, 22 Aug 2024 10:04:09 -0700 Subject: [PATCH 3/4] Updating property docs from yaml --- .../pom.xml | 2 +- .../AppConfigurationKeyValueSelector.java | 25 ++++++++- .../AppConfigurationProperties.java | 4 +- .../AppConfigurationStoreMonitoring.java | 29 ++++++++++ .../AppConfigurationStoreTrigger.java | 7 +++ .../properties/ConfigStore.java | 55 ++++++++++++++++++- .../FeatureFlagKeyValueSelector.java | 11 ++++ 7 files changed, 126 insertions(+), 7 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml index 7232ecaf67ca..bbd4e591e491 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/pom.xml @@ -27,7 +27,7 @@ org.springframework.boot spring-boot-configuration-processor 3.3.2 - false + true org.springframework.cloud 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 b8b9f1ad8684..d6d4baed1efd 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 @@ -33,11 +33,30 @@ public final class AppConfigurationKeyValueSelector { public static final String LABEL_SEPARATOR = ","; @NotNull + /** + * Key filter to use when loading configurations. The default value is + * "/application/". The key filter is used to filter configurations by key. + * The key filter must be a non-null string that does not contain an asterisk. + */ private String keyFilter = ""; + /** + * Label filter to use when loading configurations. The label filter is used to + * filter configurations by label. If the label filter is not set, the default + * value is the current active Spring profiles. If no active profiles are set, + * then all configurations with no label are loaded. The label filter must be a + * non-null string that does not contain an asterisk. + */ private String labelFilter; + /** + * Snapshot name to use when loading configurations. The snapshot name is used + * to load configurations from a snapshot. If the snapshot name is set, the key + * and label filters must not be set. The snapshot name must be a non-null + * string that does not contain an asterisk. + */ private String snapshotName = ""; + /** * @return the keyFilter */ @@ -55,8 +74,10 @@ public AppConfigurationKeyValueSelector setKeyFilter(String keyFilter) { } /** - * @param profiles List of current Spring profiles to default to using is null label is set. - * @return List of reversed label values, which are split by the separator, the latter label has higher priority + * @param profiles List of current Spring profiles to default to using is null + * label is set. + * @return List of reversed label values, which are split by the separator, the + * latter label has higher priority */ public String[] getLabelFilter(List profiles) { if (labelFilter == null && profiles.size() > 0) { diff --git a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java index 38fd248ea04c..8518170fc57c 100644 --- a/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java +++ b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/properties/AppConfigurationProperties.java @@ -9,7 +9,6 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.context.annotation.Configuration; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -30,6 +29,9 @@ public class AppConfigurationProperties { private boolean enabled = true; + /** + * List of Azure App Configuration stores to connect to. + */ private List stores = new ArrayList<>(); private Duration refreshInterval; 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 cfc724d7c126..81aea8687990 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 @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.Assert; import jakarta.annotation.PostConstruct; @@ -15,14 +16,30 @@ */ public final class AppConfigurationStoreMonitoring { + /** + * If true, the application will check for updates to the configuration store. + * When set to true at least one trigger must be set. + */ private boolean enabled = false; + /** + * The minimum time between checks. The minimum valid time is 1s. The default refresh interval is 30s. + */ private Duration refreshInterval = Duration.ofSeconds(30); + /** + * The minimum time between checks of feature flags. The minimum valid time is 1s. The default refresh interval is 30s. + */ private Duration featureFlagRefreshInterval = Duration.ofSeconds(30); + /** + * List of triggers that will cause a refresh of the configuration store. + */ private List triggers = new ArrayList<>(); + /** + * Validation tokens for push notificaiton requests. + */ private PushNotification pushNotification = new PushNotification(); /** @@ -118,8 +135,14 @@ public void validateAndInit() { */ public static class PushNotification { + /** + * Validation token for push notification requests. + */ private AccessToken primaryToken = new AccessToken(); + /** + * Secondary validation token for push notification requests. + */ private AccessToken secondaryToken = new AccessToken(); /** @@ -156,8 +179,14 @@ public void setSecondaryToken(AccessToken secondaryToken) { */ public static class AccessToken { + /** + * Name of the token. + */ private String name; + /** + * Secret for the token. + */ private String secret; /** 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 e57a6d984d69..ec69376e1bd7 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 @@ -14,9 +14,16 @@ */ public final class AppConfigurationStoreTrigger { + /** + * Key value of the configuration setting checked when looking for changes. + */ @NotNull private String key; + /** + * Label value of the configuration setting checked when looking for changes. + * If the label is not set, the default value is no label. + */ private String label; /** 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 878db9e464c8..3195196123f2 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 @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.StringUtils; import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientsBuilder; @@ -21,27 +22,73 @@ public final class ConfigStore { private static final String DEFAULT_KEYS = "/application/"; + /** + * Endpoint for the Azure Config Service. + */ private String endpoint = ""; // Config store endpoint + /** + * List of endpoints for geo-replicated config store instances. When connecting + * to Azure App Configuration, the endpoints will failover to the next endpoint + * in the list if the current endpoint is unreachable. + */ private List endpoints = new ArrayList<>(); + /** + * Connection String for the Azure Config Service. + */ private String connectionString; + /** + * List of connection strings for geo-replicated config store instances. When + * connecting to Azure App Configuration, the connection strings will failover + * to the next connection string in the list if the current connection string is + * unreachable. + */ private List connectionStrings = new ArrayList<>(); - // Label values separated by comma in the Azure Config Service, can be empty + /** + * List of key selectors to filter the keys to be retrieved from the Azure + * Config Service. If no selectors are provided, the default selector will + * retrieve all keys with the prefix "/application/" and no label. + */ 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(); + /** + * If true, the Config Store will be enabled. If false, the Config Store will be + * disabled and no keys will be retrieved from the Config Store. + */ private boolean enabled = true; + /** + * Options for monitoring the Config Store. + */ private AppConfigurationStoreMonitoring monitoring = new AppConfigurationStoreMonitoring(); + /** + * List of values to be trimmed from key names before being set to + * `@ConfigurationProperties`. By default the prefix "/application/" is trimmed + * from key names. If any trimKeyPrefix values are provided, the default prefix + * will not be trimmed. + */ private List trimKeyPrefix; + /** + * If true, the Config Store will attempt to discover the replica endpoints for + * the Config Store. If false, the Config Store will not attempt to discover the + * replica endpoints for the Config Store. + */ private boolean replicaDiscoveryEnabled = true; /** @@ -66,7 +113,8 @@ public List getEndpoints() { } /** - * @param endpoints list of endpoints to connect to geo-replicated config store instances. + * @param endpoints list of endpoints to connect to geo-replicated config store + * instances. */ public void setEndpoints(List endpoints) { this.endpoints = endpoints; @@ -185,7 +233,8 @@ public List getTrimKeyPrefix() { } /** - * @param trimKeyPrefix the values to be trimmed from key names before being set to `@ConfigurationProperties` + * @param trimKeyPrefix the values to be trimmed from key names before being set + * to `@ConfigurationProperties` */ public void setTrimKeyPrefix(List trimKeyPrefix) { this.trimKeyPrefix = trimKeyPrefix; 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 358464a6802b..b3280178e200 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 @@ -25,8 +25,18 @@ public final class FeatureFlagKeyValueSelector { */ private static final String[] EMPTY_LABEL_ARRAY = { EMPTY_LABEL }; + /** + * Key filter to use when loading feature flags. The provided key filter is + * appended after the feature flag prefix, ".appconfig.featureflag/". By + * default, all feature flags are loaded. + */ private String keyFilter = ""; + /** + * Label filter to use when loading feature flags. By default, all feature flags + * with no label are loaded. The label filter must be a non-null string that + * does not contain an asterisk. + */ private String labelFilter; /** @@ -74,6 +84,7 @@ public String[] getLabelFilter(List profiles) { /** * Get all labels as a single String + * * @param profiles current user profiles * @return comma separated list of labels */ From 7f9b96de9b80a33e2b265cdd329f763c6b8a8c60 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Mon, 26 Aug 2024 15:37:53 -0700 Subject: [PATCH 4/4] removing unused imports --- .../properties/AppConfigurationStoreMonitoring.java | 1 - .../config/implementation/properties/ConfigStore.java | 1 - 2 files changed, 2 deletions(-) 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 81aea8687990..22db1862d354 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 @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.List; -import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.Assert; import jakarta.annotation.PostConstruct; 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 3195196123f2..7444d739770f 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 @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; -import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.StringUtils; import com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationReplicaClientsBuilder;