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
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ indent_size = 2

[*.md]
trim_trailing_whitespace = false
indent_size = 2

[*.cs]
# Sort using and Import directives with System.* appearing first
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using FluentValidation;

namespace BuslyCLI.Config.Validators;

public class RabbitMQTransportConfigValidator : AbstractValidator<RabbitmqTransportConfig>
{
public RabbitMQTransportConfigValidator()
{
RuleFor(x => x.AmqpConnectionString)
.NotEmpty();

RuleFor(x => x.ManagementApi)
.SetValidator(new ManagementApiConfigValidator());
}
}

public class ManagementApiConfigValidator : AbstractValidator<ManagementApi>
{
public ManagementApiConfigValidator()
{
RuleFor(x => x)
.Must(x =>
(string.IsNullOrEmpty(x.UserName) && string.IsNullOrEmpty(x.Password)) // both empty
|| (!string.IsNullOrEmpty(x.UserName) && !string.IsNullOrEmpty(x.Password)) // both set
)
.WithMessage("Username and Password are mutually dependent: if one is set, the other must also be set.");
}
}
10 changes: 6 additions & 4 deletions src/BuslyCLI.Console/Factories/RawEndpointFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ private TransportDefinition CreateTransport(TransportConfig transportConfig)

private RabbitMQTransport CreateRabbitMQTransport(RabbitmqTransportConfig rabbitmqTransportConfig)
{
var t = new RabbitMQTransport(RoutingTopology.Conventional(QueueType.Quorum),
rabbitmqTransportConfig.AmqpConnectionString)
var t = new RabbitMQTransport(RoutingTopology.Conventional(QueueType.Quorum), rabbitmqTransportConfig.AmqpConnectionString);

if (rabbitmqTransportConfig.ManagementApi != null)
{
ManagementApiConfiguration = CreateManagementApiConfig(rabbitmqTransportConfig.ManagementApi)
};
t.ManagementApiConfiguration =
CreateManagementApiConfig(rabbitmqTransportConfig.ManagementApi);
}
return t;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Bogus;
using BuslyCLI.Config;
using BuslyCLI.Config.Validators;
using FluentValidation.TestHelper;

namespace BuslyCLI.Console.Tests.Config.Validators;

[TestFixture]
public class ManagementApiConfigValidatorTests
{
private readonly ManagementApiConfigValidator _validator;

public ManagementApiConfigValidatorTests()
{
_validator = new ManagementApiConfigValidator();
}

// [Test]
// public async Task ShouldErrorWhenAmqpConnectionStringIsNotPassed()
// {
// // Arrange
// var rabbitmqTransportConfig = new ManagementApi()
// {
// AmqpConnectionString = null
// };
// // Act
// var result = await _validator.TestValidateAsync(rabbitmqTransportConfig);
//
// // Assert
// result.ShouldHaveValidationErrorFor(c => c.AmqpConnectionString)
// .WithErrorMessage("'Amqp Connection String' must not be empty.");
// }

[Test]
public async Task ShouldNotErrorWhenOnlyAUrlStringIsPassed()
{
// Arrange
var managementApi = new ManagementApi()
{
Url = "http://localhost:15672"
};
// Act
var result = await _validator.TestValidateAsync(managementApi);

// Assert
result.ShouldNotHaveValidationErrorFor(c => c.Url);
}

[Test]
public async Task ShouldNotErrorWhenOnlyCredentialsAreIsPassed()
{
// Arrange
var managementApi = new ManagementApi()
{
UserName = new Faker().Internet.UserName(),
Password = new Faker().Internet.Password()
};
// Act
var result = await _validator.TestValidateAsync(managementApi);

// Assert
result.ShouldNotHaveAnyValidationErrors();
}

[Test]
public async Task ShouldNotErrorWhenUrlAndCredentialsAreIsPassed()
{
// Arrange
var managementApi = new ManagementApi()
{
Url = "http://localhost:15672",
UserName = new Faker().Internet.UserName(),
Password = new Faker().Internet.Password()
};
// Act
var result = await _validator.TestValidateAsync(managementApi);

// Assert
result.ShouldNotHaveAnyValidationErrors();
}

[Test]
public async Task ShouldErrorWhenUserNameIsPassedWithoutPassword()
{
// Arrange
var managementApi = new ManagementApi()
{
UserName = new Faker().Internet.UserName()
};
// Act
var result = await _validator.TestValidateAsync(managementApi);

// Assert
result.ShouldHaveValidationErrors().WithErrorMessage("Username and Password are mutually dependent: if one is set, the other must also be set.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using BuslyCLI.Config;
using BuslyCLI.Config.Validators;
using FluentValidation.TestHelper;

namespace BuslyCLI.Console.Tests.Config.Validators;

[TestFixture]
public class RabbitMQTransportConfigValidatorTests
{
private readonly RabbitMQTransportConfigValidator _validator;

public RabbitMQTransportConfigValidatorTests()
{
_validator = new RabbitMQTransportConfigValidator();
}

[Test]
public async Task ShouldErrorWhenAmqpConnectionStringIsNotPassed()
{
// Arrange
var rabbitmqTransportConfig = new RabbitmqTransportConfig()
{
AmqpConnectionString = null
};
// Act
var result = await _validator.TestValidateAsync(rabbitmqTransportConfig);

// Assert
result.ShouldHaveValidationErrorFor(c => c.AmqpConnectionString)
.WithErrorMessage("'Amqp Connection String' must not be empty.");
}

[Test]
public async Task ShouldNotErrorWhenAmqpConnectionStringIsPassed()
{
// Arrange
var rabbitmqTransportConfig = new RabbitmqTransportConfig()
{
AmqpConnectionString = "amqp://localhost"
};
// Act
var result = await _validator.TestValidateAsync(rabbitmqTransportConfig);

// Assert
result.ShouldNotHaveValidationErrorFor(c => c.AmqpConnectionString);
}
}
16 changes: 8 additions & 8 deletions website/docs/transports/learning.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ To use the Learning Transport, define it under `transports` and reference it as
current-transport: local-learning

transports:
- name: local-learning
learning-transport-config:
storage-directory: C:\Source\tutorials-quickstart\.learningtransport
restrict-payload-size: true
- name: local-learning
learning-transport-config:
storage-directory: C:\Source\tutorials-quickstart\.learningtransport
restrict-payload-size: true
```

---

## `learning-transport-config` Fields

| Field | Required | Type | Default | Description |
| ----------------------- | -------- | ------- | ------- | ----------------------------------------------------------------------------------------------------- |
| `storage-directory` | **Yes** | string | — | Absolute path where Learning Transport stores message files. Busly will not start without this value. |
| `restrict-payload-size` | No | boolean | `true` | Enforces the NServiceBus payload size limit. Set to `false` if you need to send larger payloads. |
| Field | Required | Type | Default | Description |
| ----------------------- | -------- | ------- | ------- | ------------------------------------------------------------------------------------------------ |
| `storage-directory` | **Yes** | string | — | Absolute path where Learning Transport stores message files. |
| `restrict-payload-size` | No | boolean | `true` | Enforces the NServiceBus payload size limit. Set to `false` if you need to send larger payloads. |

---

Expand Down
80 changes: 80 additions & 0 deletions website/docs/transports/rabbitmq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# RabbitMQ

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

## Configuration

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

### Example

```yaml
current-transport: local-rabbitmq

transports:
- name: local-rabbitmq
rabbitmq-transport-config:
amqp-connection-string: amqp://localhost
```

---

## `rabbitmq-transport-config` Fields

| Field | Required | Type | Default | Description |
| ------------------------ | -------- | ------ | ------- | ----------------------------------------------------------------- |
| `amqp-connection-string` | **Yes** | string | — | Full AMQP connection string used to connect to RabbitMQ. |
| `management-api` | No | object | — | Optional configuration to connect to the RabbitMQ Management API. |

---

## `management-api` Fields

| Field | Required | Type | Default | Description |
| ---------- | -------- | ------ | ------- | ------------------------------------------------------------------------------------------------------------- |
| `url` | No | string | — | Base URL for the RabbitMQ Management API. Example: http://localhost:15672. |
| `username` | No | string | — | Username for authenticating with the Management API. If username is provided, password must also be provided. |
| `password` | No | string | — | Password for authenticating with the Management API. If password is provided, username must also be provided. |

---

## Field Details

### `amqp-connection-string` (required)

A standard AMQP URI used to connect to RabbitMQ.

Examples:

```yaml
amqp-connection-string: amqp://guest:guest@localhost:5672/
```

```yaml
amqp-connection-string: amqps://user:pass@rabbitmq.example.com:5671/my-vhost
```

### `management-api` (optional)

Allows Busly to interact with the RabbitMQ Management API for monitoring or queue management.

Examples:

```yaml
management-api:
url: http://localhost:15672
```

```yaml
management-api:
username: admin
password: hello123!
```

```yaml
management-api:
url: http://localhost:15672
username: admin
password: hello123!
```
Loading