Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sdk/servicebus/azure-messaging-servicebus/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Features Added

- Added `listSessions()` and `listSessions(OffsetDateTime sessionStateUpdatedAfter)` to `ServiceBusSessionReceiverAsyncClient` (returning `PagedFlux<String>`) and `ServiceBusSessionReceiverClient` (returning `PagedIterable<String>`). The no-arg overload returns sessions with active messages; the `sessionStateUpdatedAfter` overload returns sessions whose session state was updated after the given timestamp. Implements the `com.microsoft:get-message-sessions` AMQP management operation. ([#48956](https://github.com/Azure/azure-sdk-for-java/pull/48956))
- 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

Expand All @@ -23,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -42,6 +47,6 @@ public String getVersion() {
* @return the latest {@link ServiceBusServiceVersion}.
*/
public static ServiceBusServiceVersion getLatest() {
return V2021_05;
return V2024_05;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ public final class TopicDescription implements XmlSerializable<TopicDescription>
@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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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 -&gt; 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -1129,6 +1135,13 @@ private ServiceBusAdministrationAsyncClient createClient(HttpClient httpClient)

static void configure(ServiceBusAdministrationClientBuilder builder, HttpClient httpClient,
InterceptorManager interceptorManager, AtomicReference<TokenCredential> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.http.rest.Response;
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;
import com.azure.messaging.servicebus.administration.models.CreateQueueOptions;
import com.azure.messaging.servicebus.administration.models.CreateRuleOptions;
import com.azure.messaging.servicebus.administration.models.CreateSubscriptionOptions;
Expand Down Expand Up @@ -541,6 +544,38 @@ void getTopicRuntimeProperties() {
assertEquals(0, runtimeProperties.getScheduledMessageCount());
}

@Test
@LiveOnly
void getTopicFilterCounts() {
// The topic-level SqlFilterCount / CorrelationFilterCount runtime properties are served by
// 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);

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();
Expand Down Expand Up @@ -630,6 +665,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()) {
Expand Down Expand Up @@ -842,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();
}
}
Loading
Loading