From db47d9ba84ced291f20a0a3e4fabbcc29442104e Mon Sep 17 00:00:00 2001 From: Mikey Date: Mon, 23 Mar 2026 16:42:09 +0000 Subject: [PATCH 1/4] add ScheduledEnqueueTime property to ServiceBusProduceOptions --- .../Producers/ServiceBusMessageBuilder.cs | 3 +- .../Producers/ServiceBusProduceOptions.cs | 5 +++ .../ConvertEventToMessage.cs | 35 +++++++++++-------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs index 796ad53e9..8dd453b35 100644 --- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs +++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs @@ -36,7 +36,8 @@ internal ServiceBusMessage CreateServiceBusMessage(ProducedMessage message) { CorrelationId = message.Metadata?.GetCorrelationId(), To = metadata?.GetValueOrDefault(attributes.To, options?.To)?.ToString(), ReplyTo = metadata?.GetValueOrDefault(attributes.ReplyTo, options?.ReplyTo)?.ToString(), - ReplyToSessionId = options?.ReplyToSessionId + ReplyToSessionId = options?.ReplyToSessionId, + ScheduledEnqueueTime = options?.ScheduledEnqueueTime ?? default }; // We set the SessionId only when a value is present because diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs index 119ea98d5..c6240142d 100644 --- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs +++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs @@ -36,4 +36,9 @@ public class ServiceBusProduceOptions { /// Gets or sets the time interval after which the message expires. /// public TimeSpan TimeToLive { get; set; } = TimeSpan.MaxValue; + + /// + /// Gets or sets the date and time, in UTC, when service bus makes the message available to receivers + /// + public DateTimeOffset? ScheduledEnqueueTime { get; set; } } \ No newline at end of file diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs index 9abd8e54a..2ae6c1acf 100644 --- a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs +++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs @@ -4,7 +4,7 @@ namespace Eventuous.Tests.Azure.ServiceBus; public class ConvertEventToMessage { - readonly Guid _messageId = Guid.NewGuid(); + readonly Guid _messageId = Guid.NewGuid(); readonly ServiceBusMessage _message; public ConvertEventToMessage() { @@ -13,23 +13,24 @@ public ConvertEventToMessage() { "test-stream", new(), new() { - Subject = "test-subject", - To = "test-to", - ReplyTo = "test-reply-to", - TimeToLive = TimeSpan.FromMinutes(5) + Subject = "test-subject", + To = "test-to", + ReplyTo = "test-reply-to", + TimeToLive = TimeSpan.FromMinutes(5), + ScheduledEnqueueTime = new DateTimeOffset(2026, 3, 23, 16, 31, 0, TimeSpan.Zero) } ); _message = builder.CreateServiceBusMessage( new( new SomeEvent { - Id = "event-id", + Id = "event-id", Name = "Test Event" }, new() { [MetaTags.CorrelationId] = "correlation-id", - [MetaTags.CausationId] = "causation-id", - ["AAA"] = 1111 + [MetaTags.CausationId] = "causation-id", + ["AAA"] = 1111 }, new() { ["BBB"] = "12345" }, _messageId @@ -72,6 +73,10 @@ public async Task CorrelationId() { await Assert.That(_message.CorrelationId).IsEqualTo("correlation-id"); } + [Test] + public async Task ScheduledEnqueueTime() => + await Assert.That(_message.ScheduledEnqueueTime).IsEqualTo(new DateTimeOffset(2026, 3, 23, 16, 31, 0, TimeSpan.Zero)); + [Test] [Arguments("MessageId")] [Arguments("CorrelationId")] @@ -92,21 +97,21 @@ public class WithMessagePropertiesInMetaData { public WithMessagePropertiesInMetaData() { var attributeNames = new ServiceBusMessageAttributeNames(); - var builder = new ServiceBusMessageBuilder(DefaultEventSerializer.Instance, "test-stream", attributeNames, new()); + var builder = new ServiceBusMessageBuilder(DefaultEventSerializer.Instance, "test-stream", attributeNames, new()); _message = builder.CreateServiceBusMessage( new( new SomeEvent { - Id = "event-id", + Id = "event-id", Name = "Test Event" }, new() { - [attributeNames.MessageId] = "12345", + [attributeNames.MessageId] = "12345", [attributeNames.CorrelationId] = "correlation-id", - [attributeNames.CausationId] = "causation-id", - [attributeNames.ReplyTo] = "test-reply-to", - [attributeNames.Subject] = "test-subject", - [attributeNames.To] = "test-to" + [attributeNames.CausationId] = "causation-id", + [attributeNames.ReplyTo] = "test-reply-to", + [attributeNames.Subject] = "test-subject", + [attributeNames.To] = "test-to" }, new() ) From a462ff8f6e495dd72f8dc7d3ddec8104f2d19537 Mon Sep 17 00:00:00 2001 From: Mikey Date: Mon, 23 Mar 2026 17:05:16 +0000 Subject: [PATCH 2/4] improve builder logic --- .../Producers/ServiceBusMessageBuilder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs index 8dd453b35..1924379e1 100644 --- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs +++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs @@ -36,10 +36,13 @@ internal ServiceBusMessage CreateServiceBusMessage(ProducedMessage message) { CorrelationId = message.Metadata?.GetCorrelationId(), To = metadata?.GetValueOrDefault(attributes.To, options?.To)?.ToString(), ReplyTo = metadata?.GetValueOrDefault(attributes.ReplyTo, options?.ReplyTo)?.ToString(), - ReplyToSessionId = options?.ReplyToSessionId, - ScheduledEnqueueTime = options?.ScheduledEnqueueTime ?? default + ReplyToSessionId = options?.ReplyToSessionId }; + if (options?.ScheduledEnqueueTime is {} scheduledEnqueueTime) { + serviceBusMessage.ScheduledEnqueueTime = scheduledEnqueueTime; + } + // We set the SessionId only when a value is present because // it overrides the PartitionKey, even if the SessionId is null. From 6496cbba1f6c1832c23427b42aa9990598a0339f Mon Sep 17 00:00:00 2001 From: Mikey Date: Mon, 23 Mar 2026 17:05:59 +0000 Subject: [PATCH 3/4] add newline --- .../Producers/ServiceBusProduceOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs index c6240142d..8c94cb574 100644 --- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs +++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProduceOptions.cs @@ -41,4 +41,4 @@ public class ServiceBusProduceOptions { /// Gets or sets the date and time, in UTC, when service bus makes the message available to receivers /// public DateTimeOffset? ScheduledEnqueueTime { get; set; } -} \ No newline at end of file +} From 6a7fbf35b80702815deda5f4f2c022b15f550026 Mon Sep 17 00:00:00 2001 From: Mikey Date: Mon, 23 Mar 2026 17:11:59 +0000 Subject: [PATCH 4/4] add ScheduledEnqueueTime to docs --- .../src/content/docs/infra/azure-service-bus.md | 17 +++++++++-------- .../docs/next/infra/azure-service-bus.md | 17 +++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/src/content/docs/infra/azure-service-bus.md b/docs/src/content/docs/infra/azure-service-bus.md index 05819ba0d..d6de2e1e1 100644 --- a/docs/src/content/docs/infra/azure-service-bus.md +++ b/docs/src/content/docs/infra/azure-service-bus.md @@ -39,14 +39,15 @@ Producer options: When producing messages, you can supply per-message options using `ServiceBusProduceOptions`: -| Option | Description | -|--------------------|----------------------------------------------------------| -| `Subject` | Message subject | -| `To` | Forward-to address | -| `ReplyTo` | Reply-to address | -| `SessionId` | Session id for session-enabled queues/topics | -| `ReplyToSessionId` | Reply-to session id | -| `TimeToLive` | Message time-to-live, default is `TimeSpan.MaxValue` | +| Option | Description | +|------------------------|--------------------------------------------------------------------| +| `Subject` | Message subject | +| `To` | Forward-to address | +| `ReplyTo` | Reply-to address | +| `SessionId` | Session id for session-enabled queues/topics | +| `ReplyToSessionId` | Reply-to session id | +| `TimeToLive` | Message time-to-live, default is `TimeSpan.MaxValue` | +| `ScheduledEnqueueTime` | Datetime when service bus makes the message available to receivers | Message metadata is mapped to Azure Service Bus application properties. The attribute names used for standard properties (message type, stream name, correlation id, etc.) can be customized via `ServiceBusMessageAttributeNames`. diff --git a/docs/src/content/docs/next/infra/azure-service-bus.md b/docs/src/content/docs/next/infra/azure-service-bus.md index 05819ba0d..d6de2e1e1 100644 --- a/docs/src/content/docs/next/infra/azure-service-bus.md +++ b/docs/src/content/docs/next/infra/azure-service-bus.md @@ -39,14 +39,15 @@ Producer options: When producing messages, you can supply per-message options using `ServiceBusProduceOptions`: -| Option | Description | -|--------------------|----------------------------------------------------------| -| `Subject` | Message subject | -| `To` | Forward-to address | -| `ReplyTo` | Reply-to address | -| `SessionId` | Session id for session-enabled queues/topics | -| `ReplyToSessionId` | Reply-to session id | -| `TimeToLive` | Message time-to-live, default is `TimeSpan.MaxValue` | +| Option | Description | +|------------------------|--------------------------------------------------------------------| +| `Subject` | Message subject | +| `To` | Forward-to address | +| `ReplyTo` | Reply-to address | +| `SessionId` | Session id for session-enabled queues/topics | +| `ReplyToSessionId` | Reply-to session id | +| `TimeToLive` | Message time-to-live, default is `TimeSpan.MaxValue` | +| `ScheduledEnqueueTime` | Datetime when service bus makes the message available to receivers | Message metadata is mapped to Azure Service Bus application properties. The attribute names used for standard properties (message type, stream name, correlation id, etc.) can be customized via `ServiceBusMessageAttributeNames`.