diff --git a/.env b/.env index f31f1e0c..6f77cbef 100644 --- a/.env +++ b/.env @@ -1,13 +1,9 @@ # Environment file for user defined variables in docker-compose.yml -# 1. CONFIG_PATH: Path to Config.json file -# Ex: CONFIG_PATH="C:\\Config\\Config.json" -CONFIG_PATH="/home/runner/work/btms-backend/btms-backend/config.json" - -# 2. ACCEPT_EULA: Pass 'Y' to accept license terms for Azure SQL Edge and Azure Service Bus emulator. +# ACCEPT_EULA: Pass 'Y' to accept license terms for Azure SQL Edge and Azure Service Bus emulator. # Service Bus emulator EULA : https://github.com/Azure/azure-service-bus-emulator-installer/blob/main/EMULATOR_EULA.txt # SQL Edge EULA : https://go.microsoft.com/fwlink/?linkid=2139274 ACCEPT_EULA="Y" -# 3. MSSQL_SA_PASSWORD to be filled by user as per policy : https://learn.microsoft.com/sql/relational-databases/security/strong-passwords?view=sql-server-linux-ver16 +# MSSQL_SA_PASSWORD to be filled by user as per policy : https://learn.microsoft.com/sql/relational-databases/security/strong-passwords?view=sql-server-linux-ver16 MSSQL_SA_PASSWORD="BTMS@L0c4l" \ No newline at end of file diff --git a/.github/workflows/check-pull-request.yml b/.github/workflows/check-pull-request.yml index b893df78..dd88464e 100644 --- a/.github/workflows/check-pull-request.yml +++ b/.github/workflows/check-pull-request.yml @@ -26,7 +26,7 @@ jobs: - name: Run docker-compose uses: hoverkraft-tech/compose-action@v2.0.1 with: - compose-file: "compose.yml" + compose-file: "compose.yml" - name: Start MongoDB uses: supercharge/mongodb-github-action@1.11.0 @@ -35,17 +35,15 @@ jobs: mongodb-replica-set: test-rs mongodb-port: 29017 - - name: Test + - name: Test run: dotnet test --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" --collect:"XPlat Code Coverage" --results-directory ./coverage - - - name: install dotnet coverage + - name: install dotnet coverage run: dotnet tool install --global dotnet-coverage - - name: merge coverage reports + - name: merge coverage reports run: dotnet-coverage merge *.cobertura.xml --recursive --output merged.cobertura.xml --output-format cobertura - - name: Code Coverage Report uses: irongut/CodeCoverageSummary@v1.3.0 with: diff --git a/Btms.Backend.IntegrationTests/AsbSmokeTests.cs b/Btms.Backend.IntegrationTests/AsbSmokeTests.cs index 7481b9de..bf4f7fd1 100644 --- a/Btms.Backend.IntegrationTests/AsbSmokeTests.cs +++ b/Btms.Backend.IntegrationTests/AsbSmokeTests.cs @@ -1,5 +1,6 @@ using Btms.Backend.IntegrationTests.Helpers; using Btms.Types.Alvs; +using Btms.Types.Gvms; using Btms.Types.Ipaffs; using FluentAssertions; using Microsoft.Extensions.Configuration; @@ -15,7 +16,8 @@ namespace Btms.Backend.IntegrationTests; [Trait("Category", "Integration")] public class AsbSmokeTests : BaseApiTests, IClassFixture { - public AsbSmokeTests(ApplicationFactory factory, ITestOutputHelper testOutputHelper) : base(factory, testOutputHelper, "AsbSmokeTests") + public AsbSmokeTests(ApplicationFactory factory, ITestOutputHelper testOutputHelper) : base(factory, + testOutputHelper, "AsbSmokeTests") { factory.ConfigureHostConfiguration = configurationBuilder => { @@ -27,7 +29,7 @@ public AsbSmokeTests(ApplicationFactory factory, ITestOutputHelper testOutputHel } [Fact] - public async Task AsbSmokeTest() + public async Task AsbSmokeTest_NotificationsAndMovements() { await ClearDb(); var testGeneratorFixture = new TestGeneratorFixture(Factory.TestOutputHelper); @@ -64,4 +66,16 @@ await ShouldEventually.Be(() => jsonClientResponse = Client.AsJsonApiClient().Get("api/movements"); jsonClientResponse.Data.Count.Should().Be(1); } + + [Fact] + public async Task AsbSmokeTest_Gmrs() + { + await ClearDb(); + await ServiceBusHelper.PublishGmr(new Gmr { GmrId = "123" }); + + // NB. This delay is for a visual check of what is logged + // and is temporary until a resultant action can be asserted + await Task.Delay(TimeSpan.FromSeconds(5)); + true.Should().BeTrue(); + } } \ No newline at end of file diff --git a/Btms.Backend.IntegrationTests/Helpers/ServiceBusHelper.cs b/Btms.Backend.IntegrationTests/Helpers/ServiceBusHelper.cs index 92e89082..f2066f7e 100644 --- a/Btms.Backend.IntegrationTests/Helpers/ServiceBusHelper.cs +++ b/Btms.Backend.IntegrationTests/Helpers/ServiceBusHelper.cs @@ -3,6 +3,7 @@ using Azure.Messaging.ServiceBus; using Btms.Consumers; using Btms.Types.Alvs; +using Btms.Types.Gvms; using Btms.Types.Ipaffs; using Decision = Btms.Types.Alvs.Decision; @@ -28,6 +29,11 @@ public static Task PublishNotification(ImportNotification request) return PublishMessage(request, "dev_notification_topic_vnet"); } + public static Task PublishGmr(Gmr request) + { + return PublishMessage(request, "dev_gmr_topic_vnet"); + } + private static async Task PublishMessage(T request, string topicName, string? messageType = null) { await using var client = new ServiceBusClient(ConnectionString); diff --git a/Btms.Backend/appsettings.Development.json b/Btms.Backend/appsettings.Development.json index 1949db06..8df8891f 100644 --- a/Btms.Backend/appsettings.Development.json +++ b/Btms.Backend/appsettings.Development.json @@ -50,6 +50,11 @@ "ConnectionString": "Endpoint=sb://127.0.0.1:5672;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;", "Topic": "dev_notification_topic_vnet", "Subscription": "dev_btms_sub_vnet" + }, + "gmr": { + "ConnectionString": "Endpoint=sb://127.0.0.1:5672;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;", + "Topic": "dev_gmr_topic_vnet", + "Subscription": "dev_btms_sub_vnet" } } }, diff --git a/Btms.Consumers/ConsumerOptions.cs b/Btms.Consumers/ConsumerOptions.cs index 2b8bdc13..6fade1cf 100644 --- a/Btms.Consumers/ConsumerOptions.cs +++ b/Btms.Consumers/ConsumerOptions.cs @@ -14,6 +14,7 @@ public class ConsumerOptions public int AsbAlvsMessages { get; set; } = 20; public int AsbNotifications { get; set; } = 20; + public int AsbGmrs { get; set; } = 20; public int ErrorRetries { get; set; } = 10; diff --git a/Btms.Consumers/Extensions/ServiceCollectionExtensions.cs b/Btms.Consumers/Extensions/ServiceCollectionExtensions.cs index eb536b93..b335cc6f 100644 --- a/Btms.Consumers/Extensions/ServiceCollectionExtensions.cs +++ b/Btms.Consumers/Extensions/ServiceCollectionExtensions.cs @@ -34,7 +34,7 @@ public static IServiceCollection AddConsumers(this IServiceCollection services, var serviceBusOptions = configuration .GetSection(ServiceBusOptions.SectionName) - .Get(); + .Get() ?? throw new InvalidOperationException("Service bus options not found"); services.AddBtmsMetrics(); services.AddSingleton(); @@ -45,72 +45,42 @@ public static IServiceCollection AddConsumers(this IServiceCollection services, services.AddSingleton(typeof(IConsumerInterceptor<>), typeof(JobConsumerInterceptor<>)); services.AddSingleton(typeof(IMemoryConsumerErrorHandler<>), typeof(InMemoryConsumerErrorHandler<>)); - - //Message Bus services.AddSlimMessageBus(mbb => { if (consumerOpts.EnableAsbConsumers) { mbb.AddChildBus("ASB_Notification", cbb => { - cbb.WithProviderServiceBus(cfg => - { - cfg.TopologyProvisioning = new ServiceBusTopologySettings { Enabled = false }; - - cfg.ClientFactory = (sp, settings) => - { - var clientOptions = sp.GetRequiredService().IsDevelopment() - ? new ServiceBusClientOptions() - : new ServiceBusClientOptions - { - WebProxy = sp.GetRequiredService(), - TransportType = ServiceBusTransportType.AmqpWebSockets, - }; - - return new ServiceBusClient(settings.ConnectionString, clientOptions); - }; - cfg.ConnectionString = serviceBusOptions?.NotificationSubscription.ConnectionString; - }); - cbb.AddJsonSerializer(); + ConfigureServiceBusClient(cbb, serviceBusOptions.NotificationSubscription.ConnectionString); cbb.Consume(x => x - .Topic(serviceBusOptions?.NotificationSubscription.Topic) - .SubscriptionName(serviceBusOptions?.NotificationSubscription.Subscription) + .Topic(serviceBusOptions.NotificationSubscription.Topic) + .SubscriptionName(serviceBusOptions.NotificationSubscription.Subscription) .WithConsumer() .Instances(consumerOpts.AsbNotifications)); }); mbb.AddChildBus("ASB_Alvs", cbb => { - cbb.WithProviderServiceBus(cfg => - { - cfg.TopologyProvisioning = new ServiceBusTopologySettings - { - Enabled = false - }; - - cfg.ClientFactory = (sp, settings) => - { - var clientOptions = sp.GetRequiredService().IsDevelopment() - ? new ServiceBusClientOptions() - : new ServiceBusClientOptions - { - WebProxy = sp.GetRequiredService(), - TransportType = ServiceBusTransportType.AmqpWebSockets, - }; - - return new ServiceBusClient(settings.ConnectionString, clientOptions); - }; - cfg.ConnectionString = serviceBusOptions?.AlvsSubscription.ConnectionString; - }); - cbb.AddJsonSerializer(); + ConfigureServiceBusClient(cbb, serviceBusOptions.AlvsSubscription.ConnectionString); cbb.Consume(x => x - .Topic(serviceBusOptions?.AlvsSubscription.Topic) - .SubscriptionName(serviceBusOptions?.AlvsSubscription.Subscription) + .Topic(serviceBusOptions.AlvsSubscription.Topic) + .SubscriptionName(serviceBusOptions.AlvsSubscription.Subscription) .WithConsumer() .Instances(consumerOpts.AsbAlvsMessages)); }); + + mbb.AddChildBus("ASB_Gmr", cbb => + { + ConfigureServiceBusClient(cbb, serviceBusOptions.GmrSubscription.ConnectionString); + + cbb.Consume(x => x + .Topic(serviceBusOptions.GmrSubscription.Topic) + .SubscriptionName(serviceBusOptions.GmrSubscription.Subscription) + .WithConsumer() + .Instances(consumerOpts.AsbGmrs)); + }); } mbb @@ -154,11 +124,31 @@ public static IServiceCollection AddConsumers(this IServiceCollection services, x.Topic("FINALISATIONS").WithConsumer(); }); }); - - }); return services; + + void ConfigureServiceBusClient(MessageBusBuilder cbb, string connectionString) + { + cbb.WithProviderServiceBus(cfg => + { + cfg.TopologyProvisioning = new ServiceBusTopologySettings { Enabled = false }; + cfg.ClientFactory = (sp, settings) => + { + var clientOptions = sp.GetRequiredService().IsDevelopment() + ? new ServiceBusClientOptions() + : new ServiceBusClientOptions + { + WebProxy = sp.GetRequiredService(), + TransportType = ServiceBusTransportType.AmqpWebSockets, + }; + + return new ServiceBusClient(settings.ConnectionString, clientOptions); + }; + cfg.ConnectionString = connectionString; + }); + cbb.AddJsonSerializer(); + } } } } \ No newline at end of file diff --git a/Btms.Consumers/GmrAsbConsumer.cs b/Btms.Consumers/GmrAsbConsumer.cs new file mode 100644 index 00000000..731a051e --- /dev/null +++ b/Btms.Consumers/GmrAsbConsumer.cs @@ -0,0 +1,25 @@ +using Btms.Consumers.Extensions; +using Btms.Types.Gvms; +using Microsoft.Extensions.Logging; +using SlimMessageBus; + +namespace Btms.Consumers; + +internal class GmrAsbConsumer(ILogger logger) : IConsumer, IConsumerWithContext +{ + public Task OnHandle(Gmr message, CancellationToken cancellationToken) + { + try + { + logger.LogInformation("Consumed {GmrId}, {MessageId}", message.GmrId, Context.GetMessageId()); + } + catch (Exception exception) + { + logger.LogError(exception, "Errored"); + } + + return Task.CompletedTask; + } + + public IConsumerContext Context { get; set; } = null!; +} \ No newline at end of file diff --git a/Btms.Consumers/ServiceBusOptions.cs b/Btms.Consumers/ServiceBusOptions.cs index a3521649..eb4dc608 100644 --- a/Btms.Consumers/ServiceBusOptions.cs +++ b/Btms.Consumers/ServiceBusOptions.cs @@ -12,6 +12,8 @@ public class ServiceBusOptions public ServiceBusSubscriptionOptions AlvsSubscription => Subscriptions["alvs"]; public ServiceBusSubscriptionOptions NotificationSubscription => Subscriptions["notification"]; + + public ServiceBusSubscriptionOptions GmrSubscription => Subscriptions["gmr"]; } public class ServiceBusSubscriptionOptions diff --git a/compose.yml b/compose.yml index 9798be20..a8a378a6 100644 --- a/compose.yml +++ b/compose.yml @@ -4,7 +4,7 @@ services: container_name: "servicebus-emulator" image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest volumes: - - "${CONFIG_PATH}:/ServiceBus_Emulator/ConfigFiles/Config.json" + - "./config.json:/ServiceBus_Emulator/ConfigFiles/Config.json" ports: - "5672:5672" environment: diff --git a/config.json b/config.json index 839d020c..278dd560 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,6 @@ "Namespaces": [ { "Name": "sbemulatorns", - "Topics": [ { "Name": "dev_alvs_topic_vnet", @@ -48,6 +47,28 @@ } } ] + }, + { + "Name": "dev_gmr_topic_vnet", + "Properties": { + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "RequiresDuplicateDetection": false + }, + "Subscriptions": [ + { + "Name": "dev_btms_sub_vnet", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + } + } + ] } ] }