Skip to content

Commit

Permalink
Merge pull request #2 from GodelTech/feature/init
Browse files Browse the repository at this point in the history
Feature/init
  • Loading branch information
rodchenkov authored Mar 28, 2021
2 parents 26c2a03 + ecc8d7a commit 0b3d49a
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 19 deletions.
15 changes: 15 additions & 0 deletions src/GodelTech.Messaging.AzureServiceBus/AzureServiceBusOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace GodelTech.Messaging.AzureServiceBus
{
/// <summary>
/// Azure Service Bus options
/// </summary>
public class AzureServiceBusOptions
{
/// <summary>
/// Queue dictionary that stores key value pair of internal queue key and Azure queue name.
/// </summary>
public IDictionary<string, string> Queues { get; set; }
}
}
50 changes: 50 additions & 0 deletions src/GodelTech.Messaging.AzureServiceBus/AzureServiceBusSender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Options;

namespace GodelTech.Messaging.AzureServiceBus
{
/// <summary>
/// Azure Service Bus sender
/// </summary>
public class AzureServiceBusSender
{
private readonly ServiceBusClient _serviceBusClient;
private readonly AzureServiceBusOptions _azureServiceBusOptions;

/// <summary>
/// Initializes a new instance of the <see cref="AzureServiceBusSender"/> class.
/// </summary>
/// <param name="serviceBusClient">Azure Service Bus client.</param>
/// <param name="azureServiceBusOptions">Azure Service Bus options.</param>
public AzureServiceBusSender(ServiceBusClient serviceBusClient, IOptions<AzureServiceBusOptions> azureServiceBusOptions)
{
if (azureServiceBusOptions == null) throw new ArgumentNullException(nameof(azureServiceBusOptions));

_serviceBusClient = serviceBusClient;
_azureServiceBusOptions = azureServiceBusOptions.Value;
}

/// <summary>
/// Asynchronously sends TModel object as JSON to Azure Service Bus queue.
/// Queue is select by key from options.
/// </summary>
/// <typeparam name="TModel">The type of the T model.</typeparam>
/// <param name="queueKey">Queue key.</param>
/// <param name="model">The model.</param>
/// <exception cref="ArgumentOutOfRangeException">No queue found with provided key.</exception>
public async Task SendAsync<TModel>(string queueKey, TModel model)
where TModel : class
{
if (!_azureServiceBusOptions.Queues.TryGetValue(queueKey, out var queueName)) throw new ArgumentOutOfRangeException(nameof(queueKey), queueKey, "No queue found with provided key.");

var sender = _serviceBusClient.CreateSender(queueName);

var messageToSend = new ServiceBusMessage(JsonSerializer.Serialize(model));

await sender.SendMessageAsync(messageToSend);
}
}
}
6 changes: 0 additions & 6 deletions src/GodelTech.Messaging.AzureServiceBus/Class1.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using Azure.Messaging.ServiceBus;
using GodelTech.Messaging.AzureServiceBus;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extensions to register AzureServiceBusSender with the service collection.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Register AzureServiceBusSender with the service collection.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="connectionString">Connection string to Azure Service Bus.</param>
/// <param name="optionsAction">Azure Service Bus options action.</param>
/// <returns></returns>
public static IServiceCollection AddAzureServiceBusSender(
this IServiceCollection services,
string connectionString,
Action<AzureServiceBusOptions> optionsAction)
{
// ServiceBusClient
services.AddTransient(provider => new ServiceBusClient(connectionString));

// Options
services.Configure(optionsAction);

// AzureServiceBusSender
services.AddTransient<AzureServiceBusSender>();

return services;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

<ItemGroup>
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.0" />

<!-- Source Link needs this -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using GodelTech.Messaging.AzureServiceBus.Tests.Fakes;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace GodelTech.Messaging.AzureServiceBus.Tests
{
public class AzureServiceBusSenderTests
{
private readonly Mock<ServiceBusClient> _mockServiceBusClient;

public AzureServiceBusSenderTests()
{
_mockServiceBusClient = new Mock<ServiceBusClient>(MockBehavior.Strict);
}

[Fact]
public void Constructor_ThrowsArgumentNullException()
{
// Arrange & Act & Assert
var exception = Assert.Throws<ArgumentNullException>(
() => new AzureServiceBusSender(_mockServiceBusClient.Object, null)
);

Assert.Equal("azureServiceBusOptions", exception.ParamName);
}

[Fact]
public async Task SendAsync_ThrowsArgumentOutOfRangeException()
{
// Arrange
var model = new List<FakeModel>();

var azureServiceBusOptions = new AzureServiceBusOptions
{
Queues = new Dictionary<string, string>
{
{
"OtherInternalKey",
"OtherAzureServiceBusQueueName"
}
}
};

var mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusOptions>>();
mockAzureServiceBusOptions
.Setup(x => x.Value)
.Returns(azureServiceBusOptions);

var sender = new AzureServiceBusSender(_mockServiceBusClient.Object, mockAzureServiceBusOptions.Object);

var expectedException = new ArgumentOutOfRangeException($"queueKey", "InternalKey", "No queue found with provided key.");

// Act & Assert
var exception =
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
() =>
sender.SendAsync("InternalKey", model)
);

Assert.IsType<ArgumentOutOfRangeException>(exception);
Assert.Equal("queueKey", exception.ParamName);
Assert.Equal("InternalKey", exception.ActualValue);
Assert.Equal(expectedException.Message, exception.Message);
}

[Fact]
public async Task SendAsync_Success()
{
// Arrange
var model = new List<FakeModel>
{
new FakeModel
{
Id = 1,
Name = "TestName"
}
};

var serializedModel = JsonSerializer.Serialize(model);

var azureServiceBusOptions = new AzureServiceBusOptions
{
Queues = new Dictionary<string, string>
{
{
"InternalKey",
"AzureServiceBusQueueName"
}
}
};

var mockAzureServiceBusOptions = new Mock<IOptions<AzureServiceBusOptions>>();
mockAzureServiceBusOptions
.Setup(x => x.Value)
.Returns(azureServiceBusOptions);

var sender = new AzureServiceBusSender(_mockServiceBusClient.Object, mockAzureServiceBusOptions.Object);

var mockServiceBusSender = new Mock<ServiceBusSender>(MockBehavior.Strict);
mockServiceBusSender
.Setup(
x => x.SendMessageAsync(
It.Is<ServiceBusMessage>(
serviceBusMessage =>
serviceBusMessage.Body.ToString() == serializedModel
),
It.Is<CancellationToken>(
cancellationToken => cancellationToken == default
)
)
)
.Returns(Task.CompletedTask)
.Verifiable();

_mockServiceBusClient
.Setup(
x => x.CreateSender(
It.Is<string>(queueOrTopicName => queueOrTopicName == "AzureServiceBusQueueName")
)
)
.Returns(mockServiceBusSender.Object)
.Verifiable();

// Act
await sender.SendAsync("InternalKey", model);

// Assert
_mockServiceBusClient.VerifyAll();
mockServiceBusSender.VerifyAll();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using Azure.Messaging.ServiceBus;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;

namespace GodelTech.Messaging.AzureServiceBus.Tests.DependencyInjection
{
public class ServiceCollectionExtensionsTests
{
[Fact]
public void AddAzureServiceBusSender_Success()
{
// Arrange
const string connectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=YourAccessKey";
var queues = new Dictionary<string, string>
{
{
"InternalKey",
"AzureServiceBusQueueName"
}
};
Action<AzureServiceBusOptions> optionsAction = options =>
{
options.Queues = queues;
};

var services = new ServiceCollection();

// Act
services.AddAzureServiceBusSender(connectionString, optionsAction);

// Assert
var provider = services.BuildServiceProvider();

var resultRequiredService = provider.GetRequiredService<ServiceBusClient>();
Assert.NotNull(resultRequiredService);

var resultOptionsAction = provider.GetRequiredService<IOptions<AzureServiceBusOptions>>();
Assert.NotNull(resultOptionsAction);
Assert.NotNull(resultOptionsAction.Value);
Assert.Equal(queues, resultOptionsAction.Value.Queues);

var resultAzureServiceBusSender = provider.GetRequiredService<AzureServiceBusSender>();
Assert.NotNull(resultAzureServiceBusSender);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GodelTech.Messaging.AzureServiceBus.Tests.Fakes
{
public class FakeModel
{
public int Id { get; set; }

public string Name { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
13 changes: 0 additions & 13 deletions test/GodelTech.Messaging.AzureServiceBus.Tests/UnitTest1.cs

This file was deleted.

0 comments on commit 0b3d49a

Please sign in to comment.