This commit is contained in:
Alexey Rodionov 2019-07-08 16:31:12 -07:00 коммит произвёл Alexey Rodionov
Родитель cf6a413859
Коммит adabc4f006
98 изменённых файлов: 78 добавлений и 5476 удалений

Просмотреть файл

@ -17,12 +17,13 @@ if (-not $?) { exit 1 }
$projects =
"src\Microsoft.Azure.WebJobs\WebJobs.csproj",
"src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj",
"src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.Sources.csproj",
"src\Microsoft.Azure.WebJobs.Logging\WebJobs.Logging.csproj",
"src\Microsoft.Azure.WebJobs.Logging.ApplicationInsights\WebJobs.Logging.ApplicationInsights.csproj",
"src\Microsoft.Azure.WebJobs.Extensions.EventHubs\WebJobs.Extensions.EventHubs.csproj",
"src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj",
"src\Microsoft.Azure.WebJobs.Extensions.Storage\WebJobs.Extensions.Storage.csproj",
"src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj"
"src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj",
"src\Microsoft.Azure.WebJobs.Host.TestCommon\WebJobs.Host.TestCommon.csproj"
foreach ($project in $projects)
{

Просмотреть файл

@ -35,12 +35,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{3B089351
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebJobs.Extensions.EventHubs.UnitTests", "test\Microsoft.Azure.WebJobs.Extensions.EventHubs.UnitTests\WebJobs.Extensions.EventHubs.UnitTests.csproj", "{27F0E6AC-505E-4BEC-81CA-8DF777DEA9C7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebJobs.Extensions.ServiceBus", "src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj", "{A2B3C676-3DF0-43B4-92A2-7E7DAA7BF439}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "WebJobs.Shared", "src\Microsoft.Azure.WebJobs.Shared\WebJobs.Shared.shproj", "{ADD036F5-2170-4B05-9E0A-C2ED0A08A929}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebJobs.Extensions.ServiceBus.UnitTests", "test\Microsoft.Azure.WebJobs.Extensions.ServiceBus.UnitTests\WebJobs.Extensions.ServiceBus.UnitTests.csproj", "{D37637D5-7EF9-43CB-86BE-537473CD613B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebJobs.Extensions.Storage", "src\Microsoft.Azure.WebJobs.Extensions.Storage\WebJobs.Extensions.Storage.csproj", "{A9733406-267C-4A53-AB07-D3A834E22153}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebJobs.Host.Storage", "src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj", "{DED33098-FE99-436C-96CC-B59A30BEF027}"
@ -114,14 +110,6 @@ Global
{27F0E6AC-505E-4BEC-81CA-8DF777DEA9C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27F0E6AC-505E-4BEC-81CA-8DF777DEA9C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27F0E6AC-505E-4BEC-81CA-8DF777DEA9C7}.Release|Any CPU.Build.0 = Release|Any CPU
{A2B3C676-3DF0-43B4-92A2-7E7DAA7BF439}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2B3C676-3DF0-43B4-92A2-7E7DAA7BF439}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2B3C676-3DF0-43B4-92A2-7E7DAA7BF439}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2B3C676-3DF0-43B4-92A2-7E7DAA7BF439}.Release|Any CPU.Build.0 = Release|Any CPU
{D37637D5-7EF9-43CB-86BE-537473CD613B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D37637D5-7EF9-43CB-86BE-537473CD613B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D37637D5-7EF9-43CB-86BE-537473CD613B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D37637D5-7EF9-43CB-86BE-537473CD613B}.Release|Any CPU.Build.0 = Release|Any CPU
{A9733406-267C-4A53-AB07-D3A834E22153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9733406-267C-4A53-AB07-D3A834E22153}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9733406-267C-4A53-AB07-D3A834E22153}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -158,7 +146,6 @@ Global
{C8EAAE01-E8CF-4131-9D4B-F0FDF00DA4BE} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{340AB554-5482-4B3D-B65F-46DFF5AF1684} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{27F0E6AC-505E-4BEC-81CA-8DF777DEA9C7} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{D37637D5-7EF9-43CB-86BE-537473CD613B} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{0CC5741F-ACDA-4DB8-9C17-074E8896F244} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{337B79EB-A3CB-4CE0-A7F2-DD5E638AC882} = {639967B0-0544-4C52-94AC-9A3D25E33256}
{C5E1A8E8-711F-4377-A8BD-7DB58E6C580D} = {639967B0-0544-4C52-94AC-9A3D25E33256}

Просмотреть файл

@ -56,10 +56,6 @@ test_script:
dotnet test .\test\Microsoft.Azure.Webjobs.Extensions.Storage.UnitTests\ -v q --no-build
$success = $success -and $?
dotnet test .\test\Microsoft.Azure.WebJobs.Extensions.ServiceBus.UnitTests\ -v q --no-build
$success = $success -and $?
if (-not $success) { exit 1 }

Просмотреть файл

@ -16,6 +16,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="3.0.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.0" />
@ -28,7 +29,6 @@
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Logging.ApplicationInsights\WebJobs.Logging.ApplicationInsights.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj" />
</ItemGroup>

Просмотреть файл

@ -1,83 +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;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class AsyncCollectorArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
Type parameterType = parameter.ParameterType;
if (!parameterType.IsGenericType)
{
return null;
}
Type genericTypeDefinition = parameterType.GetGenericTypeDefinition();
if (genericTypeDefinition != typeof(IAsyncCollector<>))
{
return null;
}
Type itemType = parameterType.GetGenericArguments()[0];
return CreateBinding(itemType);
}
private static IArgumentBinding<ServiceBusEntity> CreateBinding(Type itemType)
{
MethodInfo method = typeof(AsyncCollectorArgumentBindingProvider).GetMethod("CreateBindingGeneric",
BindingFlags.NonPublic | BindingFlags.Static);
Debug.Assert(method != null);
MethodInfo genericMethod = method.MakeGenericMethod(itemType);
Debug.Assert(genericMethod != null);
Func<IArgumentBinding<ServiceBusEntity>> lambda =
(Func<IArgumentBinding<ServiceBusEntity>>)Delegate.CreateDelegate(
typeof(Func<IArgumentBinding<ServiceBusEntity>>), genericMethod);
return lambda.Invoke();
}
private static IArgumentBinding<ServiceBusEntity> CreateBindingGeneric<TItem>()
{
return new AsyncCollectorArgumentBinding<TItem>(MessageConverterFactory.Create<TItem>());
}
private class AsyncCollectorArgumentBinding<TItem> : IArgumentBinding<ServiceBusEntity>
{
private readonly IConverter<TItem, Message> _converter;
public AsyncCollectorArgumentBinding(IConverter<TItem, Message> converter)
{
_converter = converter;
}
public Type ValueType
{
get { return typeof(IAsyncCollector<TItem>); }
}
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IAsyncCollector<TItem> collector = new MessageSenderAsyncCollector<TItem>(value, _converter,
context.FunctionInstanceId);
IValueProvider provider = new CollectorValueProvider(value, collector, typeof(IAsyncCollector<TItem>));
return Task.FromResult(provider);
}
}
}
}

Просмотреть файл

@ -1,41 +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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
/// <summary>
/// Utility class with factory method to create an instance of a strategy class implementing <see cref="IBindableServiceBusPath"/> interface.
/// </summary>
internal static class BindableServiceBusPath
{
/// <summary>
/// A factory method detecting parameters in supplied queue or topic name pattern and creating
/// an instance of relevant strategy class implementing <see cref="IBindableServiceBusPath"/>.
/// </summary>
/// <param name="queueOrTopicNamePattern">Service Bus queue or topic name pattern containing optional binding parameters.</param>
/// <returns>An object implementing <see cref="IBindableServiceBusPath"/></returns>
public static IBindableServiceBusPath Create(string queueOrTopicNamePattern)
{
if (queueOrTopicNamePattern == null)
{
throw new ArgumentNullException("queueOrTopicNamePattern");
}
BindingTemplate template = BindingTemplate.FromString(queueOrTopicNamePattern);
if (template.ParameterNames.Count() > 0)
{
return new ParameterizedServiceBusPath(template);
}
return new BoundServiceBusPath(queueOrTopicNamePattern);
}
}
}

Просмотреть файл

@ -1,42 +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.Collections.Generic;
using System.Linq;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
/// <summary>
/// Bindable queue or topic path strategy implementation for "degenerate" bindable patterns,
/// i.e. containing no parameters.
/// </summary>
internal class BoundServiceBusPath : IBindableServiceBusPath
{
private readonly string _queueOrTopicNamePattern;
public BoundServiceBusPath(string queueOrTopicNamePattern)
{
_queueOrTopicNamePattern = queueOrTopicNamePattern;
}
public string QueueOrTopicNamePattern
{
get { return _queueOrTopicNamePattern; }
}
public bool IsBound
{
get { return true; }
}
public IEnumerable<string> ParameterNames
{
get { return Enumerable.Empty<string>(); }
}
public string Bind(IReadOnlyDictionary<string, object> bindingData)
{
return QueueOrTopicNamePattern;
}
}
}

Просмотреть файл

@ -1,64 +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;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ByteArrayArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
if (!parameter.IsOut || parameter.ParameterType != typeof(byte[]).MakeByRefType())
{
return null;
}
return new ByteArrayArgumentBinding();
}
private class ByteArrayArgumentBinding : IArgumentBinding<ServiceBusEntity>
{
public Type ValueType
{
get { return typeof(byte[]); }
}
/// <remarks>
/// The out byte array parameter is processed as follows:
/// <list type="bullet">
/// <item>
/// <description>
/// If the value is <see langword="null"/>, no message will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value is an empty byte array, a message with empty content will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value is a non-empty byte array, a message with that content will be sent.
/// </description>
/// </item>
/// </list>
/// </remarks>
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IValueProvider provider = new NonNullConverterValueBinder<byte[]>(value,
new ByteArrayToBrokeredMessageConverter(), context.FunctionInstanceId);
return Task.FromResult(provider);
}
}
}
}

Просмотреть файл

@ -1,26 +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;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ByteArrayToBrokeredMessageConverter : IConverter<byte[], Message>
{
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public Message Convert(byte[] input)
{
if (input == null)
{
throw new InvalidOperationException("A brokered message cannot contain a null byte array instance.");
}
return new Message(input)
{
ContentType = ContentTypes.ApplicationOctetStream
};
}
}
}

Просмотреть файл

@ -1,83 +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;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class CollectorArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
Type parameterType = parameter.ParameterType;
if (!parameterType.IsGenericType)
{
return null;
}
Type genericTypeDefinition = parameterType.GetGenericTypeDefinition();
if (genericTypeDefinition != typeof(ICollector<>))
{
return null;
}
Type itemType = parameterType.GetGenericArguments()[0];
return CreateBinding(itemType);
}
private static IArgumentBinding<ServiceBusEntity> CreateBinding(Type itemType)
{
MethodInfo method = typeof(CollectorArgumentBindingProvider).GetMethod("CreateBindingGeneric",
BindingFlags.NonPublic | BindingFlags.Static);
Debug.Assert(method != null);
MethodInfo genericMethod = method.MakeGenericMethod(itemType);
Debug.Assert(genericMethod != null);
Func<IArgumentBinding<ServiceBusEntity>> lambda =
(Func<IArgumentBinding<ServiceBusEntity>>)Delegate.CreateDelegate(
typeof(Func<IArgumentBinding<ServiceBusEntity>>), genericMethod);
return lambda.Invoke();
}
private static IArgumentBinding<ServiceBusEntity> CreateBindingGeneric<TItem>()
{
return new CollectorArgumentBinding<TItem>(MessageConverterFactory.Create<TItem>());
}
private class CollectorArgumentBinding<TItem> : IArgumentBinding<ServiceBusEntity>
{
private readonly IConverter<TItem, Message> _converter;
public CollectorArgumentBinding(IConverter<TItem, Message> converter)
{
_converter = converter;
}
public Type ValueType
{
get { return typeof(ICollector<TItem>); }
}
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
ICollector<TItem> collector = new MessageSenderCollector<TItem>(value, _converter,
context.FunctionInstanceId);
IValueProvider provider = new CollectorValueProvider(value, collector, typeof(ICollector<TItem>));
return Task.FromResult(provider);
}
}
}
}

Просмотреть файл

@ -1,43 +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;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class CollectorValueProvider : IValueProvider
{
private readonly ServiceBusEntity _entity;
private readonly object _value;
private readonly Type _valueType;
public CollectorValueProvider(ServiceBusEntity entity, object value, Type valueType)
{
if (value != null && !valueType.IsAssignableFrom(value.GetType()))
{
throw new InvalidOperationException("value is not of the correct type.");
}
_entity = entity;
_value = value;
_valueType = valueType;
}
public Type Type
{
get { return _valueType; }
}
public Task<object> GetValueAsync()
{
return Task.FromResult(_value);
}
public string ToInvokeString()
{
return _entity.MessageSender.Path;
}
}
}

Просмотреть файл

@ -1,34 +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.Collections.Generic;
using System.Reflection;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class CompositeArgumentBindingProvider : IQueueArgumentBindingProvider
{
private readonly IEnumerable<IQueueArgumentBindingProvider> _providers;
public CompositeArgumentBindingProvider(params IQueueArgumentBindingProvider[] providers)
{
_providers = providers;
}
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
foreach (IQueueArgumentBindingProvider provider in _providers)
{
IArgumentBinding<ServiceBusEntity> binding = provider.TryCreate(parameter);
if (binding != null)
{
return binding;
}
}
return null;
}
}
}

Просмотреть файл

@ -1,54 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
using System.Diagnostics;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ConverterValueBinder<TInput> : IOrderedValueBinder
{
private readonly ServiceBusEntity _entity;
private readonly IConverter<TInput, Message> _converter;
private readonly Guid _functionInstanceId;
public ConverterValueBinder(ServiceBusEntity entity, IConverter<TInput, Message> converter,
Guid functionInstanceId)
{
_entity = entity;
_converter = converter;
_functionInstanceId = functionInstanceId;
}
public BindStepOrder StepOrder
{
get { return BindStepOrder.Enqueue; }
}
public Type Type
{
get { return typeof(TInput); }
}
public Task<object> GetValueAsync()
{
return Task.FromResult<object>(default(TInput));
}
public string ToInvokeString()
{
return _entity.MessageSender.Path;
}
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
Message message = _converter.Convert((TInput)value);
Debug.Assert(message != null);
return _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken);
}
}
}

Просмотреть файл

@ -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 System.Collections.Generic;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal interface IBindableServiceBusPath
{
string QueueOrTopicNamePattern { get; }
/// <summary>
/// Gets a value indicating whether this path is bound.
/// </summary>
bool IsBound { get; }
/// <summary>
/// Gets the collection of parameter names for the path.
/// </summary>
IEnumerable<string> ParameterNames { get; }
/// <summary>
/// Bind to the path.
/// </summary>
/// <param name="bindingData">The binding data.</param>
/// <returns>The path binding.</returns>
string Bind(IReadOnlyDictionary<string, object> bindingData);
/// <summary>
/// Gets a string representation of the path.
/// </summary>
/// <returns></returns>
string ToString();
}
}

Просмотреть файл

@ -1,13 +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.Reflection;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal interface IQueueArgumentBindingProvider
{
IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter);
}
}

Просмотреть файл

@ -1,100 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class MessageArgumentBinding : IArgumentBinding<ServiceBusEntity>
{
public Type ValueType
{
get { return typeof(Message); }
}
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IValueProvider provider = new MessageValueBinder(value, context.FunctionInstanceId);
return Task.FromResult(provider);
}
private class MessageValueBinder : IOrderedValueBinder
{
private readonly ServiceBusEntity _entity;
private readonly Guid _functionInstanceId;
public MessageValueBinder(ServiceBusEntity entity, Guid functionInstanceId)
{
_entity = entity;
_functionInstanceId = functionInstanceId;
}
public BindStepOrder StepOrder
{
get { return BindStepOrder.Enqueue; }
}
public Type Type
{
get { return typeof(Message); }
}
public Task<object> GetValueAsync()
{
return Task.FromResult<object>(null);
}
public string ToInvokeString()
{
return _entity.MessageSender.Path;
}
/// <summary>
/// Sends a Message to the bound queue.
/// </summary>
/// <param name="value">BrokeredMessage instance as retrieved from user's WebJobs method argument.</param>
/// <param name="cancellationToken">a cancellation token</param>
/// <remarks>
/// The out message parameter is processed as follows:
/// <list type="bullet">
/// <item>
/// <description>
/// If the value is <see langword="null"/>, no message will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value has empty content, a message with empty content will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value has non-empty content, a message with that content will be sent.
/// </description>
/// </item>
/// </list>
/// </remarks>
public async Task SetValueAsync(object value, CancellationToken cancellationToken)
{
if (value == null)
{
return;
}
var message = (Message)value;
await _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken);
}
}
}
}

Просмотреть файл

@ -1,23 +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.Reflection;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class MessageArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
if (!parameter.IsOut || parameter.ParameterType != typeof(Message).MakeByRefType())
{
return null;
}
return new MessageArgumentBinding();
}
}
}

Просмотреть файл

@ -1,43 +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;
using System.Collections;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal static class MessageConverterFactory
{
internal static IConverter<TInput, Message> Create<TInput>()
{
if (typeof(TInput) == typeof(Message))
{
return (IConverter<TInput, Message>)new IdentityConverter<TInput>();
}
else if (typeof(TInput) == typeof(string))
{
return (IConverter<TInput, Message>)new StringToBrokeredMessageConverter();
}
else if (typeof(TInput) == typeof(byte[]))
{
return (IConverter<TInput, Message>)new ByteArrayToBrokeredMessageConverter();
}
else
{
if (typeof(TInput).IsPrimitive)
{
throw new NotSupportedException("Primitive types are not supported.");
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(TInput)))
{
throw new InvalidOperationException("Nested collections are not supported.");
}
return new UserTypeToBrokeredMessageConverter<TInput>();
}
}
}
}

Просмотреть файл

@ -1,80 +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;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class MessageSenderArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
if (parameter.ParameterType != typeof(MessageSender))
{
return null;
}
return new MessageSenderArgumentBinding();
}
internal class MessageSenderArgumentBinding : IArgumentBinding<ServiceBusEntity>
{
public Type ValueType
{
get { return typeof(MessageSender); }
}
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IValueProvider provider = new MessageSenderValueBinder(value.MessageSender);
return Task.FromResult(provider);
}
private class MessageSenderValueBinder : IValueBinder
{
private readonly MessageSender _messageSender;
public MessageSenderValueBinder(MessageSender messageSender)
{
_messageSender = messageSender;
}
public BindStepOrder StepOrder
{
get { return BindStepOrder.Enqueue; }
}
public Type Type
{
get { return typeof(MessageSender); }
}
public Task<object> GetValueAsync()
{
return Task.FromResult<object>(_messageSender);
}
public string ToInvokeString()
{
return _messageSender.Path;
}
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}
}
}

Просмотреть файл

@ -1,53 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class MessageSenderAsyncCollector<T> : IAsyncCollector<T>
{
private readonly ServiceBusEntity _entity;
private readonly IConverter<T, Message> _converter;
private readonly Guid _functionInstanceId;
public MessageSenderAsyncCollector(ServiceBusEntity entity, IConverter<T, Message> converter,
Guid functionInstanceId)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
if (converter == null)
{
throw new ArgumentNullException("converter");
}
_entity = entity;
_converter = converter;
_functionInstanceId = functionInstanceId;
}
public Task AddAsync(T item, CancellationToken cancellationToken)
{
Message message = _converter.Convert(item);
if (message == null)
{
throw new InvalidOperationException("Cannot enqueue a null brokered message instance.");
}
return _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken);
}
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
// Batching not supported.
return Task.FromResult(0);
}
}
}

Просмотреть файл

@ -1,48 +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;
using System.Threading;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class MessageSenderCollector<T> : ICollector<T>
{
private readonly ServiceBusEntity _entity;
private readonly IConverter<T, Message> _converter;
private readonly Guid _functionInstanceId;
public MessageSenderCollector(ServiceBusEntity entity, IConverter<T, Message> converter,
Guid functionInstanceId)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
if (converter == null)
{
throw new ArgumentNullException("converter");
}
_entity = entity;
_converter = converter;
_functionInstanceId = functionInstanceId;
}
public void Add(T item)
{
Message message = _converter.Convert(item);
if (message == null)
{
throw new InvalidOperationException("Cannot enqueue a null brokered message instance.");
}
_entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId,
CancellationToken.None).GetAwaiter().GetResult();
}
}
}

Просмотреть файл

@ -1,32 +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;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.ServiceBus.Listeners;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal static class MessageSenderExtensions
{
public static async Task SendAndCreateEntityIfNotExists(this MessageSender sender, Message message,
Guid functionInstanceId, EntityType entityType, CancellationToken cancellationToken)
{
if (sender == null)
{
throw new ArgumentNullException("sender");
}
ServiceBusCausalityHelper.EncodePayload(functionInstanceId, message);
cancellationToken.ThrowIfCancellationRequested();
await sender.SendAsync(message);
return;
}
}
}

Просмотреть файл

@ -1,62 +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;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
// Same as ConverterValueBinder, but doesn't enqueue null values.
internal class NonNullConverterValueBinder<TInput> : IOrderedValueBinder
{
private readonly ServiceBusEntity _entity;
private readonly IConverter<TInput, Message> _converter;
private readonly Guid _functionInstanceId;
public NonNullConverterValueBinder(ServiceBusEntity entity, IConverter<TInput, Message> converter,
Guid functionInstanceId)
{
_entity = entity;
_converter = converter;
_functionInstanceId = functionInstanceId;
}
public BindStepOrder StepOrder
{
get { return BindStepOrder.Enqueue; }
}
public Type Type
{
get { return typeof(TInput); }
}
public Task<object> GetValueAsync()
{
return Task.FromResult<object>(null);
}
public string ToInvokeString()
{
return _entity.MessageSender.Path;
}
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
if (value == null)
{
return Task.FromResult(0);
}
Debug.Assert(value is TInput);
Message message = _converter.Convert((TInput)value);
Debug.Assert(message != null);
return _entity.SendAndCreateEntityIfNotExistsAsync(message, _functionInstanceId, cancellationToken);
}
}
}

Просмотреть файл

@ -1,43 +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.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Converters;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class OutputConverter<TInput> : IAsyncObjectToTypeConverter<ServiceBusEntity>
where TInput : class
{
private readonly IAsyncConverter<TInput, ServiceBusEntity> _innerConverter;
public OutputConverter(IAsyncConverter<TInput, ServiceBusEntity> innerConverter)
{
_innerConverter = innerConverter;
}
public async Task<ConversionResult<ServiceBusEntity>> TryConvertAsync(object input,
CancellationToken cancellationToken)
{
TInput typedInput = input as TInput;
if (typedInput == null)
{
return new ConversionResult<ServiceBusEntity>
{
Succeeded = false,
Result = null
};
}
ServiceBusEntity entity = await _innerConverter.ConvertAsync(typedInput, cancellationToken);
return new ConversionResult<ServiceBusEntity>
{
Succeeded = true,
Result = entity
};
}
}
}

Просмотреть файл

@ -1,55 +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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
/// <summary>
/// Implementation of <see cref="IBindableServiceBusPath"/> strategy for paths
/// containing one or more parameters.
/// </summary>
internal class ParameterizedServiceBusPath : IBindableServiceBusPath
{
private readonly BindingTemplate _template;
public ParameterizedServiceBusPath(BindingTemplate template)
{
Debug.Assert(template != null, "template must not be null");
Debug.Assert(template.ParameterNames.Count() > 0, "template must contain one or more parameters");
_template = template;
}
public string QueueOrTopicNamePattern
{
get { return _template.Pattern; }
}
public bool IsBound
{
get { return false; }
}
public IEnumerable<string> ParameterNames
{
get { return _template.ParameterNames; }
}
public string Bind(IReadOnlyDictionary<string, object> bindingData)
{
if (bindingData == null)
{
throw new ArgumentNullException("bindingData");
}
string queueOrTopicName = _template.Bind(bindingData);
return queueOrTopicName;
}
}
}

Просмотреть файл

@ -1,119 +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;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Extensions.Configuration;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ServiceBusAttributeBindingProvider : IBindingProvider
{
private static readonly IQueueArgumentBindingProvider InnerProvider =
new CompositeArgumentBindingProvider(
new MessageSenderArgumentBindingProvider(),
new MessageArgumentBindingProvider(),
new StringArgumentBindingProvider(),
new ByteArrayArgumentBindingProvider(),
new UserTypeArgumentBindingProvider(),
new CollectorArgumentBindingProvider(),
new AsyncCollectorArgumentBindingProvider());
private readonly INameResolver _nameResolver;
private readonly ServiceBusOptions _options;
private readonly IConfiguration _configuration;
private readonly MessagingProvider _messagingProvider;
public ServiceBusAttributeBindingProvider(INameResolver nameResolver, ServiceBusOptions options, IConfiguration configuration, MessagingProvider messagingProvider)
{
if (nameResolver == null)
{
throw new ArgumentNullException(nameof(nameResolver));
}
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (messagingProvider == null)
{
throw new ArgumentNullException(nameof(messagingProvider));
}
_nameResolver = nameResolver;
_options = options;
_configuration = configuration;
_messagingProvider = messagingProvider;
}
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
ParameterInfo parameter = context.Parameter;
var attribute = TypeUtility.GetResolvedAttribute<ServiceBusAttribute>(parameter);
if (attribute == null)
{
return Task.FromResult<IBinding>(null);
}
string queueOrTopicName = Resolve(attribute.QueueOrTopicName);
IBindableServiceBusPath path = BindableServiceBusPath.Create(queueOrTopicName);
ValidateContractCompatibility(path, context.BindingDataContract);
IArgumentBinding<ServiceBusEntity> argumentBinding = InnerProvider.TryCreate(parameter);
if (argumentBinding == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Can't bind ServiceBus to type '{0}'.", parameter.ParameterType));
}
attribute.Connection = Resolve(attribute.Connection);
ServiceBusAccount account = new ServiceBusAccount(_options, _configuration, attribute);
IBinding binding = new ServiceBusBinding(parameter.Name, argumentBinding, account, _options, path, attribute, _messagingProvider);
return Task.FromResult<IBinding>(binding);
}
private static void ValidateContractCompatibility(IBindableServiceBusPath path, IReadOnlyDictionary<string, Type> bindingDataContract)
{
if (path == null)
{
throw new ArgumentNullException("path");
}
IEnumerable<string> parameterNames = path.ParameterNames;
if (parameterNames != null)
{
foreach (string parameterName in parameterNames)
{
if (bindingDataContract != null && !bindingDataContract.ContainsKey(parameterName))
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "No binding parameter exists for '{0}'.", parameterName));
}
}
}
}
private string Resolve(string queueName)
{
if (_nameResolver == null)
{
return queueName;
}
return _nameResolver.ResolveWholeString(queueName);
}
}
}

Просмотреть файл

@ -1,101 +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;
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;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ServiceBusBinding : IBinding
{
private readonly string _parameterName;
private readonly IArgumentBinding<ServiceBusEntity> _argumentBinding;
private readonly ServiceBusAccount _account;
private readonly IBindableServiceBusPath _path;
private readonly IAsyncObjectToTypeConverter<ServiceBusEntity> _converter;
private readonly EntityType _entityType;
private readonly MessagingProvider _messagingProvider;
public ServiceBusBinding(string parameterName, IArgumentBinding<ServiceBusEntity> argumentBinding, ServiceBusAccount account, ServiceBusOptions config, IBindableServiceBusPath path, ServiceBusAttribute attr, MessagingProvider messagingProvider)
{
_parameterName = parameterName;
_argumentBinding = argumentBinding;
_account = account;
_path = path;
_entityType = attr.EntityType;
_messagingProvider = messagingProvider;
_converter = new OutputConverter<string>(new StringToServiceBusEntityConverter(account, _path, _entityType, _messagingProvider));
}
public bool FromAttribute
{
get { return true; }
}
public async Task<IValueProvider> BindAsync(BindingContext context)
{
context.CancellationToken.ThrowIfCancellationRequested();
string boundQueueName = _path.Bind(context.BindingData);
var messageSender = _messagingProvider.CreateMessageSender(boundQueueName, _account.ConnectionString);
var entity = new ServiceBusEntity
{
MessageSender = messageSender,
EntityType = _entityType
};
return await BindAsync(entity, context.ValueContext);
}
public async Task<IValueProvider> BindAsync(object value, ValueBindingContext context)
{
ConversionResult<ServiceBusEntity> conversionResult = await _converter.TryConvertAsync(value, context.CancellationToken);
if (!conversionResult.Succeeded)
{
throw new InvalidOperationException("Unable to convert value to ServiceBusEntity.");
}
return await BindAsync(conversionResult.Result, context);
}
public ParameterDescriptor ToParameterDescriptor()
{
return new ServiceBusParameterDescriptor
{
Name = _parameterName,
QueueOrTopicName = _path.QueueOrTopicNamePattern,
DisplayHints = CreateParameterDisplayHints(_path.QueueOrTopicNamePattern, false)
};
}
private Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
return _argumentBinding.BindAsync(value, context);
}
internal static ParameterDisplayHints CreateParameterDisplayHints(string entityPath, bool isInput)
{
ParameterDisplayHints descriptor = new ParameterDisplayHints
{
Description = isInput ?
string.Format(CultureInfo.CurrentCulture, "dequeue from '{0}'", entityPath) :
string.Format(CultureInfo.CurrentCulture, "enqueue to '{0}'", entityPath),
Prompt = isInput ?
"Enter the queue message body" :
"Enter the output entity name",
DefaultValue = isInput ? null : entityPath
};
return descriptor;
}
}
}

Просмотреть файл

@ -1,23 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ServiceBusEntity
{
public MessageSender MessageSender { get; set; }
public EntityType EntityType { get; set; } = EntityType.Queue;
public Task SendAndCreateEntityIfNotExistsAsync(Message message, Guid functionInstanceId, CancellationToken cancellationToken)
{
return MessageSender.SendAndCreateEntityIfNotExists(message, functionInstanceId, EntityType, cancellationToken);
}
}
}

Просмотреть файл

@ -1,13 +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.Protocols;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class ServiceBusParameterDescriptor : ParameterDescriptor
{
/// <summary>Gets or sets the name of the queue or topic.</summary>
public string QueueOrTopicName { get; set; }
}
}

Просмотреть файл

@ -1,64 +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;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class StringArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
if (!parameter.IsOut || parameter.ParameterType != typeof(string).MakeByRefType())
{
return null;
}
return new StringArgumentBinding();
}
private class StringArgumentBinding : IArgumentBinding<ServiceBusEntity>
{
public Type ValueType
{
get { return typeof(string); }
}
/// <remarks>
/// The out string parameter is processed as follows:
/// <list type="bullet">
/// <item>
/// <description>
/// If the value is <see langword="null"/>, no message will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value is an empty string, a message with empty content will be sent.
/// </description>
/// </item>
/// <item>
/// <description>
/// If the value is a non-empty string, a message with that content will be sent.
/// </description>
/// </item>
/// </list>
/// </remarks>
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IValueProvider provider = new NonNullConverterValueBinder<string>(value,
new StringToBrokeredMessageConverter(), context.FunctionInstanceId);
return Task.FromResult(provider);
}
}
}
}

Просмотреть файл

@ -1,30 +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;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class StringToBrokeredMessageConverter : IConverter<string, Message>
{
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public Message Convert(string input)
{
if (input == null)
{
throw new InvalidOperationException("A brokered message cannot contain a null string instance.");
}
byte[] bytes = StrictEncodings.Utf8.GetBytes(input);
return new Message(bytes)
{
ContentType = ContentTypes.TextPlain
};
}
}
}

Просмотреть файл

@ -1,52 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Core;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class StringToServiceBusEntityConverter : IAsyncConverter<string, ServiceBusEntity>
{
private readonly ServiceBusAccount _account;
private readonly IBindableServiceBusPath _defaultPath;
private readonly EntityType _entityType;
private readonly MessagingProvider _messagingProvider;
public StringToServiceBusEntityConverter(ServiceBusAccount account, IBindableServiceBusPath defaultPath, EntityType entityType, MessagingProvider messagingProvider)
{
_account = account;
_defaultPath = defaultPath;
_entityType = entityType;
_messagingProvider = messagingProvider;
}
public Task<ServiceBusEntity> ConvertAsync(string input, CancellationToken cancellationToken)
{
string queueOrTopicName;
// For convenience, treat an an empty string as a request for the default value.
if (String.IsNullOrEmpty(input) && _defaultPath.IsBound)
{
queueOrTopicName = _defaultPath.Bind(null);
}
else
{
queueOrTopicName = input;
}
cancellationToken.ThrowIfCancellationRequested();
var messageSender = _messagingProvider.CreateMessageSender(queueOrTopicName, _account.ConnectionString);
var entity = new ServiceBusEntity
{
MessageSender = messageSender,
EntityType = _entityType
};
return Task.FromResult(entity);
}
}
}

Просмотреть файл

@ -1,65 +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;
using System.Collections;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class UserTypeArgumentBindingProvider : IQueueArgumentBindingProvider
{
public IArgumentBinding<ServiceBusEntity> TryCreate(ParameterInfo parameter)
{
if (!parameter.IsOut)
{
return null;
}
Type itemType = parameter.ParameterType.GetElementType();
if (typeof(IEnumerable).IsAssignableFrom(itemType))
{
throw new InvalidOperationException("Enumerable types are not supported. Use ICollector<T> or IAsyncCollector<T> instead.");
}
else if (typeof(object) == itemType)
{
throw new InvalidOperationException("Object element types are not supported.");
}
return CreateBinding(itemType);
}
private static IArgumentBinding<ServiceBusEntity> CreateBinding(Type itemType)
{
Type genericType = typeof(UserTypeArgumentBinding<>).MakeGenericType(itemType);
return (IArgumentBinding<ServiceBusEntity>)Activator.CreateInstance(genericType);
}
private class UserTypeArgumentBinding<TInput> : IArgumentBinding<ServiceBusEntity>
{
public Type ValueType
{
get { return typeof(TInput); }
}
public Task<IValueProvider> BindAsync(ServiceBusEntity value, ValueBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IConverter<TInput, Message> converter = new UserTypeToBrokeredMessageConverter<TInput>();
IValueProvider provider = new ConverterValueBinder<TInput>(value, converter,
context.FunctionInstanceId);
return Task.FromResult(provider);
}
}
}
}

Просмотреть файл

@ -1,25 +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.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
using Newtonsoft.Json;
namespace Microsoft.Azure.WebJobs.ServiceBus.Bindings
{
internal class UserTypeToBrokeredMessageConverter<TInput> : IConverter<TInput, Message>
{
public Message Convert(TInput input)
{
string text = JsonConvert.SerializeObject(input, Constants.JsonSerializerSettings);
byte[] bytes = StrictEncodings.Utf8.GetBytes(text);
return new Message(bytes)
{
ContentType = ContentTypes.ApplicationJson
};
}
}
}

Просмотреть файл

@ -1,116 +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;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Microsoft.Azure.WebJobs.ServiceBus.Triggers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace Microsoft.Azure.WebJobs.ServiceBus.Config
{
/// <summary>
/// Extension configuration provider used to register ServiceBus triggers and binders
/// </summary>
[Extension("ServiceBus")]
internal class ServiceBusExtensionConfigProvider : IExtensionConfigProvider
{
private readonly INameResolver _nameResolver;
private readonly IConfiguration _configuration;
private readonly ILoggerFactory _loggerFactory;
private readonly ServiceBusOptions _options;
private readonly MessagingProvider _messagingProvider;
/// <summary>
/// Creates a new <see cref="ServiceBusExtensionConfigProvider"/> instance.
/// </summary>
/// <param name="options">The <see cref="ServiceBusOptions"></see> to use./></param>
public ServiceBusExtensionConfigProvider(IOptions<ServiceBusOptions> options,
MessagingProvider messagingProvider,
INameResolver nameResolver,
IConfiguration configuration,
ILoggerFactory loggerFactory)
{
_options = options.Value;
_messagingProvider = messagingProvider;
_nameResolver = nameResolver;
_configuration = configuration;
_loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
}
/// <summary>
/// Gets the <see cref="ServiceBusOptions"/>
/// </summary>
public ServiceBusOptions Options
{
get
{
return _options;
}
}
/// <inheritdoc />
public void Initialize(ExtensionConfigContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
// Set the default exception handler for background exceptions
// coming from MessageReceivers.
Options.ExceptionHandler = (e) =>
{
LogExceptionReceivedEvent(e, _loggerFactory);
};
// register our trigger binding provider
ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = new ServiceBusTriggerAttributeBindingProvider(_nameResolver, _options, _messagingProvider, _configuration);
context.AddBindingRule<ServiceBusTriggerAttribute>().BindToTrigger(triggerBindingProvider);
// register our binding provider
ServiceBusAttributeBindingProvider bindingProvider = new ServiceBusAttributeBindingProvider(_nameResolver, _options, _configuration, _messagingProvider);
context.AddBindingRule<ServiceBusAttribute>().Bind(bindingProvider);
}
internal static void LogExceptionReceivedEvent(ExceptionReceivedEventArgs e, ILoggerFactory loggerFactory)
{
try
{
var ctxt = e.ExceptionReceivedContext;
var logger = loggerFactory?.CreateLogger(LogCategories.Executor);
string message = $"MessageReceiver error (Action={ctxt.Action}, ClientId={ctxt.ClientId}, EntityPath={ctxt.EntityPath}, Endpoint={ctxt.Endpoint})";
var logLevel = GetLogLevel(e.Exception);
logger?.Log(logLevel, 0, message, e.Exception, (s, ex) => message);
}
catch
{
// best effort logging
}
}
private static LogLevel GetLogLevel(Exception ex)
{
var sbex = ex as ServiceBusException;
if (!(ex is OperationCanceledException) && (sbex == null || !sbex.IsTransient))
{
// any non-transient exceptions or unknown exception types
// we want to log as errors
return LogLevel.Error;
}
else
{
// transient messaging errors we log as info so we have a record
// of them, but we don't treat them as actual errors
return LogLevel.Information;
}
}
}
}

Просмотреть файл

@ -1,80 +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;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
/// <summary>
/// Configuration options for the ServiceBus extension.
/// </summary>
public class ServiceBusOptions : IOptionsFormatter
{
/// <summary>
/// Constructs a new instance.
/// </summary>
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 = 16
};
}
/// <summary>
/// Gets or sets the Azure ServiceBus connection string.
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Gets or sets the default <see cref="Azure.ServiceBus.MessageHandlerOptions"/> that will be used by
/// <see cref="MessageReceiver"/>s.
/// </summary>
public MessageHandlerOptions MessageHandlerOptions { get; set; }
/// <summary>
/// Gets or sets the default PrefetchCount that will be used by <see cref="MessageReceiver"/>s.
/// </summary>
public int PrefetchCount { get; set; }
internal Action<ExceptionReceivedEventArgs> ExceptionHandler { get; set; }
public string Format()
{
JObject messageHandlerOptions = null;
if (MessageHandlerOptions != null)
{
messageHandlerOptions = new JObject
{
{ nameof(MessageHandlerOptions.AutoComplete), MessageHandlerOptions.AutoComplete },
{ nameof(MessageHandlerOptions.MaxAutoRenewDuration), MessageHandlerOptions.MaxAutoRenewDuration },
{ nameof(MessageHandlerOptions.MaxConcurrentCalls), MessageHandlerOptions.MaxConcurrentCalls }
};
}
// Do not include ConnectionString in loggable options.
JObject options = new JObject
{
{ nameof(PrefetchCount), PrefetchCount },
{ nameof(MessageHandlerOptions), messageHandlerOptions }
};
return options.ToString(Formatting.Indented);
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args)
{
ExceptionHandler?.Invoke(args);
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -1,56 +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;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Azure.WebJobs.ServiceBus.Config;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.Hosting
{
public static class ServiceBusHostBuilderExtensions
{
public static IWebJobsBuilder AddServiceBus(this IWebJobsBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddServiceBus(p => { });
return builder;
}
public static IWebJobsBuilder AddServiceBus(this IWebJobsBuilder builder, Action<ServiceBusOptions> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
builder.AddExtension<ServiceBusExtensionConfigProvider>()
.ConfigureOptions<ServiceBusOptions>((config, path, options) =>
{
options.ConnectionString = config.GetConnectionString(Constants.DefaultConnectionStringName);
IConfigurationSection section = config.GetSection(path);
section.Bind(options);
configure(options);
});
builder.Services.TryAddSingleton<MessagingProvider>();
return builder;
}
}
}

Просмотреть файл

@ -1,29 +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 Newtonsoft.Json;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
public static class Constants
{
private static JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
{
// The default value, DateParseHandling.DateTime, drops time zone information from DateTimeOffets.
// This value appears to work well with both DateTimes (without time zone information) and DateTimeOffsets.
DateParseHandling = DateParseHandling.DateTimeOffset,
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
public static JsonSerializerSettings JsonSerializerSettings
{
get
{
return _serializerSettings;
}
}
public const string DefaultConnectionStringName = "ServiceBus";
}
}

Просмотреть файл

@ -1,14 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
namespace Microsoft.Azure.WebJobs.ServiceBus
{
internal static class ContentTypes
{
public const string TextPlain = "text/plain";
public const string ApplicationJson = "application/json";
public const string ApplicationOctetStream = "application/octet-stream";
}
}

Просмотреть файл

@ -1,21 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
namespace Microsoft.Azure.WebJobs.ServiceBus
{
/// <summary>
/// Service Bus entity type.
/// </summary>
public enum EntityType
{
/// <summary>
/// Service Bus Queue
/// </summary>
Queue,
/// <summary>
/// Service Bus Topic
/// </summary>
Topic
}
}

Просмотреть файл

@ -1,8 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "AzureWebJobs", Scope = "member", Target = "Microsoft.Azure.WebJobs.ServiceBus.MessagingProvider.#GetConnectionString(System.String)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Prefetch", Scope = "member", Target = "Microsoft.Azure.WebJobs.ServiceBus.ServiceBusConfiguration.#PrefetchCount")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Scope = "type", Target = "Microsoft.Azure.WebJobs.ServiceBus.EventHubListener+Listenter")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Scope = "type", Target = "Microsoft.Azure.WebJobs.ServiceBus.EventHubListener+Listener")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")]

Просмотреть файл

@ -1,36 +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;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners
{
internal static class ServiceBusCausalityHelper
{
private const string ParentGuidFieldName = "$AzureWebJobsParentId";
public static void EncodePayload(Guid functionOwner, Message msg)
{
msg.UserProperties[ParentGuidFieldName] = functionOwner.ToString();
}
public static Guid? GetOwner(Message msg)
{
object parent;
if (msg.UserProperties.TryGetValue(ParentGuidFieldName, out parent))
{
var parentString = parent as string;
if (parentString != null)
{
Guid parentGuid;
if (Guid.TryParse(parentString, out parentGuid))
{
return parentGuid;
}
}
}
return null;
}
}
}

Просмотреть файл

@ -1,126 +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;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners
{
internal sealed class ServiceBusListener : IListener
{
private readonly MessagingProvider _messagingProvider;
private readonly string _entityPath;
private readonly ServiceBusTriggerExecutor _triggerExecutor;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly MessageProcessor _messageProcessor;
private readonly ServiceBusAccount _serviceBusAccount;
private MessageReceiver _receiver;
private bool _disposed;
private bool _started;
public ServiceBusListener(string entityPath, ServiceBusTriggerExecutor triggerExecutor, ServiceBusOptions config, ServiceBusAccount serviceBusAccount, MessagingProvider messagingProvider)
{
_entityPath = entityPath;
_triggerExecutor = triggerExecutor;
_cancellationTokenSource = new CancellationTokenSource();
_messagingProvider = messagingProvider;
_serviceBusAccount = serviceBusAccount;
_messageProcessor = messagingProvider.CreateMessageProcessor(entityPath, _serviceBusAccount.ConnectionString);
}
internal MessageReceiver Receiver => _receiver;
public Task StartAsync(CancellationToken cancellationToken)
{
ThrowIfDisposed();
if (_started)
{
throw new InvalidOperationException("The listener has already been started.");
}
_receiver = _messagingProvider.CreateMessageReceiver(_entityPath, _serviceBusAccount.ConnectionString);
_receiver.RegisterMessageHandler(ProcessMessageAsync, _messageProcessor.MessageOptions);
_started = true;
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
ThrowIfDisposed();
if (!_started)
{
throw new InvalidOperationException("The listener has not yet been started or has already been stopped.");
}
// cancel our token source to signal any in progress
// ProcessMessageAsync invocations to cancel
_cancellationTokenSource.Cancel();
await _receiver.CloseAsync();
_receiver = null;
_started = false;
}
public void Cancel()
{
ThrowIfDisposed();
_cancellationTokenSource.Cancel();
}
[SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "_cancellationTokenSource")]
public void Dispose()
{
if (!_disposed)
{
// Running callers might still be using the cancellation token.
// Mark it canceled but don't dispose of the source while the callers are running.
// Otherwise, callers would receive ObjectDisposedException when calling token.Register.
// For now, rely on finalization to clean up _cancellationTokenSource's wait handle (if allocated).
_cancellationTokenSource.Cancel();
if (_receiver != null)
{
_receiver.CloseAsync().Wait();
_receiver = null;
}
_disposed = true;
}
}
private Task ProcessMessageAsync(Message message)
{
return ProcessMessageAsync(message, _cancellationTokenSource.Token);
}
internal async Task ProcessMessageAsync(Message message, CancellationToken cancellationToken)
{
if (!await _messageProcessor.BeginProcessingMessageAsync(message, cancellationToken))
{
return;
}
FunctionResult result = await _triggerExecutor.ExecuteAsync(message, cancellationToken);
await _messageProcessor.CompleteProcessingMessageAsync(message, result, cancellationToken);
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
}
}
}

Просмотреть файл

@ -1,36 +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.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners
{
internal class ServiceBusQueueListenerFactory : IListenerFactory
{
private readonly ServiceBusAccount _account;
private readonly string _queueName;
private readonly ITriggeredFunctionExecutor _executor;
private readonly ServiceBusOptions _options;
private readonly MessagingProvider _messagingProvider;
public ServiceBusQueueListenerFactory(ServiceBusAccount account, string queueName, ITriggeredFunctionExecutor executor, ServiceBusOptions options, MessagingProvider messagingProvider)
{
_account = account;
_queueName = queueName;
_executor = executor;
_options = options;
_messagingProvider = messagingProvider;
}
public Task<IListener> CreateAsync(CancellationToken cancellationToken)
{
var triggerExecutor = new ServiceBusTriggerExecutor(_executor);
var listener = new ServiceBusListener(_queueName, triggerExecutor, _options, _account, _messagingProvider);
return Task.FromResult<IListener>(listener);
}
}
}

Просмотреть файл

@ -1,41 +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.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners
{
internal class ServiceBusSubscriptionListenerFactory : IListenerFactory
{
private readonly ServiceBusAccount _account;
private readonly string _topicName;
private readonly string _subscriptionName;
private readonly ITriggeredFunctionExecutor _executor;
private readonly ServiceBusOptions _options;
private readonly MessagingProvider _messagingProvider;
public ServiceBusSubscriptionListenerFactory(ServiceBusAccount account, string topicName, string subscriptionName, ITriggeredFunctionExecutor executor, ServiceBusOptions options, MessagingProvider messagingProvider)
{
_account = account;
_topicName = topicName;
_subscriptionName = subscriptionName;
_executor = executor;
_options = options;
_messagingProvider = messagingProvider;
}
public Task<IListener> CreateAsync(CancellationToken cancellationToken)
{
string entityPath = EntityNameHelper.FormatSubscriptionPath(_topicName, _subscriptionName);
ServiceBusTriggerExecutor triggerExecutor = new ServiceBusTriggerExecutor(_executor);
var listener = new ServiceBusListener(entityPath, triggerExecutor, _options, _account, _messagingProvider);
return Task.FromResult<IListener>(listener);
}
}
}

Просмотреть файл

@ -1,45 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.ServiceBus;
using System.Collections.Generic;
namespace Microsoft.Azure.WebJobs.ServiceBus.Listeners
{
internal class ServiceBusTriggerExecutor
{
private readonly ITriggeredFunctionExecutor _innerExecutor;
public ServiceBusTriggerExecutor(ITriggeredFunctionExecutor innerExecutor)
{
_innerExecutor = innerExecutor;
}
public async Task<FunctionResult> ExecuteAsync(Message value, CancellationToken cancellationToken)
{
Guid? parentId = ServiceBusCausalityHelper.GetOwner(value);
TriggeredFunctionData input = new TriggeredFunctionData
{
ParentId = parentId,
TriggerValue = value,
TriggerDetails = PopulateTriggerDetails(value)
};
return await _innerExecutor.TryExecuteAsync(input, cancellationToken);
}
private Dictionary<string, string> PopulateTriggerDetails(Message value)
{
return new Dictionary<string, string>()
{
{ "MessageId", value.MessageId },
{ "DeliveryCount", value.SystemProperties.DeliveryCount.ToString() },
{ "EnqueuedTime", value.SystemProperties.EnqueuedTimeUtc.ToString() },
{ "LockedUntil", value.SystemProperties.LockedUntilUtc.ToString() }
};
}
}
}

Просмотреть файл

@ -1,87 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
/// <summary>
/// This class defines a strategy used for processing ServiceBus messages.
/// </summary>
/// <remarks>
/// Custom <see cref="MessageProcessor"/> implementations can be specified by implementing
/// a custom <see cref="MessagingProvider"/> and setting it via <see cref="ServiceBusOptions.MessagingProvider"/>.
/// </remarks>
public class MessageProcessor
{
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="messageReceiver">The <see cref="MessageReceiver"/>.</param>
/// <param name="messageOptions">The <see cref="MessageHandlerOptions"/> to use.</param>
public MessageProcessor(MessageReceiver messageReceiver, MessageHandlerOptions messageOptions)
{
MessageReceiver = messageReceiver ?? throw new ArgumentNullException(nameof(messageReceiver));
MessageOptions = messageOptions ?? throw new ArgumentNullException(nameof(messageOptions));
}
/// <summary>
/// Gets the <see cref="MessageHandlerOptions"/> that will be used by the <see cref="MessageReceiver"/>.
/// </summary>
public MessageHandlerOptions MessageOptions { get; }
/// <summary>
/// Gets or sets the <see cref="MessageReceiver"/> that will be used by the <see cref="MessageReceiver"/>.
/// </summary>
protected MessageReceiver MessageReceiver { get; set; }
/// <summary>
/// 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.
/// </summary>
/// <param name="message">The message to process.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>A <see cref="Task"/> that returns true if the message processing should continue, false otherwise.</returns>
public virtual async Task<bool> BeginProcessingMessageAsync(Message message, CancellationToken cancellationToken)
{
return await Task.FromResult<bool>(true);
}
/// <summary>
/// This method completes processing of the specified message, after the job function has been invoked.
/// </summary>
/// <remarks>
/// The message is completed by the ServiceBus SDK based on how the <see cref="MessageHandlerOptions.AutoComplete"/> option
/// is configured. E.g. if <see cref="MessageHandlerOptions.AutoComplete"/> is false, it is up to the job function to complete
/// the message.
/// </remarks>
/// <param name="message">The message to complete processing for.</param>
/// <param name="result">The <see cref="FunctionResult"/> from the job invocation.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param>
/// <returns>A <see cref="Task"/> that will complete the message processing.</returns>
public virtual Task CompleteProcessingMessageAsync(Message message, FunctionResult result, CancellationToken cancellationToken)
{
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
cancellationToken.ThrowIfCancellationRequested();
if (!result.Succeeded)
{
// if the invocation failed, we must propagate the
// exception back to SB so it can handle message state
// correctly
throw result.Exception;
}
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -1,112 +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;
using System.Collections.Concurrent;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Extensions.Options;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
/// <summary>
/// This class provides factory methods for the creation of instances
/// used for ServiceBus message processing.
/// </summary>
public class MessagingProvider
{
private readonly ServiceBusOptions _options;
private readonly ConcurrentDictionary<string, MessageReceiver> _messageReceiverCache = new ConcurrentDictionary<string, MessageReceiver>();
private readonly ConcurrentDictionary<string, MessageSender> _messageSenderCache = new ConcurrentDictionary<string, MessageSender>();
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="serviceBusOptions">The <see cref="ServiceBusOptions"/>.</param>
public MessagingProvider(IOptions<ServiceBusOptions> serviceBusOptions)
{
_options = serviceBusOptions?.Value ?? throw new ArgumentNullException(nameof(serviceBusOptions));
}
/// <summary>
/// Creates a <see cref="MessageProcessor"/> for the specified ServiceBus entity.
/// </summary>
/// <param name="entityPath">The ServiceBus entity to create a <see cref="MessageProcessor"/> for.</param>
/// <param name="connectionString">The ServiceBus connection string.</param>
/// <returns>The <see cref="MessageProcessor"/>.</returns>
public virtual MessageProcessor CreateMessageProcessor(string entityPath, string connectionString)
{
if (string.IsNullOrEmpty(entityPath))
{
throw new ArgumentNullException("entityPath");
}
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentNullException("connectionString");
}
return new MessageProcessor(GetOrAddMessageReceiver(entityPath, connectionString), _options.MessageHandlerOptions);
}
/// <summary>
/// Creates a <see cref="MessageReceiver"/> for the specified ServiceBus entity.
/// </summary>
/// <remarks>
/// You can override this method to customize the <see cref="MessageReceiver"/>.
/// </remarks>
/// <param name="entityPath">The ServiceBus entity to create a <see cref="MessageReceiver"/> for.</param>
/// <param name="connectionString">The ServiceBus connection string.</param>
/// <returns></returns>
public virtual MessageReceiver CreateMessageReceiver(string entityPath, string connectionString)
{
if (string.IsNullOrEmpty(entityPath))
{
throw new ArgumentNullException("entityPath");
}
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentNullException("connectionString");
}
return GetOrAddMessageReceiver(entityPath, connectionString);
}
/// <summary>
/// Creates a <see cref="MessageSender"/> for the specified ServiceBus entity.
/// </summary>
/// <remarks>
/// You can override this method to customize the <see cref="MessageSender"/>.
/// </remarks>
/// <param name="entityPath">The ServiceBus entity to create a <see cref="MessageSender"/> for.</param>
/// <param name="connectionString">The ServiceBus connection string.</param>
/// <returns></returns>
public virtual MessageSender CreateMessageSender(string entityPath, string connectionString)
{
if (string.IsNullOrEmpty(entityPath))
{
throw new ArgumentNullException("entityPath");
}
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentNullException("connectionString");
}
return GetOrAddMessageSender(entityPath, connectionString);
}
private MessageReceiver GetOrAddMessageReceiver(string entityPath, string connectionString)
{
string cacheKey = $"{entityPath}-{connectionString}";
return _messageReceiverCache.GetOrAdd(cacheKey,
new MessageReceiver(connectionString, entityPath)
{
PrefetchCount = _options.PrefetchCount
});
}
private MessageSender GetOrAddMessageSender(string entityPath, string connectionString)
{
string cacheKey = $"{entityPath}-{connectionString}";
return _messageSenderCache.GetOrAdd(cacheKey, new MessageSender(connectionString, entityPath));
}
}
}

Просмотреть файл

@ -1,8 +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.Reflection;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.ServiceBus.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

Просмотреть файл

@ -1,54 +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;
using System.Globalization;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Configuration;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
internal class ServiceBusAccount
{
private readonly ServiceBusOptions _options;
private readonly IConnectionProvider _connectionProvider;
private readonly IConfiguration _configuration;
private string _connectionString;
public ServiceBusAccount(ServiceBusOptions options, IConfiguration configuration, IConnectionProvider connectionProvider = null)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_configuration = configuration;
_connectionProvider = connectionProvider;
}
internal ServiceBusAccount()
{
}
public virtual string ConnectionString
{
get
{
if (string.IsNullOrEmpty(_connectionString))
{
_connectionString = _options.ConnectionString;
if (_connectionProvider != null && !string.IsNullOrEmpty(_connectionProvider.Connection))
{
_connectionString = _configuration.GetWebJobsConnectionString(_connectionProvider.Connection);
}
if (string.IsNullOrEmpty(_connectionString))
{
var defaultConnectionName = "AzureWebJobsServiceBus";
throw new InvalidOperationException(
string.Format(CultureInfo.InvariantCulture, "Microsoft Azure WebJobs SDK ServiceBus connection string '{0}' is missing or empty.",
Sanitizer.Sanitize(_connectionProvider.Connection) ?? defaultConnectionName));
}
}
return _connectionString;
}
}
}
}

Просмотреть файл

@ -1,55 +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
{
/// <summary>
/// Attribute used to override the default ServiceBus account used by triggers and binders.
/// </summary>
/// <remarks>
/// This attribute can be applied at the parameter/method/class level, and the precedence
/// is in that order.
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter)]
public sealed class ServiceBusAccountAttribute : Attribute, IConnectionProvider
{
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="account">A string value indicating the Service Bus connection string to use. This
/// string should be in one of the following formats. These checks will be applied in order and the
/// first match wins.
/// - The name of an "AzureWebJobs" prefixed app setting or connection string name. E.g., if your setting
/// name is "AzureWebJobsMyServiceBus", you can specify "MyServiceBus" here.
/// - Can be a string containing %% values (e.g. %StagingServiceBus%). The value provided will be passed
/// to any INameResolver registered on the JobHostConfiguration to resolve the actual setting name to use.
/// - Can be an app setting or connection string name of your choosing.
/// </param>
public ServiceBusAccountAttribute(string account)
{
Account = account;
}
/// <summary>
/// Gets or sets the name of the app setting that contains the Service Bus connection string.
/// </summary>
public string Account { get; private set; }
/// <summary>
/// Gets or sets the app setting name that contains the Service Bus connection string.
/// </summary>
string IConnectionProvider.Connection
{
get
{
return Account;
}
set
{
Account = value;
}
}
}
}

Просмотреть файл

@ -1,70 +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;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs
{
/// <summary>
/// Attribute used to bind a parameter to Azure ServiceBus Queues and Topics.
/// </summary>
/// <remarks>
/// The method parameter type can be one of the following:
/// <list type="bullet">
/// <item><description>BrokeredMessage (out parameter)</description></item>
/// <item><description><see cref="string"/> (out parameter)</description></item>
/// <item><description><see cref="T:byte[]"/> (out parameter)</description></item>
/// <item><description>A user-defined type (out parameter, serialized as JSON)</description></item>
/// <item><description>
/// <see cref="ICollection{T}"/> of these types (to enqueue multiple messages via <see cref="ICollection{T}.Add"/>
/// </description></item>
/// </list>
/// </remarks>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
[DebuggerDisplay("{QueueOrTopicName,nq}")]
[ConnectionProvider(typeof(ServiceBusAccountAttribute))]
[Binding]
public sealed class ServiceBusAttribute : Attribute, IConnectionProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusAttribute"/> class.
/// </summary>
/// <param name="queueOrTopicName">The name of the queue or topic to bind to.</param>
public ServiceBusAttribute(string queueOrTopicName)
{
QueueOrTopicName = queueOrTopicName;
EntityType = EntityType.Queue;
}
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusAttribute"/> class.
/// </summary>
/// <param name="queueOrTopicName">The name of the queue or topic to bind to.</param>
/// <param name="queueOrTopicName">The type of the entity to bind to.</param>
public ServiceBusAttribute(string queueOrTopicName, EntityType entityType)
{
QueueOrTopicName = queueOrTopicName;
EntityType = entityType;
}
/// <summary>
/// Gets the name of the queue or topic to bind to.
/// </summary>
public string QueueOrTopicName { get; private set; }
/// <summary>
/// Gets or sets the app setting name that contains the Service Bus connection string.
/// </summary>
public string Connection { get; set; }
/// <summary>
/// Value indicating the type of the entity to bind to.
/// </summary>
public EntityType EntityType { get; set; }
}
}

Просмотреть файл

@ -1,101 +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;
using System.Diagnostics;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs
{
/// <summary>
/// Attribute used to bind a parameter to a ServiceBus Queue message, causing the function to run when a
/// message is enqueued.
/// </summary>
/// <remarks>
/// The method parameter type can be one of the following:
/// <list type="bullet">
/// <item><description>BrokeredMessage</description></item>
/// <item><description><see cref="string"/></description></item>
/// <item><description><see cref="T:byte[]"/></description></item>
/// <item><description>A user-defined type (serialized as JSON)</description></item>
/// </list>
/// </remarks>
[AttributeUsage(AttributeTargets.Parameter)]
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[ConnectionProvider(typeof(ServiceBusAccountAttribute))]
[Binding]
public sealed class ServiceBusTriggerAttribute : Attribute, IConnectionProvider
{
private readonly string _queueName;
private readonly string _topicName;
private readonly string _subscriptionName;
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusTriggerAttribute"/> class.
/// </summary>
/// <param name="queueName">The name of the queue to which to bind.</param>
public ServiceBusTriggerAttribute(string queueName)
{
_queueName = queueName;
}
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusTriggerAttribute"/> class.
/// </summary>
/// <param name="topicName">The name of the topic to bind to.</param>
/// <param name="subscriptionName">The name of the subscription in <paramref name="topicName"/> to bind to.</param>
public ServiceBusTriggerAttribute(string topicName, string subscriptionName)
{
_topicName = topicName;
_subscriptionName = subscriptionName;
}
/// <summary>
/// Gets or sets the app setting name that contains the Service Bus connection string.
/// </summary>
public string Connection { get; set; }
/// <summary>
/// Gets the name of the queue to which to bind.
/// </summary>
/// <remarks>When binding to a subscription in a topic, returns <see langword="null"/>.</remarks>
public string QueueName
{
get { return _queueName; }
}
/// <summary>
/// Gets the name of the topic to which to bind.
/// </summary>
/// <remarks>When binding to a queue, returns <see langword="null"/>.</remarks>
public string TopicName
{
get { return _topicName; }
}
/// <summary>
/// Gets the name of the subscription in <see cref="TopicName"/> to bind to.
/// </summary>
/// <remarks>When binding to a queue, returns <see langword="null"/>.</remarks>
public string SubscriptionName
{
get { return _subscriptionName; }
}
private string DebuggerDisplay
{
get
{
if (_queueName != null)
{
return _queueName;
}
else
{
return _topicName + "/" + _subscriptionName;
}
}
}
}
}

Просмотреть файл

@ -1,19 +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.ServiceBus;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.Hosting;
[assembly: WebJobsStartup(typeof(ServiceBusWebJobsStartup))]
namespace Microsoft.Azure.WebJobs.ServiceBus
{
public class ServiceBusWebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddServiceBus();
}
}
}

Просмотреть файл

@ -1,18 +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.Text;
namespace Microsoft.Azure.WebJobs.ServiceBus
{
internal static class StrictEncodings
{
private static UTF8Encoding _utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false,
throwOnInvalidBytes: true);
public static UTF8Encoding Utf8
{
get { return _utf8; }
}
}
}

Просмотреть файл

@ -1,36 +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.Collections.Generic;
using System.Reflection;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class CompositeArgumentBindingProvider : IQueueTriggerArgumentBindingProvider
{
private readonly IEnumerable<IQueueTriggerArgumentBindingProvider> _providers;
public CompositeArgumentBindingProvider(params IQueueTriggerArgumentBindingProvider[] providers)
{
_providers = providers;
}
public ITriggerDataArgumentBinding<Message> TryCreate(ParameterInfo parameter)
{
foreach (IQueueTriggerArgumentBindingProvider provider in _providers)
{
ITriggerDataArgumentBinding<Message> binding = provider.TryCreate(parameter);
if (binding != null)
{
return binding;
}
}
return null;
}
}
}

Просмотреть файл

@ -1,63 +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;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class ConverterArgumentBindingProvider<T> : IQueueTriggerArgumentBindingProvider
{
private readonly IAsyncConverter<Message, T> _converter;
public ConverterArgumentBindingProvider(IAsyncConverter<Message, T> converter)
{
_converter = converter;
}
public ITriggerDataArgumentBinding<Message> TryCreate(ParameterInfo parameter)
{
if (parameter.ParameterType != typeof(T))
{
return null;
}
return new ConverterArgumentBinding(_converter);
}
internal class ConverterArgumentBinding : ITriggerDataArgumentBinding<Message>
{
private readonly IAsyncConverter<Message, T> _converter;
public ConverterArgumentBinding(IAsyncConverter<Message, T> converter)
{
_converter = converter;
}
public Type ValueType
{
get { return typeof(T); }
}
public IReadOnlyDictionary<string, Type> BindingDataContract
{
get { return null; }
}
public async Task<ITriggerData> BindAsync(Message value, ValueBindingContext context)
{
Message clone = value.Clone();
object converted = await _converter.ConvertAsync(value, context.CancellationToken);
IValueProvider provider = await MessageValueProvider.CreateAsync(clone, converted, typeof(T),
context.CancellationToken);
return new TriggerData(provider, null);
}
}
}
}

Просмотреть файл

@ -1,14 +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.Reflection;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal interface IQueueTriggerArgumentBindingProvider
{
ITriggerDataArgumentBinding<Message> TryCreate(ParameterInfo parameter);
}
}

Просмотреть файл

@ -1,28 +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;
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class MessageToByteArrayConverter : IAsyncConverter<Message, byte[]>
{
public Task<byte[]> ConvertAsync(Message input, CancellationToken cancellationToken)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(input.Body);
}
}
}

Просмотреть файл

@ -1,72 +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;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.InteropExtensions;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class MessageToStringConverter : IAsyncConverter<Message, string>
{
public async Task<string> ConvertAsync(Message input, CancellationToken cancellationToken)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
if (input.Body == null)
{
return null;
}
Stream stream = new MemoryStream(input.Body);
TextReader reader = new StreamReader(stream, StrictEncodings.Utf8);
try
{
cancellationToken.ThrowIfCancellationRequested();
try
{
return await reader.ReadToEndAsync();
}
catch (DecoderFallbackException)
{
// we'll try again below
}
// We may get here if the message is a string yet was DataContract-serialized when created. We'll
// try to deserialize it here using GetBody<string>(). This may fail as well, in which case we'll
// provide a decent error.
try
{
return input.GetBody<string>();
}
catch (Exception exception)
{
string contentType = input.ContentType ?? "null";
string msg = string.Format(CultureInfo.InvariantCulture, "The Message with ContentType '{0}' failed to deserialize to a string with the message: '{1}'",
contentType, exception.Message);
throw new InvalidOperationException(msg, exception);
}
}
finally
{
if (stream != null)
{
stream.Dispose();
}
if (reader != null)
{
reader.Dispose();
}
}
}
}
}

Просмотреть файл

@ -1,98 +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;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class MessageValueProvider : IValueProvider
{
private readonly object _value;
private readonly Type _valueType;
private readonly string _invokeString;
private MessageValueProvider(object value, Type valueType, string invokeString)
{
if (value != null && !valueType.IsAssignableFrom(value.GetType()))
{
throw new InvalidOperationException("value is not of the correct type.");
}
_value = value;
_valueType = valueType;
_invokeString = invokeString;
}
public Type Type
{
get { return _valueType; }
}
public Task<object> GetValueAsync()
{
return Task.FromResult(_value);
}
public string ToInvokeString()
{
return _invokeString;
}
public static async Task<MessageValueProvider> CreateAsync(Message clone, object value,
Type valueType, CancellationToken cancellationToken)
{
string invokeString = await CreateInvokeStringAsync(clone, cancellationToken);
return new MessageValueProvider(value, valueType, invokeString);
}
private static Task<string> CreateInvokeStringAsync(Message clonedMessage,
CancellationToken cancellationToken)
{
switch (clonedMessage.ContentType)
{
case ContentTypes.ApplicationJson:
case ContentTypes.TextPlain:
return GetTextAsync(clonedMessage, cancellationToken);
case ContentTypes.ApplicationOctetStream:
return GetBase64StringAsync(clonedMessage, cancellationToken);
default:
return GetDescriptionAsync(clonedMessage);
}
}
private static Task<string> GetBase64StringAsync(Message clonedMessage,
CancellationToken cancellationToken)
{
if (clonedMessage.Body == null)
{
return Task.FromResult((string)null);
}
string converted = Convert.ToBase64String(clonedMessage.Body);
return Task.FromResult(converted);
}
private static Task<string> GetDescriptionAsync(Message clonedMessage)
{
string description = clonedMessage.Body != null
? string.Format(CultureInfo.InvariantCulture, "byte[{0}]", clonedMessage.Body.Length) : "null";
return Task.FromResult(description);
}
private static Task<string> GetTextAsync(Message clonedMessage,
CancellationToken cancellationToken)
{
if (clonedMessage.Body == null)
{
return Task.FromResult((string)null);
}
string s = StrictEncodings.Utf8.GetString(clonedMessage.Body);
return Task.FromResult(s);
}
}
}

Просмотреть файл

@ -1,33 +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.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class OutputConverter<TInput> : IObjectToTypeConverter<Message>
where TInput : class
{
private readonly IConverter<TInput, Message> _innerConverter;
public OutputConverter(IConverter<TInput, Message> innerConverter)
{
_innerConverter = innerConverter;
}
public bool TryConvert(object input, out Message output)
{
TInput typedInput = input as TInput;
if (typedInput == null)
{
output = null;
return false;
}
output = _innerConverter.Convert(typedInput);
return true;
}
}
}

Просмотреть файл

@ -1,103 +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;
using System.Globalization;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Configuration;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class ServiceBusTriggerAttributeBindingProvider : ITriggerBindingProvider
{
private static readonly IQueueTriggerArgumentBindingProvider InnerProvider =
new CompositeArgumentBindingProvider(
new ConverterArgumentBindingProvider<Message>(
new AsyncConverter<Message, Message>(new IdentityConverter<Message>())),
new ConverterArgumentBindingProvider<string>(new MessageToStringConverter()),
new ConverterArgumentBindingProvider<byte[]>(new MessageToByteArrayConverter()),
new UserTypeArgumentBindingProvider()); // Must come last, because it will attempt to bind all types.
private readonly INameResolver _nameResolver;
private readonly ServiceBusOptions _options;
private readonly MessagingProvider _messagingProvider;
private readonly IConfiguration _configuration;
public ServiceBusTriggerAttributeBindingProvider(INameResolver nameResolver, ServiceBusOptions options, MessagingProvider messagingProvider, IConfiguration configuration)
{
_nameResolver = nameResolver ?? throw new ArgumentNullException(nameof(nameResolver));
_options = options ?? throw new ArgumentNullException(nameof(options));
_messagingProvider = messagingProvider ?? throw new ArgumentNullException(nameof(messagingProvider));
_configuration = configuration;
}
public Task<ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
ParameterInfo parameter = context.Parameter;
var attribute = TypeUtility.GetResolvedAttribute<ServiceBusTriggerAttribute>(parameter);
if (attribute == null)
{
return Task.FromResult<ITriggerBinding>(null);
}
string queueName = null;
string topicName = null;
string subscriptionName = null;
string entityPath = null;
if (attribute.QueueName != null)
{
queueName = Resolve(attribute.QueueName);
entityPath = queueName;
}
else
{
topicName = Resolve(attribute.TopicName);
subscriptionName = Resolve(attribute.SubscriptionName);
entityPath = EntityNameHelper.FormatSubscriptionPath(topicName, subscriptionName);
}
ITriggerDataArgumentBinding<Message> argumentBinding = InnerProvider.TryCreate(parameter);
if (argumentBinding == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Can't bind ServiceBusTrigger to type '{0}'.", parameter.ParameterType));
}
attribute.Connection = Resolve(attribute.Connection);
ServiceBusAccount account = new ServiceBusAccount(_options, _configuration, attribute);
ITriggerBinding binding;
if (queueName != null)
{
binding = new ServiceBusTriggerBinding(parameter.Name, parameter.ParameterType, argumentBinding, account, _options, _messagingProvider, queueName);
}
else
{
binding = new ServiceBusTriggerBinding(parameter.Name, parameter.ParameterType, argumentBinding, account, _options, _messagingProvider, topicName, subscriptionName);
}
return Task.FromResult<ITriggerBinding>(binding);
}
private string Resolve(string queueName)
{
if (_nameResolver == null)
{
return queueName;
}
return _nameResolver.ResolveWholeString(queueName);
}
}
}

Просмотреть файл

@ -1,208 +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;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Microsoft.Azure.WebJobs.ServiceBus.Listeners;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class ServiceBusTriggerBinding : ITriggerBinding
{
private readonly string _parameterName;
private readonly IObjectToTypeConverter<Message> _converter;
private readonly ITriggerDataArgumentBinding<Message> _argumentBinding;
private readonly IReadOnlyDictionary<string, Type> _bindingDataContract;
private readonly ServiceBusAccount _account;
private readonly string _queueName;
private readonly string _topicName;
private readonly string _subscriptionName;
private readonly string _entityPath;
private readonly ServiceBusOptions _options;
private ServiceBusListener _listener;
private readonly MessagingProvider _messagingProvider;
public ServiceBusTriggerBinding(string parameterName, Type parameterType, ITriggerDataArgumentBinding<Message> argumentBinding, ServiceBusAccount account,
ServiceBusOptions options, MessagingProvider messagingProvider, string queueName)
: this(parameterName, parameterType, argumentBinding, account, options, messagingProvider)
{
_queueName = queueName;
_entityPath = queueName;
}
public ServiceBusTriggerBinding(string parameterName, Type parameterType, ITriggerDataArgumentBinding<Message> argumentBinding, ServiceBusAccount account,
ServiceBusOptions options, MessagingProvider messagingProvider, string topicName, string subscriptionName)
: this(parameterName, parameterType, argumentBinding, account, options, messagingProvider)
{
_topicName = topicName;
_subscriptionName = subscriptionName;
_entityPath = EntityNameHelper.FormatSubscriptionPath(topicName, subscriptionName);
}
private ServiceBusTriggerBinding(string parameterName, Type parameterType, ITriggerDataArgumentBinding<Message> argumentBinding,
ServiceBusAccount account, ServiceBusOptions options, MessagingProvider messagingProvider)
{
_parameterName = parameterName;
_converter = CreateConverter(parameterType);
_argumentBinding = argumentBinding;
_bindingDataContract = CreateBindingDataContract(argumentBinding.BindingDataContract);
_account = account;
_options = options;
_messagingProvider = messagingProvider;
}
public Type TriggerValueType
{
get
{
return typeof(Message);
}
}
public IReadOnlyDictionary<string, Type> BindingDataContract
{
get { return _bindingDataContract; }
}
public async Task<ITriggerData> BindAsync(object value, ValueBindingContext context)
{
Message message = value as Message;
if (message == null && !_converter.TryConvert(value, out message))
{
throw new InvalidOperationException("Unable to convert trigger to BrokeredMessage.");
}
ITriggerData triggerData = await _argumentBinding.BindAsync(message, context);
IReadOnlyDictionary<string, object> bindingData = CreateBindingData(message, _listener?.Receiver, triggerData.BindingData);
return new TriggerData(triggerData.ValueProvider, bindingData);
}
public async Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IListenerFactory factory = null;
if (_queueName != null)
{
factory = new ServiceBusQueueListenerFactory(_account, _queueName, context.Executor, _options, _messagingProvider);
}
else
{
factory = new ServiceBusSubscriptionListenerFactory(_account, _topicName, _subscriptionName, context.Executor, _options, _messagingProvider);
}
_listener = (ServiceBusListener)(await factory.CreateAsync(context.CancellationToken));
return _listener;
}
internal static IReadOnlyDictionary<string, Type> CreateBindingDataContract(IReadOnlyDictionary<string, Type> argumentBindingContract)
{
var contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
contract.Add("DeliveryCount", typeof(int));
contract.Add("DeadLetterSource", typeof(string));
contract.Add("LockToken", typeof(string));
contract.Add("ExpiresAtUtc", typeof(DateTime));
contract.Add("EnqueuedTimeUtc", typeof(DateTime));
contract.Add("MessageId", typeof(string));
contract.Add("ContentType", typeof(string));
contract.Add("ReplyTo", typeof(string));
contract.Add("SequenceNumber", typeof(long));
contract.Add("To", typeof(string));
contract.Add("Label", typeof(string));
contract.Add("CorrelationId", typeof(string));
contract.Add("UserProperties", typeof(IDictionary<string, object>));
contract.Add("MessageReceiver", typeof(MessageReceiver));
if (argumentBindingContract != null)
{
foreach (KeyValuePair<string, Type> item in argumentBindingContract)
{
// In case of conflict, binding data from the value type overrides the built-in binding data above.
contract[item.Key] = item.Value;
}
}
return contract;
}
internal static IReadOnlyDictionary<string, object> CreateBindingData(Message value, MessageReceiver receiver, IReadOnlyDictionary<string, object> bindingDataFromValueType)
{
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
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.ExpiresAtUtc), value.ExpiresAtUtc));
SafeAddValue(() => bindingData.Add(nameof(value.SystemProperties.EnqueuedTimeUtc), value.SystemProperties.EnqueuedTimeUtc));
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.To), value.To));
SafeAddValue(() => bindingData.Add(nameof(value.Label), value.Label));
SafeAddValue(() => bindingData.Add(nameof(value.CorrelationId), value.CorrelationId));
SafeAddValue(() => bindingData.Add(nameof(value.UserProperties), value.UserProperties));
SafeAddValue(() => bindingData.Add("MessageReceiver", receiver));
if (bindingDataFromValueType != null)
{
foreach (KeyValuePair<string, object> item in bindingDataFromValueType)
{
// In case of conflict, binding data from the value type overrides the built-in binding data above.
bindingData[item.Key] = item.Value;
}
}
return bindingData;
}
private static void SafeAddValue(Action addValue)
{
try
{
addValue();
}
catch
{
// some message property getters can throw, based on the
// state of the message
}
}
public ParameterDescriptor ToParameterDescriptor()
{
string entityPath = _queueName != null ?
_queueName : string.Format(CultureInfo.InvariantCulture, "{0}/Subscriptions/{1}", _topicName, _subscriptionName);
return new ServiceBusTriggerParameterDescriptor
{
Name = _parameterName,
QueueName = _queueName,
TopicName = _topicName,
SubscriptionName = _subscriptionName,
DisplayHints = ServiceBusBinding.CreateParameterDisplayHints(entityPath, true)
};
}
private static IObjectToTypeConverter<Message> CreateConverter(Type parameterType)
{
return new CompositeObjectToTypeConverter<Message>(
new OutputConverter<Message>(new IdentityConverter<Message>()),
new OutputConverter<string>(StringTodMessageConverterFactory.Create(parameterType)));
}
}
}

Просмотреть файл

@ -1,40 +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.Collections.Generic;
using System.Globalization;
using Microsoft.Azure.WebJobs.Host.Protocols;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class ServiceBusTriggerParameterDescriptor : TriggerParameterDescriptor
{
/// <summary>Gets or sets the name of the queue.</summary>
/// <remarks>When binding to a subscription in a topic, returns <see langword="null"/>.</remarks>
public string QueueName { get; set; }
/// <summary>Gets or sets the name of the queue.</summary>
/// <remarks>When binding to a queue, returns <see langword="null"/>.</remarks>
public string TopicName { get; set; }
/// <summary>Gets or sets the name of the subscription in <see cref="TopicName"/>.</summary>
/// <remarks>When binding to a queue, returns <see langword="null"/>.</remarks>
public string SubscriptionName { get; set; }
/// <inheritdoc />
public override string GetTriggerReason(IDictionary<string, string> arguments)
{
string path;
if (QueueName != null)
{
path = QueueName;
}
else
{
path = string.Format(CultureInfo.InvariantCulture, "{0}/Subscriptions/{1}", TopicName, SubscriptionName);
}
return string.Format(CultureInfo.CurrentCulture, "New ServiceBus message detected on '{0}'.", path);
}
}
}

Просмотреть файл

@ -1,23 +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.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class StringToBinarydMessageConverter : IConverter<string, Message>
{
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public Message Convert(string input)
{
byte[] contents = StrictEncodings.Utf8.GetBytes(input);
Message message = new Message(contents);
message.ContentType = ContentTypes.ApplicationOctetStream;
return message;
}
}
}

Просмотреть файл

@ -1,21 +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.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class StringToJsonMessageConverter : IConverter<string, Message>
{
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public Message Convert(string input)
{
Message message = new Message(StrictEncodings.Utf8.GetBytes(input));
message.ContentType = ContentTypes.ApplicationJson;
return message;
}
}
}

Просмотреть файл

@ -1,28 +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;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class StringTodMessageConverterFactory
{
public static IConverter<string, Message> Create(Type parameterType)
{
if (parameterType == typeof(Message) || parameterType == typeof(string))
{
return new StringToTextMessageConverter();
}
else if (parameterType == typeof(byte[]))
{
return new StringToBinarydMessageConverter();
}
else
{
return new StringToJsonMessageConverter();
}
}
}
}

Просмотреть файл

@ -1,21 +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.Diagnostics.CodeAnalysis;
using Microsoft.Azure.ServiceBus;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class StringToTextMessageConverter : IConverter<string, Message>
{
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public Message Convert(string input)
{
Message message = new Message(StrictEncodings.Utf8.GetBytes(input))
{
ContentType = ContentTypes.TextPlain
};
return message;
}
}
}

Просмотреть файл

@ -1,121 +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;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.ServiceBus;
using Newtonsoft.Json;
using Microsoft.Azure.ServiceBus.InteropExtensions;
using System.Runtime.Serialization;
namespace Microsoft.Azure.WebJobs.ServiceBus.Triggers
{
internal class UserTypeArgumentBindingProvider : IQueueTriggerArgumentBindingProvider
{
public ITriggerDataArgumentBinding<Message> TryCreate(ParameterInfo parameter)
{
// At indexing time, attempt to bind all types.
// (Whether or not actual binding is possible depends on the message shape at runtime.)
return CreateBinding(parameter.ParameterType);
}
private static ITriggerDataArgumentBinding<Message> CreateBinding(Type itemType)
{
Type genericType = typeof(UserTypeArgumentBinding<>).MakeGenericType(itemType);
return (ITriggerDataArgumentBinding<Message>)Activator.CreateInstance(genericType);
}
private class UserTypeArgumentBinding<TInput> : ITriggerDataArgumentBinding<Message>
{
private readonly IBindingDataProvider _bindingDataProvider;
public UserTypeArgumentBinding()
{
_bindingDataProvider = BindingDataProvider.FromType(typeof(TInput));
}
public Type ValueType
{
get { return typeof(TInput); }
}
public IReadOnlyDictionary<string, Type> BindingDataContract
{
get { return _bindingDataProvider != null ? _bindingDataProvider.Contract : null; }
}
public async Task<ITriggerData> BindAsync(Message value, ValueBindingContext context)
{
IValueProvider provider;
Message clone = value.Clone();
TInput contents = GetBody(value);
if (contents == null)
{
provider = await MessageValueProvider.CreateAsync(clone, null, ValueType,
context.CancellationToken);
return new TriggerData(provider, null);
}
provider = await MessageValueProvider.CreateAsync(clone, contents, ValueType,
context.CancellationToken);
IReadOnlyDictionary<string, object> bindingData = (_bindingDataProvider != null)
? _bindingDataProvider.GetBindingData(contents) : null;
return new TriggerData(provider, bindingData);
}
private static TInput GetBody(Message 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
// 3. If this deserialization fails, do a final attempt at JSON deserialization to catch cases where the content type might be incorrect
if (message.ContentType == ContentTypes.ApplicationJson)
{
return DeserializeJsonObject(message);
}
else
{
try
{
return message.GetBody<TInput>();
}
catch (SerializationException)
{
return DeserializeJsonObject(message);
}
}
}
private static TInput DeserializeJsonObject(Message message)
{
string contents = StrictEncodings.Utf8.GetString(message.Body);
try
{
return JsonConvert.DeserializeObject<TInput>(contents, Constants.JsonSerializerSettings);
}
catch (JsonException e)
{
// Easy to have the queue payload not deserialize properly. So give a useful error.
string msg = string.Format(
@"Binding parameters to complex objects (such as '{0}') uses Json.NET serialization or XML object serialization.
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
3. If this deserialization fails, do a final attempt at JSON deserialization to catch cases where the content type might be incorrect
The JSON parser failed: {1}
", typeof(TInput).Name, e.Message);
throw new InvalidOperationException(msg);
}
}
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Microsoft.Azure.WebJobs.Shared\WebJobs.Shared.projitems" Label="Shared" />
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Version>$(ExtensionsServiceBusVersion)</Version>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Microsoft.Azure.WebJobs.ServiceBus</AssemblyName>
<PackageId>Microsoft.Azure.WebJobs.Extensions.ServiceBus</PackageId>
<RootNamespace>Microsoft.Azure.WebJobs.ServiceBus</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="WindowsAzure.Storage" Version="9.3.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\AsyncConverter.cs" Link="Converters\AsyncConverter.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\CompositeObjectToTypeConverter.cs" Link="Converters\CompositeObjectToTypeConverter.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\ConversionResult.cs" Link="Converters\ConversionResult.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\IAsyncObjectToTypeConverter.cs" Link="Converters\IAsyncObjectToTypeConverter.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\IdentityConverter.cs" Link="Converters\IdentityConverter.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Converters\IObjectToTypeConverter.cs" Link="Converters\IObjectToTypeConverter.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\Triggers\ITriggerDataArgumentBinding.cs" Link="Triggers\ITriggerDataArgumentBinding.cs" />
<Compile Include="..\Microsoft.Azure.WebJobs.Host\TypeUtility.cs" Link="TypeUtility.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,71 @@
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<PackageId>Microsoft.Azure.WebJobs.Sources</PackageId>
<IsPackable>true</IsPackable>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>contentFiles</ContentTargetFolders>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<NoWarn>CS8021</NoWarn>
<NoBuild>true</NoBuild>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<Compile Update="Converters\AsyncConverter.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="Converters\CompositeObjectToTypeConverter.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)\</PackagePath>
</Compile>
<Compile Update="Converters\IAsyncObjectToTypeConverter.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="Converters\ConversionResult.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="Converters\IdentityConverter.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="Converters\IObjectToTypeConverter.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="Triggers\ITriggerDataArgumentBinding.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Update="TypeUtility.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Include="..\Microsoft.Azure.WebJobs.Shared\DictionaryExtensions.cs" Link="DictionaryExtensions.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
<Compile Include="..\Microsoft.Azure.WebJobs.Shared\Sanitizer.cs" Link="Sanitizer.cs">
<Pack>true</Pack>
<PackagePath>$(ContentTargetFolders)\cs\netstandard2.0\$(PackageId)</PackagePath>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Remove="@(PackageReference)" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Target Name="Compile" />
<Target Name="CopyFilesToOutputDirectory" />
</Project>

Просмотреть файл

@ -1,53 +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.Collections.Generic;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class BindableServiceBusPathTests
{
[Fact]
public void Create_IfNonParameterizedPattern_ReturnsBoundPath()
{
const string queueOrTopicNamePattern = "queue-name-with-no-parameters";
IBindableServiceBusPath path = BindableServiceBusPath.Create(queueOrTopicNamePattern);
Assert.NotNull(path);
Assert.Equal(queueOrTopicNamePattern, path.QueueOrTopicNamePattern);
Assert.True(path.IsBound);
}
[Fact]
public void Create_IfParameterizedPattern_ReturnsNotBoundPath()
{
const string queueOrTopicNamePattern = "queue-{name}-with-{parameter}";
IBindableServiceBusPath path = BindableServiceBusPath.Create(queueOrTopicNamePattern);
Assert.NotNull(path);
Assert.Equal(queueOrTopicNamePattern, path.QueueOrTopicNamePattern);
Assert.False(path.IsBound);
}
[Fact]
public void Create_IfNullPattern_Throws()
{
ExceptionAssert.ThrowsArgumentNull(() => BindableServiceBusPath.Create(null), "queueOrTopicNamePattern");
}
[Fact]
public void Create_IfMalformedPattern_PropagatesThrownException()
{
const string queueNamePattern = "malformed-queue-{name%";
ExceptionAssert.ThrowsFormat(
() => BindableServiceBusPath.Create(queueNamePattern),
"Invalid template 'malformed-queue-{name%'. Missing closing bracket at position 17.");
}
}
}

Просмотреть файл

@ -1,39 +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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class BoundServiceBusTests
{
[Fact]
public void Bind_IfNotNullBindingData_ReturnsResolvedQueueName()
{
const string queueOrTopicNamePattern = "queue-name-with-no-parameters";
var bindingData = new Dictionary<string, object> { { "name", "value" } };
IBindableServiceBusPath path = new BoundServiceBusPath(queueOrTopicNamePattern);
string result = path.Bind(bindingData);
Assert.Equal(queueOrTopicNamePattern, result);
}
[Fact]
public void Bind_IfNullBindingData_ReturnsResolvedQueueName()
{
const string queueOrTopicNamePattern = "queue-name-with-no-parameters";
IBindableServiceBusPath path = new BoundServiceBusPath(queueOrTopicNamePattern);
string result = path.Bind(null);
Assert.Equal(queueOrTopicNamePattern, result);
}
}
}

Просмотреть файл

@ -1,43 +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.Collections.Generic;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class ParameterizedServiceBusPathTests
{
[Fact]
public void Bind_IfNotNullBindingData_ReturnsResolvedQueueName()
{
const string queueOrTopicNamePattern = "queue-{name}-with-{parameter}";
var bindingData = new Dictionary<string, object> { { "name", "name" }, { "parameter", "parameter" } };
IBindableServiceBusPath path = CreateProductUnderTest(queueOrTopicNamePattern);
string result = path.Bind(bindingData);
Assert.Equal("queue-name-with-parameter", result);
}
[Fact]
public void Bind_IfNullBindingData_Throws()
{
const string queueOrTopicNamePattern = "queue-{name}-with-{parameter}";
IBindableServiceBusPath path = CreateProductUnderTest(queueOrTopicNamePattern);
ExceptionAssert.ThrowsArgumentNull(() => path.Bind(null), "bindingData");
}
private static IBindableServiceBusPath CreateProductUnderTest(string queueOrTopicNamePattern)
{
BindingTemplate template = BindingTemplate.FromString(queueOrTopicNamePattern);
IBindableServiceBusPath path = new ParameterizedServiceBusPath(template);
return path;
}
}
}

Просмотреть файл

@ -1,71 +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;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.ServiceBus.Bindings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class ServiceBusAttributeBindingProviderTests
{
private readonly ServiceBusAttributeBindingProvider _provider;
private readonly IConfiguration _configuration;
public ServiceBusAttributeBindingProviderTests()
{
_configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
Mock<INameResolver> mockResolver = new Mock<INameResolver>(MockBehavior.Strict);
ServiceBusOptions config = new ServiceBusOptions();
_provider = new ServiceBusAttributeBindingProvider(mockResolver.Object, config, _configuration, new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(config)));
}
[Fact]
public async Task TryCreateAsync_AccountOverride_OverrideIsApplied()
{
ParameterInfo parameter = GetType().GetMethod("TestJob_AccountOverride").GetParameters()[0];
BindingProviderContext context = new BindingProviderContext(parameter, new Dictionary<string, Type>(), CancellationToken.None);
IBinding binding = await _provider.TryCreateAsync(context);
Assert.NotNull(binding);
}
[Fact]
public async Task TryCreateAsync_DefaultAccount()
{
ParameterInfo parameter = GetType().GetMethod("TestJob").GetParameters()[0];
BindingProviderContext context = new BindingProviderContext(parameter, new Dictionary<string, Type>(), CancellationToken.None);
IBinding binding = await _provider.TryCreateAsync(context);
Assert.NotNull(binding);
}
public static void TestJob_AccountOverride(
[ServiceBusAttribute("test"),
ServiceBusAccount("testaccount")] out Message message)
{
message = new Message();
}
public static void TestJob(
[ServiceBusAttribute("test")] out Message message)
{
message = new Message();
}
}
}

Просмотреть файл

@ -1,71 +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.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;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class ServiceBusTriggerAttributeBindingProviderTests
{
private readonly Mock<MessagingProvider> _mockMessagingProvider;
private readonly ServiceBusTriggerAttributeBindingProvider _provider;
private readonly IConfiguration _configuration;
public ServiceBusTriggerAttributeBindingProviderTests()
{
_configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
Mock<INameResolver> mockResolver = new Mock<INameResolver>(MockBehavior.Strict);
ServiceBusOptions config = new ServiceBusOptions();
_mockMessagingProvider = new Mock<MessagingProvider>(MockBehavior.Strict, new OptionsWrapper<ServiceBusOptions>(config));
_provider = new ServiceBusTriggerAttributeBindingProvider(mockResolver.Object, config, _mockMessagingProvider.Object, _configuration);
}
[Fact]
public async Task TryCreateAsync_AccountOverride_OverrideIsApplied()
{
ParameterInfo parameter = GetType().GetMethod("TestJob_AccountOverride").GetParameters()[0];
TriggerBindingProviderContext context = new TriggerBindingProviderContext(parameter, CancellationToken.None);
ITriggerBinding binding = await _provider.TryCreateAsync(context);
Assert.NotNull(binding);
}
[Fact]
public async Task TryCreateAsync_DefaultAccount()
{
ParameterInfo parameter = GetType().GetMethod("TestJob").GetParameters()[0];
TriggerBindingProviderContext context = new TriggerBindingProviderContext(parameter, CancellationToken.None);
ITriggerBinding binding = await _provider.TryCreateAsync(context);
Assert.NotNull(binding);
}
public static void TestJob_AccountOverride(
[ServiceBusTriggerAttribute("test"),
ServiceBusAccount("testaccount")] Message message)
{
message = new Message();
}
public static void TestJob(
[ServiceBusTriggerAttribute("test")] Message message)
{
message = new Message();
}
}
}

Просмотреть файл

@ -1,96 +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;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Azure.WebJobs.ServiceBus.Triggers;
using Microsoft.Azure.ServiceBus;
using Xunit;
using Microsoft.Azure.ServiceBus.Core;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Bindings
{
public class ServiceBusTriggerBindingTests
{
[Fact]
public void CreateBindingDataContract_ReturnsExpectedValue()
{
IReadOnlyDictionary<string, Type> argumentContract = null;
var bindingDataContract = ServiceBusTriggerBinding.CreateBindingDataContract(argumentContract);
Assert.Equal(14, bindingDataContract.Count);
Assert.Equal(bindingDataContract["DeliveryCount"], typeof(int));
Assert.Equal(bindingDataContract["DeadLetterSource"], typeof(string));
Assert.Equal(bindingDataContract["LockToken"], typeof(string));
Assert.Equal(bindingDataContract["ExpiresAtUtc"], typeof(DateTime));
Assert.Equal(bindingDataContract["EnqueuedTimeUtc"], typeof(DateTime));
Assert.Equal(bindingDataContract["MessageId"], typeof(string));
Assert.Equal(bindingDataContract["ContentType"], typeof(string));
Assert.Equal(bindingDataContract["ReplyTo"], typeof(string));
Assert.Equal(bindingDataContract["SequenceNumber"], typeof(long));
Assert.Equal(bindingDataContract["To"], typeof(string));
Assert.Equal(bindingDataContract["Label"], typeof(string));
Assert.Equal(bindingDataContract["CorrelationId"], typeof(string));
Assert.Equal(bindingDataContract["UserProperties"], typeof(IDictionary<string, object>));
Assert.Equal(bindingDataContract["MessageReceiver"], typeof(MessageReceiver));
// verify that argument binding values override built ins
argumentContract = new Dictionary<string, Type>
{
{ "DeliveryCount", typeof(string) },
{ "NewProperty", typeof(decimal) }
};
bindingDataContract = ServiceBusTriggerBinding.CreateBindingDataContract(argumentContract);
Assert.Equal(15, bindingDataContract.Count);
Assert.Equal(bindingDataContract["DeliveryCount"], typeof(string));
Assert.Equal(bindingDataContract["NewProperty"], typeof(decimal));
}
[Theory]
[InlineData("application/json")]
[InlineData("text/plain")]
public void CreateBindingData_ReturnsExpectedValue(string contentType)
{
Message message = new Message(Encoding.UTF8.GetBytes("Test Message"))
{
ReplyTo = "test-queue",
To = "test",
ContentType = contentType,
Label = "test label",
CorrelationId = Guid.NewGuid().ToString(),
};
IReadOnlyDictionary<string, object> valueBindingData = null;
var config = new ServiceBusOptions
{
ConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TestKey;SharedAccessKey=00000000000000000"
};
var messageReceiver = new MessageReceiver(config.ConnectionString, "test");
var bindingData = ServiceBusTriggerBinding.CreateBindingData(message, messageReceiver, valueBindingData);
Assert.Equal(9, bindingData.Count);
Assert.Equal(message.ReplyTo, bindingData["ReplyTo"]);
Assert.Equal(string.Empty, bindingData["lockToken"]);
Assert.Equal(message.To, bindingData["To"]);
Assert.Equal(message.MessageId, bindingData["MessageId"]);
Assert.Equal(message.ContentType, bindingData["ContentType"]);
Assert.Equal(message.Label, bindingData["Label"]);
Assert.Equal(message.CorrelationId, bindingData["CorrelationId"]);
Assert.Same(message.UserProperties, bindingData["UserProperties"]);
Assert.Same(messageReceiver, bindingData["MessageReceiver"]);
// verify that value binding data overrides built ins
valueBindingData = new Dictionary<string, object>
{
{ "ReplyTo", "override" },
{ "NewProperty", 123 }
};
bindingData = ServiceBusTriggerBinding.CreateBindingData(message, messageReceiver, valueBindingData);
Assert.Equal(10, bindingData.Count);
Assert.Equal("override", bindingData["ReplyTo"]);
Assert.Equal(123, bindingData["NewProperty"]);
}
}
}

Просмотреть файл

@ -1,107 +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;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus.Config;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Config
{
public class ServiceBusHostBuilderExtensionsTests
{
[Fact]
public void ConfigureOptions_AppliesValuesCorrectly()
{
string extensionPath = "AzureWebJobs:Extensions:ServiceBus";
var values = new Dictionary<string, string>
{
{ $"{extensionPath}:PrefetchCount", "123" },
{ $"ConnectionStrings:ServiceBus", "TestConnectionString" },
{ $"{extensionPath}:MessageHandlerOptions:MaxConcurrentCalls", "123" },
{ $"{extensionPath}:MessageHandlerOptions:AutoComplete", "false" },
{ $"{extensionPath}:MessageHandlerOptions:MaxAutoRenewDuration", "00:00:15" }
};
ServiceBusOptions options = TestHelpers.GetConfiguredOptions<ServiceBusOptions>(b =>
{
b.AddServiceBus();
}, values);
Assert.Equal(123, options.PrefetchCount);
Assert.Equal("TestConnectionString", options.ConnectionString);
Assert.Equal(123, options.MessageHandlerOptions.MaxConcurrentCalls);
Assert.Equal(false, options.MessageHandlerOptions.AutoComplete);
Assert.Equal(TimeSpan.FromSeconds(15), options.MessageHandlerOptions.MaxAutoRenewDuration);
}
[Fact]
public void AddServiceBus_ThrowsArgumentNull_WhenServiceBusOptionsIsNull()
{
IHost host = new HostBuilder()
.ConfigureDefaultTestHost(b =>
{
b.AddServiceBus();
})
.ConfigureServices(s => s.AddSingleton<IOptions<ServiceBusOptions>>(p => null))
.Build();
var exception = Assert.Throws<ArgumentNullException>(() => host.Services.GetServices<IExtensionConfigProvider>());
Assert.Equal("serviceBusOptions", exception.ParamName);
}
[Fact]
public void AddServiceBus_NoServiceBusOptions_PerformsExpectedRegistration()
{
IHost host = new HostBuilder()
.ConfigureDefaultTestHost(b =>
{
b.AddServiceBus();
})
.Build();
var extensions = host.Services.GetService<IExtensionRegistry>();
IExtensionConfigProvider[] configProviders = extensions.GetExtensions<IExtensionConfigProvider>().ToArray();
// verify that the service bus config provider was registered
var serviceBusExtensionConfig = configProviders.OfType<ServiceBusExtensionConfigProvider>().Single();
}
[Fact]
public void AddServiceBus_ServiceBusOptionsProvided_PerformsExpectedRegistration()
{
string fakeConnStr = "test service bus connection";
IHost host = new HostBuilder()
.ConfigureDefaultTestHost(b =>
{
b.AddServiceBus();
})
.ConfigureServices(s =>
{
s.Configure<ServiceBusOptions>(o =>
{
o.ConnectionString = fakeConnStr;
});
})
.Build();
// verify that the service bus config provider was registered
var extensions = host.Services.GetService<IExtensionRegistry>();
IExtensionConfigProvider[] configProviders = extensions.GetExtensions<IExtensionConfigProvider>().ToArray();
// verify that the service bus config provider was registered
var serviceBusExtensionConfig = configProviders.OfType<ServiceBusExtensionConfigProvider>().Single();
Assert.Equal(fakeConnStr, serviceBusExtensionConfig.Options.ConnectionString);
}
}
}

Просмотреть файл

@ -1,88 +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;
using System.Linq;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Azure.WebJobs.ServiceBus.Config;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Config
{
public class ServiceBusOptionsTests
{
private readonly ILoggerFactory _loggerFactory;
private readonly TestLoggerProvider _loggerProvider;
public ServiceBusOptionsTests()
{
_loggerFactory = new LoggerFactory();
_loggerProvider = new TestLoggerProvider();
_loggerFactory.AddProvider(_loggerProvider);
}
[Fact]
public void Constructor_SetsExpectedDefaults()
{
ServiceBusOptions config = new ServiceBusOptions();
Assert.Equal(16, config.MessageHandlerOptions.MaxConcurrentCalls);
Assert.Equal(0, config.PrefetchCount);
}
[Fact]
public void PrefetchCount_GetSet()
{
ServiceBusOptions config = new ServiceBusOptions();
Assert.Equal(0, config.PrefetchCount);
config.PrefetchCount = 100;
Assert.Equal(100, config.PrefetchCount);
}
[Fact]
public void LogExceptionReceivedEvent_NonTransientEvent_LoggedAsError()
{
var ex = new ServiceBusException(false);
Assert.False(ex.IsTransient);
ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient");
ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory);
var expectedMessage = $"MessageReceiver error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)";
var logMessage = _loggerProvider.GetAllLogMessages().Single();
Assert.Equal(LogLevel.Error, logMessage.Level);
Assert.Same(ex, logMessage.Exception);
Assert.Equal(expectedMessage, logMessage.FormattedMessage);
}
[Fact]
public void LogExceptionReceivedEvent_TransientEvent_LoggedAsInformation()
{
var ex = new ServiceBusException(true);
Assert.True(ex.IsTransient);
ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient");
ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory);
var expectedMessage = $"MessageReceiver error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)";
var logMessage = _loggerProvider.GetAllLogMessages().Single();
Assert.Equal(LogLevel.Information, logMessage.Level);
Assert.Same(ex, logMessage.Exception);
Assert.Equal(expectedMessage, logMessage.FormattedMessage);
}
[Fact]
public void LogExceptionReceivedEvent_NonMessagingException_LoggedAsError()
{
var ex = new MissingMethodException("What method??");
ExceptionReceivedEventArgs e = new ExceptionReceivedEventArgs(ex, "TestAction", "TestEndpoint", "TestEntity", "TestClient");
ServiceBusExtensionConfigProvider.LogExceptionReceivedEvent(e, _loggerFactory);
var expectedMessage = $"MessageReceiver error (Action=TestAction, ClientId=TestClient, EntityPath=TestEntity, Endpoint=TestEndpoint)";
var logMessage = _loggerProvider.GetAllLogMessages().Single();
Assert.Equal(LogLevel.Error, logMessage.Level);
Assert.Same(ex, logMessage.Exception);
Assert.Equal(expectedMessage, logMessage.FormattedMessage);
}
}
}

Просмотреть файл

@ -1,107 +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;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.ServiceBus.Listeners;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Listeners
{
public class ServiceBusListenerTests
{
//private readonly MessagingFactory _messagingFactory;
private readonly ServiceBusListener _listener;
private readonly Mock<ITriggeredFunctionExecutor> _mockExecutor;
private readonly Mock<MessagingProvider> _mockMessagingProvider;
private readonly Mock<MessageProcessor> _mockMessageProcessor;
private readonly string _entityPath = "test-entity-path";
private readonly string _testConnection = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123=";
public ServiceBusListenerTests()
{
_mockExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict);
MessageHandlerOptions messageOptions = new MessageHandlerOptions(ExceptionReceivedHandler);
MessageReceiver messageReceiver = new MessageReceiver(_testConnection, _entityPath);
_mockMessageProcessor = new Mock<MessageProcessor>(MockBehavior.Strict, messageReceiver, messageOptions);
ServiceBusOptions config = new ServiceBusOptions
{
MessageHandlerOptions = messageOptions
};
_mockMessagingProvider = new Mock<MessagingProvider>(MockBehavior.Strict, new OptionsWrapper<ServiceBusOptions>(config));
_mockMessagingProvider.Setup(p => p.CreateMessageProcessor(_entityPath, _testConnection))
.Returns(_mockMessageProcessor.Object);
ServiceBusTriggerExecutor triggerExecutor = new ServiceBusTriggerExecutor(_mockExecutor.Object);
var mockServiceBusAccount = new Mock<ServiceBusAccount>(MockBehavior.Strict);
mockServiceBusAccount.Setup(a => a.ConnectionString).Returns(_testConnection);
_listener = new ServiceBusListener(_entityPath, triggerExecutor, config, mockServiceBusAccount.Object, _mockMessagingProvider.Object);
}
[Fact]
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();
CancellationToken cancellationToken = new CancellationToken();
_mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(message, cancellationToken)).ReturnsAsync(true);
FunctionResult result = new FunctionResult(true);
_mockExecutor.Setup(p => p.TryExecuteAsync(It.Is<TriggeredFunctionData>(q => q.TriggerValue == message), cancellationToken)).ReturnsAsync(result);
_mockMessageProcessor.Setup(p => p.CompleteProcessingMessageAsync(message, result, cancellationToken)).Returns(Task.FromResult(0));
await _listener.ProcessMessageAsync(message, CancellationToken.None);
_mockMessageProcessor.VerifyAll();
_mockExecutor.VerifyAll();
_mockMessageProcessor.VerifyAll();
}
[Fact]
public async Task ProcessMessageAsync_BeginProcessingReturnsFalse_MessageNotProcessed()
{
var message = new CustomMessage();
message.MessageId = Guid.NewGuid().ToString();
CancellationToken cancellationToken = new CancellationToken();
_mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(message, cancellationToken)).ReturnsAsync(false);
await _listener.ProcessMessageAsync(message, CancellationToken.None);
_mockMessageProcessor.VerifyAll();
}
Task ExceptionReceivedHandler(ExceptionReceivedEventArgs 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
internal class CustomMessage : Message
{
public override string ToString()
{
return MessageId;
}
}
}

Просмотреть файл

@ -1,42 +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.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus.Listeners;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Listeners
{
public class ServiceBusQueueListenerFactoryTests
{
[Fact]
public async Task CreateAsync_Success()
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddTestSettings()
.Build();
var config = new ServiceBusOptions
{
ConnectionString = configuration.GetWebJobsConnectionString("ServiceBus")
};
var messagingProvider = new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(config));
var account = new ServiceBusAccount(config, configuration);
Mock<ITriggeredFunctionExecutor> mockExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict);
ServiceBusQueueListenerFactory factory = new ServiceBusQueueListenerFactory(account, "testqueue", mockExecutor.Object, config, messagingProvider);
IListener listener = await factory.CreateAsync(CancellationToken.None);
Assert.NotNull(listener);
}
}
}

Просмотреть файл

@ -1,61 +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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.Executors;
using Xunit;
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);
}
[Fact]
public async Task CompleteProcessingMessageAsync_Failure_PropagatesException()
{
_options.AutoComplete = false;
Message message = new Message();
var functionException = new InvalidOperationException("Kaboom!");
FunctionResult result = new FunctionResult(functionException);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await _processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None);
});
Assert.Same(functionException, ex);
}
[Fact]
public async Task CompleteProcessingMessageAsync_DefaultOnMessageOptions()
{
Message message = new Message();
FunctionResult result = new FunctionResult(true);
await _processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None);
}
[Fact]
public void MessageOptions_ReturnsOptions()
{
Assert.Same(_options, _processor.MessageOptions);
}
Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs)
{
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -1,37 +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;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.ServiceBus.Triggers;
using Microsoft.Azure.ServiceBus;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class MessageToByteArrayConverterTests
{
private const string TestString = "This is a test!";
[Theory]
[InlineData(ContentTypes.TextPlain)]
[InlineData(ContentTypes.ApplicationJson)]
[InlineData(ContentTypes.ApplicationOctetStream)]
[InlineData("some-other-contenttype")]
[InlineData(null)]
public async Task ConvertAsync_ReturnsExpectedResults(string contentType)
{
Message message = new Message(Encoding.UTF8.GetBytes(TestString));
message.ContentType = contentType;
MessageToByteArrayConverter converter = new MessageToByteArrayConverter();
byte[] result = await converter.ConvertAsync(message, CancellationToken.None);
string decoded = Encoding.UTF8.GetString(result);
Assert.Equal(TestString, decoded);
}
}
}

Просмотреть файл

@ -1,92 +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;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.ServiceBus.Triggers;
using Microsoft.Azure.ServiceBus;
using Xunit;
using System.Collections.Generic;
using Microsoft.Azure.ServiceBus.InteropExtensions;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class MessageToStringConverterTests
{
private const string TestString = "This is a test!";
private const string TestJson = "{ value: 'This is a test!' }";
[Theory]
[InlineData(ContentTypes.TextPlain, TestString)]
[InlineData(ContentTypes.ApplicationJson, TestJson)]
[InlineData(ContentTypes.ApplicationOctetStream, TestString)]
[InlineData(null, TestJson)]
[InlineData("application/xml", TestJson)]
[InlineData(ContentTypes.TextPlain, null)]
public async Task ConvertAsync_ReturnsExpectedResult_WithBinarySerializer(string contentType, string value)
{
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
DataContractBinarySerializer<string>.Instance.WriteObject(ms, value);
bytes = ms.ToArray();
}
Message message = new Message(bytes);
message.ContentType = contentType;
MessageToStringConverter converter = new MessageToStringConverter();
string result = await converter.ConvertAsync(message, CancellationToken.None);
Assert.Equal(value, result);
}
[Theory]
[InlineData(ContentTypes.TextPlain, TestString)]
[InlineData(ContentTypes.ApplicationJson, TestJson)]
[InlineData(ContentTypes.ApplicationOctetStream, TestString)]
[InlineData(null, TestJson)]
[InlineData("application/xml", TestJson)]
[InlineData(ContentTypes.TextPlain, null)]
[InlineData(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;
MessageToStringConverter converter = new MessageToStringConverter();
string result = await converter.ConvertAsync(message, CancellationToken.None);
Assert.Equal(value, result);
}
[Fact]
public async Task ConvertAsync_Throws_WithSerializedObject()
{
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
DataContractBinarySerializer<TestObject>.Instance.WriteObject(ms, new TestObject() { Text = "Test" });
bytes = ms.ToArray();
}
Message message = new Message(bytes);
MessageToStringConverter converter = new MessageToStringConverter();
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => converter.ConvertAsync(message, CancellationToken.None));
Assert.IsType<SerializationException>(exception.InnerException);
Assert.StartsWith("The Message with ContentType 'null' failed to deserialize to a string with the message:", exception.Message);
}
[Serializable]
public class TestObject
{
public string Text { get; set; }
}
}
}

Просмотреть файл

@ -1,47 +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.Extensions.Options;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class MessagingProviderTests
{
[Fact]
public void CreateMessageReceiver_ReturnsExpectedReceiver()
{
string defaultConnection = "Endpoint=sb://default.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123=";
var config = new ServiceBusOptions
{
ConnectionString = defaultConnection
};
var provider = new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(config));
var receiver = provider.CreateMessageReceiver("entityPath", defaultConnection);
Assert.Equal("entityPath", receiver.Path);
var receiver2 = provider.CreateMessageReceiver("entityPath", defaultConnection);
Assert.Same(receiver, receiver2);
config.PrefetchCount = 100;
receiver = provider.CreateMessageReceiver("entityPath1", defaultConnection);
Assert.Equal(100, receiver.PrefetchCount);
}
[Fact]
public void CreateMessageSender_ReturnsExpectedSender()
{
string defaultConnection = "Endpoint=sb://default.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123=";
var config = new ServiceBusOptions
{
ConnectionString = defaultConnection
};
var provider = new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(config));
var sender = provider.CreateMessageSender("entityPath", defaultConnection);
Assert.Equal("entityPath", sender.Path);
var sender2 = provider.CreateMessageSender("entityPath", defaultConnection);
Assert.Same(sender, sender2);
}
}
}

Просмотреть файл

@ -1,7 +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.Reflection;
using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

Просмотреть файл

@ -1,33 +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 Xunit;
namespace Microsoft.Azure.WebJobs.Host.UnitTests
{
public class PublicSurfaceTests
{
[Fact]
public void WebJobs_Extensions_ServiceBus_VerifyPublicSurfaceArea()
{
var assembly = typeof(ServiceBusAttribute).Assembly;
var expected = new[]
{
"Constants",
"EntityType",
"MessageProcessor",
"MessagingProvider",
"ServiceBusAccountAttribute",
"ServiceBusAttribute",
"ServiceBusTriggerAttribute",
"ServiceBusHostBuilderExtensions",
"ServiceBusOptions",
"ServiceBusWebJobsStartup"
};
TestHelpers.AssertPublicTypes(expected, assembly);
}
}
}

Просмотреть файл

@ -1,59 +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;
using Microsoft.Extensions.Configuration;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class ServiceBusAccountTests
{
private readonly IConfiguration _configuration;
public ServiceBusAccountTests()
{
_configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
}
[Fact]
public void GetConnectionString_ReturnsExpectedConnectionString()
{
string defaultConnection = "Endpoint=sb://default.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=abc123=";
var options = new ServiceBusOptions()
{
ConnectionString = defaultConnection
};
var attribute = new ServiceBusTriggerAttribute("entity-name");
var account = new ServiceBusAccount(options, _configuration, attribute);
Assert.True(defaultConnection == account.ConnectionString);
}
[Fact]
public void GetConnectionString_ThrowsIfConnectionStringNullOrEmpty()
{
var config = new ServiceBusOptions();
var attribute = new ServiceBusTriggerAttribute("testqueue");
attribute.Connection = "MissingConnection";
var ex = Assert.Throws<InvalidOperationException>(() =>
{
var account = new ServiceBusAccount(config, _configuration, attribute);
var cs = account.ConnectionString;
});
Assert.Equal("Microsoft Azure WebJobs SDK ServiceBus connection string 'MissingConnection' is missing or empty.", ex.Message);
attribute.Connection = null;
config.ConnectionString = null;
ex = Assert.Throws<InvalidOperationException>(() =>
{
var account = new ServiceBusAccount(config, _configuration, attribute);
var cs = account.ConnectionString;
});
Assert.Equal("Microsoft Azure WebJobs SDK ServiceBus connection string 'AzureWebJobsServiceBus' is missing or empty.", ex.Message);
}
}
}

Просмотреть файл

@ -1,17 +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 Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class ServiceBusAttributeTests
{
[Fact]
public void Constructor_Success()
{
ServiceBusAttribute attribute = new ServiceBusAttribute("testqueue");
Assert.Equal("testqueue", attribute.QueueOrTopicName);
}
}
}

Просмотреть файл

@ -1,38 +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 Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests
{
public class ServiceBusTriggerAttributeTests
{
[Fact]
public void Constructor_Queue_SetsExpectedValues()
{
ServiceBusTriggerAttribute attribute = new ServiceBusTriggerAttribute("testqueue");
Assert.Equal("testqueue", attribute.QueueName);
Assert.Null(attribute.SubscriptionName);
Assert.Null(attribute.TopicName);
attribute = new ServiceBusTriggerAttribute("testqueue");
Assert.Equal("testqueue", attribute.QueueName);
Assert.Null(attribute.SubscriptionName);
Assert.Null(attribute.TopicName);
}
[Fact]
public void Constructor_Topic_SetsExpectedValues()
{
ServiceBusTriggerAttribute attribute = new ServiceBusTriggerAttribute("testtopic", "testsubscription");
Assert.Null(attribute.QueueName);
Assert.Equal("testtopic", attribute.TopicName);
Assert.Equal("testsubscription", attribute.SubscriptionName);
attribute = new ServiceBusTriggerAttribute("testtopic", "testsubscription");
Assert.Null(attribute.QueueName);
Assert.Equal("testtopic", attribute.TopicName);
Assert.Equal("testsubscription", attribute.SubscriptionName);
}
}
}

Просмотреть файл

@ -1,42 +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.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus.Listeners;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Listeners
{
public class ServiceBusSubscriptionListenerFactoryTests
{
[Fact]
public async Task CreateAsync_Success()
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddTestSettings()
.Build();
var config = new ServiceBusOptions
{
ConnectionString = configuration.GetWebJobsConnectionString("ServiceBus")
};
var messagingProvider = new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(config));
var account = new ServiceBusAccount(config, configuration);
Mock<ITriggeredFunctionExecutor> mockExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict);
ServiceBusSubscriptionListenerFactory factory = new ServiceBusSubscriptionListenerFactory(account, "testtopic", "testsubscription", mockExecutor.Object, config, messagingProvider);
IListener listener = await factory.CreateAsync(CancellationToken.None);
Assert.NotNull(listener);
}
}
}

Просмотреть файл

@ -1,89 +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;
using System.Reflection;
using System.Text;
using System.Threading;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Azure.WebJobs.ServiceBus.Triggers;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Triggers
{
public class ServiceBusTriggerBindingIntegrationTests : IClassFixture<InvariantCultureFixture>
{
private ITriggerBinding _queueBinding;
private ITriggerBinding _topicBinding;
public ServiceBusTriggerBindingIntegrationTests()
{
IQueueTriggerArgumentBindingProvider provider = new UserTypeArgumentBindingProvider();
ParameterInfo pi = new StubParameterInfo("parameterName", typeof(UserDataType));
var argumentBinding = provider.TryCreate(pi);
var options = new ServiceBusOptions();
var messagingProvider = new MessagingProvider(new OptionsWrapper<ServiceBusOptions>(options));
_queueBinding = new ServiceBusTriggerBinding("parameterName", typeof(UserDataType), argumentBinding, null, options, messagingProvider, "queueName");
_topicBinding = new ServiceBusTriggerBinding("parameterName", typeof(UserDataType), argumentBinding, null, options, messagingProvider, "subscriptionName", "topicName");
}
[Theory]
[InlineData("RequestId", "4b957741-c22e-471d-9f0f-e1e8534b9cb6")]
[InlineData("RequestReceivedTime", "8/16/2014 12:09:36 AM")]
[InlineData("DeliveryCount", "8")]
[InlineData("IsSuccess", "False")]
public void BindAsync_IfUserDataType_ReturnsValidBindingData(string userPropertyName, string userPropertyValue)
{
// Arrange
UserDataType expectedObject = new UserDataType();
PropertyInfo userProperty = typeof(UserDataType).GetProperty(userPropertyName);
var parseMethod = userProperty.PropertyType.GetMethod(
"Parse", new Type[] { typeof(string) });
object convertedPropertyValue = parseMethod.Invoke(null, new object[] { userPropertyValue });
userProperty.SetValue(expectedObject, convertedPropertyValue);
string messageContent = JsonConvert.SerializeObject(expectedObject);
ValueBindingContext context = new ValueBindingContext(null, CancellationToken.None);
Action<ITriggerBinding> testBinding = (b) =>
{
// Act
Message message = new Message(Encoding.UTF8.GetBytes(messageContent));
message.ContentType = ContentTypes.ApplicationJson;
ITriggerData data = _queueBinding.BindAsync(message, context).GetAwaiter().GetResult();
// Assert
Assert.NotNull(data);
Assert.NotNull(data.ValueProvider);
Assert.NotNull(data.BindingData);
Assert.True(data.BindingData.ContainsKey(userPropertyName));
Assert.Equal(userProperty.GetValue(expectedObject, null), data.BindingData[userPropertyName]);
};
testBinding(_queueBinding);
testBinding(_topicBinding);
}
private class StubParameterInfo : ParameterInfo
{
public StubParameterInfo(string name, Type type)
{
NameImpl = name;
ClassImpl = type;
}
}
public class UserDataType
{
public Guid RequestId { get; set; }
public string BlobFile { get; set; }
public DateTime RequestReceivedTime { get; set; }
public Int32 DeliveryCount { get; set; }
public Boolean IsSuccess { get; set; }
}
}
}

Просмотреть файл

@ -1,61 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
<AssemblyName>Microsoft.Azure.WebJobs.ServiceBus.UnitTests</AssemblyName>
<RootNamespace>Microsoft.Azure.WebJobs.ServiceBus.UnitTests</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>
<ItemGroup>
<Compile Remove="EventHubTests.cs" />
<Compile Remove="Listeners\EventHubListenerTests.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="Moq" Version="4.7.145" />
<PackageReference Include="WindowsAzure.Storage" Version="9.3.1" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta3-build3705" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Logging.ApplicationInsights\WebJobs.Logging.ApplicationInsights.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Logging\WebJobs.Logging.csproj" />
<ProjectReference Include="..\Microsoft.Azure.WebJobs.Host.TestCommon\WebJobs.Host.TestCommon.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -1,509 +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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
{
public class ServiceBusEndToEndTests : IDisposable
{
private const string SecondaryConnectionStringKey = "ServiceBusSecondary";
private const string Prefix = "core-test-";
private const string FirstQueueName = Prefix + "queue1";
private const string SecondQueueName = Prefix + "queue2";
private const string BinderQueueName = Prefix + "queue3";
private const string TopicName = Prefix + "topic1";
private const string TopicSubscriptionName1 = "sub1";
private const string TopicSubscriptionName2 = "sub2";
private const string TriggerDetailsMessageStart = "Trigger Details:";
private const int SBTimeout = 60 * 1000;
private static EventWaitHandle _topicSubscriptionCalled1;
private static EventWaitHandle _topicSubscriptionCalled2;
// These two variables will be checked at the end of the test
private static string _resultMessage1;
private static string _resultMessage2;
private readonly RandomNameResolver _nameResolver;
private readonly string _primaryConnectionString;
private readonly string _secondaryConnectionString;
public ServiceBusEndToEndTests()
{
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddTestSettings()
.Build();
_primaryConnectionString = config.GetConnectionString(ServiceBus.Constants.DefaultConnectionStringName);
_secondaryConnectionString = config.GetConnectionString(SecondaryConnectionStringKey);
_nameResolver = new RandomNameResolver();
Cleanup().GetAwaiter().GetResult();
}
[Fact]
public async Task ServiceBusEndToEnd()
{
await ServiceBusEndToEndInternal();
}
[Fact]
public async Task ServiceBusBinderTest()
{
var hostType = typeof(ServiceBusTestJobs);
var host = CreateHost();
var method = typeof(ServiceBusTestJobs).GetMethod("ServiceBusBinderTest");
int numMessages = 10;
var args = new { message = "Test Message", numMessages = numMessages };
var jobHost = host.GetJobHost<ServiceBusTestJobs>();
await jobHost.CallAsync(method, args);
await jobHost.CallAsync(method, args);
await jobHost.CallAsync(method, args);
var count = await CleanUpEntity(BinderQueueName);
Assert.Equal(numMessages * 3, count);
}
[Fact]
public async Task CustomMessageProcessorTest()
{
IHost host = new HostBuilder()
.ConfigureDefaultTestHost<ServiceBusTestJobs>(b =>
{
b.AddAzureStorage()
.AddServiceBus();
})
.ConfigureServices(services =>
{
services.AddSingleton<MessagingProvider, CustomMessagingProvider>();
})
.Build();
var loggerProvider = host.GetTestLoggerProvider();
await ServiceBusEndToEndInternal(host: host);
// in addition to verifying that our custom processor was called, we're also
// verifying here that extensions can log
IEnumerable<LogMessage> messages = loggerProvider.GetAllLogMessages().Where(m => m.Category == CustomMessagingProvider.CustomMessagingCategory);
Assert.Equal(4, messages.Count(p => p.FormattedMessage.Contains("Custom processor Begin called!")));
Assert.Equal(4, messages.Count(p => p.FormattedMessage.Contains("Custom processor End called!")));
}
[Fact]
public async Task MultipleAccountTest()
{
IHost host = new HostBuilder()
.ConfigureDefaultTestHost<ServiceBusTestJobs>(b =>
{
b.AddAzureStorage()
.AddServiceBus();
}, nameResolver: _nameResolver)
.ConfigureServices(services =>
{
services.AddSingleton<MessagingProvider, CustomMessagingProvider>();
})
.Build();
await WriteQueueMessage(_secondaryConnectionString, FirstQueueName, "Test");
_topicSubscriptionCalled1 = new ManualResetEvent(initialState: false);
await host.StartAsync();
_topicSubscriptionCalled1.WaitOne(SBTimeout);
// ensure all logs have had a chance to flush
await Task.Delay(3000);
// Wait for the host to terminate
await host.StopAsync();
host.Dispose();
Assert.Equal("Test-topic-1", _resultMessage1);
}
private async Task<int> CleanUpEntity(string queueName, string connectionString = null)
{
var messageReceiver = new MessageReceiver(!string.IsNullOrEmpty(connectionString) ? connectionString : _primaryConnectionString, queueName, ReceiveMode.ReceiveAndDelete);
Message message;
int count = 0;
do
{
message = await messageReceiver.ReceiveAsync(TimeSpan.FromSeconds(3)).ConfigureAwait(false);
if (message != null)
{
count++;
}
else
{
break;
}
} while (true);
await messageReceiver.CloseAsync();
return count;
}
private async Task Cleanup()
{
await CleanUpEntity(FirstQueueName);
await CleanUpEntity(SecondQueueName);
await CleanUpEntity(BinderQueueName);
await CleanUpEntity(FirstQueueName, _secondaryConnectionString);
await CleanUpEntity(EntityNameHelper.FormatSubscriptionPath(TopicName, TopicSubscriptionName1));
await CleanUpEntity(EntityNameHelper.FormatSubscriptionPath(TopicName, TopicSubscriptionName2));
}
private IHost CreateHost()
{
return new HostBuilder()
.ConfigureDefaultTestHost<ServiceBusTestJobs>(b =>
{
b.AddAzureStorage()
.AddServiceBus();
})
.ConfigureServices(services =>
{
services.AddSingleton<INameResolver>(_nameResolver);
})
.Build();
}
private async Task ServiceBusEndToEndInternal(IHost host = null)
{
if (host == null)
{
host = CreateHost();
}
var jobContainerType = typeof(ServiceBusTestJobs);
await WriteQueueMessage(_primaryConnectionString, FirstQueueName, "E2E");
_topicSubscriptionCalled1 = new ManualResetEvent(initialState: false);
_topicSubscriptionCalled2 = new ManualResetEvent(initialState: false);
using (host)
{
await host.StartAsync();
_topicSubscriptionCalled1.WaitOne(SBTimeout);
_topicSubscriptionCalled2.WaitOne(SBTimeout);
// ensure all logs have had a chance to flush
await Task.Delay(4000);
// Wait for the host to terminate
await host.StopAsync();
Assert.Equal("E2E-SBQueue2SBQueue-SBQueue2SBTopic-topic-1", _resultMessage1);
Assert.Equal("E2E-SBQueue2SBQueue-SBQueue2SBTopic-topic-2", _resultMessage2);
IEnumerable<LogMessage> logMessages = host.GetTestLoggerProvider()
.GetAllLogMessages();
// filter out anything from the custom processor for easier validation.
IEnumerable<LogMessage> consoleOutput = logMessages
.Where(m => m.Category != CustomMessagingProvider.CustomMessagingCategory);
Assert.DoesNotContain(consoleOutput, p => p.Level == LogLevel.Error);
string[] consoleOutputLines = consoleOutput
.Where(p => p.FormattedMessage != null)
.SelectMany(p => p.FormattedMessage.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries))
.OrderBy(p => p)
.ToArray();
string[] expectedOutputLines = new string[]
{
"Found the following functions:",
$"{jobContainerType.FullName}.SBQueue2SBQueue",
$"{jobContainerType.FullName}.MultipleAccounts",
$"{jobContainerType.FullName}.SBQueue2SBTopic",
$"{jobContainerType.FullName}.SBTopicListener1",
$"{jobContainerType.FullName}.SBTopicListener2",
$"{jobContainerType.FullName}.ServiceBusBinderTest",
"Job host started",
$"Executing '{jobContainerType.Name}.SBQueue2SBQueue' (Reason='New ServiceBus message detected on '{FirstQueueName}'.', Id=",
$"Executed '{jobContainerType.Name}.SBQueue2SBQueue' (Succeeded, Id=",
$"Trigger Details:",
$"Executing '{jobContainerType.Name}.SBQueue2SBTopic' (Reason='New ServiceBus message detected on '{SecondQueueName}'.', Id=",
$"Executed '{jobContainerType.Name}.SBQueue2SBTopic' (Succeeded, Id=",
$"Trigger Details:",
$"Executing '{jobContainerType.Name}.SBTopicListener1' (Reason='New ServiceBus message detected on '{EntityNameHelper.FormatSubscriptionPath(TopicName, TopicSubscriptionName1)}'.', Id=",
$"Executed '{jobContainerType.Name}.SBTopicListener1' (Succeeded, Id=",
$"Trigger Details:",
$"Executing '{jobContainerType.Name}.SBTopicListener2' (Reason='New ServiceBus message detected on '{EntityNameHelper.FormatSubscriptionPath(TopicName, TopicSubscriptionName2)}'.', Id=",
$"Executed '{jobContainerType.Name}.SBTopicListener2' (Succeeded, Id=",
$"Trigger Details:",
"Job host stopped",
"Starting JobHost",
"Stopping JobHost",
"BlobsOptions",
"{",
" \"CentralizedPoisonQueue\": false",
"}",
"FunctionResultAggregatorOptions",
"{",
" \"BatchSize\": 1000",
" \"FlushTimeout\": \"00:00:30\",",
" \"IsEnabled\": true",
"}",
"LoggerFilterOptions",
"{",
" \"MinLevel\": \"Information\"",
" \"Rules\": []",
"}",
"QueuesOptions",
"{",
" \"BatchSize\": 16",
" \"MaxDequeueCount\": 5,",
" \"MaxPollingInterval\": \"00:00:02\",",
" \"NewBatchThreshold\": 8,",
" \"VisibilityTimeout\": \"00:00:00\"",
"}",
"ServiceBusOptions",
"{",
" \"PrefetchCount\": 0,",
" \"MessageHandlerOptions\": {",
" \"AutoComplete\": true,",
" \"MaxAutoRenewDuration\": \"00:05:00\",",
" \"MaxConcurrentCalls\": 16",
" }",
"}",
"SingletonOptions",
"{",
" \"ListenerLockPeriod\": \"00:01:00\"",
" \"ListenerLockRecoveryPollingInterval\": \"00:01:00\"",
" \"LockAcquisitionPollingInterval\": \"00:00:05\"",
" \"LockAcquisitionTimeout\": \"",
" \"LockPeriod\": \"00:00:15\"",
"}",
}.OrderBy(p => p).ToArray();
Action<string>[] inspectors = expectedOutputLines.Select<string, Action<string>>(p => (string m) => m.StartsWith(p)).ToArray();
Assert.Collection(consoleOutputLines, inspectors);
// Verify that trigger details are properly formatted
string[] triggerDetailsConsoleOutput = consoleOutputLines
.Where(m => m.StartsWith(TriggerDetailsMessageStart)).ToArray();
string expectedPattern = "Trigger Details: MessageId: (.*), DeliveryCount: [0-9]+, EnqueuedTime: (.*), LockedUntil: (.*)";
foreach (string msg in triggerDetailsConsoleOutput)
{
Assert.True(Regex.IsMatch(msg, expectedPattern), $"Expected trace event {expectedPattern} not found.");
}
}
}
private async Task WriteQueueMessage(string connectionString, string queueName, string message)
{
QueueClient queueClient = new QueueClient(connectionString, queueName);
await queueClient.SendAsync(new Message(Encoding.UTF8.GetBytes(message)));
await queueClient.CloseAsync();
}
public abstract class ServiceBusTestJobsBase
{
protected static Message SBQueue2SBQueue_GetOutputMessage(string input)
{
input = input + "-SBQueue2SBQueue";
return new Message
{
ContentType = "text/plain",
Body = Encoding.UTF8.GetBytes(input)
};
}
protected static Message SBQueue2SBTopic_GetOutputMessage(string input)
{
input = input + "-SBQueue2SBTopic";
return new Message(Encoding.UTF8.GetBytes(input))
{
ContentType = "text/plain"
};
}
protected static void SBTopicListener1Impl(string input)
{
_resultMessage1 = input + "-topic-1";
_topicSubscriptionCalled1.Set();
}
protected static void SBTopicListener2Impl(Message message)
{
using (Stream stream = new MemoryStream(message.Body))
using (TextReader reader = new StreamReader(stream))
{
_resultMessage2 = reader.ReadToEnd() + "-topic-2";
}
_topicSubscriptionCalled2.Set();
}
}
public class ServiceBusTestJobs : ServiceBusTestJobsBase
{
// Passes service bus message from a queue to another queue
public static async Task SBQueue2SBQueue(
[ServiceBusTrigger(FirstQueueName)] string start, int deliveryCount,
MessageReceiver messageReceiver,
string lockToken,
[ServiceBus(SecondQueueName)] MessageSender messageSender)
{
Assert.Equal(FirstQueueName, messageReceiver.Path);
Assert.Equal(1, deliveryCount);
// verify the message receiver and token are valid
await messageReceiver.RenewLockAsync(lockToken);
var message = SBQueue2SBQueue_GetOutputMessage(start);
await messageSender.SendAsync(message);
}
// Passes a service bus message from a queue to topic using a brokered message
public static void SBQueue2SBTopic(
[ServiceBusTrigger(SecondQueueName)] string message,
[ServiceBus(TopicName)] out Message output)
{
output = SBQueue2SBTopic_GetOutputMessage(message);
}
// First listener for the topic
public static void SBTopicListener1(
[ServiceBusTrigger(TopicName, TopicSubscriptionName1)] string message,
MessageReceiver messageReceiver,
string lockToken)
{
SBTopicListener1Impl(message);
}
// Second listener for the topic
// Just sprinkling Singleton here because previously we had a bug where this didn't work
// for ServiceBus.
[Singleton]
public static void SBTopicListener2(
[ServiceBusTrigger(TopicName, TopicSubscriptionName2)] Message message)
{
SBTopicListener2Impl(message);
}
// Demonstrate triggering on a queue in one account, and writing to a topic
// in the primary subscription
public static void MultipleAccounts(
[ServiceBusTrigger(FirstQueueName, Connection = SecondaryConnectionStringKey)] string input,
[ServiceBus(TopicName)] out string output)
{
output = input;
}
[NoAutomaticTrigger]
public static async Task ServiceBusBinderTest(
string message,
int numMessages,
Binder binder)
{
var attribute = new ServiceBusAttribute(BinderQueueName)
{
EntityType = EntityType.Queue
};
var collector = await binder.BindAsync<IAsyncCollector<string>>(attribute);
for (int i = 0; i < numMessages; i++)
{
await collector.AddAsync(message + i);
}
await collector.FlushAsync();
}
}
private class CustomMessagingProvider : MessagingProvider
{
public const string CustomMessagingCategory = "CustomMessagingProvider";
private readonly ILogger _logger;
private readonly ServiceBusOptions _options;
public CustomMessagingProvider(IOptions<ServiceBusOptions> serviceBusOptions, ILoggerFactory loggerFactory)
: base(serviceBusOptions)
{
_options = serviceBusOptions.Value;
_logger = loggerFactory?.CreateLogger(CustomMessagingCategory);
}
public override MessageProcessor CreateMessageProcessor(string entityPath, string connectionName = null)
{
var options = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 3,
MaxAutoRenewDuration = TimeSpan.FromMinutes(1)
};
var messageReceiver = new MessageReceiver(_options.ConnectionString, entityPath);
return new CustomMessageProcessor(messageReceiver, options, _logger);
}
private class CustomMessageProcessor : MessageProcessor
{
private readonly ILogger _logger;
public CustomMessageProcessor(MessageReceiver messageReceiver, MessageHandlerOptions messageOptions, ILogger logger)
: base(messageReceiver, messageOptions)
{
_logger = logger;
}
public override async Task<bool> BeginProcessingMessageAsync(Message message, CancellationToken cancellationToken)
{
_logger?.LogInformation("Custom processor Begin called!");
return await base.BeginProcessingMessageAsync(message, cancellationToken);
}
public override async Task CompleteProcessingMessageAsync(Message message, Executors.FunctionResult result, CancellationToken cancellationToken)
{
_logger?.LogInformation("Custom processor End called!");
await base.CompleteProcessingMessageAsync(message, result, cancellationToken);
}
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs eventArgs)
{
return Task.CompletedTask;
}
}
public void Dispose()
{
Cleanup().GetAwaiter().GetResult();
}
}
}

Просмотреть файл

@ -25,7 +25,7 @@
<PackageReference Include="appinsights.testlogger" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="3.0.6" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@ -39,7 +39,6 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.EventHubs\WebJobs.Extensions.EventHubs.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.Storage\WebJobs.Extensions.Storage.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj" />

Просмотреть файл

@ -22,6 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="3.0.6" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@ -34,7 +35,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.ServiceBus\WebJobs.Extensions.ServiceBus.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Extensions.Storage\WebJobs.Extensions.Storage.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host.Storage\WebJobs.Host.Storage.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Azure.WebJobs.Host\WebJobs.Host.csproj" />

Просмотреть файл

@ -2,7 +2,7 @@
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
<IsPackable>true</IsPackable>
<AssemblyName>Microsoft.Azure.WebJobs.Host.TestCommon</AssemblyName>
<RootNamespace>Microsoft.Azure.WebJobs.Host.TestCommon</RootNamespace>
</PropertyGroup>