diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Client/ServiceBusClient.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Client/ServiceBusClient.cs index 1c51f4bbb274..c60f3bc5b34e 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Client/ServiceBusClient.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Client/ServiceBusClient.cs @@ -208,7 +208,9 @@ public ServiceBusSender CreateSender(string queueOrTopicName, string viaQueueOrT /// /// Creates a instance that can be used for receiving and settling messages - /// from a specific queue. + /// from a specific queue. It uses to specify how messages are received. Defaults to PeekLock mode. + /// If you want to change the , use method. + /// The is set in . /// /// /// The queue to create a for. @@ -226,7 +228,8 @@ public ServiceBusReceiver CreateReceiver(string queueName) /// /// Creates a instance that can be used for receiving and settling messages - /// from a specific queue. + /// from a specific queue. It uses to specify how messages are received. Defaults to PeekLock mode. + /// The is set in . /// /// /// The queue to create a for. @@ -249,7 +252,10 @@ public ServiceBusReceiver CreateReceiver( /// /// Creates a instance that can be used for receiving and - /// settling messages from a specific subscription. + /// settling messages from a specific subscription. It uses to specify + /// how messages are received. Defaults to PeekLock mode. If you want to change the , + /// use method. + /// The is set in . /// /// /// The topic to create a for. @@ -272,7 +278,8 @@ public ServiceBusReceiver CreateReceiver( /// /// Creates a instance that can be used for - /// receiving and settling messages from a specific subscription. + /// receiving and settling messages from a specific subscription. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in . /// /// /// The topic to create a for. @@ -298,7 +305,8 @@ public ServiceBusReceiver CreateReceiver( /// /// Creates a instance that can be used for receiving - /// and settling messages from a specific session-enabled queue. + /// and settling messages from a specific session-enabled queue. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in . /// /// /// The session-enabled queue to create a for. @@ -332,7 +340,8 @@ public virtual async Task CreateSessionReceiverAsync( /// /// Creates a instance that can be used for receiving - /// and settling messages from a specific session-enabled subscription. + /// and settling messages from a specific session-enabled subscription. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in . /// /// /// The topic to create a for. @@ -368,7 +377,8 @@ public virtual async Task CreateSessionReceiverAsync( /// /// Creates a instance that can be used for receiving from the - /// dead letter queue for the specified queue. + /// dead letter queue for the specified queue. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in . /// /// /// The queue to create a for. @@ -392,7 +402,8 @@ public ServiceBusReceiver CreateDeadLetterReceiver( /// /// Creates a instance that can be used for receiving from the - /// dead letter queue for the specified subscription. + /// dead letter queue for the specified subscription. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in . /// /// /// The topic to create a for. @@ -421,7 +432,10 @@ public ServiceBusReceiver CreateDeadLetterReceiver( /// /// Creates a instance that can be used to process messages using - /// event handlers that are set on the processor. + /// event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. If you want to change the , + /// use method. + /// The is set in type. /// /// /// The queue to create a for. @@ -440,7 +454,8 @@ public ServiceBusProcessor CreateProcessor(string queueName) /// /// Creates a instance that can be used to process messages using - /// event handlers that are set on the processor. + /// event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in type. /// /// /// The queue to create a for. @@ -463,7 +478,10 @@ public ServiceBusProcessor CreateProcessor( /// /// Creates a instance that can be used to process messages using - /// event handlers that are set on the processor. + /// event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. If you want to change the , + /// use method. + /// The is set in type. /// /// /// The topic to create a for. @@ -485,7 +503,8 @@ public ServiceBusProcessor CreateProcessor( /// /// Creates a instance that can be used to process messages using - /// event handlers that are set on the processor. + /// event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in type. /// /// /// The topic to create a for. @@ -510,7 +529,8 @@ public ServiceBusProcessor CreateProcessor( /// /// Creates a instance that can be used to process session messages using - /// event handlers that are set on the processor. + /// event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in type. /// /// /// The queue to create a for. @@ -535,7 +555,8 @@ public ServiceBusSessionProcessor CreateSessionProcessor( /// /// Creates a instance that can be used to process - /// messages using event handlers that are set on the processor. + /// messages using event handlers that are set on the processor. It uses to specify + /// how messages are received. Defaults to PeekLock mode. The is set in type. /// /// /// The topic to create a for. diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Compatibility/ServiceBusClientBuilderExtensions.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Compatibility/ServiceBusClientBuilderExtensions.cs index 3aaae0d2fdb8..7d987d158fb3 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Compatibility/ServiceBusClientBuilderExtensions.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Compatibility/ServiceBusClientBuilderExtensions.cs @@ -1,75 +1,44 @@ -#pragma warning disable SA1636 // File header copyright text should match // Copyright (c) Microsoft Corporation. All rights reserved. -#pragma warning restore SA1636 // File header copyright text should match // Licensed under the MIT License. -// /// -// /// Registers a instance with the provided and -// /// -// /// -// public static IAzureClientBuilder AddEventHubProducerClient(this TBuilder builder, string connectionString, string eventHubName) -// where TBuilder : IAzureClientFactoryBuilder -// { -// return builder.RegisterClientFactory(options => new ServiceBusSenderClient(connectionString, eventHubName, options)); -// } -// /// -// /// Registers a instance with the provided and -// /// -// /// -// public static IAzureClientBuilder AddEventHubProducerClientWithNamespace(this TBuilder builder, string fullyQualifiedNamespace, string eventHubName) -// where TBuilder : IAzureClientFactoryBuilderWithCredential -// { -// return builder.RegisterClientFactory((options, token) => new ServiceBusSenderClient(fullyQualifiedNamespace, eventHubName, token, options)); -// } +using Azure.Core.Extensions; +using Azure.Messaging.ServiceBus; -// /// -// /// Registers a instance with connection options loaded from the provided instance. -// /// -// /// -// public static IAzureClientBuilder AddEventHubProducerClient(this TBuilder builder, TConfiguration configuration) -// where TBuilder : IAzureClientFactoryBuilderWithConfiguration -// { -// return builder.RegisterClientFactory(configuration); -// } +namespace Microsoft.Extensions.Azure +{ + /// + /// The set of extensions to add the Service Bus client types to the clients builder + /// + public static class ServiceBusClientBuilderExtensions + { + /// + /// Registers a instance with the provided . + /// + /// + public static IAzureClientBuilder AddServiceBusClient(this TBuilder builder, string connectionString) + where TBuilder : IAzureClientFactoryBuilder + { + return builder.RegisterClientFactory(options => new ServiceBusClient(connectionString, options)); + } -// /// -// /// Registers a instance with the provided -// /// -// /// -// public static IAzureClientBuilder AddEventHubConsumerClient(this TBuilder builder, string consumerGroup, string connectionString) -// where TBuilder : IAzureClientFactoryBuilder -// { -// return builder.RegisterClientFactory(options => new ServiceBusReceiverClient(consumerGroup, connectionString, options)); -// } + /// + /// Registers a instance with the provided . + /// + /// + public static IAzureClientBuilder AddServiceBusClientWithNamespace(this TBuilder builder, string fullyQualifiedNamespace) + where TBuilder : IAzureClientFactoryBuilderWithCredential + { + return builder.RegisterClientFactory((options, token) => new ServiceBusClient(fullyQualifiedNamespace, token, options)); + } -// /// -// /// Registers a instance with the provided and -// /// -// /// -// public static IAzureClientBuilder AddEventHubConsumerClient(this TBuilder builder, string consumerGroup, string connectionString, string eventHubName) -// where TBuilder : IAzureClientFactoryBuilder -// { -// return builder.RegisterClientFactory(options => new ServiceBusReceiverClient(connectionString, eventHubName, options)); -// } - -// /// -// /// Registers a instance with the provided and -// /// -// /// -// public static IAzureClientBuilder AddEventHubConsumerClientWithNamespace(this TBuilder builder, string consumerGroup, string fullyQualifiedNamespace, string eventHubName) -// where TBuilder : IAzureClientFactoryBuilderWithCredential -// { -// return builder.RegisterClientFactory((options, token) => new ServiceBusReceiverClient(fullyQualifiedNamespace, eventHubName, token, options)); -// } - -// /// -// /// Registers a instance with connection options loaded from the provided instance. -// /// -// /// -// public static IAzureClientBuilder AddEventHubConsumerClient(this TBuilder builder, TConfiguration configuration) -// where TBuilder : IAzureClientFactoryBuilderWithConfiguration -// { -// return builder.RegisterClientFactory(configuration); -// } -// } -//} + /// + /// Registers a instance with connection options loaded from the provided instance. + /// + /// + public static IAzureClientBuilder AddServiceBusClient(this TBuilder builder, TConfiguration configuration) + where TBuilder : IAzureClientFactoryBuilderWithConfiguration + { + return builder.RegisterClientFactory(configuration); + } + } +} diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusProcessor.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusProcessor.cs index 124caa1d2bd6..6b89ed61ed21 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusProcessor.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusProcessor.cs @@ -15,9 +15,9 @@ namespace Azure.Messaging.ServiceBus { /// - /// A is responsible for processing from a specific - /// entity using event handlers. It is constructed by calling - /// . + /// The provides an abstraction around a set of that + /// allows using an event based model for processing received . It is constructed by calling + /// . /// The event handler is specified with the /// property. The error handler is specified with the property. /// To start processing after the handlers have been specified, call . diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusSessionProcessor.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusSessionProcessor.cs index 1a9c73c77a30..5f518a028f85 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusSessionProcessor.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ServiceBusSessionProcessor.cs @@ -10,10 +10,9 @@ namespace Azure.Messaging.ServiceBus { /// - /// A is responsible for processing - /// from a specific entity using event handlers. - /// It is constructed by calling - /// . + /// The provides an abstraction around a set of that + /// allows using an event based model for processing received . + /// It is constructed by calling . /// The event handler is specified with the /// property. The error handler is specified with the property. /// To start processing after the handlers have been specified, call . diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs index f2ec40c5d097..b56bfba1a765 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs @@ -132,7 +132,7 @@ internal ServiceBusReceiver( protected ServiceBusReceiver() { } /// - /// Receives a batch of from the entity using mode. + /// Receives a batch of from the entity using mode. defaults to PeekLock mode. /// /// /// The maximum number of messages that will be received. @@ -171,7 +171,7 @@ public virtual async Task> ReceiveBatchAsync( } /// - /// Receives a from the entity using mode. + /// Receives a from the entity using mode. defaults to PeekLock mode /// /// An optional specifying the maximum time to wait for a message before returning a null if no messages are available. /// If not specified, the will be used. @@ -369,8 +369,11 @@ public virtual async Task CompleteAsync( /// A task to be resolved on when the operation has completed. public virtual async Task CompleteAsync( string lockToken, - CancellationToken cancellationToken = default) => + CancellationToken cancellationToken = default) + { + ThrowIfLockTokenIsEmpty(lockToken); await CompleteAsync(new[] { lockToken }, cancellationToken).ConfigureAwait(false); + } /// /// Completes a series of . This will delete the message from the service. @@ -482,7 +485,7 @@ public virtual async Task AbandonAsync( IDictionary propertiesToModify = null, CancellationToken cancellationToken = default) { - Argument.AssertNotNull(lockToken, nameof(lockToken)); + ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); ThrowIfNotPeekLockMode(); cancellationToken.ThrowIfCancellationRequested(); @@ -637,7 +640,7 @@ private async Task DeadLetterInternalAsync( IDictionary propertiesToModify = default, CancellationToken cancellationToken = default) { - Argument.AssertNotNull(lockToken, nameof(lockToken)); + ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); cancellationToken.ThrowIfCancellationRequested(); ThrowIfNotPeekLockMode(); @@ -711,7 +714,7 @@ public virtual async Task DeferAsync( IDictionary propertiesToModify = null, CancellationToken cancellationToken = default) { - Argument.AssertNotNull(lockToken, nameof(lockToken)); + ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); ThrowIfNotPeekLockMode(); cancellationToken.ThrowIfCancellationRequested(); @@ -745,6 +748,17 @@ private void ThrowIfNotPeekLockMode() } } + /// + /// Throws an InvalidOperationException when the lock token is empty. + /// + private void ThrowIfLockTokenIsEmpty(string lockToken) + { + if (Guid.Parse(lockToken) == Guid.Empty) + { + throw new InvalidOperationException(Resources.SettlementOperationNotSupported); + } + } + /// /// Receives a specific deferred message identified by . /// @@ -848,7 +862,7 @@ public virtual async Task RenewMessageLockAsync( string lockToken, CancellationToken cancellationToken = default) { - Argument.AssertNotNull(lockToken, nameof(lockToken)); + ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); ThrowIfNotPeekLockMode(); ThrowIfSessionReceiver(); diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs index 844ec296760a..448832491811 100755 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs @@ -492,6 +492,15 @@ internal static string SessionLockExpiredOnMessageSession { } } + /// + /// Looks up a localized string similar to The operation is not supported for peeked message. Only received message can be settled.. + /// + internal static string SettlementOperationNotSupported { + get { + return ResourceManager.GetString("SettlementOperationNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to In order to update the signature, a shared access key must have been provided when the shared access signature was created.. /// diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.resx b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.resx index 1aede7c6603c..9a01a717fe62 100755 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.resx +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.resx @@ -297,4 +297,7 @@ The message batch is currently being used in communication with the Service Bus service; messages may not be added until the active operation is complete. + + The operation is not supported for peeked message. Only received message can be settled. + \ No newline at end of file diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Sender/ServiceBusSender.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Sender/ServiceBusSender.cs index 14a8df197a67..541dfeef697b 100755 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Sender/ServiceBusSender.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Sender/ServiceBusSender.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; using Azure.Messaging.ServiceBus.Core; using Azure.Messaging.ServiceBus.Diagnostics; @@ -17,7 +16,7 @@ namespace Azure.Messaging.ServiceBus { /// /// A client responsible for sending to a specific Service Bus entity - /// (Queue or Topic). It is constructed by calling . + /// (Queue or Topic). It can be used for both session and non-session entities. It is constructed by calling . /// /// public class ServiceBusSender : IAsyncDisposable diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/ReceiverLiveTests.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/ReceiverLiveTests.cs index 45701650703d..0a08bb983809 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/ReceiverLiveTests.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/ReceiverLiveTests.cs @@ -413,5 +413,105 @@ public async Task MaxWaitTimeRespected() Assert.IsTrue(diff.TotalSeconds > 10); } } + + [Test] + public async Task ThrowIfCompletePeekedMessage() + { + await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) + { + await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); + + ServiceBusSender sender = client.CreateSender(scope.QueueName); + await sender.SendAsync(GetMessage()); + + var receiver = client.CreateReceiver(scope.QueueName); + + var peekedMessage = await receiver.PeekAsync(); + + Assert.That( + async () => await receiver.CompleteAsync(peekedMessage), + Throws.InstanceOf()); + } + } + + [Test] + public async Task ThrowIfAbandonPeekedMessage() + { + await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) + { + await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); + + ServiceBusSender sender = client.CreateSender(scope.QueueName); + await sender.SendAsync(GetMessage()); + + var receiver = client.CreateReceiver(scope.QueueName); + + var peekedMessage = await receiver.PeekAsync(); + + Assert.That( + async () => await receiver.AbandonAsync(peekedMessage), + Throws.InstanceOf()); + } + } + + [Test] + public async Task ThrowIfDeferPeekedMessage() + { + await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) + { + await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); + + ServiceBusSender sender = client.CreateSender(scope.QueueName); + await sender.SendAsync(GetMessage()); + + var receiver = client.CreateReceiver(scope.QueueName); + + var peekedMessage = await receiver.PeekAsync(); + + Assert.That( + async () => await receiver.DeferAsync(peekedMessage), + Throws.InstanceOf()); + } + } + + [Test] + public async Task ThrowIfDeadletterPeekedMessage() + { + await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) + { + await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); + + ServiceBusSender sender = client.CreateSender(scope.QueueName); + await sender.SendAsync(GetMessage()); + + var receiver = client.CreateReceiver(scope.QueueName); + + var peekedMessage = await receiver.PeekAsync(); + + Assert.That( + async () => await receiver.DeadLetterAsync(peekedMessage), + Throws.InstanceOf()); + } + } + + [Test] + public async Task ThrowIfRenewlockOfPeekedMessage() + { + await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) + { + await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); + + ServiceBusSender sender = client.CreateSender(scope.QueueName); + await sender.SendAsync(GetMessage()); + + var receiver = client.CreateReceiver(scope.QueueName); + + var peekedMessage = await receiver.PeekAsync(); + + Assert.That( + async () => await receiver.RenewMessageLockAsync(peekedMessage), + Throws.InstanceOf()); + } + } } }