diff --git a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/AttributeDiscoverer.cs b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/AttributeDiscoverer.cs deleted file mode 100644 index 1a1434c7..00000000 --- a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/AttributeDiscoverer.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace NServiceBus.AzureFunctions.InProcess.ServiceBus -{ - using System; - using System.Diagnostics; - using System.Reflection; - using Microsoft.Azure.WebJobs; - - static class TriggerDiscoverer - { - /// - /// Attempts to derive the required configuration information from the Azure Function and trigger attributes via reflection. - /// - public static TTransportTriggerAttribute TryGet() where TTransportTriggerAttribute : Attribute - { - var frames = new StackTrace().GetFrames(); - foreach (var stackFrame in frames) - { - var method = stackFrame.GetMethod(); - var functionAttribute = method.GetCustomAttribute(false); - if (functionAttribute != null) - { - foreach (var parameter in method.GetParameters()) - { - var triggerConfiguration = parameter.GetCustomAttribute(false); - if (triggerConfiguration != null) - { - return triggerConfiguration; - } - } - - return null; - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/FunctionEndpoint.cs b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/FunctionEndpoint.cs index d147f1ee..f390ae12 100644 --- a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/FunctionEndpoint.cs +++ b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/FunctionEndpoint.cs @@ -11,6 +11,7 @@ using Logging; using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus.Core; + using Microsoft.Azure.WebJobs; using Microsoft.Extensions.Logging; using Transport; using ExecutionContext = Microsoft.Azure.WebJobs.ExecutionContext; @@ -29,8 +30,17 @@ internal FunctionEndpoint(IStartableEndpointWithExternallyManagedContainer exter endpointFactory = _ => externallyManagedContainerEndpoint.Start(serviceProvider); } + /// + /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. This method will lookup the setting to determine whether to use transactional or non-transactional processing. + /// + public Task Process(Message message, ExecutionContext executionContext, IMessageReceiver messageReceiver, ILogger functionsLogger = null) => + ReflectionHelper.GetAutoCompleteValue() + ? ProcessNonTransactional(message, executionContext, messageReceiver, functionsLogger) + : ProcessTransactional(message, executionContext, messageReceiver, functionsLogger); + /// /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. All messages are committed transactionally with the successful processing of the incoming message. + /// Requires to be set to false! /// public async Task ProcessTransactional(Message message, ExecutionContext executionContext, IMessageReceiver messageReceiver, ILogger functionsLogger = null) { @@ -57,6 +67,15 @@ await InitializeEndpointIfNecessary(functionExecutionContext, CancellationToken. /// /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. /// + public Task ProcessNonTransactional(Message message, ExecutionContext executionContext, IMessageReceiver messageReceiver, ILogger functionsLogger = null) => Process(message, executionContext, functionsLogger); + + /// + /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. + /// + [ObsoleteEx( + ReplacementTypeOrMember = "Process(Message, ExecutionContext, IMessageReceiver, ILogger)", + TreatAsErrorFromVersion = "2", + RemoveInVersion = "3")] public async Task Process(Message message, ExecutionContext executionContext, ILogger functionsLogger = null) { FunctionsLoggerFactory.Instance.SetCurrentLogger(functionsLogger); diff --git a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/IFunctionEndpoint.cs b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/IFunctionEndpoint.cs index 496d5f40..5b7de981 100644 --- a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/IFunctionEndpoint.cs +++ b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/IFunctionEndpoint.cs @@ -14,13 +14,17 @@ public interface IFunctionEndpoint { /// - /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. All messages are committed transactionally with the successful processing of the incoming message. + /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. This method will lookup the setting to determine whether to use transactional or non-transactional processing. /// - Task ProcessTransactional(Message message, ExecutionContext executionContext, IMessageReceiver messageReceiver, ILogger functionsLogger = null); + Task Process(Message message, ExecutionContext executionContext, IMessageReceiver messageReceiver, ILogger functionsLogger = null); /// /// Processes a message received from an AzureServiceBus trigger using the NServiceBus message pipeline. /// + [ObsoleteEx( + ReplacementTypeOrMember = "Process(Message, ExecutionContext, IMessageReceiver, ILogger)", + TreatAsErrorFromVersion = "2", + RemoveInVersion = "3")] Task Process(Message message, ExecutionContext executionContext, ILogger functionsLogger = null); /// diff --git a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ReflectionHelper.cs b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ReflectionHelper.cs new file mode 100644 index 00000000..21d7bd08 --- /dev/null +++ b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ReflectionHelper.cs @@ -0,0 +1,50 @@ +namespace NServiceBus.AzureFunctions.InProcess.ServiceBus +{ + using System; + using System.Diagnostics; + using System.Reflection; + using Microsoft.Azure.ServiceBus; + using Microsoft.Azure.WebJobs; + + class ReflectionHelper + { + public static bool GetAutoCompleteValue() + { + var triggerAttribute = FindTriggerAttributeInternal(); + if (triggerAttribute != null) + { + return triggerAttribute.AutoComplete; + } + + throw new Exception($"Could not locate {nameof(ServiceBusTriggerAttribute)} to infer the AutoComplete setting. Make sure that the function trigger contains a parameter decorated with {nameof(ServiceBusTriggerAttribute)} or use the advanced APIs exposed via the {nameof(FunctionEndpoint)} type instead."); + } + + public static ServiceBusTriggerAttribute FindBusTriggerAttribute() => FindTriggerAttributeInternal(); + + static ServiceBusTriggerAttribute FindTriggerAttributeInternal() + { + var st = new StackTrace(skipFrames: 2); // skip first two frames because it is this method + the public method + var frames = st.GetFrames(); + foreach (var frame in frames) + { + var method = frame?.GetMethod(); + if (method?.GetCustomAttribute(false) != null) + { + foreach (var parameter in method.GetParameters()) + { + ServiceBusTriggerAttribute serviceBusTriggerAttribute; + if (parameter.ParameterType == typeof(Message) + && (serviceBusTriggerAttribute = parameter.GetCustomAttribute(false)) != null) + { + return serviceBusTriggerAttribute; + } + } + + return null; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs index 0b3a6cc0..3a2f544e 100644 --- a/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs +++ b/src/NServiceBus.AzureFunctions.InProcess.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using AzureFunctions.InProcess.ServiceBus; using Logging; - using Microsoft.Azure.WebJobs; using Serialization; using Transport; @@ -77,10 +76,10 @@ public ServiceBusTriggeredEndpointConfiguration(string endpointName, string conn /// public static ServiceBusTriggeredEndpointConfiguration FromAttributes() { - var configuration = TriggerDiscoverer.TryGet(); - if (configuration != null) + var serviceBusTriggerAttribute = ReflectionHelper.FindBusTriggerAttribute(); + if (serviceBusTriggerAttribute != null) { - return new ServiceBusTriggeredEndpointConfiguration(configuration.QueueName, configuration.Connection); + return new ServiceBusTriggeredEndpointConfiguration(serviceBusTriggerAttribute.QueueName, serviceBusTriggerAttribute.Connection); } throw new Exception( diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_enable_transactions.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_enable_transactions.approved.txt index bfdbfd4a..4c8ea9d2 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_enable_transactions.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_enable_transactions.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_override_trigger_function_name.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_override_trigger_function_name.approved.txt index efad0593..fd792927 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_override_trigger_function_name.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Can_override_trigger_function_name.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } @@ -23,6 +23,6 @@ class FunctionEndpointTrigger ILogger logger, ExecutionContext executionContext) { - await endpoint.Process(message, executionContext, logger); + await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger); } } \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.NameIsStringValue.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.NameIsStringValue.approved.txt index 99e48622..e16a7f0f 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.NameIsStringValue.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.NameIsStringValue.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } @@ -23,6 +23,6 @@ class FunctionEndpointTrigger ILogger logger, ExecutionContext executionContext) { - await endpoint.Process(message, executionContext, logger); + await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger); } } \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Two_optionals_out_of_order.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Two_optionals_out_of_order.approved.txt index bbb25c31..70cc6c8a 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Two_optionals_out_of_order.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Two_optionals_out_of_order.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Use_two_optionals.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Use_two_optionals.approved.txt index bbb25c31..70cc6c8a 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Use_two_optionals.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.Use_two_optionals.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingFullyQualifiedAttributeName.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingFullyQualifiedAttributeName.approved.txt index 99e48622..e16a7f0f 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingFullyQualifiedAttributeName.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingFullyQualifiedAttributeName.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } @@ -23,6 +23,6 @@ class FunctionEndpointTrigger ILogger logger, ExecutionContext executionContext) { - await endpoint.Process(message, executionContext, logger); + await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger); } } \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingNamespace.approved.txt b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingNamespace.approved.txt index 99e48622..e16a7f0f 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingNamespace.approved.txt +++ b/src/NServiceBus.AzureFunctions.SourceGenerator.Tests/ApprovalFiles/SourceGeneratorApprovals2.UsingNamespace.approved.txt @@ -8,9 +8,9 @@ using NServiceBus; class FunctionEndpointTrigger { - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) { this.endpoint = endpoint; } @@ -23,6 +23,6 @@ class FunctionEndpointTrigger ILogger logger, ExecutionContext executionContext) { - await endpoint.Process(message, executionContext, logger); + await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger); } } \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.SourceGenerator/TriggerFunctionGenerator2.cs b/src/NServiceBus.AzureFunctions.SourceGenerator/TriggerFunctionGenerator2.cs index 98ab503b..47805cb6 100644 --- a/src/NServiceBus.AzureFunctions.SourceGenerator/TriggerFunctionGenerator2.cs +++ b/src/NServiceBus.AzureFunctions.SourceGenerator/TriggerFunctionGenerator2.cs @@ -116,9 +116,9 @@ public void Execute(GeneratorExecutionContext context) class FunctionEndpointTrigger {{ - readonly IFunctionEndpoint endpoint; + readonly FunctionEndpoint endpoint; - public FunctionEndpointTrigger(IFunctionEndpoint endpoint) + public FunctionEndpointTrigger(FunctionEndpoint endpoint) {{ this.endpoint = endpoint; }} @@ -133,7 +133,7 @@ public async Task Run( {{ {(syntaxReceiver.enableCrossEntityTransactions ? "await endpoint.ProcessTransactional(message, executionContext, messageReceiver, logger);" - : "await endpoint.Process(message, executionContext, logger);")} + : "await endpoint.ProcessNonTransactional(message, executionContext, messageReceiver, logger);")} }} }}"; context.AddSource("NServiceBus__FunctionEndpointTrigger", SourceText.From(source, Encoding.UTF8)); diff --git a/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt b/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt index b7783608..d46ca1f2 100644 --- a/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt +++ b/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt @@ -3,7 +3,11 @@ namespace NServiceBus { public class FunctionEndpoint : NServiceBus.IFunctionEndpoint { + [System.Obsolete("Use `Process(Message, ExecutionContext, IMessageReceiver, ILogger)` instead. Will" + + " be treated as an error from version 2.0.0. Will be removed in version 3.0.0.", false)] public System.Threading.Tasks.Task Process(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } + public System.Threading.Tasks.Task Process(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Azure.ServiceBus.Core.IMessageReceiver messageReceiver, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } + public System.Threading.Tasks.Task ProcessNonTransactional(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Azure.ServiceBus.Core.IMessageReceiver messageReceiver, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } public System.Threading.Tasks.Task ProcessTransactional(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Azure.ServiceBus.Core.IMessageReceiver messageReceiver, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } public System.Threading.Tasks.Task Publish(object message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } public System.Threading.Tasks.Task Publish(object message, NServiceBus.PublishOptions options, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null) { } @@ -30,8 +34,10 @@ namespace NServiceBus } public interface IFunctionEndpoint { + [System.Obsolete("Use `Process(Message, ExecutionContext, IMessageReceiver, ILogger)` instead. Will" + + " be treated as an error from version 2.0.0. Will be removed in version 3.0.0.", false)] System.Threading.Tasks.Task Process(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null); - System.Threading.Tasks.Task ProcessTransactional(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Azure.ServiceBus.Core.IMessageReceiver messageReceiver, Microsoft.Extensions.Logging.ILogger functionsLogger = null); + System.Threading.Tasks.Task Process(Microsoft.Azure.ServiceBus.Message message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Azure.ServiceBus.Core.IMessageReceiver messageReceiver, Microsoft.Extensions.Logging.ILogger functionsLogger = null); System.Threading.Tasks.Task Publish(object message, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null); System.Threading.Tasks.Task Publish(object message, NServiceBus.PublishOptions options, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null); System.Threading.Tasks.Task Publish(System.Action messageConstructor, Microsoft.Azure.WebJobs.ExecutionContext executionContext, Microsoft.Extensions.Logging.ILogger functionsLogger = null); diff --git a/src/ServiceBus.Tests/FunctionEndpointComponent.cs b/src/ServiceBus.Tests/FunctionEndpointComponent.cs index 3aa61dbf..a5b5f21e 100644 --- a/src/ServiceBus.Tests/FunctionEndpointComponent.cs +++ b/src/ServiceBus.Tests/FunctionEndpointComponent.cs @@ -100,7 +100,7 @@ public override async Task ComponentsStarted(CancellationToken token) { var transportMessage = MessageHelper.GenerateMessage(message); var context = new ExecutionContext(); - await endpoint.Process(transportMessage, context); + await endpoint.ProcessNonTransactional(transportMessage, context, null); } } diff --git a/src/ServiceBus.Tests/ReflectionHelperTests.cs b/src/ServiceBus.Tests/ReflectionHelperTests.cs new file mode 100644 index 00000000..bea7620f --- /dev/null +++ b/src/ServiceBus.Tests/ReflectionHelperTests.cs @@ -0,0 +1,148 @@ +namespace ServiceBus.Tests +{ + using System; + using Microsoft.Azure.ServiceBus; + using Microsoft.Azure.WebJobs; + using NServiceBus.AzureFunctions.InProcess.ServiceBus; + using NUnit.Framework; + + [TestFixture] + public class ReflectionHelperTests + { + [Test] + public void When_no_attributes_defined_should_throw() + { + var exception = Assert.Throws(() => ReflectionHelper.GetAutoCompleteValue()); + + StringAssert.Contains($"Could not locate {nameof(ServiceBusTriggerAttribute)} to infer the AutoComplete setting.", exception.Message); + } + + [Test] + public void When_no_function_name_attribute_defined_should_throw() + { + var exception = Assert.Throws(() => FunctionWithNoFunctionNameAttribute(null)); + + StringAssert.Contains($"Could not locate {nameof(ServiceBusTriggerAttribute)} to infer the AutoComplete setting.", exception.Message); + + void FunctionWithNoFunctionNameAttribute( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = true)] Message _) + { + ReflectionHelper.GetAutoCompleteValue(); + } + } + + [Test] + public void When_no_trigger_attribute_defined_should_throw() + { + var exception = Assert.Throws(() => FunctionWithNoServiceBusTriggerAttribute(null)); + + StringAssert.Contains($"Could not locate {nameof(ServiceBusTriggerAttribute)} to infer the AutoComplete setting.", exception.Message); + + [FunctionName("TestFunction")] + void FunctionWithNoServiceBusTriggerAttribute( + Message _) + { + ReflectionHelper.GetAutoCompleteValue(); + } + } + + [Test] + public void When_auto_complete_set_to_false_should_return_false() + { + FunctionTriggerWithAutoCompleteExplicitlySetToFalse(null); + + [FunctionName("TestFunction")] + void FunctionTriggerWithAutoCompleteExplicitlySetToFalse( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = false)] Message _) + { + Assert.IsFalse(ReflectionHelper.GetAutoCompleteValue()); + } + } + + [Test] + public void When_auto_complete_set_to_true_should_return_true() + { + FunctionTriggerWithAutoCompleteExplicitlySetToTrue(null); + + [FunctionName("TestFunction")] + void FunctionTriggerWithAutoCompleteExplicitlySetToTrue( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = true)] Message _) + { + Assert.IsTrue(ReflectionHelper.GetAutoCompleteValue()); + } + } + + [Test] + public void When_auto_complete_not_set_should_return_true() + { + FunctionTriggerWithoutAutoCompleteConfiguration(null); + + [FunctionName("TestFunction")] + void FunctionTriggerWithoutAutoCompleteConfiguration( + [ServiceBusTrigger("queueName", "subscriptionname")] Message _) + { + Assert.True(ReflectionHelper.GetAutoCompleteValue()); + } + } + + [Test] + public void When_helper_invoked_in_nested_methods() + { + NestedTrigger(null); + + [FunctionName("TestFunction")] + void NestedTrigger( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = false)] Message _) + { + One(); + } + + void One() + { + Two(); + } + + void Two() + { + Three(); + } + + void Three() + { + Assert.IsFalse(ReflectionHelper.GetAutoCompleteValue()); + } + } + + [Test] + public void When_helper_invoked_in_local_function() + { + LocalFunction(null); + + [FunctionName("TestFunction")] + void LocalFunction( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = false)] Message _) + { + LocalFunction(); + + void LocalFunction() + { + Assert.IsFalse(ReflectionHelper.GetAutoCompleteValue()); + } + } + } + + [Test] + public void When_helper_invoked_in_expression() + { + Expression(null); + + [FunctionName("TestFunction")] + void Expression( + [ServiceBusTrigger("queueName", "subscriptionname", AutoComplete = false)] Message _) + { + Func expression = () => ReflectionHelper.GetAutoCompleteValue(); + Assert.IsFalse(expression()); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceBus.Tests/ServiceBus.Tests.csproj b/src/ServiceBus.Tests/ServiceBus.Tests.csproj index 22cc3d0f..dfe67d94 100644 --- a/src/ServiceBus.Tests/ServiceBus.Tests.csproj +++ b/src/ServiceBus.Tests/ServiceBus.Tests.csproj @@ -4,6 +4,7 @@ netcoreapp3.1;net5.0 true ..\Test.snk + 9 diff --git a/src/ServiceBus.Tests/When_shipping_handlers_in_dedicated_assembly.cs b/src/ServiceBus.Tests/When_shipping_handlers_in_dedicated_assembly.cs index da2e6ec5..23dd7501 100644 --- a/src/ServiceBus.Tests/When_shipping_handlers_in_dedicated_assembly.cs +++ b/src/ServiceBus.Tests/When_shipping_handlers_in_dedicated_assembly.cs @@ -42,7 +42,7 @@ public async Task Should_load_handlers_from_assembly_when_using_FunctionsHostBui // we need to process an actual message to have the endpoint being created - await endpoint.Process(GenerateMessage(), new ExecutionContext()); + await endpoint.ProcessNonTransactional(GenerateMessage(), new ExecutionContext(), null); // The message handler assembly should be loaded now because scanning should find and load the handler assembly Assert.True(AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == "Testing.Handlers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"));