Skip to content

Commit eb0b534

Browse files
authored
Merge pull request #136 from TraGicCode/chore/add-support-for-azure-storage-queues-transport
Add support for azure storage queues transport
2 parents e3f92b5 + 7ca9f75 commit eb0b534

12 files changed

Lines changed: 227 additions & 4 deletions

File tree

src/BuslyCLI.Console/BuslyCLI.Console.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@
3333
<PackageReference Include="NServiceBus.AmazonSQS" Version="8.0.0" />
3434
<PackageReference Include="NServiceBus.RabbitMQ" Version="10.1.6" />
3535
<PackageReference Include="NServiceBus.Transport.AzureServiceBus" Version="5.1.2" />
36+
<PackageReference Include="NServiceBus.Transport.AzureStorageQueues" Version="13.0.3" />
3637
<PackageReference Include="NServiceBus.Transport.SqlServer" Version="8.1.10" />
3738
<PackageReference Include="Spectre.Console.Cli" Version="0.53.1" />
3839
<PackageReference Include="Spectre.Console.Cli.Extensions.DependencyInjection" Version="0.20.0" />
40+
<PackageReference Include="Testcontainers.Azurite" Version="4.9.0" />
3941
<PackageReference Include="YamlDotNet" Version="16.3.0" />
4042
</ItemGroup>
4143

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace BuslyCLI.Config;
2+
3+
public class AzureStorageQueuesTransportConfig : ITransportConfig
4+
{
5+
public string ConnectionString { get; set; }
6+
}

src/BuslyCLI.Console/Config/TransportConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class TransportConfig
99
public RabbitmqTransportConfig RabbitmqTransportConfig { get; set; }
1010
public AmazonsqsTransportConfig AmazonsqsTransportConfig { get; set; }
1111
public AzureServiceBusTransportConfig AzureServiceBusTransportConfig { get; set; }
12-
12+
public AzureStorageQueuesTransportConfig AzureStorageQueuesTransportConfig { get; set; }
1313
public SqlServerTransportConfig SqlServerTransportConfig { get; set; }
1414

1515
// Helper property to unify config access:
@@ -18,5 +18,6 @@ public class TransportConfig
1818
?? (ITransportConfig)RabbitmqTransportConfig
1919
?? (ITransportConfig)AmazonsqsTransportConfig
2020
?? (ITransportConfig)AzureServiceBusTransportConfig
21+
?? (ITransportConfig)AzureStorageQueuesTransportConfig
2122
?? SqlServerTransportConfig;
2223
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using FluentValidation;
2+
3+
namespace BuslyCLI.Config.Validators;
4+
5+
public class AzureStorageQueuesTransportConfigValidator : AbstractValidator<AzureStorageQueuesTransportConfig>
6+
{
7+
public AzureStorageQueuesTransportConfigValidator()
8+
{
9+
RuleFor(x => x.ConnectionString)
10+
.NotEmpty();
11+
}
12+
}

src/BuslyCLI.Console/DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ private static IServiceCollection AddYamlDeserializer(this IServiceCollection se
3535
{ "rabbitmq-transport-config", typeof(RabbitmqTransportConfig) },
3636
{ "amazonsqs-transport-config", typeof(AmazonsqsTransportConfig) },
3737
{ "azure-service-bus-transport-config", typeof(AzureServiceBusTransportConfig) },
38+
{ "azure-storage-queues-transport-config", typeof(AzureStorageQueuesTransportConfig) },
3839
{ "sql-server-transport-config", typeof(SqlServerTransportConfig) }
3940
};
4041

src/BuslyCLI.Console/Factories/RawEndpointFactory.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ private TransportDefinition CreateTransport(TransportConfig transportConfig)
3030
return CreateRabbitMQTransport(rabbitmqTransportConfig);
3131
case AzureServiceBusTransportConfig azureServiceBusTransportConfig:
3232
return CreateAzureServiceBusTransport(azureServiceBusTransportConfig.ConnectionString);
33+
case AzureStorageQueuesTransportConfig azureStorageQueuesTransportConfig:
34+
return CreateAzureStorageQueuesTransport(azureStorageQueuesTransportConfig.ConnectionString);
3335
case AmazonsqsTransportConfig amazonSqsTransportConfig:
3436
return CreateAmazonSQSTransport(amazonSqsTransportConfig);
3537
case SqlServerTransportConfig sqlServerTransportConfig:
@@ -45,6 +47,13 @@ private TransportDefinition CreateTransport(TransportConfig transportConfig)
4547
}
4648
}
4749

50+
private TransportDefinition CreateAzureStorageQueuesTransport(string connectionString)
51+
{
52+
var transport = new AzureStorageQueueTransport(connectionString);
53+
transport.MessageWrapperSerializationDefinition = new SystemJsonSerializer();
54+
return transport;
55+
}
56+
4857
private TransportDefinition CreateSqlServerTransport(SqlServerTransportConfig sqlServerTransportConfig)
4958
{
5059
return new SqlServerTransport(sqlServerTransportConfig.ConnectionString);

tests/BuslyCLI.Console.Tests/BuslyCLI.Console.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</PackageReference>
2525
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
2626
<PackageReference Include="Spectre.Console.Cli.Testing" Version="1.0.0-alpha.0.11" />
27+
<PackageReference Include="Testcontainers.Azurite" Version="4.9.0" />
2728
<PackageReference Include="Testcontainers.LocalStack" Version="4.9.0" />
2829
<PackageReference Include="Testcontainers.MsSql" Version="4.9.0" />
2930
<PackageReference Include="Testcontainers.RabbitMq" Version="4.9.0" />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Testcontainers.Azurite;
2+
3+
namespace BuslyCLI.Console.Tests.EndToEnd.AzureStorageQueues;
4+
5+
[TestFixture]
6+
public abstract class AzureStorageQueuesEndToEndTestBase : SingletonTestFixtureBase<AzuriteContainer>
7+
{
8+
protected AzuriteContainer AzuriteContainer => Container;
9+
10+
protected override AzuriteContainer CreateContainer()
11+
{
12+
return new AzuriteBuilder()
13+
.WithCommand("--skipApiVersionCheck")
14+
.Build();
15+
}
16+
17+
protected override async Task StartContainerAsync(AzuriteContainer container)
18+
{
19+
await container.StartAsync();
20+
}
21+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System.Text;
2+
using System.Text.Json;
3+
using BuslyCLI.Console.Tests.EndToEnd.Infrastructure;
4+
using BuslyCLI.Console.Tests.TestHelpers;
5+
using BuslyCLI.DependencyInjection;
6+
using BuslyCLI.Spectre;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Spectre.Console.Cli.Extensions.DependencyInjection;
9+
using Spectre.Console.Cli.Testing;
10+
11+
namespace BuslyCLI.Console.Tests.EndToEnd.AzureStorageQueues;
12+
13+
[TestFixture]
14+
public class SendCommandAzureStorageQueuesEndToEndTests : AzureStorageQueuesEndToEndTestBase
15+
{
16+
[SetUp]
17+
public void Setup()
18+
{
19+
var registrations = new ServiceCollection();
20+
registrations.AddBuslyCLIServices();
21+
using var registrar = new DependencyInjectionRegistrar(registrations);
22+
_sut = new CommandAppTester(registrar);
23+
_sut.Configure(AppConfiguration.GetSpectreCommandConfiguration());
24+
}
25+
26+
private CommandAppTester _sut;
27+
28+
private readonly JsonSerializerOptions _jsonObjectOptions =
29+
new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
30+
31+
[Test]
32+
public async Task ShouldSendCommand()
33+
{
34+
await RunWithTestEndpoint(async testEndpoint =>
35+
{
36+
// Arrange
37+
await testEndpoint.StartEndpoint();
38+
var messageBody = new { OrderNumber = Guid.NewGuid() };
39+
var json = JsonSerializer.Serialize(messageBody, _jsonObjectOptions);
40+
var yamlFile = $"""
41+
---
42+
current-transport: local-azure-storage-queues
43+
transports:
44+
- name: local-azure-storage-queues
45+
azure-storage-queues-transport-config:
46+
connection-string: {Container.GetConnectionString()}
47+
""";
48+
using var configFile = new TestableNServiceBusConfigurationFile(yamlFile);
49+
50+
// Act
51+
var result = _sut.Run(
52+
"command",
53+
"send",
54+
"--content-type", "application/json",
55+
"--enclosed-message-type", "MessageContracts.Commands.CreateOrder",
56+
"--destination-endpoint", testEndpoint.EndpointName,
57+
"--message-body", json,
58+
"--config", configFile.FilePath);
59+
60+
// Assert
61+
Assert.That(result.ExitCode, Is.EqualTo(0));
62+
var message = testEndpoint.TryReceiveMessage();
63+
Assert.That(message.Headers["NServiceBus.EnclosedMessageTypes"],
64+
Is.EqualTo("MessageContracts.Commands.CreateOrder"));
65+
Assert.That(message.Headers["NServiceBus.ContentType"], Is.EqualTo("application/json"));
66+
Assert.That(Encoding.UTF8.GetString(message.Body.Span), Is.EqualTo(json));
67+
});
68+
}
69+
70+
[Test]
71+
public async Task ShouldPublishEvent()
72+
{
73+
await RunWithTestEndpoint(async testEndpoint =>
74+
{
75+
// Arrange
76+
await testEndpoint.StartEndpoint();
77+
await testEndpoint.Subscribe("MessageContracts.Events.OrderCreated");
78+
var messageBody = new { OrderNumber = Guid.NewGuid() };
79+
var json = JsonSerializer.Serialize(messageBody, _jsonObjectOptions);
80+
var yamlFile = $"""
81+
---
82+
current-transport: local-azure-storage-queues
83+
transports:
84+
- name: local-azure-storage-queues
85+
azure-storage-queues-transport-config:
86+
connection-string: {Container.GetConnectionString()}
87+
""";
88+
using var configFile = new TestableNServiceBusConfigurationFile(yamlFile);
89+
90+
// Act
91+
var result = _sut.Run(
92+
"event",
93+
"publish",
94+
"--content-type", "application/json",
95+
"--enclosed-message-type", "MessageContracts.Events.OrderCreated",
96+
"--message-body", json,
97+
"--config", configFile.FilePath);
98+
99+
// Assert
100+
Assert.That(result.ExitCode, Is.EqualTo(0));
101+
var message = testEndpoint.TryReceiveMessage();
102+
Assert.That(message.Headers["NServiceBus.EnclosedMessageTypes"],
103+
Is.EqualTo("MessageContracts.Events.OrderCreated"));
104+
Assert.That(message.Headers["NServiceBus.ContentType"], Is.EqualTo("application/json"));
105+
Assert.That(Encoding.UTF8.GetString(message.Body.Span), Is.EqualTo(json));
106+
});
107+
}
108+
109+
// Test Endpoint
110+
// Example of how to wait for and get messages
111+
// https://github.com/Particular/NServiceBus.RabbitMQ/blob/dba627a5a2c50519d7a2466efe3f76c8d5c8828d/src/NServiceBus.Transport.RabbitMQ.Tests/RabbitMqContext.cs#L41
112+
private async Task RunWithTestEndpoint(Func<TestEndpoint, Task> testAction)
113+
{
114+
var testEndpoint = await new TestEndpointFactory().CreateAzureStorageQueuesTestEndpoint(Container.GetConnectionString());
115+
116+
await testAction(testEndpoint);
117+
await testEndpoint.ShutDownAndCleanUp();
118+
}
119+
}

tests/BuslyCLI.Console.Tests/EndToEnd/Infrastructure/ITestEndpointFactory.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Amazon.Runtime;
22
using Amazon.SimpleNotificationService;
33
using Amazon.SQS;
4+
using NServiceBus.Settings;
45
using NServiceBus.Transport;
56

67
namespace BuslyCLI.Console.Tests.EndToEnd.Infrastructure;
@@ -67,6 +68,7 @@ public async Task<TestEndpoint> CreateAzureServiceBusTestEndpoint(string endpoin
6768
private static async Task<TestEndpoint> InternalCreateTestEndpoint(string endpointName,
6869
TransportDefinition transport)
6970
{
71+
7072
var hostSettings = new HostSettings(
7173
endpointName,
7274
endpointName,
@@ -76,7 +78,8 @@ private static async Task<TestEndpoint> InternalCreateTestEndpoint(string endpoi
7678
TestContext.Out.WriteLine("Critical error: " + exception);
7779
},
7880
// TODO: This needs to be false for "Azure Service Bus Emulator" tests to pass
79-
transport is not AzureServiceBusTransport);
81+
transport is not AzureServiceBusTransport
82+
);
8083

8184
var infrastructure = await transport.Initialize(hostSettings, [
8285
new ReceiveSettings(
@@ -97,4 +100,13 @@ public async Task<TestEndpoint> CreateSqlServerTestEndpoint(string sqlConnection
97100
var transport = new SqlServerTransport(sqlConnectionString);
98101
return await InternalCreateTestEndpoint(name, transport);
99102
}
103+
104+
public async Task<TestEndpoint> CreateAzureStorageQueuesTestEndpoint(string connectionString)
105+
{
106+
var name = GenerateUniqueEndpointName();
107+
var transport = new AzureStorageQueueTransport(connectionString);
108+
transport.MessageWrapperSerializationDefinition = new SystemJsonSerializer();
109+
110+
return await InternalCreateTestEndpoint(name, transport);
111+
}
100112
}

0 commit comments

Comments
 (0)