Skip to content

Commit 9c04836

Browse files
JoshLove-msftliliankasem
authored andcommitted
Add ability to bind to SBReceivedMessage (#1313)
1 parent 8770e77 commit 9c04836

14 files changed

Lines changed: 383 additions & 3 deletions

File tree

extensions/Worker.Extensions.ServiceBus/release_notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus <version>
88

9-
- <event>
9+
- Support binding to ServiceBusReceivedMessage (#1313)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
5+
namespace Microsoft.Azure.Functions.Worker.Extensions.ServiceBus
6+
{
7+
public static class Constants
8+
{
9+
internal const string BinaryContentType = "application/octet-stream";
10+
}
11+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4-
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
4+
using System.Runtime.CompilerServices;
5+
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
56

67
[assembly: ExtensionInformation("Microsoft.Azure.WebJobs.Extensions.ServiceBus", "5.11.0")]
8+
[assembly: InternalsVisibleTo("Microsoft.Azure.Functions.WorkerExtension.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001005148be37ac1d9f58bd40a2e472c9d380d635b6048278f7d47480b08c928858f0f7fe17a6e4ce98da0e7a7f0b8c308aecd9e9b02d7e9680a5b5b75ac7773cec096fbbc64aebd429e77cb5f89a569a79b28e9c76426783f624b6b70327eb37341eb498a2c3918af97c4860db6cdca4732787150841e395a29cfacb959c1fd971c1")]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Azure.Functions.Worker;
6+
using Microsoft.Azure.Functions.Worker.Core;
7+
using Microsoft.Extensions.Azure;
8+
9+
[assembly: WorkerExtensionStartup(typeof(ServiceBusExtensionStartup))]
10+
11+
namespace Microsoft.Azure.Functions.Worker
12+
{
13+
public class ServiceBusExtensionStartup : WorkerExtensionStartup
14+
{
15+
public override void Configure(IFunctionsWorkerApplicationBuilder applicationBuilder)
16+
{
17+
if (applicationBuilder == null)
18+
{
19+
throw new ArgumentNullException(nameof(applicationBuilder));
20+
}
21+
22+
applicationBuilder.Services.AddAzureClientsCore(); // Adds AzureComponentFactory
23+
}
24+
}
25+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Azure.Core.Amqp;
8+
using Azure.Messaging.ServiceBus;
9+
using Microsoft.Azure.Functions.Worker.Converters;
10+
using Microsoft.Azure.Functions.Worker.Core;
11+
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
12+
using Microsoft.Azure.Functions.Worker.Extensions.ServiceBus;
13+
14+
namespace Microsoft.Azure.Functions.Worker
15+
{
16+
17+
[SupportsDeferredBinding]
18+
[SupportedConverterType(typeof(ServiceBusReceivedMessage))]
19+
[SupportedConverterType(typeof(ServiceBusReceivedMessage[]))]
20+
internal class ServiceBusReceivedMessageConverter : IInputConverter
21+
{
22+
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
23+
{
24+
ConversionResult result = context?.Source switch
25+
{
26+
ModelBindingData binding => ConversionResult.Success(ConvertToServiceBusReceivedMessage(binding)),
27+
// Only array collections are currently supported, which matches the behavior of the in-proc extension.
28+
CollectionModelBindingData collection => ConversionResult.Success(collection.ModelBindingDataArray
29+
.Select(ConvertToServiceBusReceivedMessage).ToArray()),
30+
_ => ConversionResult.Unhandled()
31+
};
32+
return new ValueTask<ConversionResult>(result);
33+
}
34+
35+
private ServiceBusReceivedMessage ConvertToServiceBusReceivedMessage(ModelBindingData binding)
36+
{
37+
// The lock token is a 16 byte GUID
38+
const int lockTokenLength = 16;
39+
40+
if (binding.ContentType != Constants.BinaryContentType)
41+
{
42+
throw new InvalidOperationException(
43+
$"Unexpected content-type. Only '{Constants.BinaryContentType}' is supported.");
44+
}
45+
46+
ReadOnlyMemory<byte> bytes = binding.Content.ToMemory();
47+
ReadOnlyMemory<byte> lockTokenBytes = bytes.Slice(0, lockTokenLength);
48+
ReadOnlyMemory<byte> messageBytes = bytes.Slice(lockTokenLength, bytes.Length - lockTokenLength);
49+
return ServiceBusReceivedMessage.FromAmqpMessage(AmqpAnnotatedMessage.FromBytes(BinaryData.FromBytes(messageBytes)),
50+
BinaryData.FromBytes(lockTokenBytes));
51+
}
52+
}
53+
}

extensions/Worker.Extensions.ServiceBus/src/ServiceBusTriggerAttribute.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4-
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
4+
using System.Collections.Generic;
5+
using Azure.Messaging.ServiceBus;
6+
using Microsoft.Azure.Functions.Worker.Converters;
7+
using Microsoft.Azure.Functions.Worker.Extensions.Abstractions;
58

69
namespace Microsoft.Azure.Functions.Worker
710
{
11+
[AllowConverterFallback(true)]
12+
[InputConverter(typeof(ServiceBusReceivedMessageConverter))]
813
public sealed class ServiceBusTriggerAttribute : TriggerBindingAttribute, ISupportCardinality
914
{
1015
private bool _isBatched = false;

extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,26 @@
77

88
<!--Version information-->
99
<VersionPrefix>5.11.0</VersionPrefix>
10+
<VersionSuffix>-preview1</VersionSuffix>
1011

1112
<!--Temporarily opting out of documentation. Pending documentation-->
1213
<GenerateDocumentationFile>false</GenerateDocumentationFile>
1314
</PropertyGroup>
1415

1516
<Import Project="..\..\..\build\Extensions.props" />
1617

18+
<ItemGroup>
19+
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.14.0" />
20+
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.6.3" />
21+
</ItemGroup>
22+
1723
<ItemGroup>
1824
<ProjectReference Include="..\..\Worker.Extensions.Abstractions\src\Worker.Extensions.Abstractions.csproj" />
25+
<ProjectReference Include="..\..\..\src\DotNetWorker.Core\DotNetWorker.Core.csproj" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<SharedReference Include="..\..\Worker.Extensions.Shared/Worker.Extensions.Shared.csproj" />
1930
</ItemGroup>
2031

2132
</Project>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Azure.Messaging.ServiceBus;
5+
using Microsoft.Azure.Functions.Worker;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace SampleApp
9+
{
10+
/// <summary>
11+
/// Samples demonstrating binding to the <see cref="ServiceBusReceivedMessage"/> type.
12+
/// </summary>
13+
public class ServiceBusReceivedMessageBindingSamples
14+
{
15+
private readonly ILogger<ServiceBusReceivedMessageBindingSamples> _logger;
16+
17+
public ServiceBusReceivedMessageBindingSamples(ILogger<ServiceBusReceivedMessageBindingSamples> logger)
18+
{
19+
_logger = logger;
20+
}
21+
22+
/// <summary>
23+
/// This function demonstrates binding to a single <see cref="ServiceBusReceivedMessage"/>.
24+
/// </summary>
25+
[Function(nameof(ServiceBusReceivedMessageFunction))]
26+
public void ServiceBusReceivedMessageFunction(
27+
[ServiceBusTrigger("queue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
28+
{
29+
_logger.LogInformation("Message ID: {id}", message.MessageId);
30+
_logger.LogInformation("Message Body: {body}", message.Body);
31+
_logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);
32+
}
33+
34+
/// <summary>
35+
/// This function demonstrates binding to an array of <see cref="ServiceBusReceivedMessage"/>.
36+
/// Note that when doing so, you must also set the <see cref="ServiceBusTriggerAttribute.IsBatched"/> property
37+
/// to <value>true</value>.
38+
/// </summary>
39+
[Function(nameof(ServiceBusReceivedMessageBatchFunction))]
40+
public void ServiceBusReceivedMessageBatchFunction(
41+
[ServiceBusTrigger("queue", Connection = "ServiceBusConnection", IsBatched = true)] ServiceBusReceivedMessage[] messages)
42+
{
43+
foreach (ServiceBusReceivedMessage message in messages)
44+
{
45+
_logger.LogInformation("Message ID: {id}", message.MessageId);
46+
_logger.LogInformation("Message Body: {body}", message.Body);
47+
_logger.LogInformation("Message Content-Type: {contentType}", message.ContentType);
48+
}
49+
}
50+
51+
/// <summary>
52+
/// This functions demonstrates that it is possible to bind to both the ServiceBusReceivedMessage and any of the supported binding contract
53+
/// properties at the same time. If attempting this, the ServiceBusReceivedMessage must be the first parameter. There is not
54+
/// much benefit to doing this as all of the binding contract properties are available as properties on the ServiceBusReceivedMessage.
55+
/// </summary>
56+
[Function(nameof(ServiceBusReceivedMessageWithStringProperties))]
57+
public void ServiceBusReceivedMessageWithStringProperties(
58+
[ServiceBusTrigger("queue", Connection = "ServiceBusConnection")]
59+
ServiceBusReceivedMessage message, string messageId, int deliveryCount)
60+
{
61+
// The MessageId property and the messageId parameter are the same.
62+
_logger.LogInformation("Message ID: {id}", message.MessageId);
63+
_logger.LogInformation("Message ID: {id}", messageId);
64+
65+
// Similarly the DeliveryCount property and the deliveryCount parameter are the same.
66+
_logger.LogInformation("Delivery Count: {count}", message.DeliveryCount);
67+
_logger.LogInformation("Delivery Count: {count}", deliveryCount);
68+
}
69+
}
70+
}

samples/WorkerBindingSamples/WorkerBindingSamples.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
</ItemGroup>
1414
<ItemGroup>
1515
<ProjectReference Include="..\..\extensions\Worker.Extensions.Abstractions\src\Worker.Extensions.Abstractions.csproj" />
16+
<ProjectReference Include="..\..\extensions\Worker.Extensions.ServiceBus\src\Worker.Extensions.ServiceBus.csproj" />
1617
<ProjectReference Include="..\..\extensions\Worker.Extensions.Storage\src\Worker.Extensions.Storage.csproj" />
1718
<ProjectReference Include="..\..\src\DotNetWorker.ApplicationInsights\DotNetWorker.ApplicationInsights.csproj" />
1819
<ProjectReference Include="..\..\src\DotNetWorker\DotNetWorker.csproj" />

test/E2ETests/E2EApps/E2EApp/E2EApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
</PropertyGroup>
1919

2020
<ItemGroup>
21+
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.ServiceBus\src\Worker.Extensions.ServiceBus.csproj" />
2122
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.Storage\src\Worker.Extensions.Storage.csproj" />
2223
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.Abstractions\src\Worker.Extensions.Abstractions.csproj" />
2324
<ProjectReference Include="..\..\..\..\extensions\Worker.Extensions.CosmosDB\src\Worker.Extensions.CosmosDB.csproj" />

0 commit comments

Comments
 (0)