From 5e21cd9b7a5c5e56d874ef6840f245f99e0d06ca Mon Sep 17 00:00:00 2001 From: Eldert Grootenboer Date: Mon, 3 Aug 2026 14:06:31 -0700 Subject: [PATCH 1/4] feat: Add topic SqlFilterCount and CorrelationFilterCount runtime properties - Add getSqlFilterCount() and getCorrelationFilterCount() to TopicRuntimeProperties, exposing the total number of SQL and correlation filters across all of a topic's subscriptions - Add ServiceBusServiceVersion.V2024_05 and make it the latest; the administration client now sends api-version=2024-05 by default, which the topic filter counts require - Parse SqlFilterCount/CorrelationFilterCount ATOM elements onto TopicDescription and default to 0 when absent (older region or api-version) - Add a swagger codegen directive so a regeneration reproduces the two fields instead of dropping them - Add unit tests (service-version default and mapping, propagate and default-to-zero, XML round-trip) and a live integration test asserting the counts --- .../azure-messaging-servicebus/CHANGELOG.md | 3 + .../servicebus/ServiceBusServiceVersion.java | 9 ++- .../models/TopicDescription.java | 67 +++++++++++++++++++ .../models/TopicProperties.java | 23 +++++++ .../models/TopicRuntimeProperties.java | 22 ++++++ .../ServiceBusServiceVersionTest.java | 33 +++++++++ ...usAdministrationClientIntegrationTest.java | 35 ++++++++++ .../TopicRuntimePropertiesTest.java | 50 ++++++++++++++ .../ServiceBusManagementSerializerTest.java | 26 +++++++ .../swagger/README.md | 46 +++++++++++++ 10 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/ServiceBusServiceVersionTest.java create mode 100644 sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/TopicRuntimePropertiesTest.java diff --git a/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md b/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md index ec97b08279fc..c522e0a67596 100644 --- a/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md @@ -4,6 +4,9 @@ ### Features Added +- Added `getSqlFilterCount()` and `getCorrelationFilterCount()` to `TopicRuntimeProperties`, exposing the total number of SQL filters and correlation filters across all of a topic's subscriptions. +- Added `ServiceBusServiceVersion.V2024_05` and made it the latest version. The administration client now uses `api-version=2024-05` by default, which is required for the topic filter counts above. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/ServiceBusServiceVersion.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/ServiceBusServiceVersion.java index cfd6c3b89d63..f9b482b8ce85 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/ServiceBusServiceVersion.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/ServiceBusServiceVersion.java @@ -16,7 +16,12 @@ public enum ServiceBusServiceVersion implements ServiceVersion { /** * Service version {@code 2021-05}. */ - V2021_05("2021-05"); + V2021_05("2021-05"), + + /** + * Service version {@code 2024-05}. + */ + V2024_05("2024-05"); private final String version; @@ -42,6 +47,6 @@ public String getVersion() { * @return the latest {@link ServiceBusServiceVersion}. */ public static ServiceBusServiceVersion getLatest() { - return V2021_05; + return V2024_05; } } diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/implementation/models/TopicDescription.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/implementation/models/TopicDescription.java index 3488c90ec496..ed828e542a0a 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/implementation/models/TopicDescription.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/implementation/models/TopicDescription.java @@ -129,6 +129,18 @@ public final class TopicDescription implements XmlSerializable @Generated private Integer subscriptionCount; + /* + * The total number of SQL filters across all subscriptions of the topic. + */ + @Generated + private Integer sqlFilterCount; + + /* + * The total number of correlation filters across all subscriptions of the topic. + */ + @Generated + private Integer correlationFilterCount; + /* * ISO 8601 timeSpan idle interval after which the topic is automatically deleted. The minimum duration is 5 * minutes. @@ -551,6 +563,52 @@ public TopicDescription setSubscriptionCount(Integer subscriptionCount) { return this; } + /** + * Get the sqlFilterCount property: The total number of SQL filters across all subscriptions of the topic. + * + * @return the sqlFilterCount value. + */ + @Generated + public Integer getSqlFilterCount() { + return this.sqlFilterCount; + } + + /** + * Set the sqlFilterCount property: The total number of SQL filters across all subscriptions of the topic. + * + * @param sqlFilterCount the sqlFilterCount value to set. + * @return the TopicDescription object itself. + */ + @Generated + public TopicDescription setSqlFilterCount(Integer sqlFilterCount) { + this.sqlFilterCount = sqlFilterCount; + return this; + } + + /** + * Get the correlationFilterCount property: The total number of correlation filters across all subscriptions of the + * topic. + * + * @return the correlationFilterCount value. + */ + @Generated + public Integer getCorrelationFilterCount() { + return this.correlationFilterCount; + } + + /** + * Set the correlationFilterCount property: The total number of correlation filters across all subscriptions of the + * topic. + * + * @param correlationFilterCount the correlationFilterCount value to set. + * @return the TopicDescription object itself. + */ + @Generated + public TopicDescription setCorrelationFilterCount(Integer correlationFilterCount) { + this.correlationFilterCount = correlationFilterCount; + return this; + } + /** * Get the autoDeleteOnIdle property: ISO 8601 timeSpan idle interval after which the topic is automatically * deleted. The minimum duration is 5 minutes. @@ -762,6 +820,9 @@ public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLSt xmlWriter.writeXml(this.messageCountDetails, "CountDetails"); xmlWriter.writeNumberElement(SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT, "SubscriptionCount", this.subscriptionCount); + xmlWriter.writeNumberElement(SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT, "SqlFilterCount", this.sqlFilterCount); + xmlWriter.writeNumberElement(SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT, "CorrelationFilterCount", + this.correlationFilterCount); xmlWriter.writeStringElement(SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT, "AutoDeleteOnIdle", CoreUtils.durationToStringWithDays(this.autoDeleteOnIdle)); xmlWriter.writeBooleanElement(SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT, "EnablePartitioning", @@ -878,6 +939,12 @@ public static TopicDescription fromXml(XmlReader xmlReader, String rootElementNa } else if ("SubscriptionCount".equals(elementName.getLocalPart()) && SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT.equals(elementName.getNamespaceURI())) { deserializedTopicDescription.subscriptionCount = reader.getNullableElement(Integer::parseInt); + } else if ("SqlFilterCount".equals(elementName.getLocalPart()) + && SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT.equals(elementName.getNamespaceURI())) { + deserializedTopicDescription.sqlFilterCount = reader.getNullableElement(Integer::parseInt); + } else if ("CorrelationFilterCount".equals(elementName.getLocalPart()) + && SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT.equals(elementName.getNamespaceURI())) { + deserializedTopicDescription.correlationFilterCount = reader.getNullableElement(Integer::parseInt); } else if ("AutoDeleteOnIdle".equals(elementName.getLocalPart()) && SCHEMAS_MICROSOFT_COM_SERVICEBUS_CONNECT.equals(elementName.getNamespaceURI())) { deserializedTopicDescription.autoDeleteOnIdle = reader.getNullableElement(Duration::parse); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicProperties.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicProperties.java index 4664999334e9..f70c53e7f10b 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicProperties.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicProperties.java @@ -48,6 +48,8 @@ public final class TopicProperties { private boolean requiresDuplicateDetection; private final long sizeInBytes; private final int subscriptionCount; + private final int sqlFilterCount; + private final int correlationFilterCount; private boolean supportOrdering; private final boolean enablePartitioning; private String userMetadata; @@ -135,6 +137,8 @@ public void setName(TopicProperties topicProperties, String name) { this.sizeInBytes = toPrimitive(topic.getSizeInBytes()); this.status = topic.getStatus(); this.subscriptionCount = toPrimitive(topic.getSubscriptionCount()); + this.sqlFilterCount = toPrimitive(topic.getSqlFilterCount()); + this.correlationFilterCount = toPrimitive(topic.getCorrelationFilterCount()); this.supportOrdering = toPrimitive(topic.isSupportOrdering()); this.updatedAt = topic.getUpdatedAt(); this.userMetadata = topic.getUserMetadata(); @@ -454,6 +458,25 @@ int getSubscriptionCount() { return this.subscriptionCount; } + /** + * Get the sqlFilterCount property: The total number of SQL filters across all subscriptions of the topic. + * + * @return the sqlFilterCount value. + */ + int getSqlFilterCount() { + return this.sqlFilterCount; + } + + /** + * Get the correlationFilterCount property: The total number of correlation filters across all subscriptions of the + * topic. + * + * @return the correlationFilterCount value. + */ + int getCorrelationFilterCount() { + return this.correlationFilterCount; + } + /** * Get the entityAvailabilityStatus property: Availability status of the entity. * diff --git a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicRuntimeProperties.java b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicRuntimeProperties.java index a414c0a686fc..d9d95bbd870a 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicRuntimeProperties.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/main/java/com/azure/messaging/servicebus/administration/models/TopicRuntimeProperties.java @@ -21,6 +21,8 @@ public final class TopicRuntimeProperties { private final String name; private final int subscriptionCount; + private final int sqlFilterCount; + private final int correlationFilterCount; private final long sizeInBytes; private final OffsetDateTime accessedAt; private final OffsetDateTime createdAt; @@ -39,6 +41,8 @@ public TopicRuntimeProperties(TopicProperties topicProperties) { this.name = topicProperties.getName(); this.subscriptionCount = topicProperties.getSubscriptionCount(); + this.sqlFilterCount = topicProperties.getSqlFilterCount(); + this.correlationFilterCount = topicProperties.getCorrelationFilterCount(); this.sizeInBytes = topicProperties.getSizeInBytes(); this.accessedAt = topicProperties.getAccessedAt(); this.createdAt = topicProperties.getCreatedAt(); @@ -101,6 +105,24 @@ public int getSubscriptionCount() { return subscriptionCount; } + /** + * Gets the total number of SQL filters across all subscriptions of the topic. + * + * @return The total number of SQL filters across all subscriptions of the topic. + */ + public int getSqlFilterCount() { + return sqlFilterCount; + } + + /** + * Gets the total number of correlation filters across all subscriptions of the topic. + * + * @return The total number of correlation filters across all subscriptions of the topic. + */ + public int getCorrelationFilterCount() { + return correlationFilterCount; + } + /** * Gets the exact time the topic description was updated. * diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/ServiceBusServiceVersionTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/ServiceBusServiceVersionTest.java new file mode 100644 index 000000000000..c8419e900c07 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/ServiceBusServiceVersionTest.java @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit tests for {@link ServiceBusServiceVersion}. + */ +public class ServiceBusServiceVersionTest { + /** + * The default admin api-version must be the newest (2024-05); a regression to an older default + * silently stops the topic filter counts from being served. + */ + @Test + void getLatestReturnsNewestVersion() { + assertEquals(ServiceBusServiceVersion.V2024_05, ServiceBusServiceVersion.getLatest()); + } + + /** + * The enum -> api-version string is what the client sends on every request; a wrong value + * silently targets the wrong service API (2024-05 is what serves the topic filter counts). + */ + @Test + void versionMapsToApiVersionString() { + assertEquals("2017-04", ServiceBusServiceVersion.V2017_04.getVersion()); + assertEquals("2021-05", ServiceBusServiceVersion.V2021_05.getVersion()); + assertEquals("2024-05", ServiceBusServiceVersion.V2024_05.getVersion()); + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java index b7c3de074577..9acd45828166 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java @@ -11,10 +11,12 @@ import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.Response; +import com.azure.core.test.TestMode; import com.azure.core.test.TestProxyTestBase; import com.azure.core.util.Context; import com.azure.messaging.servicebus.TestUtils; import com.azure.messaging.servicebus.administration.models.AccessRights; +import com.azure.messaging.servicebus.administration.models.CorrelationRuleFilter; import com.azure.messaging.servicebus.administration.models.CreateQueueOptions; import com.azure.messaging.servicebus.administration.models.CreateRuleOptions; import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions; @@ -62,6 +64,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Tests {@link ServiceBusAdministrationClient}. @@ -541,6 +544,38 @@ void getTopicRuntimeProperties() { assertEquals(0, runtimeProperties.getScheduledMessageCount()); } + @Test + void getTopicFilterCounts() { + // The topic-level SqlFilterCount / CorrelationFilterCount runtime properties are + // served by the 2024-05 service API version (the builder's getLatest() default) and + // are populated only on a service build carrying the feature, so run live. + assumeTrue(super.getTestMode() == TestMode.LIVE, "Filter counts require a live feature-enabled namespace."); + + final ServiceBusAdministrationClient client = getClient(); + final String topicName = testResourceNamer.randomName("topicfc", 10); + final String subscriptionName = testResourceNamer.randomName("sub", 10); + + client.createTopic(topicName); + try { + client.createSubscription(topicName, subscriptionName); + + // A new subscription carries a default $Default rule (a SQL TrueFilter). Add an + // explicit SQL rule and a correlation rule so the topic-level counts are non-zero. + client.createRule(topicName, "sqlRule", subscriptionName, + new CreateRuleOptions().setFilter(new SqlRuleFilter("1=1"))); + client.createRule(topicName, "correlationRule", subscriptionName, + new CreateRuleOptions().setFilter(new CorrelationRuleFilter().setCorrelationId("abc"))); + + final TopicRuntimeProperties runtimeProperties = client.getTopicRuntimeProperties(topicName); + + // $Default (TrueFilter) + sqlRule = 2 SQL filters; correlationRule = 1 correlation filter. + assertEquals(2, runtimeProperties.getSqlFilterCount()); + assertEquals(1, runtimeProperties.getCorrelationFilterCount()); + } finally { + client.deleteTopic(topicName); + } + } + @Test void getSubscription() { final ServiceBusAdministrationClient client = getClient(); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/TopicRuntimePropertiesTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/TopicRuntimePropertiesTest.java new file mode 100644 index 000000000000..f51baa4a16e4 --- /dev/null +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/TopicRuntimePropertiesTest.java @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.messaging.servicebus.administration; + +import com.azure.messaging.servicebus.administration.implementation.EntityHelper; +import com.azure.messaging.servicebus.administration.implementation.models.TopicDescription; +import com.azure.messaging.servicebus.administration.models.TopicProperties; +import com.azure.messaging.servicebus.administration.models.TopicRuntimeProperties; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit tests for the topic filter-count runtime properties on {@link TopicRuntimeProperties}. + */ +public class TopicRuntimePropertiesTest { + /** + * The SqlFilterCount / CorrelationFilterCount parsed onto the description flow through to the + * public {@link TopicRuntimeProperties}. + */ + @Test + void filterCountsPropagateFromDescription() { + final TopicDescription description + = new TopicDescription().setSubscriptionCount(2).setSqlFilterCount(7).setCorrelationFilterCount(9); + + final TopicProperties properties = EntityHelper.toModel(description); + final TopicRuntimeProperties runtimeProperties = new TopicRuntimeProperties(properties); + + assertEquals(2, runtimeProperties.getSubscriptionCount()); + assertEquals(7, runtimeProperties.getSqlFilterCount()); + assertEquals(9, runtimeProperties.getCorrelationFilterCount()); + } + + /** + * A service region that has not yet deployed the topic filter-count feature omits the elements + * entirely; the counts must gracefully default to zero rather than throw. + */ + @Test + void filterCountsDefaultToZeroWhenAbsent() { + final TopicDescription description = new TopicDescription().setSubscriptionCount(1); + + final TopicProperties properties = EntityHelper.toModel(description); + final TopicRuntimeProperties runtimeProperties = new TopicRuntimeProperties(properties); + + assertEquals(1, runtimeProperties.getSubscriptionCount()); + assertEquals(0, runtimeProperties.getSqlFilterCount()); + assertEquals(0, runtimeProperties.getCorrelationFilterCount()); + } +} diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java index 2791ae8ce7f7..b0eede4c72cd 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusManagementSerializerTest.java @@ -752,6 +752,32 @@ void serializeTopicDescriptionXML() throws IOException { "Serialized payload does not contain AuthorizationRule in default namespace"); } + /** + * Verify the topic-level SqlFilterCount and CorrelationFilterCount survive an XML serialize/deserialize round-trip. + */ + @Test + void topicFilterCountsRoundTrip() throws IOException { + // Arrange + final TopicDescription topicDescription + = new TopicDescription().setSubscriptionCount(2).setSqlFilterCount(7).setCorrelationFilterCount(9); + final CreateTopicBody body + = new CreateTopicBody().setContent(new CreateTopicBodyContent().setTopicDescription(topicDescription)); + + // Act + final String serialized = SERIALIZER.serialize(body, SerializerEncoding.XML); + final CreateTopicBody roundTripped = SERIALIZER.deserialize(serialized, CreateTopicBody.class); + final TopicDescription actual = roundTripped.getContent().getTopicDescription(); + + // Assert - the elements are emitted (toXml) and read back (fromXml) + assertTrue(serialized.contains("7"), + "Serialized payload does not contain SqlFilterCount"); + assertTrue(serialized.contains("9"), + "Serialized payload does not contain CorrelationFilterCount"); + assertEquals(2, actual.getSubscriptionCount()); + assertEquals(7, actual.getSqlFilterCount()); + assertEquals(9, actual.getCorrelationFilterCount()); + } + /** * Given a file name, gets the corresponding resource and its contents as a string. * diff --git a/sdk/servicebus/azure-messaging-servicebus/swagger/README.md b/sdk/servicebus/azure-messaging-servicebus/swagger/README.md index 78ffeafa4bd3..0d7faf86d29c 100644 --- a/sdk/servicebus/azure-messaging-servicebus/swagger/README.md +++ b/sdk/servicebus/azure-messaging-servicebus/swagger/README.md @@ -222,3 +222,49 @@ directive: }); $.SubscriptionDescription.properties = newSubscriptionProperties; ``` + +### Add Topic Filter Count Runtime Properties + +Adds the read-only `SqlFilterCount` and `CorrelationFilterCount` runtime properties to `TopicDescription`. They report +the total number of SQL / correlation filters across all of a topic's subscriptions and are served by the `2024-05` +service API version. They are not present in the pinned `2021-05` input swagger, so they are injected here (mirroring the +sibling `subscriptionCount`) until the `input-file` is bumped to a spec revision that defines them. The two properties +are inserted immediately after `subscriptionCount` so the generated property and XML element order is retained. + +```yaml +directive: + - from: swagger-document + where: $.definitions + transform: > + const filterCountProperties = { + "sqlFilterCount": { + "description": "The total number of SQL filters across all subscriptions of the topic.", + "type": "integer", + "format": "int32", + "xml": { + "name": "SqlFilterCount", + "namespace": "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" + } + }, + "correlationFilterCount": { + "description": "The total number of correlation filters across all subscriptions of the topic.", + "type": "integer", + "format": "int32", + "xml": { + "name": "CorrelationFilterCount", + "namespace": "http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" + } + } + }; + + const topicProperties = $.TopicDescription.properties; + const newTopicProperties = {}; + Object.keys(topicProperties).forEach(key => { + newTopicProperties[key] = topicProperties[key]; + if (key === "subscriptionCount") { + newTopicProperties["sqlFilterCount"] = filterCountProperties["sqlFilterCount"]; + newTopicProperties["correlationFilterCount"] = filterCountProperties["correlationFilterCount"]; + } + }); + $.TopicDescription.properties = newTopicProperties; +``` From 050371a034701445e6849f9318dfcea555f71f34 Mon Sep 17 00:00:00 2001 From: Eldert Grootenboer Date: Mon, 3 Aug 2026 15:05:55 -0700 Subject: [PATCH 2/4] test: Pin recorded admin tests to api-version 2021-05 after default bump The default administration api-version is now 2024-05 (getLatest()), which the recorded playback tests would send, but their session recordings were captured at 2021-05, so the test proxy could not match the requests. - Pin the recorded (playback/record) modes to ServiceBusServiceVersion.V2021_05 in the shared configure() and the impl-client and unauthorized-client builders; live mode keeps the latest default. - Mark getTopicFilterCounts with @LiveOnly so it skips cleanly in playback (it needs the 2024-05 default and a feature-enabled live namespace, and has no recording), instead of failing setup on a missing recording. --- ...ministrationAsyncClientIntegrationTest.java | 13 +++++++++++++ ...BusAdministrationClientIntegrationTest.java | 18 +++++++++++------- ...ministrationClientImplIntegrationTests.java | 7 ++++++- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java index 7abf051f57ce..12541a9cc922 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationAsyncClientIntegrationTest.java @@ -18,6 +18,7 @@ import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.CoreUtils; import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.messaging.servicebus.ServiceBusServiceVersion; import com.azure.messaging.servicebus.TestUtils; import com.azure.messaging.servicebus.administration.models.AccessRights; import com.azure.messaging.servicebus.administration.models.CreateQueueOptions; @@ -934,6 +935,11 @@ void getSubscriptionRuntimePropertiesUnauthorizedClient(HttpClient httpClient) { .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) .connectionString(connectionStringUpdated); + // Recorded at api-version 2021-05; pin the recorded modes so requests match the recordings. + if (!interceptorManager.isLiveMode()) { + builder.serviceVersion(ServiceBusServiceVersion.V2021_05); + } + if (interceptorManager.isPlaybackMode()) { builder.httpClient(interceptorManager.getPlaybackClient()); } else if (interceptorManager.isLiveMode()) { @@ -1129,6 +1135,13 @@ private ServiceBusAdministrationAsyncClient createClient(HttpClient httpClient) static void configure(ServiceBusAdministrationClientBuilder builder, HttpClient httpClient, InterceptorManager interceptorManager, AtomicReference credentialCached) { + // The recorded sessions were captured at api-version 2021-05, but the default admin + // api-version is now 2024-05 (getLatest()). Pin the recorded (playback/record) modes to + // 2021-05 so requests match the recordings; live mode keeps the latest default. + if (!interceptorManager.isLiveMode()) { + builder.serviceVersion(ServiceBusServiceVersion.V2021_05); + } + if (interceptorManager.isPlaybackMode()) { builder.credential(TestUtils.getFullyQualifiedDomainName(true), new MockTokenCredential()); builder.httpClient(interceptorManager.getPlaybackClient()); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java index 9acd45828166..fda55a5d4701 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java @@ -11,9 +11,10 @@ import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.Response; -import com.azure.core.test.TestMode; import com.azure.core.test.TestProxyTestBase; +import com.azure.core.test.annotation.LiveOnly; import com.azure.core.util.Context; +import com.azure.messaging.servicebus.ServiceBusServiceVersion; import com.azure.messaging.servicebus.TestUtils; import com.azure.messaging.servicebus.administration.models.AccessRights; import com.azure.messaging.servicebus.administration.models.CorrelationRuleFilter; @@ -64,7 +65,6 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Tests {@link ServiceBusAdministrationClient}. @@ -545,12 +545,11 @@ void getTopicRuntimeProperties() { } @Test + @LiveOnly void getTopicFilterCounts() { - // The topic-level SqlFilterCount / CorrelationFilterCount runtime properties are - // served by the 2024-05 service API version (the builder's getLatest() default) and - // are populated only on a service build carrying the feature, so run live. - assumeTrue(super.getTestMode() == TestMode.LIVE, "Filter counts require a live feature-enabled namespace."); - + // The topic-level SqlFilterCount / CorrelationFilterCount runtime properties are served by + // the 2024-05 service API version (the builder's getLatest() default) and are populated only + // on a service build carrying the feature, so this test runs live only (no recording). final ServiceBusAdministrationClient client = getClient(); final String topicName = testResourceNamer.randomName("topicfc", 10); final String subscriptionName = testResourceNamer.randomName("sub", 10); @@ -665,6 +664,11 @@ void getSubscriptionRuntimePropertiesUnauthorizedClient() { .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)) .connectionString(connectionStringUpdated); + // Recorded at api-version 2021-05; pin the recorded modes so requests match the recordings. + if (!interceptorManager.isLiveMode()) { + builder.serviceVersion(ServiceBusServiceVersion.V2021_05); + } + if (interceptorManager.isPlaybackMode()) { builder.httpClient(interceptorManager.getPlaybackClient()); } else if (!interceptorManager.isLiveMode()) { diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusAdministrationClientImplIntegrationTests.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusAdministrationClientImplIntegrationTests.java index 30f388e1bb4b..5b07d37ac731 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusAdministrationClientImplIntegrationTests.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusAdministrationClientImplIntegrationTests.java @@ -266,8 +266,13 @@ private ServiceBusManagementClientImpl createClient(HttpClient httpClient) { .policies(policies.toArray(new HttpPipelinePolicy[0])) .build(); + // The recorded sessions were captured at api-version 2021-05; pin the recorded + // (playback/record) modes to it so requests match, while live keeps the latest default. + final ServiceBusServiceVersion serviceVersion = interceptorManager.isLiveMode() + ? ServiceBusServiceVersion.getLatest() + : ServiceBusServiceVersion.V2021_05; return new ServiceBusManagementClientImpl(pipeline, SERIALIZER, fullyQualifiedNamespace, - ServiceBusServiceVersion.getLatest().getVersion()); + serviceVersion.getVersion()); } private T deserialize(Response response, Class clazz) { From 60140af0d15e55397c135adeb460ea443d8fd62e Mon Sep 17 00:00:00 2001 From: Eldert Grootenboer Date: Wed, 5 Aug 2026 17:00:26 -0700 Subject: [PATCH 3/4] test: Pin the topic filter-count live test to api-version 2024-05 - Build the client for getTopicFilterCounts with an explicit ServiceBusServiceVersion.V2024_05 instead of relying on the builder default, matching the merged .NET counterpart. - Add a getClient(ServiceBusServiceVersion) overload that applies the version after configure(), and have the no-arg getClient() delegate to it so the setup is not duplicated. - Correct the comment on why the test is live only: the recorded modes are pinned to 2021-05 to match the existing cassettes, and no cassette covers this path yet. --- ...BusAdministrationClientIntegrationTest.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java index fda55a5d4701..b84e32fc8ab3 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/administration/ServiceBusAdministrationClientIntegrationTest.java @@ -548,9 +548,10 @@ void getTopicRuntimeProperties() { @LiveOnly void getTopicFilterCounts() { // The topic-level SqlFilterCount / CorrelationFilterCount runtime properties are served by - // the 2024-05 service API version (the builder's getLatest() default) and are populated only - // on a service build carrying the feature, so this test runs live only (no recording). - final ServiceBusAdministrationClient client = getClient(); + // the 2024-05 service API version, so use an explicit V2024_05 client rather than relying on + // the builder default. The test is live only because the recorded modes are pinned to + // 2021-05 to match the existing cassettes, and no cassette covers this path yet. + final ServiceBusAdministrationClient client = getClient(ServiceBusServiceVersion.V2024_05); final String topicName = testResourceNamer.randomName("topicfc", 10); final String subscriptionName = testResourceNamer.randomName("sub", 10); @@ -881,9 +882,20 @@ void listRules() { //endregion private ServiceBusAdministrationClient getClient() { + return getClient(null); + } + + // Builds a client, optionally overriding the api-version that configure() selects. Pass a + // version only from a @LiveOnly test: configure() pins the recorded modes to 2021-05 to match + // the existing cassettes, and the api-version participates in playback request matching, so + // overriding it in a recorded mode breaks playback. + private ServiceBusAdministrationClient getClient(ServiceBusServiceVersion serviceVersion) { final ServiceBusAdministrationClientBuilder builder = new ServiceBusAdministrationClientBuilder() .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)); configure(builder, null, interceptorManager, credentialCached); + if (serviceVersion != null) { + builder.serviceVersion(serviceVersion); + } return builder.buildClient(); } } From 60007e5ef54cc3d5964c346c0e870f2983eebfd0 Mon Sep 17 00:00:00 2001 From: Eldert Grootenboer Date: Wed, 5 Aug 2026 17:00:29 -0700 Subject: [PATCH 4/4] docs: Call out the administration service-version default bump - Add an Other Changes entry recording that the default service version used by ServiceBusAdministrationClientBuilder moved from 2021-05 to 2024-05, and how callers pin the previous behavior. - Mirrors the merged .NET counterpart, which records the same bump separately from the Features Added bullet. --- sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md b/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md index b6d2b9d0317c..2272155fb763 100644 --- a/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md @@ -25,6 +25,8 @@ ### Other Changes +- The default service version used by `ServiceBusAdministrationClientBuilder` is now `2024-05`, previously `2021-05`. Existing operations behave the same; the newer version is required to surface the new topic filter count properties. Callers that need the previous wire behavior can pin it with `serviceVersion(ServiceBusServiceVersion.V2021_05)`. + ## 7.17.19 (2026-07-01) ### Other Changes