Skip to content

Commit 0b3d49a

Browse files
authored
Merge pull request #2 from GodelTech/feature/init
Feature/init
2 parents 26c2a03 + ecc8d7a commit 0b3d49a

File tree

10 files changed

+303
-19
lines changed

10 files changed

+303
-19
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Collections.Generic;
2+
3+
namespace GodelTech.Messaging.AzureServiceBus
4+
{
5+
/// <summary>
6+
/// Azure Service Bus options
7+
/// </summary>
8+
public class AzureServiceBusOptions
9+
{
10+
/// <summary>
11+
/// Queue dictionary that stores key value pair of internal queue key and Azure queue name.
12+
/// </summary>
13+
public IDictionary<string, string> Queues { get; set; }
14+
}
15+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Threading.Tasks;
4+
using Azure.Messaging.ServiceBus;
5+
using Microsoft.Extensions.Options;
6+
7+
namespace GodelTech.Messaging.AzureServiceBus
8+
{
9+
/// <summary>
10+
/// Azure Service Bus sender
11+
/// </summary>
12+
public class AzureServiceBusSender
13+
{
14+
private readonly ServiceBusClient _serviceBusClient;
15+
private readonly AzureServiceBusOptions _azureServiceBusOptions;
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="AzureServiceBusSender"/> class.
19+
/// </summary>
20+
/// <param name="serviceBusClient">Azure Service Bus client.</param>
21+
/// <param name="azureServiceBusOptions">Azure Service Bus options.</param>
22+
public AzureServiceBusSender(ServiceBusClient serviceBusClient, IOptions<AzureServiceBusOptions> azureServiceBusOptions)
23+
{
24+
if (azureServiceBusOptions == null) throw new ArgumentNullException(nameof(azureServiceBusOptions));
25+
26+
_serviceBusClient = serviceBusClient;
27+
_azureServiceBusOptions = azureServiceBusOptions.Value;
28+
}
29+
30+
/// <summary>
31+
/// Asynchronously sends TModel object as JSON to Azure Service Bus queue.
32+
/// Queue is select by key from options.
33+
/// </summary>
34+
/// <typeparam name="TModel">The type of the T model.</typeparam>
35+
/// <param name="queueKey">Queue key.</param>
36+
/// <param name="model">The model.</param>
37+
/// <exception cref="ArgumentOutOfRangeException">No queue found with provided key.</exception>
38+
public async Task SendAsync<TModel>(string queueKey, TModel model)
39+
where TModel : class
40+
{
41+
if (!_azureServiceBusOptions.Queues.TryGetValue(queueKey, out var queueName)) throw new ArgumentOutOfRangeException(nameof(queueKey), queueKey, "No queue found with provided key.");
42+
43+
var sender = _serviceBusClient.CreateSender(queueName);
44+
45+
var messageToSend = new ServiceBusMessage(JsonSerializer.Serialize(model));
46+
47+
await sender.SendMessageAsync(messageToSend);
48+
}
49+
}
50+
}

src/GodelTech.Messaging.AzureServiceBus/Class1.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using Azure.Messaging.ServiceBus;
3+
using GodelTech.Messaging.AzureServiceBus;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace Microsoft.AspNetCore.Builder
7+
{
8+
/// <summary>
9+
/// Extensions to register AzureServiceBusSender with the service collection.
10+
/// </summary>
11+
public static class ServiceCollectionExtensions
12+
{
13+
/// <summary>
14+
/// Register AzureServiceBusSender with the service collection.
15+
/// </summary>
16+
/// <param name="services">Service collection.</param>
17+
/// <param name="connectionString">Connection string to Azure Service Bus.</param>
18+
/// <param name="optionsAction">Azure Service Bus options action.</param>
19+
/// <returns></returns>
20+
public static IServiceCollection AddAzureServiceBusSender(
21+
this IServiceCollection services,
22+
string connectionString,
23+
Action<AzureServiceBusOptions> optionsAction)
24+
{
25+
// ServiceBusClient
26+
services.AddTransient(provider => new ServiceBusClient(connectionString));
27+
28+
// Options
29+
services.Configure(optionsAction);
30+
31+
// AzureServiceBusSender
32+
services.AddTransient<AzureServiceBusSender>();
33+
34+
return services;
35+
}
36+
}
37+
}

src/GodelTech.Messaging.AzureServiceBus/GodelTech.Messaging.AzureServiceBus.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
<ItemGroup>
3131
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
32+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
33+
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.0" />
3234

3335
<!-- Source Link needs this -->
3436
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.Json;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Azure.Messaging.ServiceBus;
7+
using GodelTech.Messaging.AzureServiceBus.Tests.Fakes;
8+
using Microsoft.Extensions.Options;
9+
using Moq;
10+
using Xunit;
11+
12+
namespace GodelTech.Messaging.AzureServiceBus.Tests
13+
{
14+
public class AzureServiceBusSenderTests
15+
{
16+
private readonly Mock<ServiceBusClient> _mockServiceBusClient;
17+
18+
public AzureServiceBusSenderTests()
19+
{
20+
_mockServiceBusClient = new Mock<ServiceBusClient>(MockBehavior.Strict);
21+
}
22+
23+
[Fact]
24+
public void Constructor_ThrowsArgumentNullException()
25+
{
26+
// Arrange & Act & Assert
27+
var exception = Assert.Throws<ArgumentNullException>(
28+
() => new AzureServiceBusSender(_mockServiceBusClient.Object, null)
29+
);
30+
31+
Assert.Equal("azureServiceBusOptions", exception.ParamName);
32+
}
33+
34+
[Fact]
35+
public async Task SendAsync_ThrowsArgumentOutOfRangeException()
36+
{
37+
// Arrange
38+
var model = new List<FakeModel>();
39+
40+
var azureServiceBusOptions = new AzureServiceBusOptions
41+
{
42+
Queues = new Dictionary<string, string>
43+
{
44+
{
45+
"OtherInternalKey",
46+
"OtherAzureServiceBusQueueName"
47+
}
48+
}
49+
};
50+
51+
var mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusOptions>>();
52+
mockAzureServiceBusOptions
53+
.Setup(x => x.Value)
54+
.Returns(azureServiceBusOptions);
55+
56+
var sender = new AzureServiceBusSender(_mockServiceBusClient.Object, mockAzureServiceBusOptions.Object);
57+
58+
var expectedException = new ArgumentOutOfRangeException($"queueKey", "InternalKey", "No queue found with provided key.");
59+
60+
// Act & Assert
61+
var exception =
62+
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
63+
() =>
64+
sender.SendAsync("InternalKey", model)
65+
);
66+
67+
Assert.IsType<ArgumentOutOfRangeException>(exception);
68+
Assert.Equal("queueKey", exception.ParamName);
69+
Assert.Equal("InternalKey", exception.ActualValue);
70+
Assert.Equal(expectedException.Message, exception.Message);
71+
}
72+
73+
[Fact]
74+
public async Task SendAsync_Success()
75+
{
76+
// Arrange
77+
var model = new List<FakeModel>
78+
{
79+
new FakeModel
80+
{
81+
Id = 1,
82+
Name = "TestName"
83+
}
84+
};
85+
86+
var serializedModel = JsonSerializer.Serialize(model);
87+
88+
var azureServiceBusOptions = new AzureServiceBusOptions
89+
{
90+
Queues = new Dictionary<string, string>
91+
{
92+
{
93+
"InternalKey",
94+
"AzureServiceBusQueueName"
95+
}
96+
}
97+
};
98+
99+
var mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusOptions>>();
100+
mockAzureServiceBusOptions
101+
.Setup(x => x.Value)
102+
.Returns(azureServiceBusOptions);
103+
104+
var sender = new AzureServiceBusSender(_mockServiceBusClient.Object, mockAzureServiceBusOptions.Object);
105+
106+
var mockServiceBusSender = new Mock<ServiceBusSender>(MockBehavior.Strict);
107+
mockServiceBusSender
108+
.Setup(
109+
x => x.SendMessageAsync(
110+
It.Is<ServiceBusMessage>(
111+
serviceBusMessage =>
112+
serviceBusMessage.Body.ToString() == serializedModel
113+
),
114+
It.Is<CancellationToken>(
115+
cancellationToken => cancellationToken == default
116+
)
117+
)
118+
)
119+
.Returns(Task.CompletedTask)
120+
.Verifiable();
121+
122+
_mockServiceBusClient
123+
.Setup(
124+
x => x.CreateSender(
125+
It.Is<string>(queueOrTopicName => queueOrTopicName == "AzureServiceBusQueueName")
126+
)
127+
)
128+
.Returns(mockServiceBusSender.Object)
129+
.Verifiable();
130+
131+
// Act
132+
await sender.SendAsync("InternalKey", model);
133+
134+
// Assert
135+
_mockServiceBusClient.VerifyAll();
136+
mockServiceBusSender.VerifyAll();
137+
}
138+
}
139+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Azure.Messaging.ServiceBus;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Options;
7+
using Xunit;
8+
9+
namespace GodelTech.Messaging.AzureServiceBus.Tests.DependencyInjection
10+
{
11+
public class ServiceCollectionExtensionsTests
12+
{
13+
[Fact]
14+
public void AddAzureServiceBusSender_Success()
15+
{
16+
// Arrange
17+
const string connectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=YourAccessKey";
18+
var queues = new Dictionary<string, string>
19+
{
20+
{
21+
"InternalKey",
22+
"AzureServiceBusQueueName"
23+
}
24+
};
25+
Action<AzureServiceBusOptions> optionsAction = options =>
26+
{
27+
options.Queues = queues;
28+
};
29+
30+
var services = new ServiceCollection();
31+
32+
// Act
33+
services.AddAzureServiceBusSender(connectionString, optionsAction);
34+
35+
// Assert
36+
var provider = services.BuildServiceProvider();
37+
38+
var resultRequiredService = provider.GetRequiredService<ServiceBusClient>();
39+
Assert.NotNull(resultRequiredService);
40+
41+
var resultOptionsAction = provider.GetRequiredService<IOptions<AzureServiceBusOptions>>();
42+
Assert.NotNull(resultOptionsAction);
43+
Assert.NotNull(resultOptionsAction.Value);
44+
Assert.Equal(queues, resultOptionsAction.Value.Queues);
45+
46+
var resultAzureServiceBusSender = provider.GetRequiredService<AzureServiceBusSender>();
47+
Assert.NotNull(resultAzureServiceBusSender);
48+
}
49+
}
50+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace GodelTech.Messaging.AzureServiceBus.Tests.Fakes
2+
{
3+
public class FakeModel
4+
{
5+
public int Id { get; set; }
6+
7+
public string Name { get; set; }
8+
}
9+
}

test/GodelTech.Messaging.AzureServiceBus.Tests/GodelTech.Messaging.AzureServiceBus.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
13+
<PackageReference Include="Moq" Version="4.16.1" />
1314
<PackageReference Include="xunit" Version="2.4.1" />
1415
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
1516
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

test/GodelTech.Messaging.AzureServiceBus.Tests/UnitTest1.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)