Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/BuslyCLI.Console/BuslyCLI.Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
<PackageReference Include="NServiceBus.AmazonSQS" Version="8.0.0" />
<PackageReference Include="NServiceBus.RabbitMQ" Version="10.1.6" />
<PackageReference Include="NServiceBus.Transport.AzureServiceBus" Version="5.1.2" />
<PackageReference Include="NServiceBus.Transport.AzureStorageQueues" Version="13.0.3" />
<PackageReference Include="NServiceBus.Transport.SqlServer" Version="8.1.10" />
<PackageReference Include="Spectre.Console.Cli" Version="0.53.1" />
<PackageReference Include="Spectre.Console.Cli.Extensions.DependencyInjection" Version="0.20.0" />
<PackageReference Include="Testcontainers.Azurite" Version="4.9.0" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BuslyCLI.Config;

public class AzureStorageQueuesTransportConfig : ITransportConfig
{
public string ConnectionString { get; set; }
}
3 changes: 2 additions & 1 deletion src/BuslyCLI.Console/Config/TransportConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class TransportConfig
public RabbitmqTransportConfig RabbitmqTransportConfig { get; set; }
public AmazonsqsTransportConfig AmazonsqsTransportConfig { get; set; }
public AzureServiceBusTransportConfig AzureServiceBusTransportConfig { get; set; }

public AzureStorageQueuesTransportConfig AzureStorageQueuesTransportConfig { get; set; }
public SqlServerTransportConfig SqlServerTransportConfig { get; set; }

// Helper property to unify config access:
Expand All @@ -18,5 +18,6 @@ public class TransportConfig
?? (ITransportConfig)RabbitmqTransportConfig
?? (ITransportConfig)AmazonsqsTransportConfig
?? (ITransportConfig)AzureServiceBusTransportConfig
?? (ITransportConfig)AzureStorageQueuesTransportConfig
?? SqlServerTransportConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using FluentValidation;

namespace BuslyCLI.Config.Validators;

public class AzureStorageQueuesTransportConfigValidator : AbstractValidator<AzureStorageQueuesTransportConfig>
{
public AzureStorageQueuesTransportConfigValidator()
{
RuleFor(x => x.ConnectionString)
.NotEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private static IServiceCollection AddYamlDeserializer(this IServiceCollection se
{ "rabbitmq-transport-config", typeof(RabbitmqTransportConfig) },
{ "amazonsqs-transport-config", typeof(AmazonsqsTransportConfig) },
{ "azure-service-bus-transport-config", typeof(AzureServiceBusTransportConfig) },
{ "azure-storage-queues-transport-config", typeof(AzureStorageQueuesTransportConfig) },
{ "sql-server-transport-config", typeof(SqlServerTransportConfig) }
};

Expand Down
9 changes: 9 additions & 0 deletions src/BuslyCLI.Console/Factories/RawEndpointFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ private TransportDefinition CreateTransport(TransportConfig transportConfig)
return CreateRabbitMQTransport(rabbitmqTransportConfig);
case AzureServiceBusTransportConfig azureServiceBusTransportConfig:
return CreateAzureServiceBusTransport(azureServiceBusTransportConfig.ConnectionString);
case AzureStorageQueuesTransportConfig azureStorageQueuesTransportConfig:
return CreateAzureStorageQueuesTransport(azureStorageQueuesTransportConfig.ConnectionString);
case AmazonsqsTransportConfig amazonSqsTransportConfig:
return CreateAmazonSQSTransport(amazonSqsTransportConfig);
case SqlServerTransportConfig sqlServerTransportConfig:
Expand All @@ -45,6 +47,13 @@ private TransportDefinition CreateTransport(TransportConfig transportConfig)
}
}

private TransportDefinition CreateAzureStorageQueuesTransport(string connectionString)
{
var transport = new AzureStorageQueueTransport(connectionString);
transport.MessageWrapperSerializationDefinition = new SystemJsonSerializer();
return transport;
}

private TransportDefinition CreateSqlServerTransport(SqlServerTransportConfig sqlServerTransportConfig)
{
return new SqlServerTransport(sqlServerTransportConfig.ConnectionString);
Expand Down
1 change: 1 addition & 0 deletions tests/BuslyCLI.Console.Tests/BuslyCLI.Console.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="Spectre.Console.Cli.Testing" Version="1.0.0-alpha.0.11" />
<PackageReference Include="Testcontainers.Azurite" Version="4.9.0" />
<PackageReference Include="Testcontainers.LocalStack" Version="4.9.0" />
<PackageReference Include="Testcontainers.MsSql" Version="4.9.0" />
<PackageReference Include="Testcontainers.RabbitMq" Version="4.9.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Testcontainers.Azurite;

namespace BuslyCLI.Console.Tests.EndToEnd.AzureStorageQueues;

[TestFixture]
public abstract class AzureStorageQueuesEndToEndTestBase : SingletonTestFixtureBase<AzuriteContainer>
{
protected AzuriteContainer AzuriteContainer => Container;

protected override AzuriteContainer CreateContainer()
{
return new AzuriteBuilder()
.WithCommand("--skipApiVersionCheck")
.Build();
}

protected override async Task StartContainerAsync(AzuriteContainer container)
{
await container.StartAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System.Text;
using System.Text.Json;
using BuslyCLI.Console.Tests.EndToEnd.Infrastructure;
using BuslyCLI.Console.Tests.TestHelpers;
using BuslyCLI.DependencyInjection;
using BuslyCLI.Spectre;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli.Extensions.DependencyInjection;
using Spectre.Console.Cli.Testing;

namespace BuslyCLI.Console.Tests.EndToEnd.AzureStorageQueues;

[TestFixture]
public class SendCommandAzureStorageQueuesEndToEndTests : AzureStorageQueuesEndToEndTestBase
{
[SetUp]
public void Setup()
{
var registrations = new ServiceCollection();
registrations.AddBuslyCLIServices();
using var registrar = new DependencyInjectionRegistrar(registrations);
_sut = new CommandAppTester(registrar);
_sut.Configure(AppConfiguration.GetSpectreCommandConfiguration());
}

private CommandAppTester _sut;

private readonly JsonSerializerOptions _jsonObjectOptions =
new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };

[Test]
public async Task ShouldSendCommand()
{
await RunWithTestEndpoint(async testEndpoint =>
{
// Arrange
await testEndpoint.StartEndpoint();
var messageBody = new { OrderNumber = Guid.NewGuid() };
var json = JsonSerializer.Serialize(messageBody, _jsonObjectOptions);
var yamlFile = $"""
---
current-transport: local-azure-storage-queues
transports:
- name: local-azure-storage-queues
azure-storage-queues-transport-config:
connection-string: {Container.GetConnectionString()}
""";
using var configFile = new TestableNServiceBusConfigurationFile(yamlFile);

// Act
var result = _sut.Run(
"command",
"send",
"--content-type", "application/json",
"--enclosed-message-type", "MessageContracts.Commands.CreateOrder",
"--destination-endpoint", testEndpoint.EndpointName,
"--message-body", json,
"--config", configFile.FilePath);

// Assert
Assert.That(result.ExitCode, Is.EqualTo(0));
var message = testEndpoint.TryReceiveMessage();
Assert.That(message.Headers["NServiceBus.EnclosedMessageTypes"],
Is.EqualTo("MessageContracts.Commands.CreateOrder"));
Assert.That(message.Headers["NServiceBus.ContentType"], Is.EqualTo("application/json"));
Assert.That(Encoding.UTF8.GetString(message.Body.Span), Is.EqualTo(json));
});
}

[Test]
public async Task ShouldPublishEvent()
{
await RunWithTestEndpoint(async testEndpoint =>
{
// Arrange
await testEndpoint.StartEndpoint();
await testEndpoint.Subscribe("MessageContracts.Events.OrderCreated");
var messageBody = new { OrderNumber = Guid.NewGuid() };
var json = JsonSerializer.Serialize(messageBody, _jsonObjectOptions);
var yamlFile = $"""
---
current-transport: local-azure-storage-queues
transports:
- name: local-azure-storage-queues
azure-storage-queues-transport-config:
connection-string: {Container.GetConnectionString()}
""";
using var configFile = new TestableNServiceBusConfigurationFile(yamlFile);

// Act
var result = _sut.Run(
"event",
"publish",
"--content-type", "application/json",
"--enclosed-message-type", "MessageContracts.Events.OrderCreated",
"--message-body", json,
"--config", configFile.FilePath);

// Assert
Assert.That(result.ExitCode, Is.EqualTo(0));
var message = testEndpoint.TryReceiveMessage();
Assert.That(message.Headers["NServiceBus.EnclosedMessageTypes"],
Is.EqualTo("MessageContracts.Events.OrderCreated"));
Assert.That(message.Headers["NServiceBus.ContentType"], Is.EqualTo("application/json"));
Assert.That(Encoding.UTF8.GetString(message.Body.Span), Is.EqualTo(json));
});
}

// Test Endpoint
// Example of how to wait for and get messages
// https://github.com/Particular/NServiceBus.RabbitMQ/blob/dba627a5a2c50519d7a2466efe3f76c8d5c8828d/src/NServiceBus.Transport.RabbitMQ.Tests/RabbitMqContext.cs#L41
private async Task RunWithTestEndpoint(Func<TestEndpoint, Task> testAction)
{
var testEndpoint = await new TestEndpointFactory().CreateAzureStorageQueuesTestEndpoint(Container.GetConnectionString());

await testAction(testEndpoint);
await testEndpoint.ShutDownAndCleanUp();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Amazon.Runtime;
using Amazon.SimpleNotificationService;
using Amazon.SQS;
using NServiceBus.Settings;
using NServiceBus.Transport;

namespace BuslyCLI.Console.Tests.EndToEnd.Infrastructure;
Expand Down Expand Up @@ -67,6 +68,7 @@ public async Task<TestEndpoint> CreateAzureServiceBusTestEndpoint(string endpoin
private static async Task<TestEndpoint> InternalCreateTestEndpoint(string endpointName,
TransportDefinition transport)
{

var hostSettings = new HostSettings(
endpointName,
endpointName,
Expand All @@ -76,7 +78,8 @@ private static async Task<TestEndpoint> InternalCreateTestEndpoint(string endpoi
TestContext.Out.WriteLine("Critical error: " + exception);
},
// TODO: This needs to be false for "Azure Service Bus Emulator" tests to pass
transport is not AzureServiceBusTransport);
transport is not AzureServiceBusTransport
);

var infrastructure = await transport.Initialize(hostSettings, [
new ReceiveSettings(
Expand All @@ -97,4 +100,13 @@ public async Task<TestEndpoint> CreateSqlServerTestEndpoint(string sqlConnection
var transport = new SqlServerTransport(sqlConnectionString);
return await InternalCreateTestEndpoint(name, transport);
}

public async Task<TestEndpoint> CreateAzureStorageQueuesTestEndpoint(string connectionString)
{
var name = GenerateUniqueEndpointName();
var transport = new AzureStorageQueueTransport(connectionString);
transport.MessageWrapperSerializationDefinition = new SystemJsonSerializer();

return await InternalCreateTestEndpoint(name, transport);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Data.SqlClient;
using Testcontainers.MsSql;
using Testcontainers.MsSql;

namespace BuslyCLI.Console.Tests.EndToEnd.SqlServer;

Expand Down
40 changes: 40 additions & 0 deletions website/docs/transports/azure-storage-queues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Azure Storage Queues

The **Azure Storage Queues Transport** is used to communicate to Azure Storage Queues. It is suitable for development, testing, and production environments.

## Configuration

To use the Azure Storage Queues Transport, define it under `transports` and reference it as `current-transport`.

### Example

```yaml
current-transport: local-azure-service-bus

transports:
- name: local-azure-service-bus
azure-storage-queues-transport-config:
connection-string: Endpoint=amqp://127.0.0.1:32799/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true
```

---

## `azure-storage-queues-transport-config` Fields

| Field | Required | Type | Default | Description |
| ------------------- | -------- | ------ | ------- | ------------------------------------------ |
| `connection-string` | **Yes** | string | — | Connection string to Azure Storage Queues. |

---

## Field Details

### `connection-string` (required)

A standard conection string for Azure Storage Queues.

Examples:

```yaml
connection-string: UseDevelopmentStorage=true
```
Loading