From 039f0ca464ea5de92d80bed84aba7922a0598370 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Tue, 11 Feb 2025 15:50:35 -0800 Subject: [PATCH 1/2] Updating to use Context to pass around telemetry + added pushRefreshEnabled --- ...ationApplicationSettingPropertySource.java | 5 +- .../AppConfigurationPropertySource.java | 3 +- .../AppConfigurationRefreshUtil.java | 38 +++++++---- .../AppConfigurationReplicaClient.java | 16 ++--- ...ppConfigurationSnapshotPropertySource.java | 5 +- .../AzureAppConfigDataLoader.java | 22 ++++-- .../implementation/FeatureFlagClient.java | 5 +- .../policy/BaseAppConfigurationPolicy.java | 3 +- .../http/policy/TracingInfo.java | 6 +- ...tionSettingPropertySourceSnapshotTest.java | 17 +++-- ...nApplicationSettingPropertySourceTest.java | 22 +++--- ...nfigurationPropertySourceKeyVaultTest.java | 16 +++-- .../AppConfigurationRefreshUtilTest.java | 68 ++++++++++--------- .../AppConfigurationReplicaClientTest.java | 62 +++++++++-------- .../implementation/FeatureFlagClientTest.java | 24 ++++--- .../http/policy/TracingInfoTest.java | 20 +++--- 16 files changed, 193 insertions(+), 139 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 f7c2a6c90f8b..7302de70d4a2 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 @@ -18,6 +18,7 @@ import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.util.StringUtils; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting; @@ -60,7 +61,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, boolean isRefresh) throws InvalidConfigurationPropertyValueException { + public void initProperties(List keyPrefixTrimValues, Context context) throws InvalidConfigurationPropertyValueException { List labels = Arrays.asList(labelFilters); // Reverse labels so they have the right priority order. @@ -70,7 +71,7 @@ public void initProperties(List keyPrefixTrimValues, boolean isRefresh) SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter + "*").setLabelFilter(label); // * for wildcard match - processConfigurationSettings(replicaClient.listSettings(settingSelector, isRefresh), settingSelector.getKeyFilter(), + processConfigurationSettings(replicaClient.listSettings(settingSelector, context), 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 3a3c84907e22..9515c30258a9 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 @@ -10,6 +10,7 @@ import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.core.env.EnumerablePropertySource; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.ConfigurationClient; /** @@ -51,5 +52,5 @@ protected static String getLabelName(String[] labelFilters) { return String.join(",", labelFilters); } - protected abstract void initProperties(List trim, boolean isRefresh) throws InvalidConfigurationPropertyValueException; + protected abstract void initProperties(List trim, Context context) throws InvalidConfigurationPropertyValueException; } 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 94c1b9c8550f..bedeb0ff7af5 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 @@ -11,11 +11,13 @@ import org.slf4j.LoggerFactory; import com.azure.core.exception.HttpResponseException; +import com.azure.core.util.Context; 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.FeatureFlagState; import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.PushNotification; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagStore; public class AppConfigurationRefreshUtil { @@ -50,6 +52,14 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF clientFactory.setCurrentConfigStoreClient(originEndpoint, originEndpoint); AppConfigurationStoreMonitoring monitor = connection.getMonitoring(); + + boolean pushRefreshEnabled = false; + PushNotification notification = monitor.getPushNotification(); + if (notification.getPrimaryToken() != null + || notification.getSecondaryToken() != null) { + pushRefreshEnabled = true; + } + Context context = new Context("refresh", true).addData("pushRefreshEnabled", pushRefreshEnabled); List clients = clientFactory.getAvailableClients(originEndpoint); @@ -57,7 +67,7 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF for (AppConfigurationReplicaClient client : clients) { try { refreshWithTime(client, StateHolder.getState(originEndpoint), monitor.getRefreshInterval(), - eventData, replicaLookUp); + eventData, replicaLookUp, context); if (eventData.getDoRefresh()) { clientFactory.setCurrentConfigStoreClient(originEndpoint, client.getEndpoint()); return eventData; @@ -81,7 +91,7 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF for (AppConfigurationReplicaClient client : clients) { try { refreshWithTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(originEndpoint), - monitor.getFeatureFlagRefreshInterval(), eventData, replicaLookUp); + monitor.getFeatureFlagRefreshInterval(), eventData, replicaLookUp, context); if (eventData.getDoRefresh()) { clientFactory.setCurrentConfigStoreClient(originEndpoint, client.getEndpoint()); return eventData; @@ -115,10 +125,10 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF * @param originEndpoint config store origin endpoint * @return A refresh should be triggered. */ - static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint) { + static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint, Context context) { RefreshEventData eventData = new RefreshEventData(); if (StateHolder.getLoadState(originEndpoint)) { - refreshWithoutTime(client, StateHolder.getState(originEndpoint).getWatchKeys(), eventData); + refreshWithoutTime(client, StateHolder.getState(originEndpoint).getWatchKeys(), eventData, context); } return eventData.getDoRefresh(); } @@ -131,12 +141,12 @@ static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String or * @return true if a refresh should be triggered. */ static boolean refreshStoreFeatureFlagCheck(Boolean featureStoreEnabled, - AppConfigurationReplicaClient client) { + AppConfigurationReplicaClient client, Context context) { RefreshEventData eventData = new RefreshEventData(); String endpoint = client.getEndpoint(); if (featureStoreEnabled && StateHolder.getStateFeatureFlag(endpoint) != null) { - refreshWithoutTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(endpoint), eventData); + refreshWithoutTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(endpoint), eventData, context); } else { LOGGER.debug("Skipping feature flag refresh check for " + endpoint); } @@ -151,10 +161,10 @@ static boolean refreshStoreFeatureFlagCheck(Boolean featureStoreEnabled, * @param eventData Info for this refresh event. */ private static void refreshWithTime(AppConfigurationReplicaClient client, State state, Duration refreshInterval, - RefreshEventData eventData, ReplicaLookUp replicaLookUp) throws AppConfigurationStatusException { + RefreshEventData eventData, ReplicaLookUp replicaLookUp, Context context) throws AppConfigurationStatusException { if (Instant.now().isAfter(state.getNextRefreshCheck())) { replicaLookUp.updateAutoFailoverEndpoints(); - refreshWithoutTime(client, state.getWatchKeys(), eventData); + refreshWithoutTime(client, state.getWatchKeys(), eventData, context); StateHolder.getCurrentState().updateStateRefresh(state, refreshInterval); } @@ -168,9 +178,9 @@ private static void refreshWithTime(AppConfigurationReplicaClient client, State * @param eventData Refresh event info */ private static void refreshWithoutTime(AppConfigurationReplicaClient client, List watchKeys, - RefreshEventData eventData) throws AppConfigurationStatusException { + RefreshEventData eventData, Context context) throws AppConfigurationStatusException { for (ConfigurationSetting watchKey : watchKeys) { - ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel(), true); + ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel(), context); // If there is no result, etag will be considered empty. // A refresh will trigger once the selector returns a value. @@ -184,14 +194,14 @@ private static void refreshWithoutTime(AppConfigurationReplicaClient client, Lis } private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient client, FeatureFlagState state, - Duration refreshInterval, RefreshEventData eventData, ReplicaLookUp replicaLookUp) + Duration refreshInterval, RefreshEventData eventData, ReplicaLookUp replicaLookUp, Context context) throws AppConfigurationStatusException { Instant date = Instant.now(); if (date.isAfter(state.getNextRefreshCheck())) { replicaLookUp.updateAutoFailoverEndpoints(); for (FeatureFlags featureFlags : state.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { + if (client.checkWatchKeys(featureFlags.getSettingSelector(), context)) { String eventDataInfo = ".appconfig.featureflag/*"; // Only one refresh Event needs to be call to update all of the @@ -209,10 +219,10 @@ private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient cl } private static void refreshWithoutTimeFeatureFlags(AppConfigurationReplicaClient client, FeatureFlagState watchKeys, - RefreshEventData eventData) throws AppConfigurationStatusException { + RefreshEventData eventData, Context context) throws AppConfigurationStatusException { for (FeatureFlags featureFlags : watchKeys.getWatchKeys()) { - if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) { + if (client.checkWatchKeys(featureFlags.getSettingSelector(), context)) { 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 5eea371940c2..95e3e90768b1 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 @@ -88,10 +88,9 @@ 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, Context context) throws HttpResponseException { try { - Context context = new Context("refresh", isRefresh); ConfigurationSetting selector = new ConfigurationSetting().setKey(key).setLabel(label); ConfigurationSetting watchKey = NormalizeNull .normalizeNullLabel( @@ -111,11 +110,10 @@ 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, Context context) throws HttpResponseException { List configurationSettings = new ArrayList<>(); try { - Context context = new Context("refresh", isRefresh); PagedIterable settings = client.listConfigurationSettings(settingSelector, context); settings.forEach(setting -> { configurationSettings.add(NormalizeNull.normalizeNullLabel(setting)); @@ -130,11 +128,11 @@ List listSettings(SettingSelector settingSelector, boolean } } - FeatureFlags listFeatureFlags(SettingSelector settingSelector, boolean isRefresh) throws HttpResponseException { + FeatureFlags listFeatureFlags(SettingSelector settingSelector, Context context) + throws HttpResponseException { List configurationSettings = new ArrayList<>(); List checks = new ArrayList<>(); try { - Context context = new Context("refresh", isRefresh); client.listConfigurationSettings(settingSelector, context).streamByPage().forEach(pagedResponse -> { checks.add( new MatchConditions().setIfNoneMatch(pagedResponse.getHeaders().getValue(HttpHeaderName.ETAG))); @@ -155,12 +153,11 @@ FeatureFlags listFeatureFlags(SettingSelector settingSelector, boolean isRefresh } } - List listSettingSnapshot(String snapshotName, boolean isRefresh) { + List listSettingSnapshot(String snapshotName, Context context) { List configurationSettings = new ArrayList<>(); try { // 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."); @@ -177,8 +174,7 @@ List listSettingSnapshot(String snapshotName, boolean isRe } } - boolean checkWatchKeys(SettingSelector settingSelector, boolean isRefresh) { - Context context = new Context("refresh", false); + boolean checkWatchKeys(SettingSelector settingSelector, Context context) { 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/AppConfigurationSnapshotPropertySource.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationSnapshotPropertySource.java index 853db7e8ab11..e585997e05a5 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 @@ -7,6 +7,7 @@ import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags; @@ -45,8 +46,8 @@ final class AppConfigurationSnapshotPropertySource extends AppConfigurationAppli * @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, boolean isRefresh) throws InvalidConfigurationPropertyValueException { - processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName, isRefresh), null, trim); + public void initProperties(List trim, Context context) throws InvalidConfigurationPropertyValueException { + processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName, context), 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/AzureAppConfigDataLoader.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/main/java/com/azure/spring/cloud/appconfiguration/config/implementation/AzureAppConfigDataLoader.java index 72c1e03c3a3f..5044cfeb81db 100644 --- 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 @@ -19,11 +19,13 @@ import org.springframework.core.env.EnumerablePropertySource; import org.springframework.util.StringUtils; +import com.azure.core.util.Context; 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.AppConfigurationStoreMonitoring.PushNotification; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagKeyValueSelector; @EnableConfigurationProperties(AppConfigurationProviderProperties.class) @@ -40,6 +42,8 @@ public class AzureAppConfigDataLoader implements ConfigDataLoader watchKeysSettings = monitoring.getTriggers().stream() .map(trigger -> client.getWatchKey(trigger.getKey(), trigger.getLabel(), - resource.isRefresh())) + requestContext)) .toList(); storeState.setState(resource.getEndpoint(), watchKeysSettings, monitoring.getRefreshInterval()); @@ -157,7 +170,7 @@ private List createSettings(AppConfigurationRepl selectedKeys.getKeyFilter() + resource.getEndpoint() + "/", client, keyVaultClientFactory, selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles)); } - propertySource.initProperties(resource.getTrimKeyPrefix(), resource.isRefresh()); + propertySource.initProperties(resource.getTrimKeyPrefix(), requestContext); sourceList.add(propertySource); } return sourceList; @@ -174,9 +187,10 @@ private List createFeatureFlags(AppConfigurationReplicaClient clie 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()); + selectedKeys.getKeyFilter(), selectedKeys.getLabelFilter(profiles), requestContext); featureFlagWatchKeys.addAll(storesFeatureFlags); } 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 474f0fda49f0..047dee654f29 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 @@ -26,6 +26,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.SettingSelector; @@ -64,7 +65,7 @@ class FeatureFlagClient { * */ List loadFeatureFlags(AppConfigurationReplicaClient replicaClient, String customKeyFilter, - String[] labelFilter, boolean isRefresh) { + String[] labelFilter, Context context) { List loadedFeatureFlags = new ArrayList<>(); String keyFilter = SELECT_ALL_FEATURE_FLAGS; @@ -79,7 +80,7 @@ List loadFeatureFlags(AppConfigurationReplicaClient replicaClient, for (String label : labels) { SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter).setLabelFilter(label); - FeatureFlags features = replicaClient.listFeatureFlags(settingSelector, isRefresh); + FeatureFlags features = replicaClient.listFeatureFlags(settingSelector, context); 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/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 bd72467aa3f9..aac69c9cfb0f 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 @@ -44,11 +44,12 @@ public BaseAppConfigurationPolicy(TracingInfo tracingInfo) { @Override public Mono process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) { Boolean watchRequests = (Boolean) context.getData("refresh").orElse(false); + Boolean pushRefreshEnabled = (Boolean) context.getData("pushRefreshEnabled").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)); + tracingInfo.getValue(watchRequests, pushRefreshEnabled)); return next.process(); } 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 5f57b8e9ab78..4352905d15e8 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 @@ -28,7 +28,7 @@ public TracingInfo(boolean isKeyVaultConfigured, int replicaCount, Configuration this.configuration = configuration; } - String getValue(boolean watchRequests) { + String getValue(boolean watchRequests, boolean pushRefreshEnabled) { String track = configuration.get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); if (track != null && Boolean.valueOf(track)) { return ""; @@ -55,6 +55,10 @@ String getValue(boolean watchRequests) { sb.append(",").append(RequestTracingConstants.REPLICA_COUNT).append("=").append(replicaCount); } + if (pushRefreshEnabled) { + sb.append(",").append("PushRefreshEnabled"); + } + sb = getFeatureManagementUsage(sb); return sb.toString(); 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 7ef8af30b518..c4c9c95c025a 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 @@ -39,12 +39,14 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; + public class AppConfigurationApplicationSettingPropertySourceSnapshotTest { private static final String EMPTY_CONTENT_TYPE = ""; @@ -90,6 +92,9 @@ public class AppConfigurationApplicationSettingPropertySourceSnapshotTest { @Mock private List configurationListMock; + @Mock + private Context contextMock; + FeatureFlagClient featureFlagLoader = new FeatureFlagClient(); private MockitoSession session; @@ -128,10 +133,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(), Mockito.anyBoolean())).thenReturn(configurationListMock) + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(TRIM, false); + propertySource.initProperties(TRIM, contextMock); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = testItems.stream().map(t -> { @@ -155,10 +160,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(), Mockito.anyBoolean())).thenReturn(configurationListMock) + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(TRIM, false); + propertySource.initProperties(TRIM, contextMock); String expectedKeyName = TEST_SLASH_KEY.replace('/', '.'); String[] actualKeyNames = propertySource.getPropertyNames(); @@ -174,9 +179,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(), Mockito.anyBoolean())).thenReturn(configurationListMock); + when(clientMock.listSettingSnapshot(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock); - propertySource.initProperties(TRIM, false); + propertySource.initProperties(TRIM, contextMock); 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/AppConfigurationApplicationSettingPropertySourceTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationApplicationSettingPropertySourceTest.java index 1f7fe5ce9760..6aac4f284cb8 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 @@ -34,12 +34,13 @@ import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationProperties; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; public class AppConfigurationApplicationSettingPropertySourceTest { @@ -82,6 +83,9 @@ public class AppConfigurationApplicationSettingPropertySourceTest { @Mock private List configurationListMock; + @Mock + private Context contextMock; + private MockitoSession session; @BeforeAll @@ -116,10 +120,10 @@ public void cleanup() throws Exception { @Test public void testPropCanBeInitAndQueried() throws IOException { when(configurationListMock.iterator()).thenReturn(testItems.iterator()); - when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) + when(clientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(null, false); + propertySource.initProperties(null, contextMock); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = testItems.stream() @@ -140,10 +144,10 @@ public void testPropertyNameSlashConvertedToDots() throws IOException { settings.add(slashedProp); when(configurationListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock) + when(clientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock) .thenReturn(configurationListMock); - propertySource.initProperties(null, false); + propertySource.initProperties(null, contextMock); String expectedKeyName = TEST_SLASH_KEY.replace('/', '.'); String[] actualKeyNames = propertySource.getPropertyNames(); @@ -160,9 +164,9 @@ public void initNullValidContentTypeTest() throws IOException { items.add(ITEM_NULL); when(configurationListMock.iterator()).thenReturn(items.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock); + when(clientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock); - propertySource.initProperties(null, false); + propertySource.initProperties(null, contextMock); String[] keyNames = propertySource.getPropertyNames(); String[] expectedKeyNames = items.stream() @@ -177,9 +181,9 @@ public void jsonContentTypeWithInvalidJsonValueTest() { items.add(ITEM_INVALID_JSON); when(configurationListMock.iterator()).thenReturn(items.iterator()) .thenReturn(Collections.emptyIterator()); - when(clientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(configurationListMock); + when(clientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(configurationListMock); - assertThatThrownBy(() -> propertySource.initProperties(null, false)) + assertThatThrownBy(() -> propertySource.initProperties(null, contextMock)) .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 0bc44e5c546c..74d97b275702 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 @@ -30,6 +30,7 @@ import org.mockito.quality.Strictness; import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting; import com.azure.security.keyvault.secrets.SecretAsyncClient; @@ -70,6 +71,9 @@ public class AppConfigurationPropertySourceKeyVaultTest { @Mock private List keyVaultSecretListMock; + + @Mock + private Context contextMock; private MockitoSession session; @@ -98,7 +102,7 @@ public void testKeyVaultTest() { List settings = List.of(KEY_VAULT_ITEM); when(keyVaultSecretListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); KeyVaultSecret secret = new KeyVaultSecret("mySecret", "mySecretValue"); @@ -107,7 +111,7 @@ public void testKeyVaultTest() { when(clientManagerMock.getSecret(Mockito.any(URI.class))).thenReturn(secret); try { - propertySource.initProperties(null, false); + propertySource.initProperties(null, contextMock); } catch (InvalidConfigurationPropertyValueException e) { fail("Failed Reading in Feature Flags"); } @@ -125,11 +129,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(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); InvalidConfigurationPropertyValueException exception = assertThrows( - InvalidConfigurationPropertyValueException.class, () -> propertySource.initProperties(null, false)); + InvalidConfigurationPropertyValueException.class, () -> propertySource.initProperties(null, contextMock)); assertEquals("test_key_vault_1", exception.getName()); assertEquals("", exception.getValue()); assertEquals("Invalid URI found in JSON property field 'uri' unable to parse.", exception.getReason()); @@ -140,13 +144,13 @@ public void invalidKeyVaultReferenceParseErrorTest() { List settings = List.of(KEY_VAULT_ITEM); when(keyVaultSecretListMock.iterator()).thenReturn(settings.iterator()) .thenReturn(Collections.emptyIterator()); - when(replicaClientMock.listSettings(Mockito.any(), Mockito.anyBoolean())).thenReturn(keyVaultSecretListMock) + when(replicaClientMock.listSettings(Mockito.any(), Mockito.any(Context.class))).thenReturn(keyVaultSecretListMock) .thenReturn(keyVaultSecretListMock); 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, false)); + RuntimeException exception = assertThrows(RuntimeException.class, () -> propertySource.initProperties(null, contextMock)); 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/AppConfigurationRefreshUtilTest.java b/sdk/spring/spring-cloud-azure-appconfiguration-config/src/test/java/com/azure/spring/cloud/appconfiguration/config/implementation/AppConfigurationRefreshUtilTest.java index 161681f4740b..6ef34dda7955 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 @@ -28,6 +28,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.SettingSelector; @@ -61,6 +62,9 @@ public class AppConfigurationRefreshUtilTest { @Mock private ReplicaLookUp replicaLookUpMock; + + @Mock + private Context contextMock; private ConfigStore configStore; @@ -105,8 +109,8 @@ public void refreshWithoutTimeWatchKeyConfigStoreNotLoaded(TestInfo testInfo) { try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getLoadState(endpoint)).thenReturn(false); - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); } } @@ -119,13 +123,13 @@ 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), Mockito.anyBoolean())).thenReturn(watchKeys.get(0)); + when(clientMock.getWatchKey(Mockito.eq(KEY_FILTER), Mockito.eq(EMPTY_LABEL), Mockito.any(Context.class))).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.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); } } @@ -139,12 +143,12 @@ 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), Mockito.anyBoolean())).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))).thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); } } @@ -156,8 +160,8 @@ public void refreshWithoutTimeFeatureFlagDisabled(TestInfo testInfo) { configStore.getFeatureFlags().setEnabled(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); stateHolderMock.verify(() -> StateHolder.getLoadState(Mockito.anyString()), times(1)); } } @@ -170,8 +174,8 @@ public void refreshWithoutTimeFeatureFlagNotLoaded(TestInfo testInfo) { configStore.getFeatureFlags().setEnabled(true); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); stateHolderMock.verify(() -> StateHolder.getLoadState(Mockito.anyString()), times(1)); } } @@ -186,12 +190,12 @@ 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), Mockito.anyBoolean())).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))).thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); } } @@ -206,12 +210,12 @@ 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), Mockito.anyBoolean())).thenReturn(true); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))).thenReturn(true); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); - assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint)); - assertTrue(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock)); + assertFalse(AppConfigurationRefreshUtil.refreshStoreCheck(clientMock, endpoint, contextMock)); + assertTrue(AppConfigurationRefreshUtil.refreshStoreFeatureFlagCheck(true, clientMock, contextMock)); } } @@ -233,7 +237,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.any(Context.class)); } } @@ -252,7 +256,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.any(Context.class)); } } @@ -272,7 +276,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.any(Context.class)); } } @@ -294,7 +298,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.any(Context.class)); assertEquals(newState, StateHolder.getState(endpoint)); } } @@ -304,7 +308,7 @@ public void refreshStoresCheckSettingsTestRefreshTimeNoChange(TestInfo testInfo) endpoint = testInfo.getDisplayName() + ".azconfig.io"; setupFeatureFlagLoad(); - when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean())) + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.any(Context.class))) .thenReturn(generateWatchKeys().get(0)); State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); @@ -320,7 +324,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.any(Context.class)); } } @@ -336,7 +340,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.any(Context.class))).thenReturn(refreshKey); State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); @@ -350,7 +354,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.any(Context.class)); verify(currentStateMock, times(1)).updateStateRefresh(Mockito.any(), Mockito.any()); } } @@ -373,7 +377,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.any(Context.class)); } } @@ -395,7 +399,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.any(Context.class)); } } @@ -407,7 +411,7 @@ public void refreshStoresCheckFeatureFlagTestNoChange(TestInfo testInfo) { configStore.setMonitoring(monitoring); setupFeatureFlagLoad(); - when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.anyBoolean())).thenReturn(false); + when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.any(Context.class))).thenReturn(false); FeatureFlagState newState = new FeatureFlagState( List.of(new FeatureFlags(new SettingSelector().setKeyFilter(KEY_FILTER).setLabelFilter(EMPTY_LABEL), null)), @@ -423,7 +427,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.any(Context.class)); verify(currentStateMock, times(1)).updateFeatureFlagStateRefresh(Mockito.any(), Mockito.any()); } @@ -433,7 +437,7 @@ public void refreshStoresCheckFeatureFlagTestNoChange(TestInfo testInfo) { public void refreshStoresCheckFeatureFlagTestTriggerRefresh(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; setupFeatureFlagLoad(); - when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.anyBoolean())).thenReturn(true); + when(clientOriginMock.checkWatchKeys(Mockito.any(), Mockito.any(Context.class))).thenReturn(true); FeatureFlags featureFlags = new FeatureFlags(new SettingSelector(), watchKeysFeatureFlags); @@ -450,7 +454,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.any(Context.class)); } } 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 9e328cffd751..f19f3c54532f 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,6 +37,7 @@ 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.Context; import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.ConfigurationSnapshot; @@ -69,6 +70,9 @@ public class AppConfigurationReplicaClientTest { @Mock private Response snapshotResponseMock; + + @Mock + private Context contextMock; private final String endpoint = "clientTest.azconfig.io"; @@ -96,25 +100,25 @@ public void getWatchKeyTest() { Mockito.any())).thenReturn(configurationSettingResponse); when(configurationSettingResponse.getValue()).thenReturn(watchKey); - assertEquals(watchKey, client.getWatchKey("watch", "\0", false)); + assertEquals(watchKey, client.getWatchKey("watch", "\0", contextMock)); when(configurationSettingResponse.getValue()).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", contextMock)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", contextMock)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", contextMock)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.getWatchKey("watch", "\0", false)); + assertThrows(HttpResponseException.class, () -> client.getWatchKey("watch", "\0", contextMock)); when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.any())).thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.getWatchKey("watch", "\0", contextMock)); } @Test @@ -132,21 +136,21 @@ public void listSettingsTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenReturn(new PagedIterable<>(pagedFlux)); - assertEquals(configurations, client.listSettings(new SettingSelector(), false)); + assertEquals(configurations, client.listSettings(new SettingSelector(), contextMock)); 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(), false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listSettings(new SettingSelector(), false)); + assertThrows(HttpResponseException.class, () -> client.listSettings(new SettingSelector(), contextMock)); } @Test @@ -166,24 +170,24 @@ public void listFeatureFlagsTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenReturn(new PagedIterable<>(pagedFlux)); - assertEquals(configurations, client.listFeatureFlags(new SettingSelector(), false).getFeatureFlags()); + assertEquals(configurations, client.listFeatureFlags(new SettingSelector(), contextMock).getFeatureFlags()); 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(), false)); + () -> client.listFeatureFlags(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(408); assertThrows(AppConfigurationStatusException.class, - () -> client.listFeatureFlags(new SettingSelector(), false)); + () -> client.listFeatureFlags(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(500); assertThrows(AppConfigurationStatusException.class, - () -> client.listFeatureFlags(new SettingSelector(), false)); + () -> client.listFeatureFlags(new SettingSelector(), contextMock)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listFeatureFlags(new SettingSelector(), false)); + assertThrows(HttpResponseException.class, () -> client.listFeatureFlags(new SettingSelector(), contextMock)); } @Test @@ -192,7 +196,7 @@ public void listSettingsUnknownHostTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettings(new SettingSelector(), contextMock)); } @Test @@ -202,7 +206,7 @@ public void listSettingsNoCredentialTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenThrow(new CredentialUnavailableException("No Credential")); - assertThrows(CredentialUnavailableException.class, () -> client.listSettings(new SettingSelector(), false)); + assertThrows(CredentialUnavailableException.class, () -> client.listSettings(new SettingSelector(), contextMock)); } @Test @@ -212,7 +216,7 @@ public void getWatchNoCredentialTest() { when(clientMock.getConfigurationSettingWithResponse(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.any())) .thenThrow(new CredentialUnavailableException("No Credential")); - assertThrows(CredentialUnavailableException.class, () -> client.getWatchKey("key", "label", false)); + assertThrows(CredentialUnavailableException.class, () -> client.getWatchKey("key", "label", contextMock)); } @Test @@ -238,7 +242,7 @@ public void backoffTest() { when(clientMock.listConfigurationSettings(Mockito.any(SettingSelector.class), Mockito.any())) .thenReturn(settingsMock); - client.listSettings(new SettingSelector(), false); + client.listSettings(new SettingSelector(), contextMock); assertTrue(client.getBackoffEndTime().isBefore(Instant.now())); assertEquals(0, client.getFailedAttempts()); } @@ -256,25 +260,25 @@ public void listSettingSnapshotTest() { when(snapshotResponseMock.getValue()).thenReturn(snapshot); when(clientMock.listConfigurationSettingsForSnapshot(Mockito.any())).thenReturn(settingsMock); - assertEquals(configurations, client.listSettingSnapshot("SnapshotName", false)); + assertEquals(configurations, client.listSettingSnapshot("SnapshotName", contextMock)); when(clientMock.listConfigurationSettingsForSnapshot(Mockito.any())).thenThrow(exceptionMock); when(exceptionMock.getResponse()).thenReturn(responseMock); when(responseMock.getStatusCode()).thenReturn(429); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", contextMock)); when(responseMock.getStatusCode()).thenReturn(408); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", contextMock)); when(responseMock.getStatusCode()).thenReturn(500); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", contextMock)); when(responseMock.getStatusCode()).thenReturn(499); - assertThrows(HttpResponseException.class, () -> client.listSettingSnapshot("SnapshotName", false)); + assertThrows(HttpResponseException.class, () -> client.listSettingSnapshot("SnapshotName", contextMock)); when(clientMock.getSnapshotWithResponse(Mockito.any(), Mockito.any(), Mockito.any())) .thenThrow(new UncheckedIOException(new UnknownHostException())); - assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", false)); + assertThrows(AppConfigurationStatusException.class, () -> client.listSettingSnapshot("SnapshotName", contextMock)); } @Test @@ -289,7 +293,7 @@ public void listSettingSnapshotInvalidCompositionTypeTest() { when(snapshotResponseMock.getValue()).thenReturn(snapshot); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, - () -> client.listSettingSnapshot("SnapshotName", false)); + () -> client.listSettingSnapshot("SnapshotName", contextMock)); assertEquals("Snapshot SnapshotName needs to be of type Key.", e.getMessage()); } @@ -324,7 +328,7 @@ public void checkWatchKeysTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())) .thenReturn(new PagedIterable<>(pagedFlux)); - assertTrue(client.checkWatchKeys(new SettingSelector(), false)); + assertTrue(client.checkWatchKeys(new SettingSelector(), contextMock)); pagedResponse.close(); } catch (IOException e) { e.printStackTrace(); @@ -338,7 +342,7 @@ public void checkWatchKeysTest() { when(clientMock.listConfigurationSettings(Mockito.any(), Mockito.any())).thenReturn(new PagedIterable<>(pagedFlux)); - assertFalse(client.checkWatchKeys(new SettingSelector(), false)); + assertFalse(client.checkWatchKeys(new SettingSelector(), contextMock)); 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 07c2e5a56fa2..92704d28e88b 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 @@ -33,6 +33,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import com.azure.core.util.Context; import com.azure.data.appconfiguration.models.ConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagFilter; @@ -43,6 +44,9 @@ public class FeatureFlagClientTest { @Mock private AppConfigurationReplicaClient clientMock; + + @Mock + private Context contextMock; private FeatureFlagClient featureFlagClient; @@ -76,9 +80,9 @@ 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(), Mockito.anyBoolean())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.any(Context.class))).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, contextMock); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals("FakeKey", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -90,9 +94,9 @@ 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(), Mockito.anyBoolean())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.any(Context.class))).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, contextMock); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/Alpha", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -105,9 +109,9 @@ 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(), Mockito.anyBoolean())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.any(Context.class))).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, contextMock); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/Alpha", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -117,9 +121,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(), Mockito.anyBoolean())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.any(Context.class))).thenReturn(featureFlags); - featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); + featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, contextMock); assertEquals(1, featureFlagsList.size()); assertEquals(featureFlags, featureFlagsList.get(0)); assertEquals(".appconfig.featureflag/Alpha", featureFlagsList.get(0).getFeatureFlags().get(0).getKey()); @@ -163,9 +167,9 @@ public void loadFeatureFlagsTestTargetingFilter() { targetingFlag.addClientFilter(targetingFilter); List settings = List.of(targetingFlag); FeatureFlags featureFlags = new FeatureFlags(null, settings); - when(clientMock.listFeatureFlags(Mockito.any(), Mockito.anyBoolean())).thenReturn(featureFlags); + when(clientMock.listFeatureFlags(Mockito.any(), Mockito.any(Context.class))).thenReturn(featureFlags); - List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, false); + List featureFlagsList = featureFlagClient.loadFeatureFlags(clientMock, null, emptyLabelList, contextMock); 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/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 2bdcdd238590..5faef0cab383 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 @@ -24,37 +24,37 @@ public void getValueTest() { Configuration configuration = getConfiguration("false"); TracingInfo tracingInfo = new TracingInfo(false, 0, configuration); - assertEquals("RequestType=Startup", tracingInfo.getValue(false)); - assertEquals("RequestType=Watch", tracingInfo.getValue(true)); + assertEquals("RequestType=Startup", tracingInfo.getValue(false, false)); + assertEquals("RequestType=Watch", tracingInfo.getValue(true, false)); tracingInfo = new TracingInfo(true, 0, configuration); - assertEquals("RequestType=Startup,UsesKeyVault", tracingInfo.getValue(false)); + assertEquals("RequestType=Startup,UsesKeyVault", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 1, configuration); - assertEquals("RequestType=Startup,ReplicaCount=1", tracingInfo.getValue(false)); + assertEquals("RequestType=Startup,ReplicaCount=1", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 0, configuration); tracingInfo.getFeatureFlagTracing().updateFeatureFilterTelemetry("Random"); - assertEquals("RequestType=Startup,Filter=CSTM", tracingInfo.getValue(false)); + assertEquals("RequestType=Startup,Filter=CSTM", tracingInfo.getValue(false, false)); } @Test public void disableTracingTest() { TracingInfo tracingInfo = new TracingInfo(false, 0, getConfiguration(null)); - assertNotEquals("", tracingInfo.getValue(false)); + assertNotEquals("", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 0, getConfiguration("")); - assertNotEquals("", tracingInfo.getValue(false)); + assertNotEquals("", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 0, getConfiguration("true")); - assertEquals("", tracingInfo.getValue(false)); + assertEquals("", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 0, getConfiguration("false")); - assertNotEquals("", tracingInfo.getValue(false)); + assertNotEquals("", tracingInfo.getValue(false, false)); tracingInfo = new TracingInfo(false, 0, getConfiguration("random string")); - assertNotEquals("", tracingInfo.getValue(false)); + assertNotEquals("", tracingInfo.getValue(false, false)); } private static final ConfigurationSource EMPTY_SOURCE = new ConfigurationSource() { From ee470a8f7b624ece56b8ef1746391a9c32ff0710 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 12 Feb 2025 11:21:31 -0800 Subject: [PATCH 2/2] Updated to PUSH_REFRESH + tests --- .../AppConfigurationConstants.java | 5 + .../AppConfigurationRefreshUtil.java | 12 +- .../AzureAppConfigDataLoader.java | 13 +- .../policy/BaseAppConfigurationPolicy.java | 7 +- .../http/policy/TracingInfo.java | 11 +- .../AppConfigurationRefreshUtilTest.java | 132 +++++++++++++++--- .../http/policy/TracingInfoTest.java | 4 + 7 files changed, 147 insertions(+), 37 deletions(-) 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 2daacc4bfd41..24e5fba7b6d0 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 @@ -36,6 +36,11 @@ public class AppConfigurationConstants { * Constant for tracing if Key Vault is configured for use. */ public static final String KEY_VAULT_CONFIGURED_TRACING = "UsesKeyVault"; + + /** + * Constant for tracing if Push Refresh is enabled for the store. + */ + public static final String PUSH_REFRESH = "PushRefresh"; /** * Http Header User Agent 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 bedeb0ff7af5..a5337b08e43a 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 @@ -2,6 +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.PUSH_REFRESH; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -9,6 +10,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import com.azure.core.exception.HttpResponseException; import com.azure.core.util.Context; @@ -53,13 +55,13 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF AppConfigurationStoreMonitoring monitor = connection.getMonitoring(); - boolean pushRefreshEnabled = false; + boolean pushRefresh = false; PushNotification notification = monitor.getPushNotification(); - if (notification.getPrimaryToken() != null - || notification.getSecondaryToken() != null) { - pushRefreshEnabled = true; + if ((notification.getPrimaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName())) + || (notification.getSecondaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName()))) { + pushRefresh = true; } - Context context = new Context("refresh", true).addData("pushRefreshEnabled", pushRefreshEnabled); + Context context = new Context("refresh", true).addData(PUSH_REFRESH, pushRefresh); List clients = clientFactory.getAvailableClients(originEndpoint); 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 index 5044cfeb81db..82bbd918900c 100644 --- 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 @@ -2,6 +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.PUSH_REFRESH; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; @@ -76,14 +77,14 @@ public ConfigData load(ConfigDataLoaderContext context, AzureAppConfigDataResour boolean reloadFailed = false; - boolean pushRefreshEnabled = false; + boolean pushRefresh = false; PushNotification notification = resource.getMonitoring().getPushNotification(); - if (notification.getPrimaryToken() != null - || notification.getSecondaryToken() != null) { - pushRefreshEnabled = true; + if ((notification.getPrimaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName())) + || (notification.getSecondaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName()))) { + pushRefresh = true; } - requestContext = new Context("refresh", resource.isRefresh()).addData("pushRefreshEnabled", - pushRefreshEnabled); + requestContext = new Context("refresh", resource.isRefresh()).addData(PUSH_REFRESH, + pushRefresh); // Feature Management needs to be set in the last config store. 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 aac69c9cfb0f..e46a413fac23 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,6 +2,8 @@ // 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.PUSH_REFRESH; + import org.springframework.util.StringUtils; import com.azure.core.http.HttpHeaderName; @@ -40,16 +42,15 @@ public BaseAppConfigurationPolicy(TracingInfo tracingInfo) { this.tracingInfo = tracingInfo; } - @Override public Mono process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) { Boolean watchRequests = (Boolean) context.getData("refresh").orElse(false); - Boolean pushRefreshEnabled = (Boolean) context.getData("pushRefreshEnabled").orElse(false); + Boolean pushRefresh = (Boolean) context.getData(PUSH_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, pushRefreshEnabled)); + tracingInfo.getValue(watchRequests, pushRefresh)); return next.process(); } 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 4352905d15e8..22f1b74118ff 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 @@ -3,6 +3,7 @@ package com.azure.spring.cloud.appconfiguration.config.implementation.http.policy; import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.KEY_VAULT_CONFIGURED_TRACING; +import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.PUSH_REFRESH; import org.springframework.util.StringUtils; @@ -28,7 +29,7 @@ public TracingInfo(boolean isKeyVaultConfigured, int replicaCount, Configuration this.configuration = configuration; } - String getValue(boolean watchRequests, boolean pushRefreshEnabled) { + String getValue(boolean watchRequests, boolean pushRefresh) { String track = configuration.get(RequestTracingConstants.REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE.toString()); if (track != null && Boolean.valueOf(track)) { return ""; @@ -50,15 +51,15 @@ String getValue(boolean watchRequests, boolean pushRefreshEnabled) { if (isKeyVaultConfigured) { sb.append(",").append(KEY_VAULT_CONFIGURED_TRACING); } + + if (pushRefresh) { + sb.append(",").append(PUSH_REFRESH); + } if (replicaCount > 0) { sb.append(",").append(RequestTracingConstants.REPLICA_COUNT).append("=").append(replicaCount); } - if (pushRefreshEnabled) { - sb.append(",").append("PushRefreshEnabled"); - } - sb = getFeatureManagementUsage(sb); return sb.toString(); 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 6ef34dda7955..9e5ab6bcaf9e 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 @@ -21,6 +21,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -37,6 +38,8 @@ 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.properties.AppConfigurationStoreMonitoring; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.AccessToken; +import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.PushNotification; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagKeyValueSelector; import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagStore; @@ -62,7 +65,7 @@ public class AppConfigurationRefreshUtilTest { @Mock private ReplicaLookUp replicaLookUpMock; - + @Mock private Context contextMock; @@ -123,7 +126,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), Mockito.any(Context.class))).thenReturn(watchKeys.get(0)); + when(clientMock.getWatchKey(Mockito.eq(KEY_FILTER), Mockito.eq(EMPTY_LABEL), Mockito.any(Context.class))) + .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); @@ -143,7 +147,8 @@ 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), Mockito.any(Context.class))).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))) + .thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -184,13 +189,14 @@ public void refreshWithoutTimeFeatureFlagNotLoaded(TestInfo testInfo) { public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).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); // Config Store doesn't return a watch key change. - when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))).thenReturn(false); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))) + .thenReturn(false); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -204,13 +210,14 @@ public void refreshWithoutTimeFeatureFlagNoChange(TestInfo testInfo) { public void refreshWithoutTimeFeatureFlagEtagChanged(TestInfo testInfo) { endpoint = testInfo.getDisplayName() + ".azconfig.io"; when(clientMock.getEndpoint()).thenReturn(endpoint); - + FeatureFlags featureFlags = new FeatureFlags(new SettingSelector(), watchKeysFeatureFlags); FeatureFlagState newState = new FeatureFlagState(List.of(featureFlags), Math.toIntExact(Duration.ofMinutes(10).getSeconds()), endpoint); // Config Store does return a watch key change. - when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))).thenReturn(true); + when(clientMock.checkWatchKeys(Mockito.any(SettingSelector.class), Mockito.any(Context.class))) + .thenReturn(true); try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { stateHolderMock.when(() -> StateHolder.getStateFeatureFlag(endpoint)).thenReturn(newState); @@ -237,7 +244,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } @@ -256,7 +264,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } @@ -276,7 +285,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } @@ -297,9 +307,14 @@ public void refreshStoresCheckSettingsTestFailedRequest(TestInfo testInfo) { Duration.ofMinutes(10), (long) 60, replicaLookUpMock); assertFalse(eventData.getDoRefresh()); + ArgumentCaptor captorParam = ArgumentCaptor.forClass(Context.class); verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); - verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.any(Context.class)); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + captorParam.capture()); assertEquals(newState, StateHolder.getState(endpoint)); + Context testContext = captorParam.getValue(); + assertTrue((Boolean) testContext.getData("refresh").get()); + assertFalse((Boolean) testContext.getData("PushRefresh").get()); } } @@ -324,7 +339,82 @@ 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.any(Context.class)); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); + } + } + + @Test + public void refreshStoresPushRefreshEnabledPrimary(TestInfo testInfo) { + endpoint = testInfo.getDisplayName() + ".azconfig.io"; + setupFeatureFlagLoad(); + PushNotification pushNotificaiton = new PushNotification(); + AccessToken p1 = new AccessToken(); + p1.setName("fake name"); + p1.setSecret("value"); + pushNotificaiton.setPrimaryToken(p1); + monitoring.setPushNotification(pushNotificaiton); + when(connectionManagerMock.getMonitoring()).thenReturn(monitoring); + + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.any(Context.class))) + .thenReturn(generateWatchKeys().get(0)); + + State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); + + try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { + stateHolderMock.when(() -> StateHolder.getLoadState(endpoint)).thenReturn(true); + stateHolderMock.when(() -> StateHolder.getState(Mockito.any())).thenReturn(newState); + StateHolder updatedStateHolder = new StateHolder(); + stateHolderMock.when(() -> StateHolder.getCurrentState()).thenReturn(updatedStateHolder); + + RefreshEventData eventData = new AppConfigurationRefreshUtil().refreshStoresCheck(clientFactoryMock, + Duration.ofMinutes(10), (long) 60, replicaLookUpMock); + assertEquals(newState, StateHolder.getState(endpoint)); + assertFalse(eventData.getDoRefresh()); + ArgumentCaptor captorParam = ArgumentCaptor.forClass(Context.class); + verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + captorParam.capture()); + Context testContext = captorParam.getValue(); + assertTrue((Boolean) testContext.getData("refresh").get()); + assertTrue((Boolean) testContext.getData("PushRefresh").get()); + } + } + + @Test + public void refreshStoresPushRefreshEnabledSecondary(TestInfo testInfo) { + endpoint = testInfo.getDisplayName() + ".azconfig.io"; + setupFeatureFlagLoad(); + PushNotification pushNotificaiton = new PushNotification(); + AccessToken p2 = new AccessToken(); + p2.setName("fake name"); + p2.setSecret("value"); + pushNotificaiton.setPrimaryToken(p2); + monitoring.setPushNotification(pushNotificaiton); + when(connectionManagerMock.getMonitoring()).thenReturn(monitoring); + + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.any(Context.class))) + .thenReturn(generateWatchKeys().get(0)); + + State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); + + try (MockedStatic stateHolderMock = Mockito.mockStatic(StateHolder.class)) { + stateHolderMock.when(() -> StateHolder.getLoadState(endpoint)).thenReturn(true); + stateHolderMock.when(() -> StateHolder.getState(Mockito.any())).thenReturn(newState); + StateHolder updatedStateHolder = new StateHolder(); + stateHolderMock.when(() -> StateHolder.getCurrentState()).thenReturn(updatedStateHolder); + + RefreshEventData eventData = new AppConfigurationRefreshUtil().refreshStoresCheck(clientFactoryMock, + Duration.ofMinutes(10), (long) 60, replicaLookUpMock); + assertEquals(newState, StateHolder.getState(endpoint)); + assertFalse(eventData.getDoRefresh()); + ArgumentCaptor captorParam = ArgumentCaptor.forClass(Context.class); + verify(clientFactoryMock, times(1)).setCurrentConfigStoreClient(Mockito.eq(endpoint), Mockito.eq(endpoint)); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + captorParam.capture()); + Context testContext = captorParam.getValue(); + assertTrue((Boolean) testContext.getData("refresh").get()); + assertTrue((Boolean) testContext.getData("PushRefresh").get()); } } @@ -340,7 +430,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(), Mockito.any(Context.class))).thenReturn(refreshKey); + when(clientOriginMock.getWatchKey(Mockito.anyString(), Mockito.anyString(), Mockito.any(Context.class))) + .thenReturn(refreshKey); State newState = new State(generateWatchKeys(), Math.toIntExact(Duration.ofMinutes(-1).getSeconds()), endpoint); @@ -354,7 +445,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(1)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); verify(currentStateMock, times(1)).updateStateRefresh(Mockito.any(), Mockito.any()); } } @@ -377,7 +469,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } @@ -399,7 +492,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } @@ -427,7 +521,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); verify(currentStateMock, times(1)).updateFeatureFlagStateRefresh(Mockito.any(), Mockito.any()); } @@ -454,7 +549,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(), Mockito.any(Context.class)); + verify(clientOriginMock, times(0)).getWatchKey(Mockito.anyString(), Mockito.anyString(), + Mockito.any(Context.class)); } } 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 5faef0cab383..f93ffa0c63e6 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 @@ -37,6 +37,10 @@ public void getValueTest() { tracingInfo.getFeatureFlagTracing().updateFeatureFilterTelemetry("Random"); assertEquals("RequestType=Startup,Filter=CSTM", tracingInfo.getValue(false, false)); + + tracingInfo = new TracingInfo(true, 0, configuration); + assertEquals("RequestType=Startup,UsesKeyVault,PushRefresh", tracingInfo.getValue(false, true)); + } @Test