Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
8 changes: 4 additions & 4 deletions docs/preview/02-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,24 @@ First step in creating your message handler, is installing the following package
PS> Install-Package -Name Arcus.Messaging.Pumps.ServiceBus
```

The package makes the `IAzureServiceBusMessageHandler<>` interface available. Implementing this interface is the simplest way of creating a *'message handler'*.
The package makes the `IServiceBusMessageHandler<>` interface available. Implementing this interface is the simplest way of creating a *'message handler'*.

As the generic type, you can use the DTO (data-transfer object) to which the [`ServiceBusReceivedMessage.Body`](https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusreceivedmessage.body) should be deserialized to (default via JSON). In this case: `MyOrder`.

```csharp
using Arcus.Messaging.Abstractions.ServiceBus.MessagingHandling;
using Arcus.Messaging;

public class MyOrder
{
public string OrderId { get; set; }
public string ProductName { get; set; }
}

public class MyOrderMessageHandler : IAzureServiceBusMessageHandler<MyOrder>
public class MyOrderMessageHandler : IServiceBusMessageHandler<MyOrder>
{
public async Task ProcessMessageAsync(
MyOrder order,
AzureServiceBusMessageContext context,
ServiceBusMessageContext context,
MessageCorrelationInfo correlation,
CancellationToken cancellation)
{
Expand Down
21 changes: 10 additions & 11 deletions docs/preview/03-Features/01-Azure/01-service-bus.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ To receive the Azure Service Bus message in a deserialized form, you can impleme
Here is an example of such a message handler that expects messages of type `Order`:

```csharp
using Arcus.Messaging.Abstractions.ServiceBus.MessagingHandling;
using Arcus.Messaging;

public class Order
{
public string OrderId { get; set; }
public string ProductName { get; set; }
}

public class OrderMessageHandler : IAzureServiceBusMessageHandler<Order>
public class OrderMessageHandler : IServiceBusMessageHandler<Order>
{
private readonly ILogger _logger;

Expand All @@ -44,7 +44,7 @@ public class OrderMessageHandler : IAzureServiceBusMessageHandler<Order>
// Directly interact with your custom deserialized model (in this case 'Order'):
public async Task ProcessMessageAsync(
Order order,
AzureServiceBusMessageContext context,
ServiceBusMessageContext context,
MessageCorrelationInfo correlation,
CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -236,11 +236,13 @@ It is a good practice as application developer to dead-letter the message yourse
:::

```csharp
public class OrderMessageHandler : IAzureServiceBusMessageHandler<Order>
using Arcus.Messaging;

public class OrderMessageHandler : IServiceBusMessageHandler<Order>
{
public async Task Task ProcessMessageAsync(
Order message,
AzureServiceBusMessageContext messageContext,
ServiceBusMessageContext messageContext,
MessageCorrelationInfo correlation,
CancellationToken cancellation)
{
Expand All @@ -265,23 +267,20 @@ When your message handler interacts with an external dependency, that dependency
To interact with the message processing system within your *message handler*, you can inherit from the `CircuitBreakerServiceBusMessageHandler<>`, which allows you to 'enrich' your handler with circuit-breaker functionality.

```csharp
using Arcus.Messaging.Pumps.Abstractions.Resiliency;
using Arcus.Messaging;

// highlight-next-line
public class OrderMessageHandler : CircuitBreakerServiceBusMessageHandler<Order>
public class OrderMessageHandler : DefaultCircuitBreakerServiceBusMessageHandler<Order>
{
private readonly IMessagePumpCircuitBreaker _circuitBreaker;

public OrderMessageHandler(
IMessagePumpCircuitBreaker circuitBreaker,
ILogger<OrderMessageHandler> logger) : base(circuitBreaker, logger)
{
_circuitBreaker = circuitBreaker;
}

public override async Task ProcessMessageAsync(
Order message,
AzureServiceBusMessageContext context,
ServiceBusMessageContext context,
MessageCorrelationInfo correlation,
MessagePumpCircuitBreakerOptions options,
CancellationToken cancellation)
Expand Down
11 changes: 8 additions & 3 deletions docs/preview/03-Guides/migration-guide-v3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ All Azure EventHubs-related functionality has been removed from v3.0. This means
* `ServiceBusReceivedMessage.GetCorrelationInfo`
* `ServiceBusReceivedMessage.GetApplicationProperty`
* `MessageContext.GetMessageEncodingProperty`
### ✏️ Renamed functionality
* Renamed `IAzureServiceBusMessageHandler<>` to `IServiceBusMessageHandler<>` (in namespace `Arcus.Messaging`)
* Renamed `AzureServiceBusMessageContext` to `ServiceBusMessageContext` (in namespace `Arcus.Messaging`)
* Renamed `CircuitBreakerServiceBusMessageHandler<>` to `DefaultCircuitBreakerServiceBusMessageHandler<>` (in namespace `Arcus.Messaging`)

### ✨ New Service Bus message pump registration
Previously, the registration of the Azure Service Bus message pump involved navigating through the many available extensions, making it rather tedious to find the right authentication mechanism.
Expand Down Expand Up @@ -101,12 +105,12 @@ services.AddServiceBusQueueMessagePump(...)
### ✨ New Service Bus message settlement
Previous versions used dedicated 'template classes' that custom message handlers should inherit from to do custom Azure Service Bus message settlement (complete, dead-letter, abandon).

Starting from v3.0, the available operations are moved to the `AzureServiceBusMessageContext`. Making your custom message handlers much more accessible and flexible.
Starting from v3.0, the available operations are moved to the `ServiceBusMessageContext` (previously called `AzureServiceBusMessageContext`). Making your custom message handlers much more accessible and flexible.

```diff
public class OrderServiceBusMessageHandler
- : AzureServiceBusMessageHandler<Order>
+ : IAzureServiceBusMessageHandler<Order>
+ : IServiceBusMessageHandler<Order>
{
public OrderServiceBusMessageHandler(ILogger<OrderServiceBusMessageHandler> logger)
- : base(logger)
Expand All @@ -117,7 +121,8 @@ public class OrderServiceBusMessageHandler
- public override async Task ProcessMessageAsync(
+ public async Task ProcessMessageAsync(
Order order,
AzureServiceBusMessageContext messageContext,
- AzureServiceBusMessageContext messageContext,
+ ServiceBusMessageContext messageContext,
MessageCorrelationInfo messageCorrelation,
CancellationToken cancellation)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,18 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Arcus.Messaging.Abstractions;
using Arcus.Messaging.Abstractions.ServiceBus;
using Azure.Messaging.ServiceBus;

namespace Arcus.Messaging.Abstractions.ServiceBus
namespace Arcus.Messaging
{
/// <summary>
/// Represents the type of Azure Service Bus entity on which the message was received.
/// </summary>
public enum ServiceBusEntityType
{
/// <summary>
/// The Azure Service Bus entity is a queue.
/// </summary>
Queue,

/// <summary>
/// The Azure Service Bus entity is a topic subscription.
/// </summary>
Topic
}

/// <summary>
/// Represents the contextual information concerning an Azure Service Bus message.
/// </summary>
public class AzureServiceBusMessageContext : MessageContext
public class ServiceBusMessageContext : MessageContext
{
private readonly IMessageSettleStrategy _messageSettle;

private AzureServiceBusMessageContext(
internal ServiceBusMessageContext(
string jobId,
string fullyQualifiedNamespace,
ServiceBusEntityType entityType,
Expand All @@ -39,7 +23,8 @@ private AzureServiceBusMessageContext(
ServiceBusReceivedMessage message)
: base(message.MessageId, jobId, message.ApplicationProperties.ToDictionary(item => item.Key, item => item.Value))
{
_messageSettle = messageSettle;
MessageSettle = messageSettle;
Message = message;

FullyQualifiedNamespace = fullyQualifiedNamespace;
EntityPath = entityPath;
Expand Down Expand Up @@ -67,7 +52,7 @@ private AzureServiceBusMessageContext(
public ServiceBusEntityType EntityType { get; }

/// <summary>
/// Gets the contextual properties provided on the message provided by the Azure Service Bus runtime
/// Gets the contextual properties provided on the message provided by the Azure Service Bus runtime.
/// </summary>
public AzureServiceBusSystemProperties SystemProperties { get; }

Expand All @@ -83,14 +68,14 @@ private AzureServiceBusMessageContext(
public int DeliveryCount { get; }

/// <summary>
/// Creates a new instance of the <see cref="AzureServiceBusMessageContext"/> based on the current Azure Service bus situation.
/// Creates a new instance of the <see cref="ServiceBusMessageContext"/> based on the current Azure Service bus situation.
/// </summary>
/// <param name="jobId">The unique ID to identity the Azure Service bus message pump that is responsible for pumping messages from the <paramref name="receiver"/>.</param>
/// <param name="entityType">The type of Azure Service bus entity that the <paramref name="receiver"/> receives from.</param>
/// <param name="receiver">The Azure Service bus receiver that is responsible for receiving the <paramref name="message"/>.</param>
/// <param name="message">The Azure Service bus message that is currently being processed.</param>
/// <exception cref="ArgumentNullException">Thrown when one of the parameters is <c>null</c>.</exception>
public static AzureServiceBusMessageContext Create(
public static ServiceBusMessageContext Create(
string jobId,
ServiceBusEntityType entityType,
ServiceBusReceiver receiver,
Expand All @@ -101,7 +86,7 @@ public static AzureServiceBusMessageContext Create(
ArgumentNullException.ThrowIfNull(message);

var messageSettle = new MessageSettleViaReceiver(receiver, message);
return new AzureServiceBusMessageContext(jobId, receiver.FullyQualifiedNamespace, entityType, receiver.EntityPath, messageSettle, message);
return new ServiceBusMessageContext(jobId, receiver.FullyQualifiedNamespace, entityType, receiver.EntityPath, messageSettle, message);
}

/// <summary>
Expand All @@ -111,7 +96,7 @@ public static AzureServiceBusMessageContext Create(
/// <param name="entityType">The type of Azure Service bus entity that the <paramref name="eventArgs"/> receives from.</param>
/// <param name="eventArgs">The Azure Service bus event arguments upon receiving the message.</param>
/// <exception cref="ArgumentNullException">Thrown when one of the parameters is <c>null</c>.</exception>
public static AzureServiceBusMessageContext Create(
public static ServiceBusMessageContext Create(
string jobId,
ServiceBusEntityType entityType,
ProcessSessionMessageEventArgs eventArgs)
Expand All @@ -120,18 +105,20 @@ public static AzureServiceBusMessageContext Create(
ArgumentNullException.ThrowIfNull(eventArgs);

var messageSettle = new MessageSettleViaSessionEventArgs(eventArgs);
return new AzureServiceBusMessageContext(jobId, eventArgs.FullyQualifiedNamespace, entityType, eventArgs.EntityPath, messageSettle, eventArgs.Message);
return new ServiceBusMessageContext(jobId, eventArgs.FullyQualifiedNamespace, entityType, eventArgs.EntityPath, messageSettle, eventArgs.Message);
}

private interface IMessageSettleStrategy
internal IMessageSettleStrategy MessageSettle { get; }
internal ServiceBusReceivedMessage Message { get; }
internal interface IMessageSettleStrategy
{
Task CompleteMessageAsync(CancellationToken cancellationToken);
Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErrorDescription, CancellationToken cancellationToken);
Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErrorDescription, IDictionary<string, object> newMessageProperties, CancellationToken cancellationToken);
Task AbandonMessageAsync(IDictionary<string, object> newMessageProperties, CancellationToken cancellationToken);
}

private sealed class MessageSettleViaReceiver : IMessageSettleStrategy
internal sealed class MessageSettleViaReceiver : IMessageSettleStrategy
{
private readonly ServiceBusReceiver _receiver;
private readonly ServiceBusReceivedMessage _message;
Expand Down Expand Up @@ -202,7 +189,7 @@ public Task AbandonMessageAsync(IDictionary<string, object> newMessageProperties
/// <exception cref="InvalidOperationException">Thrown when the message handler was not initialized yet.</exception>
public Task CompleteMessageAsync(CancellationToken cancellationToken)
{
return _messageSettle.CompleteMessageAsync(cancellationToken);
return MessageSettle.CompleteMessageAsync(cancellationToken);
}

/// <summary>
Expand All @@ -214,7 +201,7 @@ public Task CompleteMessageAsync(CancellationToken cancellationToken)
/// <exception cref="InvalidOperationException">Thrown when the message handler was not initialized correctly.</exception>
public Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErrorDescription, CancellationToken cancellationToken)
{
return _messageSettle.DeadLetterMessageAsync(deadLetterReason, deadLetterErrorDescription, cancellationToken);
return MessageSettle.DeadLetterMessageAsync(deadLetterReason, deadLetterErrorDescription, cancellationToken);
}

/// <summary>
Expand All @@ -227,7 +214,7 @@ public Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErr
/// <exception cref="InvalidOperationException">Thrown when the message handler was not initialized yet.</exception>
public Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErrorDescription, IDictionary<string, object> newMessageProperties, CancellationToken cancellationToken)
{
return _messageSettle.DeadLetterMessageAsync(deadLetterReason, deadLetterErrorDescription, newMessageProperties, cancellationToken);
return MessageSettle.DeadLetterMessageAsync(deadLetterReason, deadLetterErrorDescription, newMessageProperties, cancellationToken);
}

/// <summary>
Expand All @@ -243,7 +230,52 @@ public Task DeadLetterMessageAsync(string deadLetterReason, string deadLetterErr
/// <exception cref="InvalidOperationException">Thrown when the message context was not initialized correctly.</exception>
public Task AbandonMessageAsync(IDictionary<string, object> newMessageProperties, CancellationToken cancellationToken)
{
return _messageSettle.AbandonMessageAsync(newMessageProperties, cancellationToken);
return MessageSettle.AbandonMessageAsync(newMessageProperties, cancellationToken);
}
}
}

namespace Arcus.Messaging.Abstractions.ServiceBus
{
/// <summary>
/// Represents the type of Azure Service Bus entity on which the message was received.
/// </summary>
public enum ServiceBusEntityType
{
/// <summary>
/// The Azure Service Bus entity is a queue.
/// </summary>
Queue,

/// <summary>
/// The Azure Service Bus entity is a topic subscription.
/// </summary>
Topic
}

/// <summary>
/// Represents the contextual information concerning an Azure Service Bus message.
/// </summary>
[Obsolete("Will be removed in v4.0, please use the " + nameof(ServiceBusMessageContext) + " instead")]
public class AzureServiceBusMessageContext : ServiceBusMessageContext
{
internal AzureServiceBusMessageContext(
string jobId,
string fullyQualifiedNamespace,
ServiceBusEntityType entityType,
string entityPath,
IMessageSettleStrategy messageSettle,
ServiceBusReceivedMessage message)
: base(jobId, fullyQualifiedNamespace, entityType, entityPath, messageSettle, message)
{
}

/// <summary>
/// Initializes a deprecated <see cref="AzureServiceBusMessageContext"/> from the new <see cref="ServiceBusMessageContext"/>.
/// </summary>
public AzureServiceBusMessageContext(ServiceBusMessageContext context)
: base(context.JobId, context.FullyQualifiedNamespace, context.EntityType, context.EntityPath, context.MessageSettle, context.Message)
{
}
}

Expand Down
Loading