From cb9f6b387109aab705d847c543fce2343f206e7e Mon Sep 17 00:00:00 2001 From: Michael Fyffe <6224270+TraGicCode@users.noreply.github.com> Date: Thu, 4 Dec 2025 13:41:01 -0600 Subject: [PATCH 1/2] Update docs for amazon-sqs transport along with config validation --- .../AmazonsqsTransportConfigValidator.cs | 15 ++++ .../Validators/TransportConfigValidator.cs | 1 + .../AmazonsqsTransportConfigValidatorTests.cs | 80 +++++++++++++++++++ website/docs/transports/amazon-sqs.md | 59 ++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs create mode 100644 tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs create mode 100644 website/docs/transports/amazon-sqs.md diff --git a/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs b/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs new file mode 100644 index 0000000..c53c41f --- /dev/null +++ b/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +namespace BuslyCLI.Config.Validators; + +public class AmazonsqsTransportConfigValidator : AbstractValidator +{ + public AmazonsqsTransportConfigValidator() + { + RuleFor(x => x.RegionName) + .NotEmpty(); + + RuleFor(x => x.ServiceUrl) + .NotEmpty(); + } +} \ No newline at end of file diff --git a/src/BuslyCLI.Console/Config/Validators/TransportConfigValidator.cs b/src/BuslyCLI.Console/Config/Validators/TransportConfigValidator.cs index 97f1a7c..24a33c6 100644 --- a/src/BuslyCLI.Console/Config/Validators/TransportConfigValidator.cs +++ b/src/BuslyCLI.Console/Config/Validators/TransportConfigValidator.cs @@ -16,6 +16,7 @@ public TransportConfigValidator() v.Add(new LearningTransportConfigValidator()); v.Add(new RabbitMQTransportConfigValidator()); v.Add(new AzureServiceBusTransportConfigValidator()); + v.Add(new AmazonsqsTransportConfigValidator()); }); // RuleFor(x => x.LearningTransportConfig) diff --git a/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs b/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs new file mode 100644 index 0000000..f8fa4da --- /dev/null +++ b/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs @@ -0,0 +1,80 @@ +using BuslyCLI.Config; +using BuslyCLI.Config.Validators; +using FluentValidation.TestHelper; + +namespace BuslyCLI.Console.Tests.Config.Validators; + +[TestFixture] +public class AmazonsqsTransportConfigValidatorTests +{ + private readonly AmazonsqsTransportConfigValidator _validator; + + public AmazonsqsTransportConfigValidatorTests() + { + _validator = new AmazonsqsTransportConfigValidator(); + } + + [Test] + public async Task ShouldErrorWhenRegionNameIsNotPassed() + { + // Arrange + var amazonsqsTransportConfig = new AmazonsqsTransportConfig + { + RegionName = null + }; + + // Act + var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); + + // Assert + result.ShouldHaveValidationErrorFor(c => c.RegionName) + .WithErrorMessage("'Region Name' must not be empty."); + } + + [Test] + public async Task ShouldNotErrorWhenRegionNameIsPassed() + { + // Arrange + var amazonsqsTransportConfig = new AmazonsqsTransportConfig + { + RegionName = "us-east-1" + }; + // Act + var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); + + // Assert + result.ShouldNotHaveValidationErrorFor(c => c.RegionName); + } + + [Test] + public async Task ShouldErrorWhenServiceUrlIsNotPassed() + { + // Arrange + var amazonsqsTransportConfig = new AmazonsqsTransportConfig + { + RegionName = null + }; + + // Act + var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); + + // Assert + result.ShouldHaveValidationErrorFor(c => c.ServiceUrl) + .WithErrorMessage("'Service Url' must not be empty."); + } + + [Test] + public async Task ShouldNotErrorWhenServiceUrlIsPassed() + { + // Arrange + var amazonsqsTransportConfig = new AmazonsqsTransportConfig + { + ServiceUrl = "us-east-1" + }; + // Act + var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); + + // Assert + result.ShouldNotHaveValidationErrorFor(c => c.ServiceUrl); + } +} \ No newline at end of file diff --git a/website/docs/transports/amazon-sqs.md b/website/docs/transports/amazon-sqs.md new file mode 100644 index 0000000..145182c --- /dev/null +++ b/website/docs/transports/amazon-sqs.md @@ -0,0 +1,59 @@ +# Amazon SQS + +The **Amazon SQS Transport** is used to communicate to Amazon SQS. + +## Configuration + +To use the Amazon SQS Transport, define it under `transports` and reference it as `current-transport`. + +### Example + +```yaml +current-transport: local-stack-amazon-sqs + +transports: + - name: local-stack-amazon-sqs + amazonsqs-transport-config: + service-url: http://127.0.0.1:32813/ + region-name: us-east-1 +``` + +:::info + +The Amazon SQS transport implementation currently works only with the LocalStack emulator. +Pull requests to improve functionality or add support for live AWS SQS are welcome and much appreciated! + +::: + +--- + +## `amazonsqs-transport-config` Fields + +| Field | Required | Type | Default | Description | +| ------------- | -------- | ------ | ------- | ------------------------------- | +| `service-url` | **Yes** | string | — | The Service Url for Amazon SQS. | +| `region-name` | **Yes** | string | — | The region (EX: us-east-1). | + +--- + +## Field Details + +### `service-url` (required) + +The Service Url for Amazon SQS. + +Examples: + +```yaml +service-url: http://127.0.0.1:32813/ +``` + +### `region-name` (optional) + +Allows Busly to interact with the RabbitMQ Management API for monitoring or queue management. + +Examples: + +```yaml +region-name: us-east-1 +``` From adf8f1195c5d4ac5fc16bc6e0c4e0774ef12fcda Mon Sep 17 00:00:00 2001 From: Michael Fyffe <6224270+TraGicCode@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:33:05 -0600 Subject: [PATCH 2/2] Update docs for amazon-sqs transport along with config validation --- .../Config/AmazonsqsTransportConfig.cs | 5 ++ .../AmazonsqsTransportConfigValidator.cs | 8 ++- .../Factories/RawEndpointFactory.cs | 31 ++++++---- .../AmazonsqsTransportConfigValidatorTests.cs | 31 +++++++--- website/docs/transports/amazon-sqs.md | 57 ++++++++++++++----- website/docs/transports/rabbitmq.md | 2 + 6 files changed, 99 insertions(+), 35 deletions(-) diff --git a/src/BuslyCLI.Console/Config/AmazonsqsTransportConfig.cs b/src/BuslyCLI.Console/Config/AmazonsqsTransportConfig.cs index cc241bb..244a00c 100644 --- a/src/BuslyCLI.Console/Config/AmazonsqsTransportConfig.cs +++ b/src/BuslyCLI.Console/Config/AmazonsqsTransportConfig.cs @@ -3,6 +3,11 @@ public class AmazonsqsTransportConfig : ITransportConfig { + + // Local Stack Only public string ServiceUrl { get; set; } public string RegionName { get; set; } + public string AccessKey { get; set; } + public string SecretKey { get; set; } + } \ No newline at end of file diff --git a/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs b/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs index c53c41f..41d75b9 100644 --- a/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs +++ b/src/BuslyCLI.Console/Config/Validators/AmazonsqsTransportConfigValidator.cs @@ -9,7 +9,11 @@ public AmazonsqsTransportConfigValidator() RuleFor(x => x.RegionName) .NotEmpty(); - RuleFor(x => x.ServiceUrl) - .NotEmpty(); + RuleFor(x => x) + .Must(x => + (string.IsNullOrEmpty(x.AccessKey) && string.IsNullOrEmpty(x.SecretKey)) // both empty + || (!string.IsNullOrEmpty(x.AccessKey) && !string.IsNullOrEmpty(x.SecretKey)) // both set + ) + .WithMessage("AWS AccessKey and SecretKey are mutually dependent: if one is set, the other must also be set."); } } \ No newline at end of file diff --git a/src/BuslyCLI.Console/Factories/RawEndpointFactory.cs b/src/BuslyCLI.Console/Factories/RawEndpointFactory.cs index 7d8ca9a..dce9d65 100644 --- a/src/BuslyCLI.Console/Factories/RawEndpointFactory.cs +++ b/src/BuslyCLI.Console/Factories/RawEndpointFactory.cs @@ -1,4 +1,5 @@ -using Amazon.Runtime; +using Amazon; +using Amazon.Runtime; using Amazon.SimpleNotificationService; using Amazon.SQS; using BuslyCLI.Config; @@ -73,19 +74,27 @@ private static ManagementApiConfiguration CreateManagementApiConfig(ManagementAp private TransportDefinition CreateAmazonSQSTransport(AmazonsqsTransportConfig amazonsqsTransportConfig) { - var credentials = new BasicAWSCredentials("test", "test"); - - var sqsClient = new AmazonSQSClient(credentials, new AmazonSQSConfig + var credentials = new BasicAWSCredentials(amazonsqsTransportConfig.AccessKey, amazonsqsTransportConfig.SecretKey); + var amazonSqsConfig = new AmazonSQSConfig(); + var amazonSnsConfig = new AmazonSimpleNotificationServiceConfig(); + if (!string.IsNullOrWhiteSpace(amazonsqsTransportConfig.RegionName)) { - ServiceURL = amazonsqsTransportConfig.ServiceUrl, - AuthenticationRegion = amazonsqsTransportConfig.RegionName, - }); - var snsClient = new AmazonSimpleNotificationServiceClient(credentials, new AmazonSimpleNotificationServiceConfig + amazonSqsConfig.RegionEndpoint = RegionEndpoint.GetBySystemName(amazonsqsTransportConfig.RegionName); + amazonSnsConfig.RegionEndpoint = RegionEndpoint.GetBySystemName(amazonsqsTransportConfig.RegionName); + } + + // If ServiceUrl is passed, we are assuming we are using LocalStack + // Without this, local stack will try to really authenticate with aws which will fail + if (!string.IsNullOrWhiteSpace(amazonsqsTransportConfig.ServiceUrl)) { - ServiceURL = amazonsqsTransportConfig.ServiceUrl, - AuthenticationRegion = amazonsqsTransportConfig.RegionName, - }); + amazonSnsConfig.ServiceURL = amazonsqsTransportConfig.ServiceUrl; + amazonSqsConfig.ServiceURL = amazonsqsTransportConfig.ServiceUrl; + } + + var sqsClient = new AmazonSQSClient(credentials, amazonSqsConfig); + + var snsClient = new AmazonSimpleNotificationServiceClient(credentials, amazonSnsConfig); return new SqsTransport(sqsClient, snsClient); } diff --git a/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs b/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs index f8fa4da..f4a7654 100644 --- a/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs +++ b/tests/BuslyCLI.Console.Tests/Config/Validators/AmazonsqsTransportConfigValidatorTests.cs @@ -47,34 +47,49 @@ public async Task ShouldNotErrorWhenRegionNameIsPassed() } [Test] - public async Task ShouldErrorWhenServiceUrlIsNotPassed() + public async Task ShouldNotErrorWhenServiceUrlIsPassed() { // Arrange var amazonsqsTransportConfig = new AmazonsqsTransportConfig { - RegionName = null + ServiceUrl = "us-east-1" }; + // Act + var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); + + // Assert + result.ShouldNotHaveValidationErrorFor(c => c.ServiceUrl); + } + [Test] + public async Task ShouldErrorWhenAccessKeyIsPassedWithoutSecretKey() + { + // Arrange + var amazonsqsTransportConfig = new AmazonsqsTransportConfig() + { + AccessKey = "BLAHBLAHBLAH", + SecretKey = null + }; // Act var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); // Assert - result.ShouldHaveValidationErrorFor(c => c.ServiceUrl) - .WithErrorMessage("'Service Url' must not be empty."); + result.ShouldHaveValidationErrors().WithErrorMessage("AWS AccessKey and SecretKey are mutually dependent: if one is set, the other must also be set."); } [Test] - public async Task ShouldNotErrorWhenServiceUrlIsPassed() + public async Task ShouldErrorWhenSecretIsPassedWithoutAccessKey() { // Arrange - var amazonsqsTransportConfig = new AmazonsqsTransportConfig + var amazonsqsTransportConfig = new AmazonsqsTransportConfig() { - ServiceUrl = "us-east-1" + AccessKey = null, + SecretKey = "BLAHBLAHBLAH" }; // Act var result = await _validator.TestValidateAsync(amazonsqsTransportConfig); // Assert - result.ShouldNotHaveValidationErrorFor(c => c.ServiceUrl); + result.ShouldHaveValidationErrors().WithErrorMessage("AWS AccessKey and SecretKey are mutually dependent: if one is set, the other must also be set."); } } \ No newline at end of file diff --git a/website/docs/transports/amazon-sqs.md b/website/docs/transports/amazon-sqs.md index 145182c..8a4aa93 100644 --- a/website/docs/transports/amazon-sqs.md +++ b/website/docs/transports/amazon-sqs.md @@ -1,6 +1,6 @@ # Amazon SQS -The **Amazon SQS Transport** is used to communicate to Amazon SQS. +The **Amazon SQS Transport** is used to communicate to Amazon SQS. It is suitable for development, testing, and production environments. ## Configuration @@ -14,14 +14,15 @@ current-transport: local-stack-amazon-sqs transports: - name: local-stack-amazon-sqs amazonsqs-transport-config: - service-url: http://127.0.0.1:32813/ region-name: us-east-1 + access-key: test + secret-key: test + service-url: http://127.0.0.1:32813/ # (optional) Only used when connecting to local-stack ``` :::info -The Amazon SQS transport implementation currently works only with the LocalStack emulator. -Pull requests to improve functionality or add support for live AWS SQS are welcome and much appreciated! +The Amazon SQS transport implementation currently works only with **AWS access key and secret key authentication**. Pull requests that add support for additional authentication methods are welcome and greatly appreciated! ::: @@ -29,31 +30,59 @@ Pull requests to improve functionality or add support for live AWS SQS are welco ## `amazonsqs-transport-config` Fields -| Field | Required | Type | Default | Description | -| ------------- | -------- | ------ | ------- | ------------------------------- | -| `service-url` | **Yes** | string | — | The Service Url for Amazon SQS. | -| `region-name` | **Yes** | string | — | The region (EX: us-east-1). | +| Field | Required | Type | Default | Description | +| ------------- | -------- | ------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `region-name` | **Yes** | string | — | The AWS region. All Region codes can be found [here](https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions.html) (EX: us-east-1, us-east2, us-west1..etc). | +| `access-key` | **Yes** | string | — | The AWS Access Key. | +| `secret-key` | **Yes** | string | — | The AWS Secret Key. | +| `service-url` | No | string | — | The service URL used to connect to LocalStack for local development. | --- ## Field Details -### `service-url` (required) +### `region-name` (required) -The Service Url for Amazon SQS. +The AWS Region SQS is hosted in. Examples: ```yaml -service-url: http://127.0.0.1:32813/ +region-name: us-east-1 ``` -### `region-name` (optional) +--- + +### `access-key` (required) -Allows Busly to interact with the RabbitMQ Management API for monitoring or queue management. +The AWS Access Key. Examples: ```yaml -region-name: us-east-1 +access-key: test +``` + +--- + +### `secret-key` (required) + +The AWS Secret Key. + +Examples: + +```yaml +secret-key: test +``` + +--- + +### `service-url` (optional) + +The service URL used to connect to LocalStack for local development. + +Examples: + +```yaml +service-url: http://127.0.0.1:32813/ ``` diff --git a/website/docs/transports/rabbitmq.md b/website/docs/transports/rabbitmq.md index 5215c6b..47dd171 100644 --- a/website/docs/transports/rabbitmq.md +++ b/website/docs/transports/rabbitmq.md @@ -55,6 +55,8 @@ amqp-connection-string: amqp://guest:guest@localhost:5672/ 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.