diff --git a/sdk/appconfiguration/azure-data-appconfiguration/CHANGELOG.md b/sdk/appconfiguration/azure-data-appconfiguration/CHANGELOG.md
index 52fadc363fb8..56bf36a68dbd 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/CHANGELOG.md
+++ b/sdk/appconfiguration/azure-data-appconfiguration/CHANGELOG.md
@@ -4,6 +4,8 @@
### Features Added
+- Added `checkConfigurationSettings` method to `ConfigurationClient` and `ConfigurationAsyncClient` that performs HEAD requests to efficiently check if configuration settings have changed by comparing page-level ETags without retrieving the full response body.
+
### Breaking Changes
### Bugs Fixed
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationAsyncClient.java b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationAsyncClient.java
index 1fee78715b51..c8ab55c04835 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationAsyncClient.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationAsyncClient.java
@@ -84,7 +84,8 @@
*
*
* ConfigurationAsyncClient configurationAsyncClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildAsyncClient();
*
*
@@ -1050,6 +1051,56 @@ public PagedFlux listConfigurationSettings(SettingSelector
.map(ConfigurationSettingDeserializationHelper::toConfigurationSettingWithPagedResponse)));
}
+ /**
+ * Checks configuration settings using a HEAD request, returning only headers without the response body.
+ * This is useful for efficiently checking if settings have changed by comparing ETags.
+ *
+ * The returned items will be empty since HEAD requests do not return a body. Use {@code byPage()} iteration
+ * to access page-level ETags for change detection.
+ *
+ * Code Samples
+ *
+ * Check all settings that use the key "prodDBConnection".
+ *
+ *
+ *
+ * SettingSelector selector = new SettingSelector().setKeyFilter("my-app/*");
+ * client.checkConfigurationSettings(selector)
+ * .byPage()
+ * .subscribe(page -> {
+ * System.out.println("Status code: " + page.getStatusCode());
+ * System.out.println("Page ETag: " + page.getHeaders().getValue(com.azure.core.http.HttpHeaderName.ETAG));
+ * });
+ *
+ *
+ *
+ * @param selector Optional. Selector to filter configuration setting results from the service.
+ * @return A Flux of ConfigurationSettings with empty items. Use {@code byPage()} to access page-level ETags.
+ * @throws HttpResponseException If a client or service error occurs, such as a 404, 409, 429 or 500.
+ */
+ @ServiceMethod(returns = ReturnType.COLLECTION)
+ public PagedFlux checkConfigurationSettings(SettingSelector selector) {
+ final String keyFilter = selector == null ? null : selector.getKeyFilter();
+ final String labelFilter = selector == null ? null : selector.getLabelFilter();
+ final String acceptDateTime = selector == null ? null : selector.getAcceptDateTime();
+ final List settingFields = selector == null ? null : toSettingFieldsList(selector.getFields());
+ final List matchConditionsList = selector == null ? null : selector.getMatchConditions();
+ final List tagsFilter = selector == null ? null : selector.getTagsFilter();
+ AtomicInteger pageETagIndex = new AtomicInteger(0);
+ return new PagedFlux<>(() -> withContext(context -> serviceClient
+ .checkKeyValuesWithResponseAsync(keyFilter, labelFilter, null, acceptDateTime, settingFields, null, null,
+ getPageETag(matchConditionsList, pageETagIndex), tagsFilter, context)
+ .map(Utility::toHeadPagedResponse)
+ .onErrorResume(HttpResponseException.class,
+ (Function>>) Utility::handleHeadNotModifiedErrorToValidResponse)),
+ afterToken -> withContext(context -> serviceClient
+ .checkKeyValuesWithResponseAsync(keyFilter, labelFilter, afterToken, acceptDateTime, settingFields,
+ null, null, getPageETag(matchConditionsList, pageETagIndex), tagsFilter, context)
+ .map(Utility::toHeadPagedResponse)
+ .onErrorResume(HttpResponseException.class,
+ (Function>>) Utility::handleHeadNotModifiedErrorToValidResponse)));
+ }
+
/**
* Fetches the configuration settings in a snapshot that matches the {@code snapshotName}. If {@code snapshotName}
* is {@code null}, then all the {@link ConfigurationSetting configuration settings} are fetched with their
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClient.java b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClient.java
index e09a97c5d6d9..7719f4e13081 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClient.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClient.java
@@ -11,6 +11,7 @@
import com.azure.core.exception.ResourceModifiedException;
import com.azure.core.exception.ResourceNotFoundException;
import com.azure.core.http.HttpResponse;
+import com.azure.core.http.HttpHeaderName;
import com.azure.core.http.MatchConditions;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.http.rest.PagedResponse;
@@ -52,6 +53,8 @@
import static com.azure.data.appconfiguration.implementation.Utility.getETag;
import static com.azure.data.appconfiguration.implementation.Utility.getPageETag;
import static com.azure.data.appconfiguration.implementation.Utility.handleNotModifiedErrorToValidResponse;
+import static com.azure.data.appconfiguration.implementation.Utility.toHeadPagedResponse;
+import static com.azure.data.appconfiguration.implementation.Utility.handleHeadNotModifiedErrorToValidResponse;
import static com.azure.data.appconfiguration.implementation.Utility.toKeyValue;
import static com.azure.data.appconfiguration.implementation.Utility.toSettingFieldsList;
import static com.azure.data.appconfiguration.implementation.Utility.updateSnapshotSync;
@@ -84,7 +87,8 @@
*
*
* ConfigurationClient configurationClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildClient();
*
*
@@ -1080,6 +1084,99 @@ public PagedIterable listConfigurationSettings(SettingSele
});
}
+ /**
+ * Checks configuration settings using a HEAD request, returning only headers without the response body.
+ * This is useful for efficiently checking if settings have changed by comparing ETags.
+ *
+ * The returned items will be empty since HEAD requests do not return a body. Use
+ * {@link PagedIterable#iterableByPage()} to access page-level ETags for change detection.
+ *
+ * Code Samples
+ *
+ * Check all settings that use the key "prodDBConnection".
+ *
+ *
+ *
+ * SettingSelector selector = new SettingSelector().setKeyFilter("my-app/*");
+ * client.checkConfigurationSettings(selector)
+ * .iterableByPage()
+ * .forEach(page -> {
+ * System.out.println("Status code: " + page.getStatusCode());
+ * System.out.println("Page ETag: " + page.getHeaders().getValue(com.azure.core.http.HttpHeaderName.ETAG));
+ * });
+ *
+ *
+ *
+ * @param selector Optional. Selector to filter configuration setting results from the service.
+ * @return A {@link PagedIterable} of ConfigurationSettings with empty items. Use {@code iterableByPage()} to access
+ * page-level ETags.
+ * @throws HttpResponseException If a client or service error occurs, such as a 404, 409, 429 or 500.
+ */
+ @ServiceMethod(returns = ReturnType.COLLECTION)
+ public PagedIterable checkConfigurationSettings(SettingSelector selector) {
+ return checkConfigurationSettings(selector, Context.NONE);
+ }
+
+ /**
+ * Checks configuration settings using a HEAD request, returning only headers without the response body.
+ * This is useful for efficiently checking if settings have changed by comparing ETags.
+ *
+ * The returned items will be empty since HEAD requests do not return a body. Use
+ * {@link PagedIterable#iterableByPage()} to access page-level ETags for change detection.
+ *
+ * Code Samples
+ *
+ * Check all settings that use the key "prodDBConnection".
+ *
+ *
+ *
+ *
+ * SettingSelector selector = new SettingSelector().setKeyFilter("my-app/*");
+ * Context ctx = new Context(key1, value1);
+ * client.checkConfigurationSettings(selector, ctx)
+ * .iterableByPage()
+ * .forEach(page -> {
+ * System.out.println("Status code: " + page.getStatusCode());
+ * System.out.println("Page ETag: " + page.getHeaders().getValue(com.azure.core.http.HttpHeaderName.ETAG));
+ * });
+ *
+ *
+ *
+ * @param selector Optional. Selector to filter configuration setting results from the service.
+ * @param context Additional context that is passed through the Http pipeline during the service call.
+ * @return A {@link PagedIterable} of ConfigurationSettings with empty items. Use {@code iterableByPage()} to access
+ * page-level ETags.
+ * @throws HttpResponseException If a client or service error occurs, such as a 404, 409, 429 or 500.
+ */
+ @ServiceMethod(returns = ReturnType.COLLECTION)
+ public PagedIterable checkConfigurationSettings(SettingSelector selector, Context context) {
+ final String keyFilter = selector == null ? null : selector.getKeyFilter();
+ final String labelFilter = selector == null ? null : selector.getLabelFilter();
+ final String acceptDateTime = selector == null ? null : selector.getAcceptDateTime();
+ final List settingFields = selector == null ? null : toSettingFieldsList(selector.getFields());
+ final List matchConditionsList = selector == null ? null : selector.getMatchConditions();
+ final List tagsFilter = selector == null ? null : selector.getTagsFilter();
+
+ AtomicInteger pageETagIndex = new AtomicInteger(0);
+ return new PagedIterable<>(() -> {
+ try {
+ return toHeadPagedResponse(serviceClient.checkKeyValuesWithResponse(keyFilter, labelFilter, null,
+ acceptDateTime, settingFields, null, null, getPageETag(matchConditionsList, pageETagIndex),
+ tagsFilter, context));
+ } catch (HttpResponseException ex) {
+ return handleHeadNotModifiedErrorToValidResponse(ex, LOGGER, true);
+ }
+ }, afterToken -> {
+ try {
+ return toHeadPagedResponse(serviceClient.checkKeyValuesWithResponse(keyFilter, labelFilter, afterToken,
+ acceptDateTime, settingFields, null, null, getPageETag(matchConditionsList, pageETagIndex),
+ tagsFilter, context));
+ } catch (HttpResponseException ex) {
+ return handleHeadNotModifiedErrorToValidResponse(ex, LOGGER, true);
+ }
+ });
+ }
+
/**
* Fetches the configuration settings in a snapshot that matches the {@code snapshotName}. If {@code snapshotName}
* is {@code null}, then all the {@link ConfigurationSetting configuration settings} are fetched with their current
@@ -1589,4 +1686,5 @@ public PagedIterable listLabels(SettingLabelSelector selector, Con
public void updateSyncToken(String token) {
syncTokenPolicy.updateSyncToken(token);
}
+
}
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClientBuilder.java b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClientBuilder.java
index 26a0b21942b4..e5c2d81b055f 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClientBuilder.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/ConfigurationClientBuilder.java
@@ -72,7 +72,8 @@
*
*
* ConfigurationAsyncClient configurationAsyncClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildAsyncClient();
*
*
@@ -82,7 +83,8 @@
*
*
* ConfigurationClient configurationClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildClient();
*
*
@@ -102,7 +104,7 @@
* ConfigurationClient configurationClient = new ConfigurationClientBuilder()
* .pipeline(pipeline)
* .endpoint("https://dummy.azure.net/")
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
* .buildClient();
*
*
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/implementation/Utility.java b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/implementation/Utility.java
index ee7c029a9279..e41c7f4f940e 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/implementation/Utility.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/implementation/Utility.java
@@ -15,6 +15,7 @@
import com.azure.core.util.Context;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
+import com.azure.data.appconfiguration.implementation.models.CheckKeyValuesHeaders;
import com.azure.data.appconfiguration.implementation.models.KeyValue;
import com.azure.data.appconfiguration.implementation.models.SnapshotUpdateParameters;
import com.azure.data.appconfiguration.implementation.models.UpdateSnapshotHeaders;
@@ -26,6 +27,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -210,4 +212,62 @@ public static List getTagsFilterInString(Map tagsFilter)
}
return tagsFilters;
}
+
+ // Parse the 'after' query parameter value from the Link header.
+ // Link header format: ; rel="next"
+ public static String parseAfterParam(String linkHeader) {
+ String nextLink = parseNextLink(linkHeader);
+ if (nextLink == null) {
+ return null;
+ }
+ int afterIdx = nextLink.indexOf("after=");
+ if (afterIdx == -1) {
+ return null;
+ }
+ String afterValue = nextLink.substring(afterIdx + 6);
+ int ampIdx = afterValue.indexOf('&');
+ return ampIdx != -1 ? afterValue.substring(0, ampIdx) : afterValue;
+ }
+
+ // Convert a HEAD response to a PagedResponse with empty items.
+ public static PagedResponse
+ toHeadPagedResponse(ResponseBase response) {
+ String continuationToken = parseAfterParam(response.getHeaders().getValue(HttpHeaderName.LINK));
+ return new PagedResponseBase<>(response.getRequest(), response.getStatusCode(), response.getHeaders(),
+ Collections.emptyList(), continuationToken, null);
+ }
+
+ // Handle 304 status code from HEAD request to a valid response - Async handler
+ public static Mono>
+ handleHeadNotModifiedErrorToValidResponse(HttpResponseException error) {
+ HttpResponse httpResponse = error.getResponse();
+ if (httpResponse == null) {
+ return Mono.error(error);
+ }
+
+ String continuationToken = parseAfterParam(httpResponse.getHeaderValue(HttpHeaderName.LINK));
+ if (httpResponse.getStatusCode() == 304) {
+ return Mono.just(new PagedResponseBase<>(httpResponse.getRequest(), httpResponse.getStatusCode(),
+ httpResponse.getHeaders(), Collections.emptyList(), continuationToken, null));
+ }
+
+ return Mono.error(error);
+ }
+
+ // Handle 304 status code from HEAD request to a valid response - Sync handler
+ public static PagedResponse
+ handleHeadNotModifiedErrorToValidResponse(HttpResponseException error, ClientLogger logger, boolean isHead) {
+ HttpResponse httpResponse = error.getResponse();
+ if (httpResponse == null) {
+ throw logger.logExceptionAsError(error);
+ }
+
+ String continuationToken = parseAfterParam(httpResponse.getHeaderValue(HttpHeaderName.LINK));
+ if (httpResponse.getStatusCode() == 304) {
+ return new PagedResponseBase<>(httpResponse.getRequest(), httpResponse.getStatusCode(),
+ httpResponse.getHeaders(), Collections.emptyList(), continuationToken, null);
+ }
+
+ throw logger.logExceptionAsError(error);
+ }
}
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/package-info.java b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/package-info.java
index 284c471a8cd7..24873502c0d1 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/package-info.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/main/java/com/azure/data/appconfiguration/package-info.java
@@ -34,7 +34,8 @@
*
*
* ConfigurationAsyncClient configurationAsyncClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildAsyncClient();
*
*
@@ -48,7 +49,8 @@
*
*
* ConfigurationClient configurationClient = new ConfigurationClientBuilder()
- * .connectionString(connectionString)
+ * .credential(new DefaultAzureCredentialBuilder().build())
+ * .endpoint(endpoint)
* .buildClient();
*
*
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/samples/java/com/azure/data/appconfiguration/CheckConfigurationSettingsForChanges.java b/sdk/appconfiguration/azure-data-appconfiguration/src/samples/java/com/azure/data/appconfiguration/CheckConfigurationSettingsForChanges.java
new file mode 100644
index 000000000000..d9ba0133f4cc
--- /dev/null
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/samples/java/com/azure/data/appconfiguration/CheckConfigurationSettingsForChanges.java
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.data.appconfiguration;
+
+import com.azure.core.http.HttpHeaderName;
+import com.azure.core.http.MatchConditions;
+import com.azure.core.http.rest.PagedIterable;
+import com.azure.core.http.rest.PagedResponse;
+import com.azure.core.util.Configuration;
+import com.azure.data.appconfiguration.models.ConfigurationSetting;
+import com.azure.data.appconfiguration.models.SettingSelector;
+import com.azure.identity.DefaultAzureCredentialBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sample demonstrates how to use HEAD requests to efficiently check for configuration changes.
+ * This is useful for polling scenarios where you want to minimize bandwidth usage.
+ */
+public class CheckConfigurationSettingsForChanges {
+ /**
+ * Runs the sample algorithm demonstrating HEAD-based change detection.
+ *
+ * @param args Unused. Arguments to the program.
+ */
+ public static void main(String[] args) {
+ // The endpoint can be obtained by going to your App Configuration instance in the Azure portal
+ // and navigating to "Overview" page. Looking for the "Endpoint" keyword.
+ String endpoint = Configuration.getGlobalConfiguration().get("AZ_CONFIG_ENDPOINT");
+
+ // Instantiate a client that will be used to call the service.
+ final ConfigurationClient client = new ConfigurationClientBuilder()
+ .credential(new DefaultAzureCredentialBuilder().build())
+ .endpoint(endpoint)
+ .buildClient();
+
+ // Create test settings
+ final String key = "hello";
+ final String value = "world";
+ ConfigurationSetting setting = client.setConfigurationSetting(key, null, value);
+ System.out.printf("[SetConfigurationSetting] Key: %s, Value: %s%n", setting.getKey(), setting.getValue());
+
+ SettingSelector selector = new SettingSelector().setKeyFilter(key);
+
+ // Perform an initial HEAD request to capture page ETags
+ List cachedPageETags = new ArrayList<>();
+ PagedIterable headResult = client.checkConfigurationSettings(selector);
+ for (PagedResponse page : headResult.iterableByPage()) {
+ String pageETag = page.getHeaders().getValue(HttpHeaderName.ETAG);
+ cachedPageETags.add(pageETag);
+ System.out.printf("[CheckConfigurationSettings] Captured page ETag: %s%n", pageETag);
+ }
+
+ // Check for changes using cached ETags with If-None-Match
+ // If no changes occurred, the service returns 304 Not Modified
+ List matchConditions = new ArrayList<>();
+ for (String cachedETag : cachedPageETags) {
+ matchConditions.add(new MatchConditions().setIfNoneMatch(cachedETag));
+ }
+
+ SettingSelector conditionalSelector = new SettingSelector()
+ .setKeyFilter(key)
+ .setMatchConditions(matchConditions);
+
+ PagedIterable conditionalResult = client.checkConfigurationSettings(conditionalSelector);
+ for (PagedResponse page : conditionalResult.iterableByPage()) {
+ if (page.getStatusCode() == 304) {
+ System.out.println("[CheckConfigurationSettings] No changes detected (304 Not Modified)");
+ } else {
+ System.out.printf("[CheckConfigurationSettings] Changes detected. New ETag: %s%n",
+ page.getHeaders().getValue(HttpHeaderName.ETAG));
+ }
+ }
+
+ // Update the setting to simulate a change
+ setting = client.setConfigurationSetting(key, null, "new-value");
+ System.out.printf("[SetConfigurationSetting] Updated Key: %s, Value: %s%n", setting.getKey(), setting.getValue());
+
+ // Check again with the same cached ETags - changes should be detected
+ conditionalResult = client.checkConfigurationSettings(conditionalSelector);
+ boolean hasChanges = false;
+ for (PagedResponse page : conditionalResult.iterableByPage()) {
+ if (page.getStatusCode() == 304) {
+ System.out.println("[CheckConfigurationSettings] No changes detected (304 Not Modified)");
+ } else {
+ System.out.printf("[CheckConfigurationSettings] Changes detected. New ETag: %s%n",
+ page.getHeaders().getValue(HttpHeaderName.ETAG));
+ hasChanges = true;
+ }
+ }
+
+ // Fetch full data only if changes were detected
+ if (hasChanges) {
+ PagedIterable fullResult = client.listConfigurationSettings(selector);
+ for (ConfigurationSetting retrievedSetting : fullResult) {
+ System.out.printf("[ListConfigurationSettings] Key: %s, Value: %s%n",
+ retrievedSetting.getKey(), retrievedSetting.getValue());
+ }
+ }
+
+ // Clean up
+ setting = client.deleteConfigurationSetting(key, null);
+ System.out.printf("[DeleteConfigurationSetting] Key: %s, Value: %s%n", setting.getKey(), setting.getValue());
+ }
+}
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationAsyncClientTest.java b/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationAsyncClientTest.java
index f17eb6cf2cf3..d3b577359336 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationAsyncClientTest.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationAsyncClientTest.java
@@ -47,6 +47,7 @@
import static com.azure.data.appconfiguration.implementation.Utility.getTagsFilterInString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -949,6 +950,75 @@ public void listConfigurationSettingsAcceptDateTime() {
.verifyComplete();
}
+ /**
+ * Verifies that we can check configuration settings using HEAD requests and get page-level ETags.
+ */
+ @Test
+ public void checkConfigurationSettings() {
+ String key = getKey();
+ String key2 = getKey();
+ listWithMultipleKeysRunner(key, key2, (setting, setting2) -> {
+ StepVerifier.create(client.addConfigurationSettingWithResponse(setting))
+ .assertNext(response -> assertConfigurationEquals(setting, response))
+ .verifyComplete();
+
+ StepVerifier.create(client.addConfigurationSettingWithResponse(setting2))
+ .assertNext(response -> assertConfigurationEquals(setting2, response))
+ .verifyComplete();
+
+ SettingSelector selector = new SettingSelector().setKeyFilter(key + "," + key2);
+
+ // HEAD requests return empty items but pages should have ETags
+ StepVerifier.create(client.checkConfigurationSettings(selector).byPage()).assertNext(page -> {
+ assertNotNull(page.getHeaders());
+ // Items should be empty since HEAD returns no body
+ assertTrue(page.getElements() == null || page.getElements().isEmpty());
+ // Page should have an ETag header
+ assertNotNull(page.getHeaders().getValue(HttpHeaderName.ETAG));
+ }).verifyComplete();
+
+ return client.listConfigurationSettings(selector).collectList().block();
+ });
+ }
+
+ /**
+ * Verifies that we can check configuration settings using HEAD requests and detect changes
+ * via ETag comparison.
+ */
+ @Test
+ public void checkConfigurationSettingsETagChanged() {
+ String key = getKey();
+ ConfigurationSetting setting = new ConfigurationSetting().setKey(key).setValue("myValue");
+ StepVerifier.create(client.setConfigurationSetting(setting))
+ .assertNext(response -> assertConfigurationEquals(setting, response))
+ .verifyComplete();
+
+ SettingSelector selector = new SettingSelector().setKeyFilter(key);
+
+ // First HEAD request to get initial ETag
+ String initialETag = client.checkConfigurationSettings(selector)
+ .byPage()
+ .blockFirst()
+ .getHeaders()
+ .getValue(HttpHeaderName.ETAG);
+ assertNotNull(initialETag);
+
+ // Modify the setting
+ ConfigurationSetting updatedSetting = new ConfigurationSetting().setKey(key).setValue("updatedValue");
+ StepVerifier.create(client.setConfigurationSetting(updatedSetting))
+ .assertNext(response -> assertConfigurationEquals(updatedSetting, response))
+ .verifyComplete();
+
+ // Second HEAD request should give a different ETag
+ String updatedETag = client.checkConfigurationSettings(selector)
+ .byPage()
+ .blockFirst()
+ .getHeaders()
+ .getValue(HttpHeaderName.ETAG);
+ assertNotNull(updatedETag);
+ assertNotEquals(initialETag, updatedETag);
+ }
+
/**
* Verifies that we can get all of the revisions for this ConfigurationSetting. Then verifies that we can select
* specific fields.
diff --git a/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationClientTest.java b/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationClientTest.java
index f274947927ff..9f667525ad43 100644
--- a/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationClientTest.java
+++ b/sdk/appconfiguration/azure-data-appconfiguration/src/test/java/com/azure/data/appconfiguration/ConfigurationClientTest.java
@@ -9,6 +9,7 @@
import com.azure.core.http.MatchConditions;
import com.azure.core.http.policy.AddHeadersFromContextPolicy;
import com.azure.core.http.rest.PagedIterable;
+import com.azure.core.http.rest.PagedResponse;
import com.azure.core.http.rest.Response;
import com.azure.core.test.http.AssertingHttpClientBuilder;
import com.azure.core.test.models.CustomMatcher;
@@ -46,6 +47,7 @@
import static com.azure.data.appconfiguration.implementation.Utility.getTagsFilterInString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -756,6 +758,66 @@ public void listConfigurationSettingsAcceptDateTime() {
(client.listConfigurationSettings(options).stream().collect(Collectors.toList())).get(0));
}
+ /**
+ * Verifies that we can check configuration settings using HEAD requests and get page-level ETags.
+ */
+ @Test
+ public void checkConfigurationSettings() {
+ String key = getKey();
+ String key2 = getKey();
+ listWithMultipleKeysRunner(key, key2, (setting, setting2) -> {
+ assertConfigurationEquals(setting,
+ client.addConfigurationSettingWithResponse(setting, Context.NONE).getValue());
+ assertConfigurationEquals(setting2,
+ client.addConfigurationSettingWithResponse(setting2, Context.NONE).getValue());
+
+ SettingSelector selector = new SettingSelector().setKeyFilter(key + "," + key2);
+ PagedIterable result = client.checkConfigurationSettings(selector);
+
+ // HEAD requests return empty items but pages should have ETags
+ for (PagedResponse page : result.iterableByPage()) {
+ assertNotNull(page.getHeaders());
+ // Items should be empty since HEAD returns no body
+ assertTrue(page.getElements() == null || page.getElements().isEmpty());
+ // Page should have an ETag header
+ assertNotNull(page.getHeaders().getValue(HttpHeaderName.ETAG));
+ }
+
+ return client.listConfigurationSettings(selector);
+ });
+ }
+
+ /**
+ * Verifies that we can check configuration settings using HEAD requests and detect changes
+ * via ETag comparison.
+ */
+ @Test
+ public void checkConfigurationSettingsETagChanged() {
+ String key = getKey();
+ ConfigurationSetting setting = new ConfigurationSetting().setKey(key).setValue("myValue");
+ client.setConfigurationSetting(setting);
+
+ SettingSelector selector = new SettingSelector().setKeyFilter(key);
+
+ // First HEAD request to get initial ETag
+ String initialETag = null;
+ for (PagedResponse page : client.checkConfigurationSettings(selector).iterableByPage()) {
+ initialETag = page.getHeaders().getValue(HttpHeaderName.ETAG);
+ }
+ assertNotNull(initialETag);
+
+ // Modify the setting
+ client.setConfigurationSetting(new ConfigurationSetting().setKey(key).setValue("updatedValue"));
+
+ // Second HEAD request should give a different ETag
+ String updatedETag = null;
+ for (PagedResponse page : client.checkConfigurationSettings(selector).iterableByPage()) {
+ updatedETag = page.getHeaders().getValue(HttpHeaderName.ETAG);
+ }
+ assertNotNull(updatedETag);
+ assertNotEquals(initialETag, updatedETag);
+ }
+
/**
* Verifies that we can get all of the revisions for this ConfigurationSetting. Then verifies that we can select
* specific fields.