From 9392017b9017733d31e4f1bbccb6fa4f074bca62 Mon Sep 17 00:00:00 2001 From: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> Date: Thu, 4 Feb 2021 13:39:29 -0800 Subject: [PATCH] Migrate SB extensions to new SDK (#18330) * Migrate SB extensions to new SDK * Fixes * API * Remove ExceptionHandler API * PR fb * Fix test * add back cleanupentity --- .../src/Config/EventHubOptions.cs | 2 +- ...bs.Extensions.ServiceBus.netstandard2.0.cs | 44 ++-- .../AsyncCollectorArgumentBindingProvider.cs | 6 +- .../ByteArrayArgumentBindingProvider.cs | 2 +- ...rter.cs => ByteArrayToMessageConverter.cs} | 8 +- .../CollectorArgumentBindingProvider.cs | 6 +- .../src/Bindings/CollectorValueProvider.cs | 2 +- .../src/Bindings/ConverterValueBinder.cs | 10 +- .../src/Bindings/MessageArgumentBinding.cs | 10 +- .../MessageArgumentBindingProvider.cs | 4 +- .../src/Bindings/MessageConverterFactory.cs | 12 +- .../MessageSenderArgumentBindingProvider.cs | 14 +- .../Bindings/MessageSenderAsyncCollector.cs | 8 +- .../src/Bindings/MessageSenderCollector.cs | 8 +- .../src/Bindings/MessageSenderExtensions.cs | 7 +- .../Bindings/NonNullConverterValueBinder.cs | 10 +- .../src/Bindings/ServiceBusBinding.cs | 1 - .../src/Bindings/ServiceBusEntity.cs | 7 +- .../StringToBrokeredMessageConverter.cs | 12 +- .../StringToServiceBusEntityConverter.cs | 1 - .../UserTypeArgumentBindingProvider.cs | 4 +- .../UserTypeToBrokeredMessageConverter.cs | 8 +- .../src/Config/BatchOptions.cs | 27 --- .../ServiceBusExtensionConfigProvider.cs | 17 +- .../Config/ServiceBusHostBuilderExtensions.cs | 31 +++ .../src/Config/ServiceBusOptions.cs | 207 ++++++++++++----- .../Listeners/ServiceBusCausalityHelper.cs | 20 +- .../src/Listeners/ServiceBusListener.cs | 156 ++++++------- .../Listeners/ServiceBusListenerFactory.cs | 3 - .../src/Listeners/ServiceBusScaleMonitor.cs | 58 ++--- .../src/MessageProcessor.cs | 43 ++-- .../src/MessagingProvider.cs | 135 +++++++----- ...Azure.WebJobs.Extensions.ServiceBus.csproj | 13 +- .../src/ServiceBusAttribute.cs | 1 - .../src/ServiceBusTriggerAttribute.cs | 1 - .../src/SessionMessageProcessor.cs | 34 ++- .../Triggers/DataContractBinarySerializer.cs | 121 ++++++++++ .../Triggers/MessageToByteArrayConverter.cs | 8 +- .../src/Triggers/MessageToPocoConverter.cs | 15 +- .../src/Triggers/MessageToStringConverter.cs | 17 +- .../src/Triggers/OutputConverter.cs | 10 +- ...rviceBusTriggerAttributeBindingProvider.cs | 4 +- .../ServiceBusTriggerBindingStrategy.cs | 68 +++--- .../src/Triggers/ServiceBusTriggerInput.cs | 33 +-- ...ServiceBusAttributeBindingProviderTests.cs | 10 +- ...BusTriggerAttributeBindingProviderTests.cs | 10 +- .../ServiceBusHostBuilderExtensionsTests.cs | 93 +++++++- .../tests/Config/ServiceBusOptionsTests.cs | 21 +- .../Listeners/ServiceBusListenerTests.cs | 65 +++--- .../Listeners/ServiceBusScaleMonitorTests.cs | 31 +-- .../tests/MessageInteropTests.cs | 90 ++++++++ .../tests/MessageProcessorTests.cs | 33 ++- .../tests/MessageToByteArrayConverterTests.cs | 8 +- .../tests/MessageToStringConverterTests.cs | 28 ++- .../tests/MessagingProviderTests.cs | 30 +-- ...WebJobs.Extensions.ServiceBus.Tests.csproj | 11 +- .../tests/PublicSurfaceTests.cs | 35 --- .../tests/ServiceBusEndToEndTests.cs | 208 ++++++++---------- .../tests/ServiceBusSessionsEndToEndTests.cs | 185 +++++----------- .../tests/ServiceBusTriggerStrategyTests.cs | 92 ++++---- .../tests/WebJobsServiceBusTestBase.cs | 31 ++- 61 files changed, 1202 insertions(+), 987 deletions(-) rename sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/{ByteArrayToBrokeredMessageConverter.cs => ByteArrayToMessageConverter.cs} (74%) delete mode 100644 sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/BatchOptions.cs create mode 100644 sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/DataContractBinarySerializer.cs create mode 100644 sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageInteropTests.cs delete mode 100644 sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/PublicSurfaceTests.cs diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Config/EventHubOptions.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Config/EventHubOptions.cs index d86b2bcf79f5..2ef0ecb3bb08 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Config/EventHubOptions.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Config/EventHubOptions.cs @@ -384,7 +384,7 @@ private JObject ConstructConnectionOptions() => new JObject { { nameof(EventHubConnectionOptions.TransportType), ConnectionOptions.TransportType.ToString() }, - { nameof(EventHubConnectionOptions.Proxy), ConnectionOptions.Proxy?.ToString()}, + { nameof(EventHubConnectionOptions.Proxy), ConnectionOptions.Proxy?.ToString() ?? string.Empty}, }; private JObject ConstructRetryOptions() => diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/api/Microsoft.Azure.WebJobs.Extensions.ServiceBus.netstandard2.0.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/api/Microsoft.Azure.WebJobs.Extensions.ServiceBus.netstandard2.0.cs index 51f55190de9f..1376402a0c2f 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/api/Microsoft.Azure.WebJobs.Extensions.ServiceBus.netstandard2.0.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/api/Microsoft.Azure.WebJobs.Extensions.ServiceBus.netstandard2.0.cs @@ -36,13 +36,6 @@ public ServiceBusTriggerAttribute(string topicName, string subscriptionName) { } } namespace Microsoft.Azure.WebJobs.ServiceBus { - public partial class BatchOptions - { - public BatchOptions() { } - public bool AutoComplete { get { throw null; } set { } } - public int MaxMessageCount { get { throw null; } set { } } - public System.TimeSpan OperationTimeout { get { throw null; } set { } } - } public static partial class Constants { public const string AzureWebsiteSku = "WEBSITE_SKU"; @@ -58,30 +51,35 @@ public enum EntityType } public partial class MessageProcessor { - public MessageProcessor(Microsoft.Azure.ServiceBus.Core.MessageReceiver messageReceiver, Microsoft.Azure.ServiceBus.MessageHandlerOptions messageOptions) { } - public Microsoft.Azure.ServiceBus.MessageHandlerOptions MessageOptions { get { throw null; } } - protected Microsoft.Azure.ServiceBus.Core.MessageReceiver MessageReceiver { get { throw null; } set { } } - public virtual System.Threading.Tasks.Task BeginProcessingMessageAsync(Microsoft.Azure.ServiceBus.Message message, System.Threading.CancellationToken cancellationToken) { throw null; } - public virtual System.Threading.Tasks.Task CompleteProcessingMessageAsync(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.Host.Executors.FunctionResult result, System.Threading.CancellationToken cancellationToken) { throw null; } + public MessageProcessor(Azure.Messaging.ServiceBus.ServiceBusProcessor processor) { } + public virtual System.Threading.Tasks.Task BeginProcessingMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceiver receiver, Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task CompleteProcessingMessageAsync(Azure.Messaging.ServiceBus.ServiceBusReceiver receiver, Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, Microsoft.Azure.WebJobs.Host.Executors.FunctionResult result, System.Threading.CancellationToken cancellationToken) { throw null; } } public partial class MessagingProvider { public MessagingProvider(Microsoft.Extensions.Options.IOptions serviceBusOptions) { } - public virtual Microsoft.Azure.ServiceBus.ClientEntity CreateClientEntity(string entityPath, string connectionString) { throw null; } + public virtual Azure.Messaging.ServiceBus.ServiceBusReceiver CreateBatchMessageReceiver(string entityPath, string connectionString) { throw null; } + public virtual Azure.Messaging.ServiceBus.ServiceBusClient CreateClient(string connectionString) { throw null; } public virtual Microsoft.Azure.WebJobs.ServiceBus.MessageProcessor CreateMessageProcessor(string entityPath, string connectionString) { throw null; } - public virtual Microsoft.Azure.ServiceBus.Core.MessageReceiver CreateMessageReceiver(string entityPath, string connectionString) { throw null; } - public virtual Microsoft.Azure.ServiceBus.Core.MessageSender CreateMessageSender(string entityPath, string connectionString) { throw null; } - public virtual Microsoft.Azure.ServiceBus.SessionClient CreateSessionClient(string entityPath, string connectionString) { throw null; } + public virtual Azure.Messaging.ServiceBus.ServiceBusSender CreateMessageSender(string entityPath, string connectionString) { throw null; } + public virtual Azure.Messaging.ServiceBus.ServiceBusProcessor CreateProcessor(string entityPath, string connectionString) { throw null; } public virtual Microsoft.Azure.WebJobs.ServiceBus.SessionMessageProcessor CreateSessionMessageProcessor(string entityPath, string connectionString) { throw null; } } public partial class ServiceBusOptions : Microsoft.Azure.WebJobs.Hosting.IOptionsFormatter { public ServiceBusOptions() { } - public Microsoft.Azure.WebJobs.ServiceBus.BatchOptions BatchOptions { get { throw null; } set { } } + public bool AutoCompleteMessages { get { throw null; } set { } } public string ConnectionString { get { throw null; } set { } } - public Microsoft.Azure.ServiceBus.MessageHandlerOptions MessageHandlerOptions { get { throw null; } set { } } + public System.Func ExceptionHandler { get { throw null; } set { } } + public System.TimeSpan MaxAutoLockRenewalDuration { get { throw null; } set { } } + public int MaxConcurrentCalls { get { throw null; } set { } } + public int MaxConcurrentSessions { get { throw null; } set { } } + public int MaxMessages { get { throw null; } set { } } + public System.TimeSpan? MaxWaitTime { get { throw null; } set { } } public int PrefetchCount { get { throw null; } set { } } - public Microsoft.Azure.ServiceBus.SessionHandlerOptions SessionHandlerOptions { get { throw null; } set { } } + public Azure.Messaging.ServiceBus.ServiceBusRetryOptions RetryOptions { get { throw null; } set { } } + public Azure.Messaging.ServiceBus.ServiceBusTransportType TransportType { get { throw null; } set { } } + public System.Net.IWebProxy WebProxy { get { throw null; } set { } } public string Format() { throw null; } } public partial class ServiceBusWebJobsStartup : Microsoft.Azure.WebJobs.Hosting.IWebJobsStartup @@ -91,11 +89,9 @@ public void Configure(Microsoft.Azure.WebJobs.IWebJobsBuilder builder) { } } public partial class SessionMessageProcessor { - public SessionMessageProcessor(Microsoft.Azure.ServiceBus.ClientEntity clientEntity, Microsoft.Azure.ServiceBus.SessionHandlerOptions sessionHandlerOptions) { } - protected Microsoft.Azure.ServiceBus.ClientEntity ClientEntity { get { throw null; } set { } } - public Microsoft.Azure.ServiceBus.SessionHandlerOptions SessionHandlerOptions { get { throw null; } } - public virtual System.Threading.Tasks.Task BeginProcessingMessageAsync(Microsoft.Azure.ServiceBus.IMessageSession session, Microsoft.Azure.ServiceBus.Message message, System.Threading.CancellationToken cancellationToken) { throw null; } - public virtual System.Threading.Tasks.Task CompleteProcessingMessageAsync(Microsoft.Azure.ServiceBus.IMessageSession session, Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.Host.Executors.FunctionResult result, System.Threading.CancellationToken cancellationToken) { throw null; } + public SessionMessageProcessor(Azure.Messaging.ServiceBus.ServiceBusSessionProcessor processor) { } + public virtual System.Threading.Tasks.Task BeginProcessingMessageAsync(Azure.Messaging.ServiceBus.ServiceBusSessionReceiver receiver, Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task CompleteProcessingMessageAsync(Azure.Messaging.ServiceBus.ServiceBusSessionReceiver receiver, Azure.Messaging.ServiceBus.ServiceBusReceivedMessage message, Microsoft.Azure.WebJobs.Host.Executors.FunctionResult result, System.Threading.CancellationToken cancellationToken) { throw null; } } } namespace Microsoft.Extensions.Hosting diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/AsyncCollectorArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/AsyncCollectorArgumentBindingProvider.cs index 36dea113e955..7270615cbe5d 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/AsyncCollectorArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/AsyncCollectorArgumentBindingProvider.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Reflection; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Converters; @@ -53,9 +53,9 @@ private static IArgumentBinding CreateBindingGeneric() private class AsyncCollectorArgumentBinding : IArgumentBinding { - private readonly IConverter _converter; + private readonly IConverter _converter; - public AsyncCollectorArgumentBinding(IConverter converter) + public AsyncCollectorArgumentBinding(IConverter converter) { _converter = converter; } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayArgumentBindingProvider.cs index dc5b25c96521..b70c489dc876 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayArgumentBindingProvider.cs @@ -55,7 +55,7 @@ public Task BindAsync(ServiceBusEntity value, ValueBindingContex } IValueProvider provider = new NonNullConverterValueBinder(value, - new ByteArrayToBrokeredMessageConverter(), context.FunctionInstanceId); + new ByteArrayToMessageConverter(), context.FunctionInstanceId); return Task.FromResult(provider); } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToBrokeredMessageConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToMessageConverter.cs similarity index 74% rename from sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToBrokeredMessageConverter.cs rename to sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToMessageConverter.cs index 205edcff8ab4..ad688afeebed 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToBrokeredMessageConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ByteArrayToMessageConverter.cs @@ -3,21 +3,21 @@ using System; using System.Diagnostics.CodeAnalysis; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { - internal class ByteArrayToBrokeredMessageConverter : IConverter + internal class ByteArrayToMessageConverter : IConverter { [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - public Message Convert(byte[] input) + public ServiceBusMessage Convert(byte[] input) { if (input == null) { throw new InvalidOperationException("A brokered message cannot contain a null byte array instance."); } - return new Message(input) + return new ServiceBusMessage(input) { ContentType = ContentTypes.ApplicationOctetStream }; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorArgumentBindingProvider.cs index 05f3da39235f..a1bd4a7561f0 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorArgumentBindingProvider.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Reflection; using System.Threading.Tasks; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { @@ -53,9 +53,9 @@ private static IArgumentBinding CreateBindingGeneric() private class CollectorArgumentBinding : IArgumentBinding { - private readonly IConverter _converter; + private readonly IConverter _converter; - public CollectorArgumentBinding(IConverter converter) + public CollectorArgumentBinding(IConverter converter) { _converter = converter; } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorValueProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorValueProvider.cs index 9d6beee23ff9..27de3743c807 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorValueProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/CollectorValueProvider.cs @@ -37,7 +37,7 @@ public Task GetValueAsync() public string ToInvokeString() { - return _entity.MessageSender.Path; + return _entity.MessageSender.EntityPath; } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ConverterValueBinder.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ConverterValueBinder.cs index b608eef67690..a0fd700492e6 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ConverterValueBinder.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ConverterValueBinder.cs @@ -4,19 +4,19 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; using System.Diagnostics; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { internal class ConverterValueBinder : IOrderedValueBinder { private readonly ServiceBusEntity _entity; - private readonly IConverter _converter; + private readonly IConverter _converter; private readonly Guid _functionInstanceId; - public ConverterValueBinder(ServiceBusEntity entity, IConverter converter, + public ConverterValueBinder(ServiceBusEntity entity, IConverter converter, Guid functionInstanceId) { _entity = entity; @@ -41,12 +41,12 @@ public Task GetValueAsync() public string ToInvokeString() { - return _entity.MessageSender.Path; + return _entity.MessageSender.EntityPath; } public Task SetValueAsync(object value, CancellationToken cancellationToken) { - Message message = _converter.Convert((TInput)value); + ServiceBusMessage message = _converter.Convert((TInput)value); Debug.Assert(message != null); return _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken); } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBinding.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBinding.cs index 3a324a336014..89780ff5c979 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBinding.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBinding.cs @@ -4,7 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings @@ -13,7 +13,7 @@ internal class MessageArgumentBinding : IArgumentBinding { public Type ValueType { - get { return typeof(Message); } + get { return typeof(ServiceBusMessage); } } public Task BindAsync(ServiceBusEntity value, ValueBindingContext context) @@ -46,7 +46,7 @@ public BindStepOrder StepOrder public Type Type { - get { return typeof(Message); } + get { return typeof(ServiceBusMessage); } } public Task GetValueAsync() @@ -56,7 +56,7 @@ public Task GetValueAsync() public string ToInvokeString() { - return _entity.MessageSender.Path; + return _entity.MessageSender.EntityPath; } /// @@ -91,7 +91,7 @@ public async Task SetValueAsync(object value, CancellationToken cancellationToke return; } - var message = (Message)value; + var message = (ServiceBusMessage)value; await _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken).ConfigureAwait(false); } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBindingProvider.cs index aaded5bfc760..77fcc129e087 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageArgumentBindingProvider.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Reflection; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings @@ -11,7 +11,7 @@ internal class MessageArgumentBindingProvider : IQueueArgumentBindingProvider { public IArgumentBinding TryCreate(ParameterInfo parameter) { - if (!parameter.IsOut || parameter.ParameterType != typeof(Message).MakeByRefType()) + if (!parameter.IsOut || parameter.ParameterType != typeof(ServiceBusMessage).MakeByRefType()) { return null; } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageConverterFactory.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageConverterFactory.cs index 5bf44aef262c..0069cdaf944c 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageConverterFactory.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageConverterFactory.cs @@ -4,25 +4,25 @@ using System; using System.Collections; using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { internal static class MessageConverterFactory { - internal static IConverter Create() + internal static IConverter Create() { - if (typeof(TInput) == typeof(Message)) + if (typeof(TInput) == typeof(ServiceBusMessage)) { - return (IConverter)new IdentityConverter(); + return (IConverter)new IdentityConverter(); } else if (typeof(TInput) == typeof(string)) { - return (IConverter)new StringToBrokeredMessageConverter(); + return (IConverter)new StringToBrokeredMessageConverter(); } else if (typeof(TInput) == typeof(byte[])) { - return (IConverter)new ByteArrayToBrokeredMessageConverter(); + return (IConverter)new ByteArrayToMessageConverter(); } else { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderArgumentBindingProvider.cs index 9af76cd3baa1..87e55d99d7ed 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderArgumentBindingProvider.cs @@ -5,7 +5,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings @@ -14,7 +14,7 @@ internal class MessageSenderArgumentBindingProvider : IQueueArgumentBindingProvi { public IArgumentBinding TryCreate(ParameterInfo parameter) { - if (parameter.ParameterType != typeof(MessageSender)) + if (parameter.ParameterType != typeof(ServiceBusSender)) { return null; } @@ -26,7 +26,7 @@ internal class MessageSenderArgumentBinding : IArgumentBinding { public Type ValueType { - get { return typeof(MessageSender); } + get { return typeof(ServiceBusSender); } } public Task BindAsync(ServiceBusEntity value, ValueBindingContext context) @@ -43,9 +43,9 @@ public Task BindAsync(ServiceBusEntity value, ValueBindingContex private class MessageSenderValueBinder : IValueBinder { - private readonly MessageSender _messageSender; + private readonly ServiceBusSender _messageSender; - public MessageSenderValueBinder(MessageSender messageSender) + public MessageSenderValueBinder(ServiceBusSender messageSender) { _messageSender = messageSender; } @@ -57,7 +57,7 @@ public static BindStepOrder StepOrder public Type Type { - get { return typeof(MessageSender); } + get { return typeof(ServiceBusSender); } } public Task GetValueAsync() @@ -67,7 +67,7 @@ public Task GetValueAsync() public string ToInvokeString() { - return _messageSender.Path; + return _messageSender.EntityPath; } public Task SetValueAsync(object value, CancellationToken cancellationToken) diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderAsyncCollector.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderAsyncCollector.cs index 71e767a36da1..bc5175f620b4 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderAsyncCollector.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderAsyncCollector.cs @@ -4,17 +4,17 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { internal class MessageSenderAsyncCollector : IAsyncCollector { private readonly ServiceBusEntity _entity; - private readonly IConverter _converter; + private readonly IConverter _converter; private readonly Guid _functionInstanceId; - public MessageSenderAsyncCollector(ServiceBusEntity entity, IConverter converter, + public MessageSenderAsyncCollector(ServiceBusEntity entity, IConverter converter, Guid functionInstanceId) { if (entity == null) @@ -34,7 +34,7 @@ public MessageSenderAsyncCollector(ServiceBusEntity entity, IConverter : ICollector { private readonly ServiceBusEntity _entity; - private readonly IConverter _converter; + private readonly IConverter _converter; private readonly Guid _functionInstanceId; - public MessageSenderCollector(ServiceBusEntity entity, IConverter converter, + public MessageSenderCollector(ServiceBusEntity entity, IConverter converter, Guid functionInstanceId) { if (entity == null) @@ -35,7 +35,7 @@ public MessageSenderCollector(ServiceBusEntity entity, IConverter co public void Add(T item) { - Message message = _converter.Convert(item); + ServiceBusMessage message = _converter.Convert(item); if (message == null) { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderExtensions.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderExtensions.cs index 9e83568a111c..c229ef335672 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderExtensions.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/MessageSenderExtensions.cs @@ -6,14 +6,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.ServiceBus.Listeners; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { internal static class MessageSenderExtensions { - public static async Task SendAndCreateEntityIfNotExists(this MessageSender sender, Message message, + public static async Task SendAndCreateEntityIfNotExists(this ServiceBusSender sender, ServiceBusMessage message, Guid functionInstanceId, CancellationToken cancellationToken) { if (sender == null) @@ -25,7 +24,7 @@ public static async Task SendAndCreateEntityIfNotExists(this MessageSender sende cancellationToken.ThrowIfCancellationRequested(); - await sender.SendAsync(message).ConfigureAwait(false); + await sender.SendMessageAsync(message, cancellationToken).ConfigureAwait(false); return; } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/NonNullConverterValueBinder.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/NonNullConverterValueBinder.cs index a211c2956883..13797005931b 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/NonNullConverterValueBinder.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/NonNullConverterValueBinder.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings @@ -14,10 +14,10 @@ namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings internal class NonNullConverterValueBinder : IOrderedValueBinder { private readonly ServiceBusEntity _entity; - private readonly IConverter _converter; + private readonly IConverter _converter; private readonly Guid _functionInstanceId; - public NonNullConverterValueBinder(ServiceBusEntity entity, IConverter converter, + public NonNullConverterValueBinder(ServiceBusEntity entity, IConverter converter, Guid functionInstanceId) { _entity = entity; @@ -42,7 +42,7 @@ public Task GetValueAsync() public string ToInvokeString() { - return _entity.MessageSender.Path; + return _entity.MessageSender.EntityPath; } public Task SetValueAsync(object value, CancellationToken cancellationToken) @@ -53,7 +53,7 @@ public Task SetValueAsync(object value, CancellationToken cancellationToken) } Debug.Assert(value is TInput); - Message message = _converter.Convert((TInput)value); + ServiceBusMessage message = _converter.Convert((TInput)value); Debug.Assert(message != null); return _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusBinding.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusBinding.cs index 42cba0adfef2..ba3b14e3e4da 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusBinding.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusBinding.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus.Core; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Converters; using Microsoft.Azure.WebJobs.Host.Protocols; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusEntity.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusEntity.cs index 78a6f4c35569..12c362a166b9 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusEntity.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/ServiceBusEntity.cs @@ -4,18 +4,17 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { internal class ServiceBusEntity { - public MessageSender MessageSender { get; set; } + public ServiceBusSender MessageSender { get; set; } public EntityType EntityType { get; set; } = EntityType.Queue; - public Task SendAndCreateEntityIfNotExistsAsync(Message message, Guid functionInstanceId, CancellationToken cancellationToken) + public Task SendAndCreateEntityIfNotExistsAsync(ServiceBusMessage message, Guid functionInstanceId, CancellationToken cancellationToken) { return MessageSender.SendAndCreateEntityIfNotExists(message, functionInstanceId, cancellationToken); } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToBrokeredMessageConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToBrokeredMessageConverter.cs index f9a5bf91cde4..65c951ca2399 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToBrokeredMessageConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToBrokeredMessageConverter.cs @@ -2,17 +2,13 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { - internal class StringToBrokeredMessageConverter : IConverter + internal class StringToBrokeredMessageConverter : IConverter { - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - public Message Convert(string input) + public ServiceBusMessage Convert(string input) { if (input == null) { @@ -21,7 +17,7 @@ public Message Convert(string input) byte[] bytes = StrictEncodings.Utf8.GetBytes(input); - return new Message(bytes) + return new ServiceBusMessage(bytes) { ContentType = ContentTypes.TextPlain }; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToServiceBusEntityConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToServiceBusEntityConverter.cs index f70b37253e9c..284e3da42fcd 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToServiceBusEntityConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/StringToServiceBusEntityConverter.cs @@ -4,7 +4,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus.Core; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeArgumentBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeArgumentBindingProvider.cs index abbda2c27147..090614cdefef 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeArgumentBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeArgumentBindingProvider.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { @@ -54,7 +54,7 @@ public Task BindAsync(ServiceBusEntity value, ValueBindingContex throw new ArgumentNullException(nameof(context)); } - IConverter converter = new UserTypeToBrokeredMessageConverter(); + IConverter converter = new UserTypeToBrokeredMessageConverter(); IValueProvider provider = new ConverterValueBinder(value, converter, context.FunctionInstanceId); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeToBrokeredMessageConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeToBrokeredMessageConverter.cs index 12dd537b6c28..89bfc779096c 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeToBrokeredMessageConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Bindings/UserTypeToBrokeredMessageConverter.cs @@ -4,19 +4,19 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; using Newtonsoft.Json; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings { - internal class UserTypeToBrokeredMessageConverter : IConverter + internal class UserTypeToBrokeredMessageConverter : IConverter { - public Message Convert(TInput input) + public ServiceBusMessage Convert(TInput input) { string text = JsonConvert.SerializeObject(input, Constants.JsonSerializerSettings); byte[] bytes = StrictEncodings.Utf8.GetBytes(text); - return new Message(bytes) + return new ServiceBusMessage(bytes) { ContentType = ContentTypes.ApplicationJson }; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/BatchOptions.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/BatchOptions.cs deleted file mode 100644 index c23d312d31af..000000000000 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/BatchOptions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -using System; - -namespace Microsoft.Azure.WebJobs.ServiceBus -{ - /// - /// Configuration options for ServiceBus batch receive. - /// - public class BatchOptions - { - /// - /// The maximum number of messages that will be received. - /// - public int MaxMessageCount { get; set; } - - /// - /// The time span the client waits for receiving a message before it times out. - /// - public TimeSpan OperationTimeout { get; set; } - - /// - /// Gets or sets a value that indicates whether the messages should be completed after successful processing. - /// - public bool AutoComplete { get; set; } - } -} diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusExtensionConfigProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusExtensionConfigProvider.cs index 4f0e5490632e..56f887b422f0 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusExtensionConfigProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusExtensionConfigProvider.cs @@ -2,7 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Microsoft.Azure.ServiceBus; +using System.Threading.Tasks; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Description; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Config; @@ -72,12 +73,13 @@ public void Initialize(ExtensionConfigContext context) Options.ExceptionHandler = (e) => { LogExceptionReceivedEvent(e, _loggerFactory); + return Task.CompletedTask; }; context - .AddConverter(new MessageToStringConverter()) - .AddConverter(new MessageToByteArrayConverter()) - .AddOpenConverter(typeof(MessageToPocoConverter<>)); + .AddConverter(new MessageToStringConverter()) + .AddConverter(new MessageToByteArrayConverter()) + .AddOpenConverter(typeof(MessageToPocoConverter<>)); // register our trigger binding provider ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = new ServiceBusTriggerAttributeBindingProvider(_nameResolver, _options, _messagingProvider, _configuration, _loggerFactory, _converterManager); @@ -89,13 +91,14 @@ public void Initialize(ExtensionConfigContext context) context.AddBindingRule().Bind(bindingProvider); } - internal static void LogExceptionReceivedEvent(ExceptionReceivedEventArgs e, ILoggerFactory loggerFactory) + internal static void LogExceptionReceivedEvent(ProcessErrorEventArgs e, ILoggerFactory loggerFactory) { try { - var ctxt = e.ExceptionReceivedContext; + var errorSource = e.ErrorSource; var logger = loggerFactory?.CreateLogger(LogCategories.Executor); - string message = $"Message processing error (Action={ctxt.Action}, ClientId={ctxt.ClientId}, EntityPath={ctxt.EntityPath}, Endpoint={ctxt.Endpoint})"; + //TODO new SDK does not expose client ID in event args or on clients + string message = $"Message processing error (Action={errorSource}, EntityPath={e.EntityPath}, Endpoint={e.FullyQualifiedNamespace})"; var logLevel = GetLogLevel(e.Exception); logger?.Log(logLevel, 0, message, e.Exception, (s, ex) => message); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusHostBuilderExtensions.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusHostBuilderExtensions.cs index 495122290659..fdc75e010f1d 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusHostBuilderExtensions.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusHostBuilderExtensions.cs @@ -44,6 +44,37 @@ public static IWebJobsBuilder AddServiceBus(this IWebJobsBuilder builder, Action config[Constants.DefaultConnectionSettingStringName]; IConfigurationSection section = config.GetSection(path); + + bool? autoCompleteMessages = section.GetValue( + "MessageHandlerOptions:AutoComplete", + section.GetValue("SessionHandlerOptions:AutoComplete")); + autoCompleteMessages ??= section.GetValue("BatchOptions:AutoComplete"); + if (autoCompleteMessages != null) + { + options.AutoCompleteMessages = autoCompleteMessages.Value; + } + + options.PrefetchCount = section.GetValue( + "PrefetchCount", + options.PrefetchCount); + + var maxAutoLockDuration = section.GetValue( + "MessageHandlerOptions:MaxAutoRenewDuration", + section.GetValue("SessionHandlerOptions:MaxAutoRenewDuration")); + + if (maxAutoLockDuration != null) + { + options.MaxAutoLockRenewalDuration = maxAutoLockDuration.Value; + } + + options.MaxConcurrentCalls = section.GetValue( + "MessageHandlerOptions:MaxConcurrentCalls", + options.MaxConcurrentCalls); + + options.MaxConcurrentSessions = section.GetValue( + "SessionHandlerOptions:MaxConcurrentSessions", + options.MaxConcurrentSessions); + section.Bind(options); configure(options); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusOptions.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusOptions.cs index a53169b8754e..4aae22401ed7 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusOptions.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Config/ServiceBusOptions.cs @@ -2,8 +2,10 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Net; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Hosting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -20,24 +22,6 @@ public class ServiceBusOptions : IOptionsFormatter /// public ServiceBusOptions() { - // Our default options will delegate to our own exception - // logger. Customers can override this completely by setting their - // own MessageHandlerOptions instance. - MessageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler) - { - MaxConcurrentCalls = Utility.GetProcessorCount() * 16 - }; - - SessionHandlerOptions = new SessionHandlerOptions(ExceptionReceivedHandler); - - // Default operation timeout is 1 minute in ServiceBus SDK - // https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/servicebus/Microsoft.Azure.ServiceBus/src/Constants.cs#L30 - BatchOptions = new BatchOptions() - { - MaxMessageCount = 1000, - OperationTimeout = TimeSpan.FromMinutes(1), - AutoComplete = true - }; } /// @@ -46,83 +30,184 @@ public ServiceBusOptions() public string ConnectionString { get; set; } /// - /// Gets or sets the default that will be used by - /// s. + /// Gets or sets the PrefetchCount that will be used when receiving messages. The default value is 0. /// - public MessageHandlerOptions MessageHandlerOptions { get; set; } + public int PrefetchCount { get; set; } /// - /// Gets or sets the default that will be used by - /// s. + /// The set of options to use for determining whether a failed operation should be retried and, + /// if so, the amount of time to wait between retry attempts. These options also control the + /// amount of time allowed for receiving messages and other interactions with the Service Bus service. /// - public SessionHandlerOptions SessionHandlerOptions { get; set; } + public ServiceBusRetryOptions RetryOptions + { + get => _retryOptions; + set + { + Argument.AssertNotNull(value, nameof(RetryOptions)); + _retryOptions = value; + } + } + private ServiceBusRetryOptions _retryOptions = new ServiceBusRetryOptions(); /// - /// Gets or sets the default PrefetchCount that will be used by s. + /// The type of protocol and transport that will be used for communicating with the Service Bus + /// service. /// - public int PrefetchCount { get; set; } + /// + public ServiceBusTransportType TransportType { get; set; } = ServiceBusTransportType.AmqpTcp; /// - /// Gets or sets the default that will be used by - /// s. + /// The proxy to use for communication over web sockets. /// - public BatchOptions BatchOptions { get; set; } + /// + /// + /// A proxy cannot be used for communication over TCP; if web sockets are not in + /// use, specifying a proxy is an invalid option. + /// + /// + public IWebProxy WebProxy { get; set; } - internal Action ExceptionHandler { get; set; } + /// + /// Gets or sets whether to automatically complete messages after successful execution of the function. + /// The default value is true. + /// + public bool AutoCompleteMessages { get; set; } = true; - public string Format() + /// + /// Gets or sets the maximum duration within which the lock will be renewed automatically. This + /// value should be greater than the longest message lock duration; for example, the LockDuration Property. + /// The default value is 5 minutes. This does not apply for functions that receive a batch of messages. + /// + public TimeSpan MaxAutoLockRenewalDuration { - JObject messageHandlerOptions = null; - if (MessageHandlerOptions != null) + get => _maxAutoRenewDuration; + + set { - messageHandlerOptions = new JObject - { - { nameof(MessageHandlerOptions.AutoComplete), MessageHandlerOptions.AutoComplete }, - { nameof(MessageHandlerOptions.MaxAutoRenewDuration), MessageHandlerOptions.MaxAutoRenewDuration }, - { nameof(MessageHandlerOptions.MaxConcurrentCalls), MessageHandlerOptions.MaxConcurrentCalls } - }; + Argument.AssertNotNegative(value, nameof(MaxAutoLockRenewalDuration)); + _maxAutoRenewDuration = value; } + } + private TimeSpan _maxAutoRenewDuration = TimeSpan.FromMinutes(5); - JObject sessionHandlerOptions = null; - if (SessionHandlerOptions != null) + /// Gets or sets the maximum number of concurrent calls to a function. Note each call + /// would be passing a different message. This does not apply for functions that receive a batch of messages. + /// The default is 16 times the return value of . + /// + public int MaxConcurrentCalls + { + get => _maxConcurrentCalls; + + set { - sessionHandlerOptions = new JObject - { - { nameof(SessionHandlerOptions.AutoComplete), SessionHandlerOptions.AutoComplete }, - { nameof(SessionHandlerOptions.MaxAutoRenewDuration), SessionHandlerOptions.MaxAutoRenewDuration }, - { nameof(SessionHandlerOptions.MaxConcurrentSessions), SessionHandlerOptions.MaxConcurrentSessions }, - { nameof(SessionHandlerOptions.MessageWaitTimeout), SessionHandlerOptions.MessageWaitTimeout } - }; + Argument.AssertAtLeast(value, 1, nameof(MaxConcurrentCalls)); + _maxConcurrentCalls = value; } + } + private int _maxConcurrentCalls = Utility.GetProcessorCount() * 16; + + /// + /// Gets or sets the maximum number of sessions that can be processed concurrently by a function. + /// The default value is 8. This does not apply for functions that receive a batch of messages. + /// + public int MaxConcurrentSessions + { + get => _maxConcurrentSessions; - JObject batchOptions = null; - if (BatchOptions != null) + set { - batchOptions = new JObject - { - { nameof(BatchOptions.MaxMessageCount), BatchOptions.MaxMessageCount }, - { nameof(BatchOptions.OperationTimeout), BatchOptions.OperationTimeout }, - { nameof(BatchOptions.AutoComplete), BatchOptions.AutoComplete }, - }; + Argument.AssertAtLeast(value, 1, nameof(MaxConcurrentSessions)); + _maxConcurrentSessions = value; } + } + // TODO the default value in Track 1 was the default value from the Track 1 SDK, which was 2000. + // Verify that we are okay to diverge here. + private int _maxConcurrentSessions = 8; + + /// + /// Gets or sets an optional exception handler that will be invoked if an exception occurs while attempting to process + /// a message. This does not apply for functions that receive a batch of messages. + /// + public Func ExceptionHandler { get; set; } + + /// + /// Gets or sets an optional specifying the maximum time to wait when attempting to receive messages. + /// If not specified, the will be used. This only applies for functions that receive + /// a batch of messages. + /// + public TimeSpan? MaxWaitTime { get; set; } // TODO this should probably be exposed in the Processor as well and would then also apply for functions that receive a single message. + /// + /// Gets or sets the maximum number of messages that will be passed to each function call. This only applies for functions that receive + /// a batch of messages. The default value is 1000. + /// + public int MaxMessages { get; set; } = 1000; + + /// + /// Formats the options as JSON objects for display. + /// + /// Options formatted as JSON. + public string Format() + { // Do not include ConnectionString in loggable options. + var retryOptions = new JObject + { + { nameof(ServiceBusClientOptions.RetryOptions.Mode), RetryOptions.Mode.ToString() }, + { nameof(ServiceBusClientOptions.RetryOptions.TryTimeout), RetryOptions.TryTimeout }, + { nameof(ServiceBusClientOptions.RetryOptions.Delay), RetryOptions.Delay }, + { nameof(ServiceBusClientOptions.RetryOptions.MaxDelay), RetryOptions.MaxDelay }, + { nameof(ServiceBusClientOptions.RetryOptions.MaxRetries), RetryOptions.MaxRetries }, + }; + JObject options = new JObject { + { nameof(RetryOptions), retryOptions }, + { nameof(TransportType), TransportType.ToString()}, + { nameof(WebProxy), WebProxy?.ToString() ?? string.Empty }, + { nameof(AutoCompleteMessages), AutoCompleteMessages }, { nameof(PrefetchCount), PrefetchCount }, - { nameof(MessageHandlerOptions), messageHandlerOptions }, - { nameof(SessionHandlerOptions), sessionHandlerOptions }, - { nameof(BatchOptions), batchOptions} + { nameof(MaxAutoLockRenewalDuration), MaxAutoLockRenewalDuration }, + { nameof(MaxConcurrentCalls), MaxConcurrentCalls }, + { nameof(MaxConcurrentSessions), MaxConcurrentSessions }, + { nameof(MaxMessages), MaxMessages }, + { nameof(MaxWaitTime), MaxWaitTime.ToString() ?? string.Empty } }; return options.ToString(Formatting.Indented); } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args) + internal Task ExceptionReceivedHandler(ProcessErrorEventArgs args) { ExceptionHandler?.Invoke(args); return Task.CompletedTask; } + + internal ServiceBusProcessorOptions ToProcessorOptions() => + new ServiceBusProcessorOptions + { + AutoCompleteMessages = AutoCompleteMessages, + PrefetchCount = PrefetchCount, + MaxAutoLockRenewalDuration = MaxAutoLockRenewalDuration, + MaxConcurrentCalls = MaxConcurrentCalls + }; + + internal ServiceBusSessionProcessorOptions ToSessionProcessorOptions() => + new ServiceBusSessionProcessorOptions + { + AutoCompleteMessages = AutoCompleteMessages, + PrefetchCount = PrefetchCount, + MaxAutoLockRenewalDuration = MaxAutoLockRenewalDuration, + MaxConcurrentSessions = MaxConcurrentSessions + }; + + internal ServiceBusClientOptions ToClientOptions() => + new ServiceBusClientOptions + { + RetryOptions = RetryOptions, + WebProxy = WebProxy, + TransportType = TransportType + }; } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusCausalityHelper.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusCausalityHelper.cs index bf192a4b5cbd..3c8be39b9a3e 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusCausalityHelper.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusCausalityHelper.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners { @@ -10,24 +10,18 @@ internal static class ServiceBusCausalityHelper { private const string ParentGuidFieldName = "$AzureWebJobsParentId"; - public static void EncodePayload(Guid functionOwner, Message msg) + public static void EncodePayload(Guid functionOwner, ServiceBusMessage msg) { - msg.UserProperties[ParentGuidFieldName] = functionOwner.ToString(); + msg.ApplicationProperties[ParentGuidFieldName] = functionOwner.ToString(); } - public static Guid? GetOwner(Message msg) + public static Guid? GetOwner(ServiceBusReceivedMessage message) { - object parent; - if (msg.UserProperties.TryGetValue(ParentGuidFieldName, out parent)) + if (message.ApplicationProperties.TryGetValue(ParentGuidFieldName, out var parent)) { - var parentString = parent as string; - if (parentString != null) + if (Guid.TryParse(parent?.ToString(), out var parentGuid)) { - Guid parentGuid; - if (Guid.TryParse(parentString, out parentGuid)) - { - return parentGuid; - } + return parentGuid; } } return null; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListener.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListener.cs index 43fb3071e92f..02315b155ffe 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListener.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListener.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Core.Pipeline; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Executors; using Microsoft.Azure.WebJobs.Host.Listeners; using Microsoft.Azure.WebJobs.Host.Scale; @@ -32,15 +33,13 @@ internal sealed class ServiceBusListener : IListener, IScaleMonitorProvider private readonly bool _singleDispatch; private readonly ILogger _logger; - private Lazy _receiver; - private Lazy _sessionClient; - private ClientEntity _clientEntity; + private Lazy _batchReceiver; + private Lazy _sessionClient; private bool _disposed; private bool _started; // Serialize execution of StopAsync to avoid calling Unregister* concurrently private readonly SemaphoreSlim _stopAsyncSemaphore = new SemaphoreSlim(1, 1); - private IMessageSession _messageSession; private SessionMessageProcessor _sessionMessageProcessor; private Lazy _scaleMonitor; @@ -58,9 +57,9 @@ public ServiceBusListener(string functionId, EntityType entityType, string entit _serviceBusAccount = serviceBusAccount; _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger(); - _receiver = CreateMessageReceiver(); + _batchReceiver = CreateMessageReceiver(); _sessionClient = CreateSessionClient(); - _scaleMonitor = new Lazy(() => new ServiceBusScaleMonitor(_functionId, _entityType, _entityPath, _serviceBusAccount.ConnectionString, _receiver, _loggerFactory)); + _scaleMonitor = new Lazy(() => new ServiceBusScaleMonitor(_functionId, _entityType, _entityPath, _serviceBusAccount.ConnectionString, _batchReceiver, _loggerFactory)); _singleDispatch = singleDispatch; if (_isSessionsEnabled) @@ -74,11 +73,9 @@ public ServiceBusListener(string functionId, EntityType entityType, string entit _serviceBusOptions = config; } - internal MessageReceiver Receiver => _receiver.Value; + internal ServiceBusReceiver BatchReceiver => _batchReceiver.Value; - internal IMessageSession MessageSession => _messageSession; - - public Task StartAsync(CancellationToken cancellationToken) + public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -91,20 +88,13 @@ public Task StartAsync(CancellationToken cancellationToken) { if (_isSessionsEnabled) { - _clientEntity = _messagingProvider.CreateClientEntity(_entityPath, _serviceBusAccount.ConnectionString); - if (_clientEntity is QueueClient queueClient) - { - queueClient.RegisterSessionHandler(ProcessSessionMessageAsync, _serviceBusOptions.SessionHandlerOptions); - } - else - { - SubscriptionClient subscriptionClient = _clientEntity as SubscriptionClient; - subscriptionClient.RegisterSessionHandler(ProcessSessionMessageAsync, _serviceBusOptions.SessionHandlerOptions); - } + _sessionMessageProcessor.Processor.ProcessMessageAsync += ProcessSessionMessageAsync; + await _sessionMessageProcessor.Processor.StartProcessingAsync(cancellationToken).ConfigureAwait(false); } else { - Receiver.RegisterMessageHandler(ProcessMessageAsync, _serviceBusOptions.MessageHandlerOptions); + _messageProcessor.Processor.ProcessMessageAsync += ProcessMessageAsync; + await _messageProcessor.Processor.StartProcessingAsync(cancellationToken).ConfigureAwait(false); } } else @@ -112,8 +102,6 @@ public Task StartAsync(CancellationToken cancellationToken) StartMessageBatchReceiver(_cancellationTokenSource.Token); } _started = true; - - return Task.CompletedTask; } public async Task StopAsync(CancellationToken cancellationToken) @@ -136,25 +124,11 @@ public async Task StopAsync(CancellationToken cancellationToken) { if (_isSessionsEnabled) { - if (_clientEntity != null) - { - if (_clientEntity is QueueClient queueClient) - { - await queueClient.UnregisterSessionHandlerAsync(TimeSpan.MaxValue).ConfigureAwait(false); - } - else - { - SubscriptionClient subscriptionClient = _clientEntity as SubscriptionClient; - await subscriptionClient.UnregisterSessionHandlerAsync(TimeSpan.MaxValue).ConfigureAwait(false); - } - } + await _sessionMessageProcessor.Processor.StopProcessingAsync(cancellationToken).ConfigureAwait(false); } else { - if (_receiver != null && _receiver.IsValueCreated) - { - await Receiver.UnregisterMessageHandlerAsync(TimeSpan.MaxValue).ConfigureAwait(false); - } + await _messageProcessor.Processor.StopProcessingAsync(cancellationToken).ConfigureAwait(false); } } // Batch processing will be stopped via the _started flag on its next iteration @@ -185,24 +159,20 @@ public void Dispose() // For now, rely on finalization to clean up _cancellationTokenSource's wait handle (if allocated). _cancellationTokenSource.Cancel(); - if (_receiver != null && _receiver.IsValueCreated) + if (_batchReceiver != null && _batchReceiver.IsValueCreated) { - Receiver.CloseAsync().Wait(); - _receiver = null; + BatchReceiver.CloseAsync().Wait(); + _batchReceiver = null; } if (_sessionClient != null && _sessionClient.IsValueCreated) { - _sessionClient.Value.CloseAsync().Wait(); +#pragma warning disable AZC0107 // DO NOT call public asynchronous method in synchronous scope. + _sessionClient.Value.DisposeAsync().EnsureCompleted(); +#pragma warning restore AZC0107 // DO NOT call public asynchronous method in synchronous scope. _sessionClient = null; } - if (_clientEntity != null) - { - _clientEntity.CloseAsync().Wait(); - _clientEntity = null; - } - _stopAsyncSemaphore.Dispose(); _cancellationTokenSource.Dispose(); @@ -210,64 +180,68 @@ public void Dispose() } } - private Lazy CreateMessageReceiver() + private Lazy CreateMessageReceiver() { - return new Lazy(() => _messagingProvider.CreateMessageReceiver(_entityPath, _serviceBusAccount.ConnectionString)); + return new Lazy(() => _messagingProvider.CreateBatchMessageReceiver(_entityPath, _serviceBusAccount.ConnectionString)); } - private Lazy CreateSessionClient() + private Lazy CreateSessionClient() { - return new Lazy(() => _messagingProvider.CreateSessionClient(_entityPath, _serviceBusAccount.ConnectionString)); + return new Lazy(() => _messagingProvider.CreateSessionClient(_serviceBusAccount.ConnectionString)); } - internal async Task ProcessMessageAsync(Message message, CancellationToken cancellationToken) + internal async Task ProcessMessageAsync(ProcessMessageEventArgs args) { - using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token)) + using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(args.CancellationToken, _cancellationTokenSource.Token)) { - if (!await _messageProcessor.BeginProcessingMessageAsync(message, linkedCts.Token).ConfigureAwait(false)) + //TODO consider using internals visible or exposing the Receiver property instead of reflection + ServiceBusReceiver receiver = (ServiceBusReceiver) typeof(ProcessMessageEventArgs).GetField("_receiver", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args); + + if (!await _messageProcessor.BeginProcessingMessageAsync(receiver, args.Message, linkedCts.Token).ConfigureAwait(false)) { return; } - ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(message); - input.MessageReceiver = Receiver; + ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message); + input.Receiver = receiver; TriggeredFunctionData data = input.GetTriggerFunctionData(); FunctionResult result = await _triggerExecutor.TryExecuteAsync(data, linkedCts.Token).ConfigureAwait(false); - await _messageProcessor.CompleteProcessingMessageAsync(message, result, linkedCts.Token).ConfigureAwait(false); + await _messageProcessor.CompleteProcessingMessageAsync(receiver, args.Message, result, linkedCts.Token).ConfigureAwait(false); } } - internal async Task ProcessSessionMessageAsync(IMessageSession session, Message message, CancellationToken cancellationToken) + internal async Task ProcessSessionMessageAsync(ProcessSessionMessageEventArgs args) { - using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token)) + using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(args.CancellationToken, _cancellationTokenSource.Token)) { - _messageSession = session; - if (!await _sessionMessageProcessor.BeginProcessingMessageAsync(session, message, linkedCts.Token).ConfigureAwait(false)) + ServiceBusSessionReceiver receiver = (ServiceBusSessionReceiver)typeof(ProcessSessionMessageEventArgs).GetField("_sessionReceiver", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args); + + if (!await _sessionMessageProcessor.BeginProcessingMessageAsync(receiver, args.Message, linkedCts.Token).ConfigureAwait(false)) { return; } - ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(message); - input.MessageReceiver = session; + ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message); + input.SessionReceiver = receiver; TriggeredFunctionData data = input.GetTriggerFunctionData(); FunctionResult result = await _triggerExecutor.TryExecuteAsync(data, linkedCts.Token).ConfigureAwait(false); - await _sessionMessageProcessor.CompleteProcessingMessageAsync(session, message, result, linkedCts.Token).ConfigureAwait(false); + await _sessionMessageProcessor.CompleteProcessingMessageAsync(receiver, args.Message, result, linkedCts.Token).ConfigureAwait(false); } } internal void StartMessageBatchReceiver(CancellationToken cancellationToken) { - SessionClient sessionClient = null; - IMessageReceiver receiver = null; + ServiceBusClient sessionClient = null; + ServiceBusReceiver receiver = null; if (_isSessionsEnabled) { sessionClient = _sessionClient.Value; } else { - receiver = Receiver; + receiver = BatchReceiver; } Task.Run(async () => @@ -282,28 +256,39 @@ internal void StartMessageBatchReceiver(CancellationToken cancellationToken) return; } - if (_isSessionsEnabled && ( receiver == null || receiver.IsClosedOrClosing)) + if (_isSessionsEnabled && ( receiver == null || receiver.IsClosed)) { try { - receiver = await sessionClient.AcceptMessageSessionAsync().ConfigureAwait(false); - receiver.PrefetchCount = _serviceBusOptions.PrefetchCount; + receiver = await sessionClient.AcceptNextSessionAsync(_entityPath, new ServiceBusSessionReceiverOptions + { + PrefetchCount = _serviceBusOptions.PrefetchCount + }).ConfigureAwait(false); } - catch (ServiceBusTimeoutException) + catch (ServiceBusException ex) + when (ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // it's expected if the entity is empty, try next time continue; } } - IList messages = await receiver.ReceiveAsync(_serviceBusOptions.BatchOptions.MaxMessageCount, _serviceBusOptions.BatchOptions.OperationTimeout).ConfigureAwait(false); + IReadOnlyList messages = await receiver.ReceiveMessagesAsync( + _serviceBusOptions.MaxMessages, + _serviceBusOptions.MaxWaitTime).ConfigureAwait(false); if (messages != null) { - Message[] messagesArray = messages.ToArray(); + ServiceBusReceivedMessage[] messagesArray = messages.ToArray(); ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray); - input.MessageReceiver = receiver; - + if (_isSessionsEnabled) + { + input.SessionReceiver = (ServiceBusSessionReceiver) receiver; + } + else + { + input.Receiver = receiver; + } FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) @@ -312,18 +297,23 @@ internal void StartMessageBatchReceiver(CancellationToken cancellationToken) } // Complete batch of messages only if the execution was successful - if (_serviceBusOptions.BatchOptions.AutoComplete && _started) + if (_serviceBusOptions.AutoCompleteMessages && _started) { if (result.Succeeded) { - await receiver.CompleteAsync(messagesArray.Select(x => x.SystemProperties.LockToken)).ConfigureAwait(false); + List completeTasks = new List(); + foreach (ServiceBusReceivedMessage message in messagesArray) + { + completeTasks.Add(receiver.CompleteMessageAsync(message)); + } + await Task.WhenAll(completeTasks).ConfigureAwait(false); } else { List abandonTasks = new List(); - foreach (var lockTocken in messagesArray.Select(x => x.SystemProperties.LockToken)) + foreach (ServiceBusReceivedMessage message in messagesArray) { - abandonTasks.Add(receiver.AbandonAsync(lockTocken)); + abandonTasks.Add(receiver.AbandonMessageAsync(message)); } await Task.WhenAll(abandonTasks).ConfigureAwait(false); } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListenerFactory.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListenerFactory.cs index 6b8fca434d89..df924835ef9b 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListenerFactory.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusListenerFactory.cs @@ -3,9 +3,6 @@ using Microsoft.Azure.WebJobs.Host.Executors; using Microsoft.Azure.WebJobs.Host.Listeners; -using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusScaleMonitor.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusScaleMonitor.cs index 0f0fd35618b5..750d64a3cb41 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusScaleMonitor.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Listeners/ServiceBusScaleMonitor.cs @@ -7,12 +7,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; -using Microsoft.Azure.ServiceBus.Management; using Microsoft.Azure.WebJobs.Host.Scale; using Microsoft.Extensions.Logging; using System.Globalization; +using Azure.Messaging.ServiceBus; +using Azure.Messaging.ServiceBus.Administration; namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners { @@ -26,13 +25,13 @@ internal class ServiceBusScaleMonitor : IScaleMonitor private readonly string _connectionString; private readonly ScaleMonitorDescriptor _scaleMonitorDescriptor; private readonly bool _isListeningOnDeadLetterQueue; - private readonly Lazy _receiver; - private readonly Lazy _managementClient; + private readonly Lazy _receiver; + private readonly Lazy _administrationClient; private readonly ILogger _logger; private DateTime _nextWarningTime; - public ServiceBusScaleMonitor(string functionId, EntityType entityType, string entityPath, string connectionString, Lazy receiver, ILoggerFactory loggerFactory) + public ServiceBusScaleMonitor(string functionId, EntityType entityType, string entityPath, string connectionString, Lazy receiver, ILoggerFactory loggerFactory) { _functionId = functionId; _entityType = entityType; @@ -41,7 +40,7 @@ public ServiceBusScaleMonitor(string functionId, EntityType entityType, string e _scaleMonitorDescriptor = new ScaleMonitorDescriptor($"{_functionId}-ServiceBusTrigger-{_entityPath}".ToLower(CultureInfo.InvariantCulture)); _isListeningOnDeadLetterQueue = entityPath.EndsWith(DeadLetterQueuePath, StringComparison.OrdinalIgnoreCase); _receiver = receiver; - _managementClient = new Lazy(() => new ManagementClient(_connectionString)); + _administrationClient = new Lazy(() => new ServiceBusAdministrationClient(_connectionString)); _logger = loggerFactory.CreateLogger(); _nextWarningTime = DateTime.UtcNow; } @@ -61,7 +60,7 @@ async Task IScaleMonitor.GetMetricsAsync() public async Task GetMetricsAsync() { - Message message = null; + ServiceBusReceivedMessage message = null; string entityName = _entityType == EntityType.Queue ? "queue" : "topic"; try @@ -69,7 +68,7 @@ public async Task GetMetricsAsync() // Peek the first message in the queue without removing it from the queue // PeekAsync remembers the sequence number of the last message, so the second call returns the second message instead of the first one // Use PeekBySequenceNumberAsync with fromSequenceNumber = 0 to always get the first available message - message = await _receiver.Value.PeekBySequenceNumberAsync(0).ConfigureAwait(false); + message = await _receiver.Value.PeekMessageAsync(fromSequenceNumber: 0).ConfigureAwait(false); if (_entityType == EntityType.Queue) { @@ -80,11 +79,12 @@ public async Task GetMetricsAsync() return await GetTopicMetricsAsync(message).ConfigureAwait(false); } } - catch (MessagingEntityNotFoundException) + catch (ServiceBusException ex) + when (ex.Reason == ServiceBusFailureReason.MessagingEntityNotFound) { _logger.LogWarning($"ServiceBus {entityName} '{_entityPath}' was not found."); } - catch (UnauthorizedException) // When manage claim is not used on Service Bus connection string + catch (UnauthorizedAccessException) // When manage claim is not used on Service Bus connection string { if (TimeToLogWarning()) { @@ -101,55 +101,55 @@ public async Task GetMetricsAsync() return CreateTriggerMetrics(message, 0, 0, 0, _isListeningOnDeadLetterQueue); } - private async Task GetQueueMetricsAsync(Message message) + private async Task GetQueueMetricsAsync(ServiceBusReceivedMessage message) { - QueueRuntimeInfo queueRuntimeInfo; - QueueDescription queueDescription; + QueueRuntimeProperties queueRuntimeProperties; + QueueProperties queueProperties; long activeMessageCount = 0, deadLetterCount = 0; int partitionCount = 0; - queueRuntimeInfo = await _managementClient.Value.GetQueueRuntimeInfoAsync(_entityPath).ConfigureAwait(false); - activeMessageCount = queueRuntimeInfo.MessageCountDetails.ActiveMessageCount; - deadLetterCount = queueRuntimeInfo.MessageCountDetails.DeadLetterMessageCount; + queueRuntimeProperties = await _administrationClient.Value.GetQueueRuntimePropertiesAsync(_entityPath).ConfigureAwait(false); + activeMessageCount = queueRuntimeProperties.ActiveMessageCount; + deadLetterCount = queueRuntimeProperties.DeadLetterMessageCount; // If partitioning is turned on, then Service Bus automatically partitions queues into 16 partitions // See more information here: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-partitioning#standard - queueDescription = await _managementClient.Value.GetQueueAsync(_entityPath).ConfigureAwait(false); - partitionCount = queueDescription.EnablePartitioning ? 16 : 0; + queueProperties = await _administrationClient.Value.GetQueueAsync(_entityPath).ConfigureAwait(false); + partitionCount = queueProperties.EnablePartitioning ? 16 : 0; return CreateTriggerMetrics(message, activeMessageCount, deadLetterCount, partitionCount, _isListeningOnDeadLetterQueue); } - private async Task GetTopicMetricsAsync(Message message) + private async Task GetTopicMetricsAsync(ServiceBusReceivedMessage message) { - TopicDescription topicDescription; - SubscriptionRuntimeInfo subscriptionRuntimeInfo; + TopicProperties topicProperties; + SubscriptionRuntimeProperties subscriptionProperties; string topicPath, subscriptionPath; long activeMessageCount = 0, deadLetterCount = 0; int partitionCount = 0; ServiceBusEntityPathHelper.ParseTopicAndSubscription(_entityPath, out topicPath, out subscriptionPath); - subscriptionRuntimeInfo = await _managementClient.Value.GetSubscriptionRuntimeInfoAsync(topicPath, subscriptionPath).ConfigureAwait(false); - activeMessageCount = subscriptionRuntimeInfo.MessageCountDetails.ActiveMessageCount; - deadLetterCount = subscriptionRuntimeInfo.MessageCountDetails.DeadLetterMessageCount; + subscriptionProperties = await _administrationClient.Value.GetSubscriptionRuntimePropertiesAsync(topicPath, subscriptionPath).ConfigureAwait(false); + activeMessageCount = subscriptionProperties.ActiveMessageCount; + deadLetterCount = subscriptionProperties.DeadLetterMessageCount; // If partitioning is turned on, then Service Bus automatically partitions queues into 16 partitions // See more information here: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-partitioning#standard - topicDescription = await _managementClient.Value.GetTopicAsync(topicPath).ConfigureAwait(false); - partitionCount = topicDescription.EnablePartitioning ? 16 : 0; + topicProperties = await _administrationClient.Value.GetTopicAsync(topicPath).ConfigureAwait(false); + partitionCount = topicProperties.EnablePartitioning ? 16 : 0; return CreateTriggerMetrics(message, activeMessageCount, deadLetterCount, partitionCount, _isListeningOnDeadLetterQueue); } - internal static ServiceBusTriggerMetrics CreateTriggerMetrics(Message message, long activeMessageCount, long deadLetterCount, int partitionCount, bool isListeningOnDeadLetterQueue) + internal static ServiceBusTriggerMetrics CreateTriggerMetrics(ServiceBusReceivedMessage message, long activeMessageCount, long deadLetterCount, int partitionCount, bool isListeningOnDeadLetterQueue) { long totalNewMessageCount = 0; TimeSpan queueTime = TimeSpan.Zero; if (message != null) { - queueTime = DateTime.UtcNow.Subtract(message.SystemProperties.EnqueuedTimeUtc); + queueTime = DateTimeOffset.UtcNow.Subtract(message.EnqueuedTime); totalNewMessageCount = 1; // There's at least one if message != null. Default for connection string with no manage claim } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessageProcessor.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessageProcessor.cs index 8adb5bea58e5..613e913473ee 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessageProcessor.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessageProcessor.cs @@ -5,8 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Host.Executors; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus { @@ -14,7 +13,7 @@ namespace Microsoft.Azure.WebJobs.ServiceBus /// This class defines a strategy used for processing ServiceBus messages. /// /// - /// Custom implementations can be specified by implementing + /// Custom implementations can be specified by implementing /// a custom and setting it via ServiceBusOptions.MessagingProvider. /// public class MessageProcessor @@ -22,32 +21,26 @@ public class MessageProcessor /// /// Constructs a new instance. /// - /// The . - /// The to use. - public MessageProcessor(MessageReceiver messageReceiver, MessageHandlerOptions messageOptions) + /// The . + public MessageProcessor(ServiceBusProcessor processor) { - MessageReceiver = messageReceiver ?? throw new ArgumentNullException(nameof(messageReceiver)); - MessageOptions = messageOptions ?? throw new ArgumentNullException(nameof(messageOptions)); + Processor = processor ?? throw new ArgumentNullException(nameof(processor)); } /// - /// Gets the that will be used by the . + /// Gets or sets the that will be used by the . /// - public MessageHandlerOptions MessageOptions { get; } - - /// - /// Gets or sets the that will be used by the . - /// - protected MessageReceiver MessageReceiver { get; set; } + internal ServiceBusProcessor Processor { get; set; } /// /// This method is called when there is a new message to process, before the job function is invoked. /// This allows any preprocessing to take place on the message before processing begins. /// - /// The message to process. - /// The to use. + /// + /// + /// /// A that returns true if the message processing should continue, false otherwise. - public virtual Task BeginProcessingMessageAsync(Message message, CancellationToken cancellationToken) + public virtual Task BeginProcessingMessageAsync(ServiceBusReceiver receiver, ServiceBusReceivedMessage message, CancellationToken cancellationToken) { return Task.FromResult(true); } @@ -56,16 +49,22 @@ public virtual Task BeginProcessingMessageAsync(Message message, Cancellat /// This method completes processing of the specified message, after the job function has been invoked. /// /// - /// The message is completed by the ServiceBus SDK based on how the option - /// is configured. E.g. if is false, it is up to the job function to complete + /// The message is completed by the ServiceBus SDK based on how the option + /// is configured. E.g. if is false, it is up to the job function to complete /// the message. /// - /// The message to complete processing for. + /// + /// /// The from the job invocation. /// The to use /// A that will complete the message processing. - public virtual Task CompleteProcessingMessageAsync(Message message, FunctionResult result, CancellationToken cancellationToken) + public virtual Task CompleteProcessingMessageAsync(ServiceBusReceiver receiver, ServiceBusReceivedMessage message, FunctionResult result, CancellationToken cancellationToken) { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + if (result == null) { throw new ArgumentNullException(nameof(result)); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessagingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessagingProvider.cs index bace14229d07..541893af04d8 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessagingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/MessagingProvider.cs @@ -3,8 +3,7 @@ using System; using System.Collections.Concurrent; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.ServiceBus.Listeners; using Microsoft.Extensions.Options; @@ -17,10 +16,11 @@ namespace Microsoft.Azure.WebJobs.ServiceBus public class MessagingProvider { private readonly ServiceBusOptions _options; - private readonly ConcurrentDictionary _messageReceiverCache = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _messageSenderCache = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _clientEntityCache = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _sessionClientCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _messageSenderCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _messageReceiverCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _clientCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _processorCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _sessionProcessorCache = new ConcurrentDictionary(); /// /// Constructs a new instance. @@ -48,19 +48,19 @@ public virtual MessageProcessor CreateMessageProcessor(string entityPath, string throw new ArgumentNullException(nameof(connectionString)); } - return new MessageProcessor(GetOrAddMessageReceiver(entityPath, connectionString), _options.MessageHandlerOptions); + return new MessageProcessor(GetOrAddProcessor(entityPath, connectionString)); } /// - /// Creates a for the specified ServiceBus entity. + /// Creates a for the specified ServiceBus entity. /// /// - /// You can override this method to customize the . + /// You can override this method to customize the . /// - /// The ServiceBus entity to create a for. + /// The ServiceBus entity to create a for. /// The ServiceBus connection string. /// - public virtual MessageReceiver CreateMessageReceiver(string entityPath, string connectionString) + public virtual ServiceBusProcessor CreateProcessor(string entityPath, string connectionString) { if (string.IsNullOrEmpty(entityPath)) { @@ -71,19 +71,19 @@ public virtual MessageReceiver CreateMessageReceiver(string entityPath, string c throw new ArgumentNullException(nameof(connectionString)); } - return GetOrAddMessageReceiver(entityPath, connectionString); + return GetOrAddProcessor(entityPath, connectionString); } /// - /// Creates a for the specified ServiceBus entity. + /// Creates a for the specified ServiceBus entity. /// /// - /// You can override this method to customize the . + /// You can override this method to customize the . /// - /// The ServiceBus entity to create a for. + /// The ServiceBus entity to create a for. /// The ServiceBus connection string. /// - public virtual MessageSender CreateMessageSender(string entityPath, string connectionString) + public virtual ServiceBusSender CreateMessageSender(string entityPath, string connectionString) { if (string.IsNullOrEmpty(entityPath)) { @@ -98,15 +98,15 @@ public virtual MessageSender CreateMessageSender(string entityPath, string conne } /// - /// Creates a for the specified ServiceBus entity. + /// Creates a for the specified ServiceBus entity. /// /// - /// You can override this method to customize the . + /// You can override this method to customize the . /// - /// The ServiceBus entity to create a for. + /// The ServiceBus entity to create a for. /// The ServiceBus connection string. /// - public virtual ClientEntity CreateClientEntity(string entityPath, string connectionString) + public virtual ServiceBusReceiver CreateBatchMessageReceiver(string entityPath, string connectionString) { if (string.IsNullOrEmpty(entityPath)) { @@ -117,30 +117,25 @@ public virtual ClientEntity CreateClientEntity(string entityPath, string connect throw new ArgumentNullException(nameof(connectionString)); } - return GetOrAddClientEntity(entityPath, connectionString); + return GetOrAddMessageReceiver(entityPath, connectionString); } /// - /// Creates a for the specified ServiceBus entity. + /// Creates a for the specified ServiceBus connection. /// /// - /// You can override this method to customize the . + /// You can override this method to customize the . /// - /// The ServiceBus entity to create a for. /// The ServiceBus connection string. /// - public virtual SessionClient CreateSessionClient(string entityPath, string connectionString) + public virtual ServiceBusClient CreateClient(string connectionString) { - if (string.IsNullOrEmpty(entityPath)) - { - throw new ArgumentNullException(nameof(entityPath)); - } if (string.IsNullOrEmpty(connectionString)) { throw new ArgumentNullException(nameof(connectionString)); } - return GetOrAddSessionClient(entityPath, connectionString); + return new ServiceBusClient(connectionString, _options.ToClientOptions()); } /// @@ -160,53 +155,71 @@ public virtual SessionMessageProcessor CreateSessionMessageProcessor(string enti throw new ArgumentNullException(nameof(connectionString)); } - return new SessionMessageProcessor(GetOrAddClientEntity(entityPath, connectionString), _options.SessionHandlerOptions); + return new SessionMessageProcessor(GetOrAddSessionProcessor(entityPath, connectionString)); } - private MessageReceiver GetOrAddMessageReceiver(string entityPath, string connectionString) + private ServiceBusSender GetOrAddMessageSender(string entityPath, string connectionString) { - string cacheKey = $"{entityPath}-{connectionString}"; - return _messageReceiverCache.GetOrAdd(cacheKey, - new MessageReceiver(connectionString, entityPath) - { - PrefetchCount = _options.PrefetchCount - }); + ServiceBusClient client = _clientCache.GetOrAdd(connectionString, CreateClient(connectionString)); + return _messageSenderCache.GetOrAdd(entityPath, client.CreateSender(entityPath)); } - private MessageSender GetOrAddMessageSender(string entityPath, string connectionString) + private ServiceBusReceiver GetOrAddMessageReceiver(string entityPath, string connectionString) { - string cacheKey = $"{entityPath}-{connectionString}"; - return _messageSenderCache.GetOrAdd(cacheKey, new MessageSender(connectionString, entityPath)); + ServiceBusClient client = _clientCache.GetOrAdd(connectionString, (_) => CreateClient(connectionString)); + return _messageReceiverCache.GetOrAdd(entityPath, (_) => client.CreateReceiver(entityPath, new ServiceBusReceiverOptions + { + PrefetchCount = _options.PrefetchCount + })); } - private ClientEntity GetOrAddClientEntity(string entityPath, string connectionString) + private ServiceBusProcessor GetOrAddProcessor(string entityPath, string connectionString) { - string cacheKey = $"{entityPath}-{connectionString}"; - if (ServiceBusEntityPathHelper.ParseEntityType(entityPath) == EntityType.Topic) + ServiceBusClient client = _clientCache.GetOrAdd(connectionString, (_) => CreateClient(connectionString)); + return _processorCache.GetOrAdd(entityPath, (_) => { - string topic, subscription; - - // entityPath for a subscription is "{TopicName}/Subscriptions/{SubscriptionName}" - ServiceBusEntityPathHelper.ParseTopicAndSubscription(entityPath, out topic, out subscription); - return _clientEntityCache.GetOrAdd(cacheKey, new SubscriptionClient(connectionString, topic, subscription) + ServiceBusProcessor processor; + if (ServiceBusEntityPathHelper.ParseEntityType(entityPath) == EntityType.Topic) { - PrefetchCount = _options.PrefetchCount - }); - } - else + // entityPath for a subscription is "{TopicName}/Subscriptions/{SubscriptionName}" + ServiceBusEntityPathHelper.ParseTopicAndSubscription(entityPath, out string topic, out string subscription); + processor = client.CreateProcessor(topic, subscription, _options.ToProcessorOptions()); + } + else + { + // entityPath for a queue is "{QueueName}" + processor = client.CreateProcessor(entityPath, _options.ToProcessorOptions()); + } + processor.ProcessErrorAsync += _options.ExceptionReceivedHandler; + return processor; + }); + } + + private ServiceBusSessionProcessor GetOrAddSessionProcessor(string entityPath, string connectionString) + { + ServiceBusClient client = _clientCache.GetOrAdd(connectionString, (_) => CreateClient(connectionString)); + return _sessionProcessorCache.GetOrAdd(entityPath, (_) => { - // entityPath for a queue is "{QueueName}" - return _clientEntityCache.GetOrAdd(cacheKey, new QueueClient(connectionString, entityPath) + ServiceBusSessionProcessor processor; + if (ServiceBusEntityPathHelper.ParseEntityType(entityPath) == EntityType.Topic) { - PrefetchCount = _options.PrefetchCount - }); - } + // entityPath for a subscription is "{TopicName}/Subscriptions/{SubscriptionName}" + ServiceBusEntityPathHelper.ParseTopicAndSubscription(entityPath, out string topic, out string subscription); + processor = client.CreateSessionProcessor(topic, subscription, _options.ToSessionProcessorOptions()); + } + else + { + // entityPath for a queue is "{QueueName}" + processor = client.CreateSessionProcessor(entityPath, _options.ToSessionProcessorOptions()); + } + processor.ProcessErrorAsync += _options.ExceptionReceivedHandler; + return processor; + }); } - private SessionClient GetOrAddSessionClient(string entityPath, string connectionString) + internal ServiceBusClient CreateSessionClient(string connectionString) { - string cacheKey = $"{entityPath}-{connectionString}"; - return _sessionClientCache.GetOrAdd(cacheKey, new SessionClient(connectionString, entityPath)); + return _clientCache.GetOrAdd(connectionString, (_) => CreateClient(connectionString)); } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Microsoft.Azure.WebJobs.Extensions.ServiceBus.csproj b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Microsoft.Azure.WebJobs.Extensions.ServiceBus.csproj index aa2e7cfa4337..8fa122316aae 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Microsoft.Azure.WebJobs.Extensions.ServiceBus.csproj +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Microsoft.Azure.WebJobs.Extensions.ServiceBus.csproj @@ -9,7 +9,6 @@ - @@ -17,6 +16,16 @@ - + + + + + + + + + + + diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusAttribute.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusAttribute.cs index 29660d254593..aa56db8a12ef 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusAttribute.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusAttribute.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using Microsoft.Azure.WebJobs.Description; using Microsoft.Azure.WebJobs.ServiceBus; -using Microsoft.Azure.ServiceBus; namespace Microsoft.Azure.WebJobs { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusTriggerAttribute.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusTriggerAttribute.cs index 0e0d0a00f347..dcabf005c996 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusTriggerAttribute.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/ServiceBusTriggerAttribute.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using Microsoft.Azure.WebJobs.Description; -using Microsoft.Azure.ServiceBus; namespace Microsoft.Azure.WebJobs { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/SessionMessageProcessor.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/SessionMessageProcessor.cs index 03f53e075582..49ac1e970c8c 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/SessionMessageProcessor.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/SessionMessageProcessor.cs @@ -1,11 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Executors; using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -13,31 +11,25 @@ namespace Microsoft.Azure.WebJobs.ServiceBus { public class SessionMessageProcessor { - public SessionMessageProcessor(ClientEntity clientEntity, SessionHandlerOptions sessionHandlerOptions) + public SessionMessageProcessor(ServiceBusSessionProcessor processor) { - ClientEntity = clientEntity ?? throw new ArgumentNullException(nameof(clientEntity)); - SessionHandlerOptions = sessionHandlerOptions ?? throw new ArgumentNullException(nameof(sessionHandlerOptions)); + Processor = processor ?? throw new ArgumentNullException(nameof(processor)); } /// - /// Gets the that will be used by the . + /// Gets or sets the that will be used by the . /// - public SessionHandlerOptions SessionHandlerOptions { get; } - - /// - /// Gets or sets the that will be used by the . - /// - protected ClientEntity ClientEntity { get; set; } + internal ServiceBusSessionProcessor Processor { get; set; } /// /// This method is called when there is a new message to process, before the job function is invoked. /// This allows any preprocessing to take place on the message before processing begins. /// - /// The session associated with the message. - /// The message to process. + /// + /// /// The to use. /// A that returns true if the message processing should continue, false otherwise. - public virtual Task BeginProcessingMessageAsync(IMessageSession session, Message message, CancellationToken cancellationToken) + public virtual Task BeginProcessingMessageAsync(ServiceBusSessionReceiver receiver, ServiceBusReceivedMessage message, CancellationToken cancellationToken) { return Task.FromResult(true); } @@ -46,16 +38,16 @@ public virtual Task BeginProcessingMessageAsync(IMessageSession session, M /// This method completes processing of the specified message, after the job function has been invoked. /// /// - /// The message is completed by the ServiceBus SDK based on how the option - /// is configured. E.g. if is false, it is up to the job function to complete + /// The message is completed by the ServiceBus SDK based on how the option + /// is configured. E.g. if is false, it is up to the job function to complete /// the message. /// - /// The session associated with the message. - /// The message to complete processing for. + /// + /// /// The from the job invocation. /// The to use /// A that will complete the message processing. - public virtual Task CompleteProcessingMessageAsync(IMessageSession session, Message message, FunctionResult result, CancellationToken cancellationToken) + public virtual Task CompleteProcessingMessageAsync(ServiceBusSessionReceiver receiver, ServiceBusReceivedMessage message, FunctionResult result, CancellationToken cancellationToken) { if (result == null) { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/DataContractBinarySerializer.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/DataContractBinarySerializer.cs new file mode 100644 index 000000000000..4a44970172cc --- /dev/null +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/DataContractBinarySerializer.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Runtime.Serialization; +using System.Xml; + +namespace Microsoft.Azure.WebJobs.Extensions.ServiceBus.Triggers +{ + /// + /// This class describes a serializer class used to serialize and deserialize an Object. + /// This class is almost identical to DataContractSerializer; only difference is that + /// ReadObject(Stream) and WriteObject(Stream, object) pick Binary Xml Reader/Writer + /// instead of text. + /// + internal sealed class DataContractBinarySerializer : XmlObjectSerializer + { + private readonly DataContractSerializer _dataContractSerializer; + + /// + /// Initializes a new DataContractBinarySerializer instance. + /// + public DataContractBinarySerializer(Type type) + { + _dataContractSerializer = new DataContractSerializer(type); + } + + /// + /// Converts from stream to the corresponding object. + /// + /// Object corresponding to the stream + /// Override the default (Text) and use Binary Xml Reader instead + public override object ReadObject(Stream stream) + { + return ReadObject(XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max)); + } + + /// + /// Serializes the object into the stream. + /// + /// Override the default (Text) and use Binary Xml Reader instead + public override void WriteObject(Stream stream, object graph) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + var xmlDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(stream, null, null, false); + WriteObject(xmlDictionaryWriter, graph); + xmlDictionaryWriter.Flush(); + } + + /// + /// Serializes the object into the stream using the XmlDictionaryWriter. + /// + public override void WriteObject(XmlDictionaryWriter writer, object graph) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + _dataContractSerializer.WriteObject(writer, graph); + } + + /// + /// This method simply delegates to the DataContractSerializer implementation. + /// + public override bool IsStartObject(XmlDictionaryReader reader) + { + return _dataContractSerializer.IsStartObject(reader); + } + + /// + /// This method simply delegates to the DataContractSerializer implementation. + /// + public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) + { + return _dataContractSerializer.ReadObject(reader, verifyObjectName); + } + + /// + /// This method simply delegates to the DataContractSerializer implementation. + /// + public override void WriteEndObject(XmlDictionaryWriter writer) + { + _dataContractSerializer.WriteEndObject(writer); + } + + /// + /// This method simply delegates to the DataContractSerializer implementation. + /// + public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) + { + _dataContractSerializer.WriteObjectContent(writer, graph); + } + + /// + /// This method simply delegates to the DataContractSerializer implementation. + /// + public override void WriteStartObject(XmlDictionaryWriter writer, object graph) + { + _dataContractSerializer.WriteStartObject(writer, graph); + } + } + + /// + /// Returns a static instance of type T. + /// +#pragma warning disable SA1402 // File may only contain a single type + internal static class DataContractBinarySerializer +#pragma warning restore SA1402 // File may only contain a single type + { + /// + /// Initializes a DataContractBinarySerializer instance of type T. + /// + public static readonly XmlObjectSerializer Instance = new DataContractBinarySerializer(typeof(T)); + } +} diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToByteArrayConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToByteArrayConverter.cs index e8ca4777175e..071710744d82 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToByteArrayConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToByteArrayConverter.cs @@ -7,13 +7,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers { - internal class MessageToByteArrayConverter : IAsyncConverter + internal class MessageToByteArrayConverter : IAsyncConverter { - public Task ConvertAsync(Message input, CancellationToken cancellationToken) + public Task ConvertAsync(ServiceBusReceivedMessage input, CancellationToken cancellationToken) { if (input == null) { @@ -22,7 +22,7 @@ public Task ConvertAsync(Message input, CancellationToken cancellationTo cancellationToken.ThrowIfCancellationRequested(); - return Task.FromResult(input.Body); + return Task.FromResult(input.Body.ToArray()); } } } \ No newline at end of file diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToPocoConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToPocoConverter.cs index 48005ed64b21..30f1c83e7746 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToPocoConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToPocoConverter.cs @@ -5,21 +5,21 @@ using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.InteropExtensions; using Newtonsoft.Json; using System.Globalization; +using Azure.Messaging.ServiceBus; +using Microsoft.Azure.WebJobs.Extensions.ServiceBus.Triggers; namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers { // Convert from Message --> T - internal class MessageToPocoConverter : IConverter + internal class MessageToPocoConverter : IConverter { public MessageToPocoConverter() { } - public TElement Convert(Message message) + public TElement Convert(ServiceBusReceivedMessage message) { // 1. If ContentType is "application/json" deserialize as JSON // 2. If ContentType is not "application/json" attempt to deserialize using Message.GetBody, which will handle cases like XML object serialization @@ -33,7 +33,8 @@ public TElement Convert(Message message) { try { - return message.GetBody(); + var serializer = DataContractBinarySerializer.Instance; + return (TElement)serializer.ReadObject(message.Body.ToStream()); } catch (SerializationException) { @@ -42,9 +43,9 @@ public TElement Convert(Message message) } } - private static TElement DeserializeJsonObject(Message message) + private static TElement DeserializeJsonObject(ServiceBusReceivedMessage message) { - string contents = StrictEncodings.Utf8.GetString(message.Body); + string contents = StrictEncodings.Utf8.GetString(message.Body.ToArray()); try { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToStringConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToStringConverter.cs index d216925f8519..8bc9e20f7d4a 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToStringConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/MessageToStringConverter.cs @@ -7,14 +7,14 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.InteropExtensions; +using Azure.Messaging.ServiceBus; +using Microsoft.Azure.WebJobs.Extensions.ServiceBus.Triggers; namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers { - internal class MessageToStringConverter : IAsyncConverter + internal class MessageToStringConverter : IAsyncConverter { - public async Task ConvertAsync(Message input, CancellationToken cancellationToken) + public async Task ConvertAsync(ServiceBusReceivedMessage input, CancellationToken cancellationToken) { if (input == null) { @@ -24,7 +24,8 @@ public async Task ConvertAsync(Message input, CancellationToken cancella { return null; } - Stream stream = new MemoryStream(input.Body); + + Stream stream = input.Body.ToStream(); TextReader reader = new StreamReader(stream, StrictEncodings.Utf8); try @@ -45,12 +46,14 @@ public async Task ConvertAsync(Message input, CancellationToken cancella try { - return input.GetBody(); + var serializer = DataContractBinarySerializer.Instance; + stream.Position = 0; + return (string)serializer.ReadObject(stream); } catch { // always possible to get a valid string from the message - return Encoding.UTF8.GetString(input.Body, 0, input.Body.Length); + return input.Body.ToString(); } } finally diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/OutputConverter.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/OutputConverter.cs index 771806d38ce5..17ec9a024360 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/OutputConverter.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/OutputConverter.cs @@ -2,21 +2,21 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Azure.WebJobs.Host.Converters; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers { - internal class OutputConverter : IObjectToTypeConverter + internal class OutputConverter : IObjectToTypeConverter where TInput : class { - private readonly IConverter _innerConverter; + private readonly IConverter _innerConverter; - public OutputConverter(IConverter innerConverter) + public OutputConverter(IConverter innerConverter) { _innerConverter = innerConverter; } - public bool TryConvert(object input, out Message output) + public bool TryConvert(object input, out ServiceBusMessage output) { TInput typedInput = input as TInput; diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerAttributeBindingProvider.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerAttributeBindingProvider.cs index 274bae2e28a6..b40dd7d9773e 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerAttributeBindingProvider.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerAttributeBindingProvider.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.Reflection; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; using Microsoft.Azure.WebJobs.Host; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Listeners; @@ -15,6 +14,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Azure.WebJobs.ServiceBus.Listeners; using Microsoft.Azure.WebJobs.Host.Config; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers { @@ -69,7 +69,7 @@ public Task TryCreateAsync(TriggerBindingProviderContext contex { topicName = Resolve(attribute.TopicName); subscriptionName = Resolve(attribute.SubscriptionName); - entityPath = EntityNameHelper.FormatSubscriptionPath(topicName, subscriptionName); + entityPath = EntityNameFormatter.FormatSubscriptionPath(topicName, subscriptionName); entityType = EntityType.Topic; } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerBindingStrategy.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerBindingStrategy.cs index d5cb7abaa8ee..0c331e103e7d 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerBindingStrategy.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerBindingStrategy.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.Host.Triggers; @@ -13,20 +13,19 @@ namespace Microsoft.Azure.WebJobs.ServiceBus { // Binding strategy for a service bus triggers. #pragma warning disable 618 - internal class ServiceBusTriggerBindingStrategy : ITriggerBindingStrategy + internal class ServiceBusTriggerBindingStrategy : ITriggerBindingStrategy #pragma warning restore 618 { public ServiceBusTriggerInput ConvertFromString(string input) { - byte[] bytes = Encoding.UTF8.GetBytes(input); - Message message = new Message(bytes); + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage(new BinaryData(input)); // Return a single message. Doesn't support multiple dispatch return ServiceBusTriggerInput.CreateSingle(message); } // Single instance: Core --> Message - public Message BindSingle(ServiceBusTriggerInput value, ValueBindingContext context) + public ServiceBusReceivedMessage BindSingle(ServiceBusTriggerInput value, ValueBindingContext context) { if (value == null) { @@ -36,7 +35,7 @@ public Message BindSingle(ServiceBusTriggerInput value, ValueBindingContext cont return value.Messages[0]; } - public Message[] BindMultiple(ServiceBusTriggerInput value, ValueBindingContext context) + public ServiceBusReceivedMessage[] BindMultiple(ServiceBusTriggerInput value, ValueBindingContext context) { if (value == null) { @@ -54,8 +53,8 @@ public Dictionary GetBindingData(ServiceBusTriggerInput value) } var bindingData = new Dictionary(StringComparer.OrdinalIgnoreCase); - SafeAddValue(() => bindingData.Add(nameof(value.MessageReceiver), value.MessageReceiver as MessageReceiver)); - SafeAddValue(() => bindingData.Add("MessageSession", value.MessageReceiver as IMessageSession)); + SafeAddValue(() => bindingData.Add("MessageReceiver", value.Receiver)); + SafeAddValue(() => bindingData.Add("MessageSession", value.SessionReceiver)); if (value.IsSingleDispatch) { @@ -84,14 +83,14 @@ public Dictionary GetBindingContract(bool isSingleDispatch = true) AddBindingContractMember(contract, "To", typeof(string), isSingleDispatch); AddBindingContractMember(contract, "Label", typeof(string), isSingleDispatch); AddBindingContractMember(contract, "CorrelationId", typeof(string), isSingleDispatch); - AddBindingContractMember(contract, "UserProperties", typeof(IDictionary), isSingleDispatch); - contract.Add("MessageReceiver", typeof(MessageReceiver)); - contract.Add("MessageSession", typeof(IMessageSession)); + AddBindingContractMember(contract, "ApplicationProperties", typeof(IDictionary), isSingleDispatch); + contract.Add("MessageReceiver", typeof(ServiceBusReceiver)); + contract.Add("MessageSession", typeof(ServiceBusSessionReceiver)); return contract; } - internal static void AddBindingData(Dictionary bindingData, Message[] messages) + internal static void AddBindingData(Dictionary bindingData, ServiceBusReceivedMessage[] messages) { int length = messages.Length; var deliveryCounts = new int[length]; @@ -104,9 +103,9 @@ internal static void AddBindingData(Dictionary bindingData, Mess var replyTos = new string[length]; var sequenceNumbers = new long[length]; var tos = new string[length]; - var labels = new string[length]; + var subjects = new string[length]; var correlationIds = new string[length]; - var userProperties = new IDictionary[length]; + var applicationProperties = new IDictionary[length]; SafeAddValue(() => bindingData.Add("DeliveryCountArray", deliveryCounts)); SafeAddValue(() => bindingData.Add("DeadLetterSourceArray", deadLetterSources)); @@ -118,45 +117,46 @@ internal static void AddBindingData(Dictionary bindingData, Mess SafeAddValue(() => bindingData.Add("ReplyToArray", replyTos)); SafeAddValue(() => bindingData.Add("SequenceNumberArray", sequenceNumbers)); SafeAddValue(() => bindingData.Add("ToArray", tos)); - SafeAddValue(() => bindingData.Add("LabelArray", labels)); + SafeAddValue(() => bindingData.Add("SubjectArray", subjects)); SafeAddValue(() => bindingData.Add("CorrelationIdArray", correlationIds)); - SafeAddValue(() => bindingData.Add("UserPropertiesArray", userProperties)); + SafeAddValue(() => bindingData.Add("ApplicationPropertiesArray", applicationProperties)); for (int i = 0; i < messages.Length; i++) { - deliveryCounts[i] = messages[i].SystemProperties.DeliveryCount; - deadLetterSources[i] = messages[i].SystemProperties.DeadLetterSource; - lockTokens[i] = messages[i].SystemProperties.IsLockTokenSet ? messages[i].SystemProperties.LockToken : string.Empty; + deliveryCounts[i] = messages[i].DeliveryCount; + deadLetterSources[i] = messages[i].DeadLetterSource; + lockTokens[i] = messages[i].LockToken; //this is temporary until the Service Bus SDK addresses the missing timezone issue in case DateTime.MaxValue, github.com/Azure/azure-sdk-for-net/issues/15343 - expiresAtUtcs[i] = messages[i].ExpiresAtUtc.ToUniversalTime(); - enqueuedTimeUtcs[i] = messages[i].SystemProperties.EnqueuedTimeUtc; + expiresAtUtcs[i] = messages[i].ExpiresAt.DateTime.ToUniversalTime(); + enqueuedTimeUtcs[i] = messages[i].EnqueuedTime.DateTime; messageIds[i] = messages[i].MessageId; contentTypes[i] = messages[i].ContentType; replyTos[i] = messages[i].ReplyTo; - sequenceNumbers[i] = messages[i].SystemProperties.SequenceNumber; + sequenceNumbers[i] = messages[i].SequenceNumber; tos[i] = messages[i].To; - labels[i] = messages[i].Label; + subjects[i] = messages[i].Subject; correlationIds[i] = messages[i].CorrelationId; - userProperties[i] = messages[i].UserProperties; + applicationProperties[i] = messages[i].ApplicationProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } } - private static void AddBindingData(Dictionary bindingData, Message value) + //TODO add tests for all binding parameters + private static void AddBindingData(Dictionary bindingData, ServiceBusReceivedMessage value) { - SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.DeliveryCount), value.SystemProperties.DeliveryCount)); - SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.DeadLetterSource), value.SystemProperties.DeadLetterSource)); - SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.LockToken), value.SystemProperties.IsLockTokenSet ? value.SystemProperties.LockToken : string.Empty)); + SafeAddValue(() => bindingData.Add(nameof(value.DeliveryCount), value.DeliveryCount)); + SafeAddValue(() => bindingData.Add(nameof(value.DeadLetterSource), value.DeadLetterSource)); + SafeAddValue(() => bindingData.Add(nameof(value.LockToken), value.LockToken)); //this is temporary until the Service Bus SDK addresses the missing timezone issue in case DateTime.MaxValue, github.com/Azure/azure-sdk-for-net/issues/15343 - SafeAddValue(() => bindingData.Add(nameof(value.ExpiresAtUtc), value.ExpiresAtUtc.ToUniversalTime())); - SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.EnqueuedTimeUtc), value.SystemProperties.EnqueuedTimeUtc)); + SafeAddValue(() => bindingData.Add("ExpiresAtUtc", value.ExpiresAt.ToUniversalTime())); + SafeAddValue(() => bindingData.Add("EnqueuedTimeUtc", value.EnqueuedTime)); SafeAddValue(() => bindingData.Add(nameof(value.MessageId), value.MessageId)); SafeAddValue(() => bindingData.Add(nameof(value.ContentType), value.ContentType)); SafeAddValue(() => bindingData.Add(nameof(value.ReplyTo), value.ReplyTo)); - SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.SequenceNumber), value.SystemProperties.SequenceNumber)); + SafeAddValue(() => bindingData.Add(nameof(value.SequenceNumber), value.SequenceNumber)); SafeAddValue(() => bindingData.Add(nameof(value.To), value.To)); - SafeAddValue(() => bindingData.Add(nameof(value.Label), value.Label)); + SafeAddValue(() => bindingData.Add("Label", value.Subject)); SafeAddValue(() => bindingData.Add(nameof(value.CorrelationId), value.CorrelationId)); - SafeAddValue(() => bindingData.Add(nameof(value.UserProperties), value.UserProperties)); + SafeAddValue(() => bindingData.Add(nameof(value.ApplicationProperties), value.ApplicationProperties)); } private static void SafeAddValue(Action addValue) diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerInput.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerInput.cs index 10b705aaef6d..0832b9cff8b6 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerInput.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/src/Triggers/ServiceBusTriggerInput.cs @@ -3,11 +3,10 @@ using System; using System.Collections.Generic; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; using Microsoft.Azure.WebJobs.Host.Executors; using Microsoft.Azure.WebJobs.ServiceBus.Listeners; using System.Globalization; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus { @@ -19,15 +18,17 @@ internal sealed class ServiceBusTriggerInput private ServiceBusTriggerInput() { } - public IMessageReceiver MessageReceiver; + public ServiceBusReceiver Receiver { get; set; } - public Message[] Messages { get; set; } + public ServiceBusSessionReceiver SessionReceiver { get; set; } - public static ServiceBusTriggerInput CreateSingle(Message message) + public ServiceBusReceivedMessage[] Messages { get; set; } + + public static ServiceBusTriggerInput CreateSingle(ServiceBusReceivedMessage message) { return new ServiceBusTriggerInput { - Messages = new Message[] + Messages = new ServiceBusReceivedMessage[] { message }, @@ -35,7 +36,7 @@ public static ServiceBusTriggerInput CreateSingle(Message message) }; } - public static ServiceBusTriggerInput CreateBatch(Message[] messages) + public static ServiceBusTriggerInput CreateBatch(ServiceBusReceivedMessage[] messages) { return new ServiceBusTriggerInput { @@ -56,7 +57,7 @@ public TriggeredFunctionData GetTriggerFunctionData() { if (Messages.Length > 0) { - Message message = Messages[0]; + ServiceBusReceivedMessage message = Messages[0]; if (IsSingleDispatch) { Guid? parentId = ServiceBusCausalityHelper.GetOwner(message); @@ -67,10 +68,10 @@ public TriggeredFunctionData GetTriggerFunctionData() TriggerDetails = new Dictionary() { { "MessageId", message.MessageId }, - { "SequenceNumber", message.SystemProperties.SequenceNumber.ToString(CultureInfo.InvariantCulture) }, - { "DeliveryCount", message.SystemProperties.DeliveryCount.ToString(CultureInfo.InvariantCulture) }, - { "EnqueuedTimeUtc", message.SystemProperties.EnqueuedTimeUtc.ToUniversalTime().ToString("o") }, - { "LockedUntilUtc", message.SystemProperties.LockedUntilUtc.ToUniversalTime().ToString("o") }, + { "SequenceNumber", message.SequenceNumber.ToString(CultureInfo.InvariantCulture) }, + { "DeliveryCount", message.DeliveryCount.ToString(CultureInfo.InvariantCulture) }, + { "EnqueuedTimeUtc", message.EnqueuedTime.ToUniversalTime().ToString("o") }, + { "LockedUntilUtc", message.LockedUntil.ToUniversalTime().ToString("o") }, { "SessionId", message.SessionId } } }; @@ -90,10 +91,10 @@ public TriggeredFunctionData GetTriggerFunctionData() for (int i = 0; i < Messages.Length; i++) { messageIds[i] = Messages[i].MessageId; - sequenceNumbers[i] = Messages[i].SystemProperties.SequenceNumber; - deliveryCounts[i] = Messages[i].SystemProperties.DeliveryCount; - enqueuedTimes[i] = Messages[i].SystemProperties.EnqueuedTimeUtc.ToUniversalTime().ToString("o"); - lockedUntils[i] = Messages[i].SystemProperties.LockedUntilUtc.ToUniversalTime().ToString("o"); + sequenceNumbers[i] = Messages[i].SequenceNumber; + deliveryCounts[i] = Messages[i].DeliveryCount; + enqueuedTimes[i] = Messages[i].EnqueuedTime.ToUniversalTime().ToString("o"); + lockedUntils[i] = Messages[i].LockedUntil.ToUniversalTime().ToString("o"); } return new TriggeredFunctionData() diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusAttributeBindingProviderTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusAttributeBindingProviderTests.cs index c4e26932305d..c08d53ef42b0 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusAttributeBindingProviderTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusAttributeBindingProviderTests.cs @@ -6,7 +6,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Bindings; using Microsoft.Azure.WebJobs.ServiceBus.Bindings; using Microsoft.Extensions.Configuration; @@ -55,15 +55,15 @@ public async Task TryCreateAsync_DefaultAccount() internal static void TestJob_AccountOverride( [ServiceBusAttribute("test"), - ServiceBusAccount(Constants.DefaultConnectionStringName)] out Message message) + ServiceBusAccount(Constants.DefaultConnectionStringName)] out ServiceBusMessage message) { - message = new Message(); + message = new ServiceBusMessage(); } internal static void TestJob( - [ServiceBusAttribute("test", Connection = Constants.DefaultConnectionStringName)] out Message message) + [ServiceBusAttribute("test", Connection = Constants.DefaultConnectionStringName)] out ServiceBusMessage message) { - message = new Message(); + message = new ServiceBusMessage(); } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusTriggerAttributeBindingProviderTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusTriggerAttributeBindingProviderTests.cs index e5726d842f81..a4243e4c54c5 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusTriggerAttributeBindingProviderTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Bindings/ServiceBusTriggerAttributeBindingProviderTests.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; using Microsoft.Azure.WebJobs.Host.Triggers; using Microsoft.Azure.WebJobs.ServiceBus.Triggers; using Microsoft.Extensions.Configuration; @@ -12,6 +11,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; +using Azure.Messaging.ServiceBus; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings { @@ -60,15 +60,15 @@ public async Task TryCreateAsync_DefaultAccount() internal static void TestJob_AccountOverride( [ServiceBusTriggerAttribute("test"), - ServiceBusAccount(Constants.DefaultConnectionStringName)] Message message) + ServiceBusAccount(Constants.DefaultConnectionStringName)] ServiceBusMessage message) { - message = new Message(); + message = new ServiceBusMessage(); } internal static void TestJob( - [ServiceBusTriggerAttribute("test", Connection = Constants.DefaultConnectionStringName)] Message message) + [ServiceBusTriggerAttribute("test", Connection = Constants.DefaultConnectionStringName)] ServiceBusMessage message) { - message = new Message(); + message = new ServiceBusMessage(); } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusHostBuilderExtensionsTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusHostBuilderExtensionsTests.cs index 1839259b80a7..c6db9a1aca05 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusHostBuilderExtensionsTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusHostBuilderExtensionsTests.cs @@ -13,14 +13,92 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; using NUnit.Framework; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Config { public class ServiceBusHostBuilderExtensionsTests { + [Test] + public void ConfigureOptions_AppliesValuesCorrectly_BackCompat() + { + ServiceBusOptions options = CreateOptionsFromConfigBackCompat(); + + Assert.AreEqual(123, options.PrefetchCount); + Assert.AreEqual("TestConnectionString", options.ConnectionString); + Assert.AreEqual(123, options.MaxConcurrentCalls); + Assert.False(options.AutoCompleteMessages); + Assert.AreEqual(TimeSpan.FromSeconds(15), options.MaxAutoLockRenewalDuration); + } + + [Test] + public void ConfigureOptions_Format_Returns_Expected_BackCompat() + { + ServiceBusOptions options = CreateOptionsFromConfigBackCompat(); + + string format = options.Format(); + JObject iObj = JObject.Parse(format); + ServiceBusOptions result = iObj.ToObject(); + + Assert.AreEqual(123, result.PrefetchCount); + // can't round trip the connection string + Assert.IsNull(result.ConnectionString); + Assert.AreEqual(123, result.MaxConcurrentCalls); + Assert.False(result.AutoCompleteMessages); + Assert.AreEqual(TimeSpan.FromSeconds(15), result.MaxAutoLockRenewalDuration); + } + [Test] public void ConfigureOptions_AppliesValuesCorrectly() + { + ServiceBusOptions options = CreateOptionsFromConfig(); + + Assert.AreEqual(123, options.PrefetchCount); + Assert.AreEqual("TestConnectionString", options.ConnectionString); + Assert.AreEqual(123, options.MaxConcurrentCalls); + Assert.False(options.AutoCompleteMessages); + Assert.AreEqual(TimeSpan.FromSeconds(15), options.MaxAutoLockRenewalDuration); + } + + [Test] + public void ConfigureOptions_Format_Returns_Expected() + { + ServiceBusOptions options = CreateOptionsFromConfig(); + + string format = options.Format(); + JObject iObj = JObject.Parse(format); + ServiceBusOptions result = iObj.ToObject(); + + Assert.AreEqual(123, result.PrefetchCount); + // can't round trip the connection string + Assert.IsNull(result.ConnectionString); + Assert.AreEqual(123, result.MaxConcurrentCalls); + Assert.False(result.AutoCompleteMessages); + Assert.AreEqual(TimeSpan.FromSeconds(15), result.MaxAutoLockRenewalDuration); + } + + private static ServiceBusOptions CreateOptionsFromConfig() + { + string extensionPath = "AzureWebJobs:Extensions:ServiceBus"; + var values = new Dictionary + { + { $"{extensionPath}:PrefetchCount", "123" }, + { $"ConnectionStrings:ServiceBus", "TestConnectionString" }, + { $"{extensionPath}:MaxConcurrentCalls", "123" }, + { $"{extensionPath}:AutoCompleteMessages", "false" }, + { $"{extensionPath}:MaxAutoLockRenewalDuration", "00:00:15" }, + { $"{extensionPath}:MaxConcurrentSessions", "123" }, + }; + + ServiceBusOptions options = TestHelpers.GetConfiguredOptions(b => + { + b.AddServiceBus(); + }, values); + return options; + } + + private static ServiceBusOptions CreateOptionsFromConfigBackCompat() { string extensionPath = "AzureWebJobs:Extensions:ServiceBus"; var values = new Dictionary @@ -29,19 +107,18 @@ public void ConfigureOptions_AppliesValuesCorrectly() { $"ConnectionStrings:ServiceBus", "TestConnectionString" }, { $"{extensionPath}:MessageHandlerOptions:MaxConcurrentCalls", "123" }, { $"{extensionPath}:MessageHandlerOptions:AutoComplete", "false" }, - { $"{extensionPath}:MessageHandlerOptions:MaxAutoRenewDuration", "00:00:15" } + { $"{extensionPath}:MessageHandlerOptions:MaxAutoRenewDuration", "00:00:15" }, + { $"{extensionPath}:SessionHandlerOptions:MaxConcurrentSessions", "123" }, + { $"{extensionPath}:BatchOptions:OperationTimeout","00:00:15" }, + { $"{extensionPath}:BatchOptions:MaxMessageCount", "123" }, + { $"{extensionPath}:BatchOptions:AutoComplete", "true" }, }; ServiceBusOptions options = TestHelpers.GetConfiguredOptions(b => { b.AddServiceBus(); }, values); - - Assert.AreEqual(123, options.PrefetchCount); - Assert.AreEqual("TestConnectionString", options.ConnectionString); - Assert.AreEqual(123, options.MessageHandlerOptions.MaxConcurrentCalls); - Assert.False(options.MessageHandlerOptions.AutoComplete); - Assert.AreEqual(TimeSpan.FromSeconds(15), options.MessageHandlerOptions.MaxAutoRenewDuration); + return options; } [Test] @@ -111,7 +188,7 @@ public void AddServiceBus_ServiceBusOptionsProvided_PerformsExpectedRegistration [TestCase("DefaultConnectionString", null, "DefaultConnectionString")] [TestCase(null, "DefaultConectionSettingString", "DefaultConectionSettingString")] [TestCase(null, null, null)] - public void ReadDeafultConnectionString(string defaultConnectionString, string sefaultConectionSettingString, string expectedValue) + public void ReadDefaultConnectionString(string defaultConnectionString, string sefaultConectionSettingString, string expectedValue) { ServiceBusOptions options = TestHelpers.GetConfiguredOptions(b => { diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusOptionsTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusOptionsTests.cs index 5f047c8b1eaa..597d79608a84 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusOptionsTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Config/ServiceBusOptionsTests.cs @@ -3,7 +3,8 @@ using System; using System.Linq; -using Microsoft.Azure.ServiceBus; +using System.Threading; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.TestCommon; using Microsoft.Azure.WebJobs.ServiceBus.Config; using Microsoft.Extensions.Logging; @@ -29,7 +30,7 @@ public void Setup() public void Constructor_SetsExpectedDefaults() { ServiceBusOptions config = new ServiceBusOptions(); - Assert.AreEqual(16 * Utility.GetProcessorCount(), config.MessageHandlerOptions.MaxConcurrentCalls); + Assert.AreEqual(16 * Utility.GetProcessorCount(), config.MaxConcurrentCalls); Assert.AreEqual(0, config.PrefetchCount); } @@ -45,12 +46,12 @@ public void PrefetchCount_GetSet() [Test] public void LogExceptionReceivedEvent_NonTransientEvent_LoggedAsError() { - var ex = new ServiceBusException(false); + var ex = new ServiceBusException(isTransient: false, message: "message"); Assert.False(ex.IsTransient); - ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient"); + ProcessErrorEventArgs e = new ProcessErrorEventArgs(ex, ServiceBusErrorSource.Abandon, "TestEndpoint", "TestEntity", CancellationToken.None); ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory); - var expectedMessage = $"Message processing error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)"; + var expectedMessage = $"Message processing error (Action=Abandon, EntityPath=TestEntity, Endpoint=TestEndpoint)"; var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual(LogLevel.Error, logMessage.Level); Assert.AreSame(ex, logMessage.Exception); @@ -60,12 +61,12 @@ public void LogExceptionReceivedEvent_NonTransientEvent_LoggedAsError() [Test] public void LogExceptionReceivedEvent_TransientEvent_LoggedAsInformation() { - var ex = new ServiceBusException(true); + var ex = new ServiceBusException(message: "message", isTransient: true); Assert.True(ex.IsTransient); - ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient"); + ProcessErrorEventArgs e = new ProcessErrorEventArgs(ex, ServiceBusErrorSource.Receive, "TestEndpoint", "TestEntity", CancellationToken.None); ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory); - var expectedMessage = $"Message processing error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)"; + var expectedMessage = $"Message processing error (Action=Receive, EntityPath=TestEntity, Endpoint=TestEndpoint)"; var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual(LogLevel.Information, logMessage.Level); Assert.AreSame(ex, logMessage.Exception); @@ -76,10 +77,10 @@ public void LogExceptionReceivedEvent_TransientEvent_LoggedAsInformation() public void LogExceptionReceivedEvent_NonMessagingException_LoggedAsError() { var ex = new MissingMethodException("What method??"); - ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient"); + ProcessErrorEventArgs e = new ProcessErrorEventArgs(ex, ServiceBusErrorSource.Complete, "TestEndpoint", "TestEntity", CancellationToken.None); ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory); - var expectedMessage = $"Message processing error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)"; + var expectedMessage = $"Message processing error (Action=Complete, EntityPath=TestEntity, Endpoint=TestEndpoint)"; var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual(LogLevel.Error, logMessage.Level); Assert.AreSame(ex, logMessage.Exception); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusListenerTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusListenerTests.cs index 5170fe2cfdf3..93651711e7f0 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusListenerTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusListenerTests.cs @@ -5,8 +5,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Executors; using Microsoft.Azure.WebJobs.Host.Scale; using Microsoft.Azure.WebJobs.Host.TestCommon; @@ -15,7 +14,6 @@ using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; -using static Microsoft.Azure.ServiceBus.Message; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Listeners { @@ -35,14 +33,16 @@ public ServiceBusListenerTests() { _mockExecutor = new Mock(MockBehavior.Strict); - MessageHandlerOptions messageOptions = new MessageHandlerOptions(ExceptionReceivedHandler); - MessageReceiver messageReceiver = new MessageReceiver(_testConnection, _entityPath); - _mockMessageProcessor = new Mock(MockBehavior.Strict, messageReceiver, messageOptions); + var client = new ServiceBusClient(_testConnection); + ServiceBusProcessor processor = client.CreateProcessor(_entityPath); + ServiceBusReceiver receiver = client.CreateReceiver(_entityPath); ServiceBusOptions config = new ServiceBusOptions { - MessageHandlerOptions = messageOptions + ExceptionHandler = ExceptionReceivedHandler }; + _mockMessageProcessor = new Mock(MockBehavior.Strict, processor); + _mockMessagingProvider = new Mock(MockBehavior.Strict, new OptionsWrapper(config)); _mockMessagingProvider @@ -50,8 +50,8 @@ public ServiceBusListenerTests() .Returns(_mockMessageProcessor.Object); _mockMessagingProvider - .Setup(p => p.CreateMessageReceiver(_entityPath, _testConnection)) - .Returns(messageReceiver); + .Setup(p => p.CreateBatchMessageReceiver(_entityPath, _testConnection)) + .Returns(receiver); Mock mockServiceBusAccount = new Mock(MockBehavior.Strict); mockServiceBusAccount.Setup(a => a.ConnectionString).Returns(_testConnection); @@ -67,23 +67,22 @@ public ServiceBusListenerTests() [Test] public async Task ProcessMessageAsync_Success() { - var message = new CustomMessage(); - var systemProperties = new Message.SystemPropertiesCollection(); - typeof(Message.SystemPropertiesCollection).GetProperty("SequenceNumber").SetValue(systemProperties, 1); - typeof(Message.SystemPropertiesCollection).GetProperty("DeliveryCount").SetValue(systemProperties, 55); - typeof(Message.SystemPropertiesCollection).GetProperty("EnqueuedTimeUtc").SetValue(systemProperties, DateTime.Now); - typeof(Message.SystemPropertiesCollection).GetProperty("LockedUntilUtc").SetValue(systemProperties, DateTime.Now); - typeof(Message).GetProperty("SystemProperties").SetValue(message, systemProperties); - - message.MessageId = Guid.NewGuid().ToString(); - _mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(message, It.IsAny())).ReturnsAsync(true); + var message = ServiceBusModelFactory.ServiceBusReceivedMessage( + messageId: Guid.NewGuid().ToString(), + sequenceNumber: 1, + deliveryCount: 55, + enqueuedTime: DateTimeOffset.Now, + lockedUntil: DateTimeOffset.Now); + var receiver = new Mock().Object; + var args = new ProcessMessageEventArgs(message, receiver, CancellationToken.None); + _mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(receiver, message, It.IsAny())).ReturnsAsync(true); FunctionResult result = new FunctionResult(true); _mockExecutor.Setup(p => p.TryExecuteAsync(It.Is(q => ((ServiceBusTriggerInput)q.TriggerValue).Messages[0] == message), It.IsAny())).ReturnsAsync(result); - _mockMessageProcessor.Setup(p => p.CompleteProcessingMessageAsync(message, result, It.IsAny())).Returns(Task.FromResult(0)); + _mockMessageProcessor.Setup(p => p.CompleteProcessingMessageAsync(receiver, message, result, It.IsAny())).Returns(Task.FromResult(0)); - await _listener.ProcessMessageAsync(message, CancellationToken.None); + await _listener.ProcessMessageAsync(args); _mockMessageProcessor.VerifyAll(); _mockExecutor.VerifyAll(); @@ -93,11 +92,13 @@ public async Task ProcessMessageAsync_Success() [Test] public async Task ProcessMessageAsync_BeginProcessingReturnsFalse_MessageNotProcessed() { - var message = new CustomMessage(); - message.MessageId = Guid.NewGuid().ToString(); - _mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(message, It.IsAny())).ReturnsAsync(false); + var message = ServiceBusModelFactory.ServiceBusReceivedMessage(messageId: Guid.NewGuid().ToString()); + var receiver = new Mock().Object; - await _listener.ProcessMessageAsync(message, CancellationToken.None); + _mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(receiver, message, It.IsAny())).ReturnsAsync(false); + var args = new ProcessMessageEventArgs(message, receiver, CancellationToken.None); + + await _listener.ProcessMessageAsync(args); _mockMessageProcessor.VerifyAll(); } @@ -115,21 +116,9 @@ public void GetMonitor_ReturnsExpectedValue() Assert.AreSame(scaleMonitor, scaleMonitor2); } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs) + private Task ExceptionReceivedHandler(ProcessErrorEventArgs eventArgs) { return Task.CompletedTask; } } - - // Mock calls ToString() for Mesage. This ckass fixes bug in azure-service-bus-dotnet. - // https://github.com/Azure/azure-service-bus-dotnet/blob/dev/src/Microsoft.Azure.ServiceBus/Message.cs#L291 -#pragma warning disable SA1402 // File may only contain a single type - internal class CustomMessage : Message -#pragma warning restore SA1402 // File may only contain a single type - { - public override string ToString() - { - return MessageId; - } - } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusScaleMonitorTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusScaleMonitorTests.cs index 401bafbf8a89..39dca96e751e 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusScaleMonitorTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Listeners/ServiceBusScaleMonitorTests.cs @@ -5,8 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Executors; using Microsoft.Azure.WebJobs.Host.Scale; using Microsoft.Azure.WebJobs.Host.TestCommon; @@ -38,15 +37,12 @@ public class ServiceBusScaleMonitorTests public void Setup() { _mockExecutor = new Mock(MockBehavior.Strict); + var client = new ServiceBusClient(_testConnection); + ServiceBusProcessorOptions processorOptions = new ServiceBusProcessorOptions(); + ServiceBusProcessor messageProcessor = client.CreateProcessor(_entityPath); + _mockMessageProcessor = new Mock(MockBehavior.Strict, messageProcessor); - MessageHandlerOptions messageOptions = new MessageHandlerOptions(ExceptionReceivedHandler); - MessageReceiver messageReceiver = new MessageReceiver(_testConnection, _entityPath); - _mockMessageProcessor = new Mock(MockBehavior.Strict, messageReceiver, messageOptions); - - _serviceBusOptions = new ServiceBusOptions - { - MessageHandlerOptions = messageOptions - }; + _serviceBusOptions = new ServiceBusOptions(); _mockMessagingProvider = new Mock(MockBehavior.Strict, new OptionsWrapper(_serviceBusOptions)); _mockMessagingProvider @@ -65,11 +61,6 @@ public void Setup() _scaleMonitor = (ServiceBusScaleMonitor)_listener.GetMonitor(); } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs) - { - return Task.CompletedTask; - } - [Test] public void ScaleMonitorDescriptor_ReturnsExpectedValue() { @@ -119,8 +110,8 @@ public async Task GetMetrics_HandlesExceptions() { // MessagingEntityNotFoundException _mockMessagingProvider - .Setup(p => p.CreateMessageReceiver(_entityPath, _testConnection)) - .Throws(new MessagingEntityNotFoundException("")); + .Setup(p => p.CreateBatchMessageReceiver(_entityPath, _testConnection)) + .Throws(new ServiceBusException("", reason: ServiceBusFailureReason.MessagingEntityNotFound)); ServiceBusListener listener = new ServiceBusListener(_functionId, EntityType.Queue, _entityPath, false, _mockExecutor.Object, _serviceBusOptions, _mockServiceBusAccount.Object, _mockMessagingProvider.Object, _loggerFactory, false); @@ -137,8 +128,8 @@ public async Task GetMetrics_HandlesExceptions() // UnauthorizedAccessException _mockMessagingProvider - .Setup(p => p.CreateMessageReceiver(_entityPath, _testConnection)) - .Throws(new UnauthorizedException("")); + .Setup(p => p.CreateBatchMessageReceiver(_entityPath, _testConnection)) + .Throws(new UnauthorizedAccessException("")); listener = new ServiceBusListener(_functionId, EntityType.Queue, _entityPath, false, _mockExecutor.Object, _serviceBusOptions, _mockServiceBusAccount.Object, _mockMessagingProvider.Object, _loggerFactory, false); @@ -157,7 +148,7 @@ public async Task GetMetrics_HandlesExceptions() // Generic Exception _mockMessagingProvider - .Setup(p => p.CreateMessageReceiver(_entityPath, _testConnection)) + .Setup(p => p.CreateBatchMessageReceiver(_entityPath, _testConnection)) .Throws(new Exception("Uh oh")); listener = new ServiceBusListener(_functionId, EntityType.Queue, _entityPath, false, _mockExecutor.Object, _serviceBusOptions, _mockServiceBusAccount.Object, _mockMessagingProvider.Object, _loggerFactory, false); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageInteropTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageInteropTests.cs new file mode 100644 index 000000000000..0b97ded5a253 --- /dev/null +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageInteropTests.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Azure.Messaging.ServiceBus; +using Microsoft.Azure.WebJobs.Extensions.ServiceBus.Triggers; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; + +namespace Microsoft.Azure.ServiceBus.UnitTests.MessageInterop +{ + public class MessageInteropTests + { + private static IDictionary SerializerTestCases = new XmlObjectSerializer[] + { + new DataContractBinarySerializer(typeof(TestBook)), + new DataContractSerializer(typeof(TestBook)) + }.ToDictionary(item => item.ToString(), item => item); + + public static IEnumerable SerializerTestCaseNames => SerializerTestCases.Select(testCase => new[] { testCase.Key }); + + [Test] + [TestCaseSource(nameof(SerializerTestCaseNames))] + public void RunSerializerTests(string testCaseName) + { + var serializer = SerializerTestCases[testCaseName]; + var book = new TestBook("contoso", 1, 5); + var message = GetBrokeredMessage(serializer, book); + var returned = (TestBook)serializer.ReadObject(message.Body.ToStream()); + Assert.AreEqual(book, returned); + } + + private ServiceBusMessage GetBrokeredMessage(XmlObjectSerializer serializer, TestBook book) + { + byte[] payload = null; + using (var memoryStream = new MemoryStream(10)) + { + serializer.WriteObject(memoryStream, book); + memoryStream.Flush(); + memoryStream.Position = 0; + payload = memoryStream.ToArray(); + }; + + return new ServiceBusMessage(payload); + } + } + +#pragma warning disable SA1402 // File may only contain a single type + public class TestBook +#pragma warning restore SA1402 // File may only contain a single type + { + public TestBook() { } + + public TestBook(string name, int id, int count) + { + Name = name; + Count = count; + Id = id; + } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + var testBook = (TestBook)obj; + + return + Name.Equals(testBook.Name, StringComparison.OrdinalIgnoreCase) && + Count == testBook.Count && + Id == testBook.Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public string Name { get; set; } + + public int Count { get; set; } + + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageProcessorTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageProcessorTests.cs index d89713c3d574..0e99aa129976 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageProcessorTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageProcessorTests.cs @@ -4,8 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.Executors; using NUnit.Framework; @@ -14,26 +13,26 @@ namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests public class MessageProcessorTests { private readonly MessageProcessor _processor; - private readonly MessageHandlerOptions _options; public MessageProcessorTests() { - _options = new MessageHandlerOptions(ExceptionReceivedHandler); - MessageReceiver receiver = new MessageReceiver("Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123=", "test-entity"); - _processor = new MessageProcessor(receiver, _options); + var client = new ServiceBusClient("Endpoint = sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123="); + var processor = client.CreateProcessor("test-entity"); + processor.ProcessErrorAsync += ExceptionReceivedHandler; + _processor = new MessageProcessor(processor); } [Test] public void CompleteProcessingMessageAsync_Failure_PropagatesException() { - _options.AutoComplete = false; - - Message message = new Message(); + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage(); var functionException = new InvalidOperationException("Kaboom!"); FunctionResult result = new FunctionResult(functionException); + var client = new ServiceBusClient("Endpoint = sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123="); + var receiver = client.CreateReceiver("test-entity"); var ex = Assert.ThrowsAsync(async () => { - await _processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None); + await _processor.CompleteProcessingMessageAsync(receiver, message, result, CancellationToken.None); }); Assert.AreSame(functionException, ex); @@ -42,18 +41,14 @@ public void CompleteProcessingMessageAsync_Failure_PropagatesException() [Test] public async Task CompleteProcessingMessageAsync_DefaultOnMessageOptions() { - Message message = new Message(); + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage(); FunctionResult result = new FunctionResult(true); - await _processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None); - } - - [Test] - public void MessageOptions_ReturnsOptions() - { - Assert.AreSame(_options, _processor.MessageOptions); + var client = new ServiceBusClient("Endpoint = sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123="); + var receiver = client.CreateReceiver("test-entity"); + await _processor.CompleteProcessingMessageAsync(receiver, message, result, CancellationToken.None); } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs) + private Task ExceptionReceivedHandler(ProcessErrorEventArgs eventArgs) { return Task.CompletedTask; } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToByteArrayConverterTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToByteArrayConverterTests.cs index 0de559a35058..ae7137fc978d 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToByteArrayConverterTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToByteArrayConverterTests.cs @@ -5,8 +5,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.ServiceBus.Triggers; -using Microsoft.Azure.ServiceBus; using NUnit.Framework; +using Azure.Messaging.ServiceBus; +using System; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests { @@ -22,8 +23,9 @@ public class MessageToByteArrayConverterTests [TestCase(null)] public async Task ConvertAsync_ReturnsExpectedResults(string contentType) { - Message message = new Message(Encoding.UTF8.GetBytes(TestString)); - message.ContentType = contentType; + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage( + body: new BinaryData(TestString), + contentType: contentType); MessageToByteArrayConverter converter = new MessageToByteArrayConverter(); byte[] result = await converter.ConvertAsync(message, CancellationToken.None); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToStringConverterTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToStringConverterTests.cs index fb5bbd2da1d8..594cf6253713 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToStringConverterTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessageToStringConverterTests.cs @@ -7,9 +7,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.ServiceBus.Triggers; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.InteropExtensions; using NUnit.Framework; +using Azure.Messaging.ServiceBus; +using Microsoft.Azure.WebJobs.Extensions.ServiceBus.Triggers; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests { @@ -34,8 +34,7 @@ public async Task ConvertAsync_ReturnsExpectedResult_WithBinarySerializer(string bytes = ms.ToArray(); } - Message message = new Message(bytes); - message.ContentType = contentType; + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage(body: new BinaryData(bytes), contentType: contentType); MessageToStringConverter converter = new MessageToStringConverter(); string result = await converter.ConvertAsync(message, CancellationToken.None); @@ -53,12 +52,23 @@ public async Task ConvertAsync_ReturnsExpectedResult_WithBinarySerializer(string [TestCase(ContentTypes.TextPlain, "")] public async Task ConvertAsync_ReturnsExpectedResult_WithSerializedString(string contentType, string value) { - Message message = new Message(value == null ? null : Encoding.UTF8.GetBytes(value)); - message.ContentType = contentType; + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage( + body: value == null ? null : new BinaryData(value), + contentType: contentType); MessageToStringConverter converter = new MessageToStringConverter(); string result = await converter.ConvertAsync(message, CancellationToken.None); - Assert.AreEqual(value, result); + // A received message will never have a null body as a body section is required when sending even if it + // is empty. This was true in Track 1 as well, but in Track 1 the actual body property could be null when + // constructing the message, but in practice it wouldn't be null when receiving. + if (value == null) + { + Assert.AreEqual("", result); + } + else + { + Assert.AreEqual(value, result); + } } [Test] @@ -71,11 +81,11 @@ public async Task ConvertAsync_ReturnsExpectedResult_WithSerializedObject() bytes = ms.ToArray(); } - Message message = new Message(bytes); + ServiceBusReceivedMessage message = ServiceBusModelFactory.ServiceBusReceivedMessage(body: new BinaryData(bytes)); MessageToStringConverter converter = new MessageToStringConverter(); string result = await converter.ConvertAsync(message, CancellationToken.None); - Assert.AreEqual(Encoding.UTF8.GetString(message.Body), result); + Assert.AreEqual(message.Body.ToString(), result); } [Serializable] diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessagingProviderTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessagingProviderTests.cs index 4fdb47a08b4c..760e50c8cf40 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessagingProviderTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/MessagingProviderTests.cs @@ -1,9 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using Microsoft.Azure.ServiceBus; using Microsoft.Extensions.Options; using NUnit.Framework; +using System; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests { @@ -18,19 +18,19 @@ public void CreateMessageReceiver_ReturnsExpectedReceiver() ConnectionString = defaultConnection }; var provider = new MessagingProvider(new OptionsWrapper(config)); - var receiver = provider.CreateMessageReceiver("entityPath", defaultConnection); - Assert.AreEqual("entityPath", receiver.Path); + var receiver = provider.CreateBatchMessageReceiver("entityPath", defaultConnection); + Assert.AreEqual("entityPath", receiver.EntityPath); - var receiver2 = provider.CreateMessageReceiver("entityPath", defaultConnection); + var receiver2 = provider.CreateBatchMessageReceiver("entityPath", defaultConnection); Assert.AreSame(receiver, receiver2); config.PrefetchCount = 100; - receiver = provider.CreateMessageReceiver("entityPath1", defaultConnection); + receiver = provider.CreateBatchMessageReceiver("entityPath1", defaultConnection); Assert.AreEqual(100, receiver.PrefetchCount); } [Test] - public void CreateClientEntity_ReturnsExpectedReceiver() + public void CreateProcessor_ReturnsExpectedProcessor() { string defaultConnection = "Endpoint=sb://default.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123="; var config = new ServiceBusOptions @@ -38,15 +38,19 @@ public void CreateClientEntity_ReturnsExpectedReceiver() ConnectionString = defaultConnection }; var provider = new MessagingProvider(new OptionsWrapper(config)); - var clientEntity = provider.CreateClientEntity("entityPath", defaultConnection); - Assert.AreEqual("entityPath", clientEntity.Path); + var processor = provider.CreateProcessor("entityPath", defaultConnection); + Assert.AreEqual("entityPath", processor.EntityPath); - var receiver2 = provider.CreateClientEntity("entityPath", defaultConnection); - Assert.AreSame(clientEntity, receiver2); + var processor2 = provider.CreateProcessor("entityPath", defaultConnection); + Assert.AreSame(processor, processor2); config.PrefetchCount = 100; - clientEntity = provider.CreateClientEntity("entityPath1", defaultConnection); - Assert.AreEqual(100, ((QueueClient)clientEntity).PrefetchCount); + config.MaxConcurrentCalls = 5; + config.MaxAutoLockRenewalDuration = TimeSpan.FromSeconds(30); + processor = provider.CreateProcessor("entityPath1", defaultConnection); + Assert.AreEqual(config.PrefetchCount, processor.PrefetchCount); + Assert.AreEqual(config.MaxConcurrentCalls, processor.MaxConcurrentCalls); + Assert.AreEqual(config.MaxAutoLockRenewalDuration, processor.MaxAutoLockRenewalDuration); } [Test] @@ -59,7 +63,7 @@ public void CreateMessageSender_ReturnsExpectedSender() }; var provider = new MessagingProvider(new OptionsWrapper(config)); var sender = provider.CreateMessageSender("entityPath", defaultConnection); - Assert.AreEqual("entityPath", sender.Path); + Assert.AreEqual("entityPath", sender.EntityPath); var sender2 = provider.CreateMessageSender("entityPath", defaultConnection); Assert.AreSame(sender, sender2); diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Microsoft.Azure.WebJobs.Extensions.ServiceBus.Tests.csproj b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Microsoft.Azure.WebJobs.Extensions.ServiceBus.Tests.csproj index ef0f5932892d..33add9a3828f 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Microsoft.Azure.WebJobs.Extensions.ServiceBus.Tests.csproj +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/Microsoft.Azure.WebJobs.Extensions.ServiceBus.Tests.csproj @@ -21,12 +21,9 @@ - - - - - - - + + + + \ No newline at end of file diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/PublicSurfaceTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/PublicSurfaceTests.cs deleted file mode 100644 index c96918b95bfd..000000000000 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/PublicSurfaceTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using Microsoft.Azure.WebJobs.Host.TestCommon; -using NUnit.Framework; - -namespace Microsoft.Azure.WebJobs.Host.UnitTests -{ - public class PublicSurfaceTests - { - [Test] - public void WebJobs_Extensions_ServiceBus_VerifyPublicSurfaceArea() - { - var assembly = typeof(ServiceBusAttribute).Assembly; - - var expected = new[] - { - "Constants", - "EntityType", - "MessageProcessor", - "MessagingProvider", - "ServiceBusAccountAttribute", - "ServiceBusAttribute", - "ServiceBusTriggerAttribute", - "ServiceBusHostBuilderExtensions", - "ServiceBusOptions", - "ServiceBusWebJobsStartup", - "SessionMessageProcessor", - "BatchOptions" - }; - - TestHelpers.AssertPublicTypes(expected, assembly); - } - } -} diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusEndToEndTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusEndToEndTests.cs index 5c224ebf554c..51dda6086688 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusEndToEndTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusEndToEndTests.cs @@ -3,16 +3,14 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Azure.Messaging.ServiceBus; using Azure.Messaging.ServiceBus.Tests; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; using Microsoft.Azure.WebJobs.Host.TestCommon; using Microsoft.Azure.WebJobs.ServiceBus; using Microsoft.Extensions.DependencyInjection; @@ -72,6 +70,31 @@ public async Task ServiceBusBinderTest() } } + private async Task CleanUpEntity(string queueName) + { + await using var client = new ServiceBusClient(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString); + var receiver = client.CreateReceiver(queueName, new ServiceBusReceiverOptions + { + ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete + }); + ServiceBusReceivedMessage message; + int count = 0; + + do + { + message = await receiver.ReceiveMessageAsync(TimeSpan.FromSeconds(1)).ConfigureAwait(false); + if (message != null) + { + count++; + } + else + { + break; + } + } while (true); + return count; + } + [Test] public async Task CustomMessageProcessorTest() { @@ -247,10 +270,9 @@ private static Action BuildDrainHost() b.AddServiceBus(sbOptions => { // We want to ensure messages can be completed in the function code before signaling success to the test - sbOptions.MessageHandlerOptions.AutoComplete = false; - sbOptions.BatchOptions.AutoComplete = false; - sbOptions.MessageHandlerOptions.MaxAutoRenewDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin); - sbOptions.MessageHandlerOptions.MaxConcurrentCalls = 1; + sbOptions.AutoCompleteMessages = false; + sbOptions.MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin); + sbOptions.MaxConcurrentCalls = 1; })); } @@ -309,30 +331,6 @@ private async Task TestMultipleDrainMode(bool sendToQueue) } } - private async Task CleanUpEntity(string queueName) - { - var messageReceiver = new MessageReceiver(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, queueName, ReceiveMode.ReceiveAndDelete); - Message message; - int count = 0; - - do - { - message = await messageReceiver.ReceiveAsync(TimeSpan.FromSeconds(1)).ConfigureAwait(false); - if (message != null) - { - count++; - } - else - { - break; - } - } while (true); - - await messageReceiver.CloseAsync(); - - return count; - } - private async Task ServiceBusEndToEndInternal(IHost host) { var jobContainerType = typeof(T); @@ -419,23 +417,22 @@ private async Task ServiceBusEndToEndInternal(IHost host) "ServiceBusOptions", "{", " \"PrefetchCount\": 0,", - " \"MessageHandlerOptions\": {", - " \"AutoComplete\": true,", - " \"MaxAutoRenewDuration\": \"00:05:00\",", - $" \"MaxConcurrentCalls\": {16 * Utility.GetProcessorCount()}", - " }", - " \"SessionHandlerOptions\": {", - " \"AutoComplete\": true,", - " \"MaxAutoRenewDuration\": \"00:05:00\",", - " \"MaxConcurrentSessions\": 2000,", - " \"MessageWaitTimeout\": \"00:01:00\"", + " \"AutoCompleteMessages\": true,", + " \"MaxAutoLockRenewalDuration\": \"00:05:00\",", + $" \"MaxConcurrentCalls\": {16 * Utility.GetProcessorCount()},", + " \"MaxConcurrentSessions\": 8,", + " \"MaxMessages\": 1000,", + " \"MaxWaitTime\": \"\"", + " \"RetryOptions\": {", + " \"Mode\": \"Exponential\",", + " \"TryTimeout\": \"00:00:10\",", + " \"Delay\": \"00:00:00.8000000\",", + " \"MaxDelay\": \"00:01:00\",", + " \"MaxRetries\": 3", " }", + " \"TransportType\": \"AmqpTcp\",", + " \"WebProxy\": \"\",", "}", - " \"BatchOptions\": {", - " \"MaxMessageCount\": 1000,", - " \"OperationTimeout\": \"00:01:00\",", - " \"AutoComplete\": true", - " }", "SingletonOptions", "{", " \"ListenerLockPeriod\": \"00:01:00\",", @@ -468,21 +465,21 @@ private async Task ServiceBusEndToEndInternal(IHost host) public abstract class ServiceBusTestJobsBase { - protected static Message SBQueue2SBQueue_GetOutputMessage(string input) + protected static ServiceBusMessage SBQueue2SBQueue_GetOutputMessage(string input) { input = input + "-SBQueue2SBQueue"; - return new Message + return new ServiceBusMessage { ContentType = "text/plain", - Body = Encoding.UTF8.GetBytes(input) + Body = new BinaryData(input) }; } - protected static Message SBQueue2SBTopic_GetOutputMessage(string input) + protected static ServiceBusMessage SBQueue2SBTopic_GetOutputMessage(string input) { input = input + "-SBQueue2SBTopic"; - return new Message(Encoding.UTF8.GetBytes(input)) + return new ServiceBusMessage(Encoding.UTF8.GetBytes(input)) { ContentType = "text/plain" }; @@ -494,14 +491,9 @@ protected static void SBTopicListener1Impl(string input) _topicSubscriptionCalled1.Set(); } - protected static void SBTopicListener2Impl(Message message) + protected static void SBTopicListener2Impl(ServiceBusReceivedMessage message) { - using (Stream stream = new MemoryStream(message.Body)) - using (TextReader reader = new StreamReader(stream)) - { - _resultMessage2 = reader.ReadToEnd() + "-topic-2"; - } - + _resultMessage2 = message.Body.ToString() + "-topic-2"; _topicSubscriptionCalled2.Set(); } } @@ -511,24 +503,25 @@ public class ServiceBusTestJobs : ServiceBusTestJobsBase // Passes service bus message from a queue to another queue public async Task SBQueue2SBQueue( [ServiceBusTrigger(FirstQueueNameKey)] string start, int deliveryCount, - MessageReceiver messageReceiver, + ServiceBusReceiver messageReceiver, string lockToken, - [ServiceBus(SecondQueueNameKey)] MessageSender messageSender) + [ServiceBus(SecondQueueNameKey)] ServiceBusSender messageSender) { - Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.EntityPath); Assert.AreEqual(1, deliveryCount); // verify the message receiver and token are valid - await messageReceiver.RenewLockAsync(lockToken); + // TODO lockToken overload not supported in new SDK + //await messageReceiver.RenewLockAsync(lockToken); var message = SBQueue2SBQueue_GetOutputMessage(start); - await messageSender.SendAsync(message); + await messageSender.SendMessageAsync(message); } // Passes a service bus message from a queue to topic using a brokered message public static void SBQueue2SBTopic( [ServiceBusTrigger(SecondQueueNameKey)] string message, - [ServiceBus(TopicNameKey)] out Message output) + [ServiceBus(TopicNameKey)] out ServiceBusMessage output) { output = SBQueue2SBTopic_GetOutputMessage(message); } @@ -536,7 +529,7 @@ public static void SBQueue2SBTopic( // First listener for the topic public static void SBTopicListener1( [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey)] string message, - MessageReceiver messageReceiver, + ServiceBusReceiver messageReceiver, string lockToken) { SBTopicListener1Impl(message); @@ -547,7 +540,7 @@ public static void SBTopicListener1( // for ServiceBus. [Singleton] public static void SBTopicListener2( - [ServiceBusTrigger(TopicNameKey, SecondSubscriptionNameKey)] Message message) + [ServiceBusTrigger(TopicNameKey, SecondSubscriptionNameKey)] ServiceBusReceivedMessage message) { SBTopicListener2Impl(message); } @@ -613,11 +606,11 @@ public class ServiceBusMultipleMessagesTestJob_BindToStringArray { public static async Task SBQueue2SBQueue( [ServiceBusTrigger(FirstQueueNameKey)] string[] messages, - MessageReceiver messageReceiver, CancellationToken cancellationToken) + ServiceBusReceiver messageReceiver, CancellationToken cancellationToken) { try { - Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.EntityPath); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); await Task.Delay(0, cancellationToken); } @@ -630,18 +623,11 @@ public static async Task SBQueue2SBQueue( public class ServiceBusMultipleMessagesTestJob_BindToMessageArray { public static void SBQueue2SBQueue( - [ServiceBusTrigger(FirstQueueNameKey)] Message[] array, - MessageReceiver messageReceiver) + [ServiceBusTrigger(FirstQueueNameKey)] ServiceBusReceivedMessage[] array, + ServiceBusReceiver messageReceiver) { - Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.Path); - string[] messages = array.Select(x => - { - using (Stream stream = new MemoryStream(x.Body)) - using (TextReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - }).ToArray(); + Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.EntityPath); + string[] messages = array.Select(x => x.Body.ToString()).ToArray(); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); } } @@ -650,9 +636,9 @@ public class ServiceBusMultipleMessagesTestJob_BindToPocoArray { public static void SBQueue2SBQueue( [ServiceBusTrigger(FirstQueueNameKey)] TestPoco[] array, - MessageReceiver messageReceiver) + ServiceBusReceiver messageReceiver) { - Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageReceiver.EntityPath); string[] messages = array.Select(x => "{'Name': '" + x.Name + "', 'Value': 'Value'}").ToArray(); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); } @@ -685,8 +671,8 @@ public static void BindToString( public class DrainModeTestJobQueue { public static async Task QueueNoSessions( - [ServiceBusTrigger(FirstQueueNameKey)] Message msg, - MessageReceiver messageReceiver, + [ServiceBusTrigger(FirstQueueNameKey)] ServiceBusReceivedMessage msg, + ServiceBusReceiver messageReceiver, CancellationToken cancellationToken, ILogger logger) { @@ -694,7 +680,7 @@ public static async Task QueueNoSessions( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - await messageReceiver.CompleteAsync(msg.SystemProperties.LockToken); + await messageReceiver.CompleteMessageAsync(msg); _drainValidationPostDelay.Set(); } } @@ -702,8 +688,8 @@ public static async Task QueueNoSessions( public class DrainModeTestJobTopic { public static async Task TopicNoSessions( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey)] Message msg, - MessageReceiver messageReceiver, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey)] ServiceBusReceivedMessage msg, + ServiceBusReceiver messageReceiver, CancellationToken cancellationToken, ILogger logger) { @@ -711,7 +697,7 @@ public static async Task TopicNoSessions( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - await messageReceiver.CompleteAsync(msg.SystemProperties.LockToken); + await messageReceiver.CompleteMessageAsync(msg); _drainValidationPostDelay.Set(); } } @@ -719,8 +705,8 @@ public static async Task TopicNoSessions( public class DrainModeTestJobQueueBatch { public static async Task QueueNoSessionsBatch( - [ServiceBusTrigger(FirstQueueNameKey)] Message[] array, - MessageReceiver messageReceiver, + [ServiceBusTrigger(FirstQueueNameKey)] ServiceBusReceivedMessage[] array, + ServiceBusReceiver messageReceiver, CancellationToken cancellationToken, ILogger logger) { @@ -729,9 +715,9 @@ public static async Task QueueNoSessionsBatch( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - foreach (Message msg in array) + foreach (ServiceBusReceivedMessage msg in array) { - await messageReceiver.CompleteAsync(msg.SystemProperties.LockToken); + await messageReceiver.CompleteMessageAsync(msg); } _drainValidationPostDelay.Set(); } @@ -740,8 +726,8 @@ public static async Task QueueNoSessionsBatch( public class DrainModeTestJobTopicBatch { public static async Task TopicNoSessionsBatch( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey)] Message[] array, - MessageReceiver messageReceiver, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey)] ServiceBusReceivedMessage[] array, + ServiceBusReceiver messageReceiver, CancellationToken cancellationToken, ILogger logger) { @@ -750,9 +736,9 @@ public static async Task TopicNoSessionsBatch( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - foreach (Message msg in array) + foreach (ServiceBusReceivedMessage msg in array) { - await messageReceiver.CompleteAsync(msg.SystemProperties.LockToken); + await messageReceiver.CompleteMessageAsync(msg); } _drainValidationPostDelay.Set(); } @@ -786,46 +772,44 @@ public CustomMessagingProvider(IOptions serviceBusOptions, IL _logger = loggerFactory?.CreateLogger(CustomMessagingCategory); } - public override MessageProcessor CreateMessageProcessor(string entityPath, string connectionName = null) + public override MessageProcessor CreateMessageProcessor(string entityPath, string connectionString) { - var options = new MessageHandlerOptions(ExceptionReceivedHandler) + var options = new ServiceBusProcessorOptions() { MaxConcurrentCalls = 3, - MaxAutoRenewDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin) + MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin) }; - var messageReceiver = new MessageReceiver(_options.ConnectionString, entityPath); - - return new CustomMessageProcessor(messageReceiver, options, _logger); + var client = base.CreateClient(connectionString); + var processor = client.CreateProcessor(entityPath, options); + // TODO decide whether it makes sense to still default error handler when there is a custom provider + // currently user needs to set it. + processor.ProcessErrorAsync += args => Task.CompletedTask; + return new CustomMessageProcessor(processor, _logger); } private class CustomMessageProcessor : MessageProcessor { private readonly ILogger _logger; - public CustomMessageProcessor(MessageReceiver messageReceiver, MessageHandlerOptions messageOptions, ILogger logger) - : base(messageReceiver, messageOptions) + public CustomMessageProcessor(ServiceBusProcessor processor, ILogger logger) + : base(processor) { _logger = logger; } - public override async Task BeginProcessingMessageAsync(Message message, CancellationToken cancellationToken) + public override async Task BeginProcessingMessageAsync(ServiceBusReceiver receiver, ServiceBusReceivedMessage message, CancellationToken cancellationToken) { _logger?.LogInformation("Custom processor Begin called!"); - return await base.BeginProcessingMessageAsync(message, cancellationToken); + return await base.BeginProcessingMessageAsync(receiver, message, cancellationToken); } - public override async Task CompleteProcessingMessageAsync(Message message, Executors.FunctionResult result, CancellationToken cancellationToken) + public override async Task CompleteProcessingMessageAsync(ServiceBusReceiver receiver, ServiceBusReceivedMessage message, Executors.FunctionResult result, CancellationToken cancellationToken) { _logger?.LogInformation("Custom processor End called!"); - await base.CompleteProcessingMessageAsync(message, result, cancellationToken); + await base.CompleteProcessingMessageAsync(receiver, message, result, cancellationToken); } } - - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs) - { - return Task.CompletedTask; - } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusSessionsEndToEndTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusSessionsEndToEndTests.cs index 46bc1fa4af7e..ec363eedc0b9 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusSessionsEndToEndTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusSessionsEndToEndTests.cs @@ -3,13 +3,10 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Azure.Messaging.ServiceBus.Tests; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; +using Azure.Messaging.ServiceBus; using Microsoft.Azure.WebJobs.Host.TestCommon; using Microsoft.Azure.WebJobs.ServiceBus; using Microsoft.Extensions.DependencyInjection; @@ -358,10 +355,9 @@ public async Task MessageDraining_TopicWithSessions_Batch() b.AddServiceBus(sbOptions => { // Will be disabled for drain mode validation as messages are completed by functoin code to validate draining allows completion - sbOptions.SessionHandlerOptions.AutoComplete = autoComplete; - sbOptions.BatchOptions.AutoComplete = autoComplete; - sbOptions.SessionHandlerOptions.MaxAutoRenewDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin); - sbOptions.SessionHandlerOptions.MaxConcurrentSessions = 1; + sbOptions.AutoCompleteMessages = autoComplete; + sbOptions.MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(MaxAutoRenewDurationMin); + sbOptions.MaxConcurrentSessions = 1; })) .ConfigureServices(services => { @@ -454,39 +450,30 @@ private async Task TestMultipleDrainMode(bool sendToQueue) } } - private async Task Cleanup() - { - var tasks = new List() - { - ServiceBusSessionsTestHelper.CleanUpQueue(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, _firstQueueScope.QueueName), - ServiceBusSessionsTestHelper.CleanUpSubscription(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, _topicScope.TopicName, _topicScope.SubscriptionNames.First()) - }; - - await Task.WhenAll(tasks); - } - public class ServiceBusSessionsTestJobs1 { public static void SBQueue1Trigger( - [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] Message message, int deliveryCount, - IMessageSession messageSession, + [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage message, int deliveryCount, + ServiceBusSessionReceiver messageSession, ILogger log, string lockToken) { - Assert.AreEqual(_firstQueueScope.QueueName, messageSession.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageSession.EntityPath); Assert.AreEqual(1, deliveryCount); + Assert.AreEqual(message.LockToken, lockToken); ServiceBusSessionsTestHelper.ProcessMessage(message, log, _waitHandle1, _waitHandle2); } public static void SBSub1Trigger( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] Message message, int deliveryCount, - IMessageSession messageSession, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage message, int deliveryCount, + ServiceBusSessionReceiver messageSession, ILogger log, string lockToken) { - Assert.AreEqual(EntityNameHelper.FormatSubscriptionPath(_topicScope.TopicName, _topicScope.SubscriptionNames.First()), messageSession.Path); + Assert.AreEqual(EntityNameFormatter.FormatSubscriptionPath(_topicScope.TopicName, _topicScope.SubscriptionNames.First()), messageSession.EntityPath); Assert.AreEqual(1, deliveryCount); + Assert.AreEqual(message.LockToken, lockToken); ServiceBusSessionsTestHelper.ProcessMessage(message, log, _waitHandle1, _waitHandle2); } @@ -495,14 +482,14 @@ public static void SBSub1Trigger( public class ServiceBusSessionsTestJobs2 { public static void SBQueue2Trigger( - [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] Message message, + [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage message, ILogger log) { ServiceBusSessionsTestHelper.ProcessMessage(message, log, _waitHandle1, _waitHandle2); } public static void SBSub2Trigger( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] Message message, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage message, ILogger log) { ServiceBusSessionsTestHelper.ProcessMessage(message, log, _waitHandle1, _waitHandle2); @@ -512,8 +499,8 @@ public static void SBSub2Trigger( public class DrainModeTestJobQueue { public static async Task QueueWithSessions( - [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] Message msg, - IMessageSession messageSession, + [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage msg, + ServiceBusSessionReceiver messageSession, CancellationToken cancellationToken, ILogger logger) { @@ -522,7 +509,7 @@ public static async Task QueueWithSessions( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - await messageSession.CompleteAsync(msg.SystemProperties.LockToken); + await messageSession.CompleteMessageAsync(msg); _drainValidationPostDelay.Set(); } } @@ -530,8 +517,8 @@ public static async Task QueueWithSessions( public class DrainModeTestJobTopic { public static async Task TopicWithSessions( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] Message msg, - IMessageSession messageSession, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage msg, + ServiceBusSessionReceiver messageSession, CancellationToken cancellationToken, ILogger logger) { @@ -540,7 +527,7 @@ public static async Task TopicWithSessions( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - await messageSession.CompleteAsync(msg.SystemProperties.LockToken); + await messageSession.CompleteMessageAsync(msg); _drainValidationPostDelay.Set(); } } @@ -548,8 +535,8 @@ public static async Task TopicWithSessions( public class DrainModeTestJobQueueBatch { public static async Task QueueWithSessionsBatch( - [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] Message[] array, - IMessageSession messageSession, + [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage[] array, + ServiceBusSessionReceiver messageSession, CancellationToken cancellationToken, ILogger logger) { @@ -559,9 +546,9 @@ public static async Task QueueWithSessionsBatch( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - foreach (Message msg in array) + foreach (ServiceBusReceivedMessage msg in array) { - await messageSession.CompleteAsync(msg.SystemProperties.LockToken); + await messageSession.CompleteMessageAsync(msg); } _drainValidationPostDelay.Set(); } @@ -570,8 +557,8 @@ public static async Task QueueWithSessionsBatch( public class DrainModeTestJobTopicBatch { public static async Task TopicWithSessionsBatch( - [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] Message[] array, - MessageReceiver messageReceiver, + [ServiceBusTrigger(TopicNameKey, FirstSubscriptionNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage[] array, + ServiceBusSessionReceiver messageSession, CancellationToken cancellationToken, ILogger logger) { @@ -581,9 +568,9 @@ public static async Task TopicWithSessionsBatch( _drainValidationPreDelay.Set(); await DrainModeHelper.WaitForCancellation(cancellationToken); Assert.True(cancellationToken.IsCancellationRequested); - foreach (Message msg in array) + foreach (ServiceBusReceivedMessage msg in array) { - await messageReceiver.CompleteAsync(msg.SystemProperties.LockToken); + await messageSession.CompleteMessageAsync(msg); } _drainValidationPostDelay.Set(); } @@ -631,11 +618,11 @@ public class ServiceBusMultipleMessagesTestJob_BindToStringArray { public static async Task SBQueue2SBQueue( [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] string[] messages, - IMessageSession messageSession, CancellationToken cancellationToken) + ServiceBusSessionReceiver messageSession, CancellationToken cancellationToken) { try { - Assert.AreEqual(_firstQueueScope.QueueName, messageSession.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageSession.EntityPath); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); await Task.Delay(0, cancellationToken); } @@ -648,18 +635,11 @@ public static async Task SBQueue2SBQueue( public class ServiceBusMultipleMessagesTestJob_BindToMessageArray { public static void SBQueue2SBQueue( - [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] Message[] array, - IMessageSession messageSession) + [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] ServiceBusReceivedMessage[] array, + ServiceBusSessionReceiver messageSession) { - Assert.AreEqual(_firstQueueScope.QueueName, messageSession.Path); - string[] messages = array.Select(x => - { - using (Stream stream = new MemoryStream(x.Body)) - using (TextReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - }).ToArray(); + Assert.AreEqual(_firstQueueScope.QueueName, messageSession.EntityPath); + string[] messages = array.Select(x => x.Body.ToString()).ToArray(); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); } } @@ -668,9 +648,9 @@ public class ServiceBusMultipleMessagesTestJob_BindToPocoArray { public static void SBQueue2SBQueue( [ServiceBusTrigger(FirstQueueNameKey, IsSessionsEnabled = true)] TestPoco[] array, - IMessageSession messageSession) + ServiceBusSessionReceiver messageSession) { - Assert.AreEqual(_firstQueueScope.QueueName, messageSession.Path); + Assert.AreEqual(_firstQueueScope.QueueName, messageSession.EntityPath); string[] messages = array.Select(x => "{'Name': '" + x.Name + "', 'Value': 'Value'}").ToArray(); ServiceBusMultipleTestJobsBase.ProcessMessages(messages); } @@ -686,51 +666,51 @@ public CustomMessagingProvider(IOptions serviceBusOptions, IL : base(serviceBusOptions) { _options = serviceBusOptions.Value; - _options.SessionHandlerOptions.MessageWaitTimeout = TimeSpan.FromSeconds(90); - _options.SessionHandlerOptions.MaxConcurrentSessions = 1; + //_options.SessionProcessorOptions.MessageWaitTimeout = TimeSpan.FromSeconds(90); + _options.RetryOptions.TryTimeout = TimeSpan.FromSeconds(90); + _options.MaxConcurrentSessions = 1; _logger = loggerFactory?.CreateLogger(CustomMessagingCategory); } public override SessionMessageProcessor CreateSessionMessageProcessor(string entityPath, string connectionString) { + var client = new ServiceBusClient(connectionString, _options.ToClientOptions()); + ServiceBusSessionProcessor processor; if (entityPath == _firstQueueScope.QueueName) { - return new CustomSessionMessageProcessor(new QueueClient(connectionString, entityPath), _options.SessionHandlerOptions, _logger); + processor = client.CreateSessionProcessor(entityPath, _options.ToSessionProcessorOptions()); } else { string[] arr = entityPath.Split('/'); - return new CustomSessionMessageProcessor(new SubscriptionClient(connectionString, arr[0], arr[2]), _options.SessionHandlerOptions, _logger); + processor = client.CreateSessionProcessor(arr[0], arr[2], _options.ToSessionProcessorOptions()); } + processor.ProcessErrorAsync += args => Task.CompletedTask; + return new CustomSessionMessageProcessor(processor, _logger); } private class CustomSessionMessageProcessor : SessionMessageProcessor { private readonly ILogger _logger; - public CustomSessionMessageProcessor(ClientEntity clientEntity, SessionHandlerOptions messageOptions, ILogger logger) - : base(clientEntity, messageOptions) + public CustomSessionMessageProcessor(ServiceBusSessionProcessor sessionProcessor, ILogger logger) + : base(sessionProcessor) { _logger = logger; } - public override async Task BeginProcessingMessageAsync(IMessageSession session, Message message, CancellationToken cancellationToken) + public override async Task BeginProcessingMessageAsync(ServiceBusSessionReceiver receiver, ServiceBusReceivedMessage message, CancellationToken cancellationToken) { - _logger?.LogInformation("Custom processor Begin called!" + ServiceBusSessionsTestHelper.GetStringMessage(message)); - return await base.BeginProcessingMessageAsync(session, message, cancellationToken); + _logger?.LogInformation("Custom processor Begin called!" + message.Body.ToString()); + return await base.BeginProcessingMessageAsync(receiver, message, cancellationToken); } - public override async Task CompleteProcessingMessageAsync(IMessageSession session, Message message, Executors.FunctionResult result, CancellationToken cancellationToken) + public override async Task CompleteProcessingMessageAsync(ServiceBusSessionReceiver receiver, ServiceBusReceivedMessage message, Executors.FunctionResult result, CancellationToken cancellationToken) { - _logger?.LogInformation("Custom processor End called!" + ServiceBusSessionsTestHelper.GetStringMessage(message)); - await base.CompleteProcessingMessageAsync(session, message, result, cancellationToken); + _logger?.LogInformation("Custom processor End called!" + message.Body.ToString()); + await base.CompleteProcessingMessageAsync(receiver, message, result, cancellationToken); } } - - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs) - { - return Task.CompletedTask; - } } public void Dispose() @@ -743,7 +723,6 @@ public void Dispose() { _waitHandle2.Dispose(); } - Cleanup().GetAwaiter().GetResult(); } } @@ -751,58 +730,9 @@ public void Dispose() internal class ServiceBusSessionsTestHelper #pragma warning restore SA1402 // File may only contain a single type { - private static SessionHandlerOptions sessionHandlerOptions = new SessionHandlerOptions(ExceptionReceivedHandler); - public static async Task CleanUpQueue(string connectionString, string queueName) - { - await CleanUpEntity(connectionString, queueName); - } - - public static async Task CleanUpSubscription(string connectionString, string topicName, string subscriptionName) - { - await CleanUpEntity(connectionString, EntityNameHelper.FormatSubscriptionPath(topicName, subscriptionName)); - } - - private static async Task CleanUpEntity(string connectionString, string entityPath) - { - var client = new SessionClient(connectionString, entityPath, ReceiveMode.PeekLock); - client.OperationTimeout = TimeSpan.FromSeconds(5); - - IMessageSession session = null; - try - { - session = await client.AcceptMessageSessionAsync(); - var messages = await session.ReceiveAsync(1000, TimeSpan.FromSeconds(1)); - await session.CompleteAsync(messages.Select(m => m.SystemProperties.LockToken)); - } - catch (ServiceBusException) - { - } - finally - { - if (session != null) - { - await session.CloseAsync(); - } - } - } - - private static async Task ProcessMessagesInSessionAsync(IMessageSession messageSession, Message message, CancellationToken token) - { - await messageSession.CompleteAsync(message.SystemProperties.LockToken); - } - - public static string GetStringMessage(Message message) - { - using (Stream stream = new MemoryStream(message.Body)) - using (TextReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } - - public static void ProcessMessage(Message message, ILogger log, EventWaitHandle waitHandle1, EventWaitHandle waitHandle2) + public static void ProcessMessage(ServiceBusReceivedMessage message, ILogger log, EventWaitHandle waitHandle1, EventWaitHandle waitHandle2) { - string messageString = ServiceBusSessionsTestHelper.GetStringMessage(message); + string messageString = message.Body.ToString(); log.LogInformation($"{messageString}-{message.SessionId}"); if (messageString == "message5" && message.SessionId == "test-session1") @@ -829,10 +759,5 @@ public static string GetLogsAsString(List messages) } return reuslt; } - - private static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args) - { - return Task.CompletedTask; - } } } \ No newline at end of file diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusTriggerStrategyTests.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusTriggerStrategyTests.cs index ada211f1da32..4eac291d6a77 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusTriggerStrategyTests.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/ServiceBusTriggerStrategyTests.cs @@ -3,12 +3,8 @@ using System; using System.Collections.Generic; -using System.Text; -using Microsoft.Azure.ServiceBus; -using Microsoft.Azure.ServiceBus.Core; -using Microsoft.Azure.WebJobs.Host.TestCommon; +using Azure.Messaging.ServiceBus; using NUnit.Framework; -using static Microsoft.Azure.ServiceBus.Message; namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests { @@ -51,43 +47,41 @@ public void GetBindingContract_MultipleDispatch_ReturnsExpectedValue() Assert.AreEqual(typeof(string[]), bindingDataContract["ToArray"]); Assert.AreEqual(typeof(string[]), bindingDataContract["LabelArray"]); Assert.AreEqual(typeof(string[]), bindingDataContract["CorrelationIdArray"]); - Assert.AreEqual(typeof(IDictionary[]), bindingDataContract["UserPropertiesArray"]); - Assert.AreEqual(typeof(MessageReceiver), bindingDataContract["MessageReceiver"]); - Assert.AreEqual(typeof(IMessageSession), bindingDataContract["MessageSession"]); + Assert.AreEqual(typeof(IDictionary[]), bindingDataContract["ApplicationPropertiesArray"]); + Assert.AreEqual(typeof(ServiceBusReceiver), bindingDataContract["MessageReceiver"]); + Assert.AreEqual(typeof(ServiceBusSessionReceiver), bindingDataContract["MessageSession"]); } [Test] public void GetBindingData_SingleDispatch_ReturnsExpectedValue() { - var message = new Message(new byte[] { }); - SystemPropertiesCollection sysProp = GetSystemProperties(); - TestHelpers.SetField(message, "SystemProperties", sysProp); IDictionary userProps = new Dictionary(); userProps.Add(new KeyValuePair("prop1", "value1")); userProps.Add(new KeyValuePair("prop2", "value2")); - TestHelpers.SetField(message, "UserProperties", userProps); + var message = CreateMessageWithSystemProperties(applicationProperties: userProps); var input = ServiceBusTriggerInput.CreateSingle(message); var strategy = new ServiceBusTriggerBindingStrategy(); var bindingData = strategy.GetBindingData(input); - Assert.AreEqual(15, bindingData.Count); // SystemPropertiesCollection is sealed + Assert.AreEqual(15, bindingData.Count); - Assert.AreSame(input.MessageReceiver as MessageReceiver, bindingData["MessageReceiver"]); - Assert.AreSame(input.MessageReceiver as IMessageSession, bindingData["MessageSession"]); - Assert.AreEqual(message.SystemProperties.LockToken, bindingData["LockToken"]); - Assert.AreEqual(message.SystemProperties.SequenceNumber, bindingData["SequenceNumber"]); - Assert.AreEqual(message.SystemProperties.DeliveryCount, bindingData["DeliveryCount"]); - Assert.AreSame(message.SystemProperties.DeadLetterSource, bindingData["DeadLetterSource"]); - Assert.AreEqual(message.ExpiresAtUtc, bindingData["ExpiresAtUtc"]); + Assert.AreSame(input.Receiver, bindingData["MessageReceiver"]); + Assert.AreSame(input.SessionReceiver, bindingData["MessageSession"]); + Assert.AreEqual(message.LockToken, bindingData["LockToken"]); + Assert.AreEqual(message.SequenceNumber, bindingData["SequenceNumber"]); + Assert.AreEqual(message.DeliveryCount, bindingData["DeliveryCount"]); + Assert.AreSame(message.DeadLetterSource, bindingData["DeadLetterSource"]); + Assert.AreEqual(message.ExpiresAt, bindingData["ExpiresAtUtc"]); + Assert.AreEqual(message.EnqueuedTime, bindingData["EnqueuedTimeUtc"]); Assert.AreSame(message.MessageId, bindingData["MessageId"]); Assert.AreSame(message.ContentType, bindingData["ContentType"]); Assert.AreSame(message.ReplyTo, bindingData["ReplyTo"]); Assert.AreSame(message.To, bindingData["To"]); - Assert.AreSame(message.Label, bindingData["Label"]); + Assert.AreSame(message.Subject, bindingData["Label"]); Assert.AreSame(message.CorrelationId, bindingData["CorrelationId"]); - IDictionary bindingDataUserProps = bindingData["UserProperties"] as Dictionary; + IDictionary bindingDataUserProps = bindingData["ApplicationProperties"] as IDictionary; Assert.NotNull(bindingDataUserProps); Assert.AreEqual("value1", bindingDataUserProps["prop1"]); Assert.AreEqual("value2", bindingDataUserProps["prop2"]); @@ -96,26 +90,20 @@ public void GetBindingData_SingleDispatch_ReturnsExpectedValue() [Test] public void GetBindingData_MultipleDispatch_ReturnsExpectedValue() { - var messages = new Message[3] + var messages = new ServiceBusReceivedMessage[3] { - new Message(Encoding.UTF8.GetBytes("Event 1")), - new Message(Encoding.UTF8.GetBytes("Event 2")), - new Message(Encoding.UTF8.GetBytes("Event 3")), + CreateMessageWithSystemProperties("Event 1"), + CreateMessageWithSystemProperties("Event 2"), + CreateMessageWithSystemProperties("Event 3"), }; - foreach (var message in messages) - { - SystemPropertiesCollection sysProps = GetSystemProperties(); - TestHelpers.SetField(message, "SystemProperties", sysProps); - } - - var input = ServiceBusTriggerInput.CreateBatch(messages); + var input = ServiceBusTriggerInput.CreateBatch(messages); var strategy = new ServiceBusTriggerBindingStrategy(); var bindingData = strategy.GetBindingData(input); Assert.AreEqual(15, bindingData.Count); - Assert.AreSame(input.MessageReceiver as MessageReceiver, bindingData["MessageReceiver"]); - Assert.AreSame(input.MessageReceiver as IMessageSession, bindingData["MessageSession"]); + Assert.AreSame(input.Receiver, bindingData["MessageReceiver"]); + Assert.AreSame(input.SessionReceiver, bindingData["MessageSession"]); // verify an array was created for each binding data type Assert.AreEqual(messages.Length, ((int[])bindingData["DeliveryCountArray"]).Length); @@ -128,9 +116,9 @@ public void GetBindingData_MultipleDispatch_ReturnsExpectedValue() Assert.AreEqual(messages.Length, ((string[])bindingData["ReplyToArray"]).Length); Assert.AreEqual(messages.Length, ((long[])bindingData["SequenceNumberArray"]).Length); Assert.AreEqual(messages.Length, ((string[])bindingData["ToArray"]).Length); - Assert.AreEqual(messages.Length, ((string[])bindingData["LabelArray"]).Length); + Assert.AreEqual(messages.Length, ((string[])bindingData["SubjectArray"]).Length); Assert.AreEqual(messages.Length, ((string[])bindingData["CorrelationIdArray"]).Length); - Assert.AreEqual(messages.Length, ((IDictionary[])bindingData["UserPropertiesArray"]).Length); + Assert.AreEqual(messages.Length, ((IDictionary[])bindingData["ApplicationPropertiesArray"]).Length); } [Test] @@ -143,8 +131,8 @@ public void BindSingle_Returns_Exptected_Message() var contract = strategy.GetBindingData(triggerInput); - Message single = strategy.BindSingle(triggerInput, null); - string body = Encoding.UTF8.GetString(single.Body); + ServiceBusReceivedMessage single = strategy.BindSingle(triggerInput, null); + string body = single.Body.ToString(); Assert.AreEqual(data, body); Assert.Null(contract["MessageReceiver"]); @@ -166,21 +154,23 @@ private static void CheckBindingContract(Dictionary bindingDataCon Assert.AreEqual(typeof(string), bindingDataContract["To"]); Assert.AreEqual(typeof(string), bindingDataContract["Label"]); Assert.AreEqual(typeof(string), bindingDataContract["CorrelationId"]); - Assert.AreEqual(typeof(IDictionary), bindingDataContract["UserProperties"]); - Assert.AreEqual(typeof(MessageReceiver), bindingDataContract["MessageReceiver"]); - Assert.AreEqual(typeof(IMessageSession), bindingDataContract["MessageSession"]); + Assert.AreEqual(typeof(IDictionary), bindingDataContract["ApplicationProperties"]); + Assert.AreEqual(typeof(ServiceBusReceiver), bindingDataContract["MessageReceiver"]); + Assert.AreEqual(typeof(ServiceBusSessionReceiver), bindingDataContract["MessageSession"]); } - private static SystemPropertiesCollection GetSystemProperties() + private static ServiceBusReceivedMessage CreateMessageWithSystemProperties(string body = default, IDictionary applicationProperties = default) { - SystemPropertiesCollection sysProps = new SystemPropertiesCollection(); - TestHelpers.SetField(sysProps, "deliveryCount", 1); - TestHelpers.SetField(sysProps, "lockedUntilUtc", DateTime.MinValue); - TestHelpers.SetField(sysProps, "sequenceNumber", 1); - TestHelpers.SetField(sysProps, "enqueuedTimeUtc", DateTime.MinValue); - TestHelpers.SetField(sysProps, "lockTokenGuid", Guid.NewGuid()); - TestHelpers.SetField(sysProps, "deadLetterSource", "test"); - return sysProps; + ServiceBusReceivedMessage receivedMessage = ServiceBusModelFactory.ServiceBusReceivedMessage( + body: body == null ? null : new BinaryData(body), + deliveryCount: 1, + lockedUntil: DateTime.MinValue, + sequenceNumber: 1, + enqueuedTime: DateTime.MinValue, + lockTokenGuid: Guid.NewGuid(), + deadLetterSource: "test", + properties: applicationProperties); + return receivedMessage; } } } diff --git a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/WebJobsServiceBusTestBase.cs b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/WebJobsServiceBusTestBase.cs index 19183f30f5e2..42af65bb40ef 100644 --- a/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/WebJobsServiceBusTestBase.cs +++ b/sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/tests/WebJobsServiceBusTestBase.cs @@ -5,12 +5,11 @@ using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; -using System.Text; using System.Threading.Tasks; using System.Xml; using Azure.Core.TestFramework; +using Azure.Messaging.ServiceBus; using Azure.Messaging.ServiceBus.Tests; -using Microsoft.Azure.ServiceBus; using Microsoft.Azure.WebJobs.Host.TestCommon; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -125,7 +124,7 @@ public async Task FixtureTearDown() }) .ConfigureDefaultTestHost(b => { - b.AddServiceBus(); + b.AddServiceBus(options => options.RetryOptions.TryTimeout = TimeSpan.FromSeconds(10)); }) .ConfigureServices(s => { @@ -144,16 +143,14 @@ public async Task FixtureTearDown() internal async Task WriteQueueMessage(string message, string sessionId = null, string connectionString = default, string queueName = default) { - QueueClient queueClient = new QueueClient( - connectionString ?? ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, - queueName ?? _firstQueueScope.QueueName); - Message messageObj = new Message(Encoding.UTF8.GetBytes(message)); + await using ServiceBusClient client = new ServiceBusClient(connectionString ?? ServiceBusTestEnvironment.Instance.ServiceBusConnectionString); + var sender = client.CreateSender(queueName ?? _firstQueueScope.QueueName); + ServiceBusMessage messageObj = new ServiceBusMessage(message); if (!string.IsNullOrEmpty(sessionId)) { messageObj.SessionId = sessionId; } - await queueClient.SendAsync(messageObj); - await queueClient.CloseAsync(); + await sender.SendMessageAsync(messageObj); } internal async Task WriteQueueMessage(TestPoco obj, string sessionId = null) @@ -170,26 +167,26 @@ internal async Task WriteQueueMessage(TestPoco obj, string sessionId = null) payload = memoryStream.ToArray(); } - QueueClient queueClient = new QueueClient(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, _firstQueueScope.QueueName); - Message messageObj = new Message(payload); + await using ServiceBusClient client = new ServiceBusClient(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString); + var sender = client.CreateSender(_firstQueueScope.QueueName); + ServiceBusMessage messageObj = new ServiceBusMessage(payload); if (!string.IsNullOrEmpty(sessionId)) { messageObj.SessionId = sessionId; } - await queueClient.SendAsync(messageObj); - await queueClient.CloseAsync(); + await sender.SendMessageAsync(messageObj); } internal async Task WriteTopicMessage(string message, string sessionId = null) { - TopicClient client = new TopicClient(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString, _topicScope.TopicName); - Message messageObj = new Message(Encoding.UTF8.GetBytes(message)); + await using ServiceBusClient client = new ServiceBusClient(ServiceBusTestEnvironment.Instance.ServiceBusConnectionString); + var sender = client.CreateSender(_topicScope.TopicName); + ServiceBusMessage messageObj = new ServiceBusMessage(message); if (!string.IsNullOrEmpty(sessionId)) { messageObj.SessionId = sessionId; } - await client.SendAsync(messageObj); - await client.CloseAsync(); + await sender.SendMessageAsync(messageObj); } } } \ No newline at end of file