Skip to content

Commit 2f592cc

Browse files
[Service Bus] Add new DeadLetterMessageAsync overloads (Azure#30758)
* adding overload * edge case fix * API gen * additional tests and temporary project ref * new test * adding overload to message actions * API gen * messed up parentheses * removing unnecessary code * update argument and add tests * updating documentation * removing amqpobject cast * new test * refactoring argument checks * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * Update sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> * build errors Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
1 parent 02bb79d commit 2f592cc

File tree

10 files changed

+326
-12
lines changed

10 files changed

+326
-12
lines changed

sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public ProcessMessageEventArgs(Azure.Messaging.ServiceBus.ServiceBusReceivedMess
3535
public Azure.Messaging.ServiceBus.ServiceBusReceivedMessage Message { get { throw null; } }
3636
public virtual System.Threading.Tasks.Task AbandonMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
3737
public virtual System.Threading.Tasks.Task CompleteMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
38+
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
3839
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
3940
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
4041
public virtual System.Threading.Tasks.Task DeferMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
@@ -77,6 +78,7 @@ public ProcessSessionMessageEventArgs(Azure.Messaging.ServiceBus.ServiceBusRecei
7778
public System.DateTimeOffset SessionLockedUntil { get { throw null; } }
7879
public virtual System.Threading.Tasks.Task AbandonMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
7980
public virtual System.Threading.Tasks.Task CompleteMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
81+
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.Dictionary<string, object> propertiesToModify, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
8082
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
8183
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
8284
public virtual System.Threading.Tasks.Task DeferMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
@@ -342,6 +344,7 @@ protected ServiceBusReceiver(Azure.Messaging.ServiceBus.ServiceBusClient client,
342344
public virtual System.Threading.Tasks.Task AbandonMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
343345
public virtual System.Threading.Tasks.Task CloseAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
344346
public virtual System.Threading.Tasks.Task CompleteMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
347+
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
345348
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
346349
public virtual System.Threading.Tasks.Task DeadLetterMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, string deadLetterReason, string deadLetterErrorDescription = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
347350
public virtual System.Threading.Tasks.Task DeferMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Collections.Generic.IDictionary<string, object> propertiesToModify = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }

sdk/servicebus/Azure.Messaging.ServiceBus/src/Amqp/AmqpReceiver.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,14 @@ public override async Task<IReadOnlyList<ServiceBusReceivedMessage>> ReceiveMess
288288
CancellationToken cancellationToken)
289289
{
290290
return await _retryPolicy.RunOperation(static async (value, timeout, token) =>
291-
{
292-
var (receiver, maxMessages, maxWaitTime) = value;
293-
return await receiver.ReceiveMessagesAsyncInternal(
294-
maxMessages,
295-
maxWaitTime,
296-
timeout,
297-
token).ConfigureAwait(false);
298-
},
291+
{
292+
var (receiver, maxMessages, maxWaitTime) = value;
293+
return await receiver.ReceiveMessagesAsyncInternal(
294+
maxMessages,
295+
maxWaitTime,
296+
timeout,
297+
token).ConfigureAwait(false);
298+
},
299299
(this, maxMessages, maxWaitTime),
300300
_connectionScope,
301301
cancellationToken).ConfigureAwait(false);
@@ -374,7 +374,7 @@ private async Task<IReadOnlyList<ServiceBusReceivedMessage>> ReceiveMessagesAsyn
374374
}
375375
finally
376376
{
377-
registration.Dispose();
377+
registration.Dispose();
378378
}
379379
}
380380

@@ -778,7 +778,7 @@ private async Task DisposeMessageRequestResponseAsync(
778778
amqpRequestMessage.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLink.Name;
779779
}
780780

781-
amqpRequestMessage.Map[ManagementConstants.Properties.LockTokens] = new Guid[]{ lockToken };
781+
amqpRequestMessage.Map[ManagementConstants.Properties.LockTokens] = new Guid[] { lockToken };
782782
amqpRequestMessage.Map[ManagementConstants.Properties.DispositionStatus] = dispositionStatus.ToString().ToLowerInvariant();
783783

784784
if (deadLetterReason != null)

sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ProcessMessageEventArgs.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,24 @@ await _receiver.DeadLetterMessageAsync(
176176
message.IsSettled = true;
177177
}
178178

179+
///<inheritdoc cref="ServiceBusReceiver.DeadLetterMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, string, string, CancellationToken)"/>
180+
public virtual async Task DeadLetterMessageAsync(
181+
ServiceBusReceivedMessage message,
182+
IDictionary<string, object> propertiesToModify,
183+
string deadLetterReason,
184+
string deadLetterErrorDescription = default,
185+
CancellationToken cancellationToken = default)
186+
{
187+
await _receiver.DeadLetterMessageAsync(
188+
message,
189+
propertiesToModify,
190+
deadLetterReason: deadLetterReason,
191+
deadLetterErrorDescription: deadLetterErrorDescription,
192+
cancellationToken)
193+
.ConfigureAwait(false);
194+
message.IsSettled = true;
195+
}
196+
179197
///<inheritdoc cref="ServiceBusReceiver.DeferMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, CancellationToken)"/>
180198
public virtual async Task DeferMessageAsync(
181199
ServiceBusReceivedMessage message,

sdk/servicebus/Azure.Messaging.ServiceBus/src/Processor/ProcessSessionMessageEventArgs.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,24 @@ await _sessionReceiver.DeadLetterMessageAsync(
189189
message.IsSettled = true;
190190
}
191191

192+
/// <inheritdoc cref="ServiceBusReceiver.DeadLetterMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, string, string, CancellationToken)"/>
193+
public virtual async Task DeadLetterMessageAsync(
194+
ServiceBusReceivedMessage message,
195+
Dictionary<string, object> propertiesToModify,
196+
string deadLetterReason,
197+
string deadLetterErrorDescription = default,
198+
CancellationToken cancellationToken = default)
199+
{
200+
await _sessionReceiver.DeadLetterMessageAsync(
201+
message,
202+
propertiesToModify,
203+
deadLetterReason,
204+
deadLetterErrorDescription,
205+
cancellationToken)
206+
.ConfigureAwait(false);
207+
message.IsSettled = true;
208+
}
209+
192210
/// <inheritdoc cref="ServiceBusReceiver.DeferMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, CancellationToken)"/>
193211
public virtual async Task DeferMessageAsync(
194212
ServiceBusReceivedMessage message,

sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
using System.Threading.Tasks;
1313
using Azure.Core;
1414
using Azure.Core.Pipeline;
15+
using Azure.Messaging.ServiceBus.Amqp;
1516
using Azure.Messaging.ServiceBus.Core;
1617
using Azure.Messaging.ServiceBus.Diagnostics;
18+
using Azure.Messaging.ServiceBus.Primitives;
19+
using Microsoft.Azure.Amqp.Framing;
1720

1821
namespace Azure.Messaging.ServiceBus
1922
{
@@ -683,6 +686,86 @@ await DeadLetterInternalAsync(
683686
cancellationToken: cancellationToken).ConfigureAwait(false);
684687
}
685688

689+
/// <summary>
690+
/// Moves a message to the dead-letter subqueue.
691+
/// </summary>
692+
///
693+
/// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to dead-letter.</param>
694+
/// <param name="deadLetterReason">The reason for dead-lettering the message.</param>
695+
/// <param name="deadLetterErrorDescription">The error description for dead-lettering the message.</param>
696+
/// <param name="propertiesToModify">The properties of the message to modify while moving to subqueue.</param>
697+
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
698+
///
699+
/// <remarks>
700+
/// In order to receive a message from the dead-letter queue or transfer dead-letter queue,
701+
/// set the <see cref="ServiceBusReceiverOptions.SubQueue"/> property to <see cref="SubQueue.DeadLetter"/>
702+
/// or <see cref="SubQueue.TransferDeadLetter"/> when calling
703+
/// <see cref="ServiceBusClient.CreateReceiver(string, ServiceBusReceiverOptions)"/> or
704+
/// <see cref="ServiceBusClient.CreateReceiver(string, string, ServiceBusReceiverOptions)"/>.
705+
/// This operation can only be performed when <see cref="ReceiveMode"/> is set to <see cref="ServiceBusReceiveMode.PeekLock"/>.
706+
/// The dead letter reason and error description can only be specified either through the method parameters or hard coded
707+
/// using this properties.
708+
/// </remarks>
709+
/// <exception cref="ServiceBusException">
710+
/// <list type="bullet">
711+
/// <item>
712+
/// <description>
713+
/// The lock for the message has expired or the message has already been completed. This does not apply for session-enabled entities.
714+
/// The <see cref="ServiceBusException.Reason" /> will be set to <see cref="ServiceBusFailureReason.MessageLockLost"/> in this case.
715+
/// </description>
716+
/// </item>
717+
/// <item>
718+
/// <description>
719+
/// The lock for the session has expired or the message has already been completed. This only applies for session-enabled entities.
720+
/// The <see cref="ServiceBusException.Reason" /> will be set to <see cref="ServiceBusFailureReason.SessionLockLost"/> in this case.
721+
/// </description>
722+
/// </item>
723+
/// </list>
724+
/// </exception>
725+
/// <exception cref="InvalidOperationException">
726+
/// <list type="bullet">
727+
/// <item>
728+
/// <description>
729+
/// The dead letter reason or dead letter error exception was specified in both the parameter and the properties dictionary.
730+
/// </description>
731+
/// </item>
732+
/// </list>
733+
/// </exception>
734+
public virtual async Task DeadLetterMessageAsync(
735+
ServiceBusReceivedMessage message,
736+
IDictionary<string, object> propertiesToModify,
737+
string deadLetterReason,
738+
string deadLetterErrorDescription = default,
739+
CancellationToken cancellationToken = default)
740+
{
741+
Argument.AssertNotNull(message, nameof(message));
742+
Argument.AssertNotNull(propertiesToModify, nameof(propertiesToModify));
743+
744+
// Prevent properties and arguments from setting distinct deadletter reasons or error descriptions
745+
bool containsReasonHeader = propertiesToModify.TryGetValue(AmqpMessageConstants.DeadLetterReasonHeader, out object reasonHeaderProperty);
746+
bool containsDescriptionHeader = propertiesToModify.TryGetValue(AmqpMessageConstants.DeadLetterErrorDescriptionHeader, out object descriptionHeaderProperty);
747+
748+
bool setsReasonHeaderTwice = containsReasonHeader && deadLetterReason != null;
749+
bool setsDescriptionHeaderTwice = containsDescriptionHeader && deadLetterErrorDescription != null;
750+
751+
if (setsReasonHeaderTwice && (reasonHeaderProperty is not string || reasonHeaderProperty.ToString() != deadLetterReason))
752+
{
753+
throw new InvalidOperationException("Differing deadletter reasons cannot be specified for both the 'propertiesToModify' and 'deadLetterReason' parameters. The values should either be identical or only be specified in one of the parameters.");
754+
}
755+
756+
if (setsDescriptionHeaderTwice && (descriptionHeaderProperty is not string || descriptionHeaderProperty.ToString() != deadLetterErrorDescription))
757+
{
758+
throw new InvalidOperationException("Differing deadletter error descriptions cannot be specified for both the 'propertiesToModify' and 'deadLetterErrorDescription' parameters. The values should either be identical or only be specified in one of the parameters.");
759+
}
760+
761+
await DeadLetterInternalAsync(
762+
message: message,
763+
deadLetterReason: deadLetterReason,
764+
deadLetterErrorDescription: deadLetterErrorDescription,
765+
propertiesToModify: propertiesToModify,
766+
cancellationToken: cancellationToken).ConfigureAwait(false);
767+
}
768+
686769
/// <summary>
687770
/// Moves a message to the dead-letter subqueue.
688771
/// </summary>
@@ -747,7 +830,7 @@ await DeadLetterInternalAsync(
747830
/// You can use EntityNameHelper.FormatDeadLetterPath(string) to help with this.
748831
/// This operation can only be performed on messages that were received by this receiver.
749832
/// </remarks>
750-
private async Task DeadLetterInternalAsync(
833+
internal virtual async Task DeadLetterInternalAsync(
751834
ServiceBusReceivedMessage message,
752835
string deadLetterReason = default,
753836
string deadLetterErrorDescription = default,

0 commit comments

Comments
 (0)