From 1ada669752134a6e66790a6da83c768dbb2d9380 Mon Sep 17 00:00:00 2001 From: Jesper Justesen Date: Mon, 28 Oct 2024 15:34:34 +0100 Subject: [PATCH] Added notification sending --- .../DatabaseMigration.csproj | 1 + ...IsNotificationSent_to_SettlementReport.sql | 3 + .../DispatchIntegrationEventsTrigger.cs | 35 ++++++++++ .../ISettlementReportRepository.cs | 2 + .../SettlementReports_v2/SettlementReport.cs | 9 +++ .../UserNotificationTriggeredMetadata.cs | 21 ++++++ .../user_notification_triggered.proto | 65 +++++++++++++++++ .../CalculationResultsExtensions.cs | 11 +-- .../Notifications/IntegrationEventProvider.cs | 69 +++++++++++++++++++ .../SettlementReportRepository.cs | 9 +++ .../SettlementReports.Infrastructure.csproj | 16 +++++ 11 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 source/settlement-report/DatabaseMigration/Scripts/202410261405_Add_IsNotificationSent_to_SettlementReport.sql create mode 100644 source/settlement-report/Orchestration.SettlementReports/Functions/SettlementReports/DispatchIntegrationEventsTrigger.cs create mode 100644 source/settlement-report/SettlementReports.Infrastructure/Contracts/UserNotificationTriggeredMetadata.cs create mode 100644 source/settlement-report/SettlementReports.Infrastructure/Contracts/user_notification_triggered.proto create mode 100644 source/settlement-report/SettlementReports.Infrastructure/Notifications/IntegrationEventProvider.cs diff --git a/source/settlement-report/DatabaseMigration/DatabaseMigration.csproj b/source/settlement-report/DatabaseMigration/DatabaseMigration.csproj index dc6b66b..86d81ce 100644 --- a/source/settlement-report/DatabaseMigration/DatabaseMigration.csproj +++ b/source/settlement-report/DatabaseMigration/DatabaseMigration.csproj @@ -30,6 +30,7 @@ limitations under the License. + diff --git a/source/settlement-report/DatabaseMigration/Scripts/202410261405_Add_IsNotificationSent_to_SettlementReport.sql b/source/settlement-report/DatabaseMigration/Scripts/202410261405_Add_IsNotificationSent_to_SettlementReport.sql new file mode 100644 index 0000000..2940dfd --- /dev/null +++ b/source/settlement-report/DatabaseMigration/Scripts/202410261405_Add_IsNotificationSent_to_SettlementReport.sql @@ -0,0 +1,3 @@ +ALTER TABLE [settlementreports].[SettlementReport] +ADD [IsNotficationSent] [bit] NOT NULL DEFAULT(1); --For all existing records, we will assume that notification has been sent. +GO \ No newline at end of file diff --git a/source/settlement-report/Orchestration.SettlementReports/Functions/SettlementReports/DispatchIntegrationEventsTrigger.cs b/source/settlement-report/Orchestration.SettlementReports/Functions/SettlementReports/DispatchIntegrationEventsTrigger.cs new file mode 100644 index 0000000..12020c3 --- /dev/null +++ b/source/settlement-report/Orchestration.SettlementReports/Functions/SettlementReports/DispatchIntegrationEventsTrigger.cs @@ -0,0 +1,35 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Energinet.DataHub.Core.Messaging.Communication.Publisher; +using Microsoft.Azure.Functions.Worker; + +namespace Energinet.DataHub.SettlementReport.Orchestration.SettlementReports.Functions.SettlementReports; + +public sealed class DispatchIntegrationEventsTrigger +{ + private readonly IPublisher _publisher; + + public DispatchIntegrationEventsTrigger(IPublisher publisher) + { + _publisher = publisher; + } + + [Function(nameof(DispatchIntegrationEventsTrigger))] + public Task RunAsync([TimerTrigger("* */1 * * *")] FunctionContext context) + { + ArgumentNullException.ThrowIfNull(context); + return _publisher.PublishAsync(context.CancellationToken); + } +} diff --git a/source/settlement-report/SettlementReports.Application/SettlementReports_v2/ISettlementReportRepository.cs b/source/settlement-report/SettlementReports.Application/SettlementReports_v2/ISettlementReportRepository.cs index ee81cee..cb3dbaa 100644 --- a/source/settlement-report/SettlementReports.Application/SettlementReports_v2/ISettlementReportRepository.cs +++ b/source/settlement-report/SettlementReports.Application/SettlementReports_v2/ISettlementReportRepository.cs @@ -31,4 +31,6 @@ public interface ISettlementReportRepository Task> GetForJobsAsync(); Task> GetForJobsAsync(Guid actorId); + + Task> GetNeedsNotificationSent(); } diff --git a/source/settlement-report/SettlementReports.Application/SettlementReports_v2/SettlementReport.cs b/source/settlement-report/SettlementReports.Application/SettlementReports_v2/SettlementReport.cs index 4afa811..cfb4807 100644 --- a/source/settlement-report/SettlementReports.Application/SettlementReports_v2/SettlementReport.cs +++ b/source/settlement-report/SettlementReports.Application/SettlementReports_v2/SettlementReport.cs @@ -58,6 +58,8 @@ public sealed class SettlementReport public long? JobId { get; init; } + public bool IsNotficationSent { get; private set; } + public SettlementReport( IClock clock, Guid userId, @@ -80,6 +82,7 @@ public SettlementReport( SplitReportPerGridArea = request.SplitReportPerGridArea; IncludeMonthlyAmount = request.IncludeMonthlyAmount; GridAreas = JsonSerializer.Serialize(request.Filter.GridAreas); + IsNotficationSent = false; } public SettlementReport( @@ -106,6 +109,7 @@ public SettlementReport( SplitReportPerGridArea = request.SplitReportPerGridArea; IncludeMonthlyAmount = request.IncludeMonthlyAmount; GridAreas = JsonSerializer.Serialize(request.Filter.GridAreas); + IsNotficationSent = false; } // EF Core Constructor. @@ -132,4 +136,9 @@ public void MarkAsFailed() { Status = SettlementReportStatus.Failed; } + + public void MarkAsNotificationSent() + { + IsNotficationSent = true; + } } diff --git a/source/settlement-report/SettlementReports.Infrastructure/Contracts/UserNotificationTriggeredMetadata.cs b/source/settlement-report/SettlementReports.Infrastructure/Contracts/UserNotificationTriggeredMetadata.cs new file mode 100644 index 0000000..07c5515 --- /dev/null +++ b/source/settlement-report/SettlementReports.Infrastructure/Contracts/UserNotificationTriggeredMetadata.cs @@ -0,0 +1,21 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Energinet.DataHub.SettlementReport.Infrastructure.Contracts; + +public partial class UserNotificationTriggered +{ + public const string EventName = "UserNotificationTriggered"; + public const int CurrentMinorVersion = 1; +} diff --git a/source/settlement-report/SettlementReports.Infrastructure/Contracts/user_notification_triggered.proto b/source/settlement-report/SettlementReports.Infrastructure/Contracts/user_notification_triggered.proto new file mode 100644 index 0000000..e463611 --- /dev/null +++ b/source/settlement-report/SettlementReports.Infrastructure/Contracts/user_notification_triggered.proto @@ -0,0 +1,65 @@ +/* Copyright 2020 Energinet DataHub A/S + * + * Licensed under the Apache License, Version 2.0 (the "License2"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "Energinet.DataHub.SettlementReport.Infrastructure.Contracts"; + +/* + * UserNotificationTriggered Integration Event. + * + * Occurs when some action triggers the notification of a set of users. + */ +message UserNotificationTriggered { + + /* + * An identifier specifying the reason for the notification. + */ + string reason_identifier = 1; + + /* + * The unique identifier of the actor whose users will receive the notification. + */ + string target_actor_id = 2; + + oneof target { + + /* + * The unique identifier of the user that should receive the notification. + */ + string target_user_id = 3; + + /* + * The unique identifier of the group of users having the specified permission that should receive the notification. + */ + string target_permissions = 4; + } + + /* + * A reason-specific id of an entity that the notification targets. + */ + string related_id = 5; + + /* + * A timestamp for when the notification was generated. + */ + google.protobuf.Timestamp occurred_at = 6; + + /* + * A timestamp for when the notification expires by itself. + */ + google.protobuf.Timestamp expires_at = 7; +} diff --git a/source/settlement-report/SettlementReports.Infrastructure/Extensions/DependencyInjection/CalculationResultsExtensions.cs b/source/settlement-report/SettlementReports.Infrastructure/Extensions/DependencyInjection/CalculationResultsExtensions.cs index f0dacb2..66987b8 100644 --- a/source/settlement-report/SettlementReports.Infrastructure/Extensions/DependencyInjection/CalculationResultsExtensions.cs +++ b/source/settlement-report/SettlementReports.Infrastructure/Extensions/DependencyInjection/CalculationResultsExtensions.cs @@ -21,6 +21,7 @@ using Energinet.DataHub.SettlementReport.Common.Infrastructure.Extensions.Options; using Energinet.DataHub.SettlementReport.Common.Infrastructure.HealthChecks; using Energinet.DataHub.SettlementReport.Common.Infrastructure.Options; +using Energinet.DataHub.SettlementReport.Infrastructure.Notifications; using Energinet.DataHub.SettlementReport.Infrastructure.Persistence; using Energinet.DataHub.SettlementReport.Infrastructure.Persistence.Databricks; using Energinet.DataHub.SettlementReport.Infrastructure.Persistence.SettlementReportRequest; @@ -42,10 +43,8 @@ public static IServiceCollection AddSettlementReportsV2Module(this IServiceColle { ArgumentNullException.ThrowIfNull(configuration); - services - .AddOptions() - .BindConfiguration(ServiceBusNamespaceOptions.SectionName) - .ValidateDataAnnotations(); + services.AddOptions().BindConfiguration(IntegrationEventsOptions.SectionName).ValidateDataAnnotations(); + services.AddOptions().BindConfiguration(ServiceBusNamespaceOptions.SectionName).ValidateDataAnnotations(); services.AddDatabricksSqlStatementForApplication(configuration); @@ -74,7 +73,6 @@ public static IServiceCollection AddSettlementReportsV2Module(this IServiceColle services.AddScoped(); services.AddScoped(); services.AddSettlementReportBlobStorage(); - services.AddServiceBusClientForApplication(configuration); services.AddScoped(); services.AddDbContext( options => options.UseSqlServer( @@ -105,6 +103,9 @@ public static IServiceCollection AddSettlementReportsV2Module(this IServiceColle // Used by sql statements (queries) services.AddOptions().Bind(configuration); + services.AddServiceBusClientForApplication(configuration); + services.AddIntegrationEventsPublisher(configuration); + return services; } } diff --git a/source/settlement-report/SettlementReports.Infrastructure/Notifications/IntegrationEventProvider.cs b/source/settlement-report/SettlementReports.Infrastructure/Notifications/IntegrationEventProvider.cs new file mode 100644 index 0000000..3b57489 --- /dev/null +++ b/source/settlement-report/SettlementReports.Infrastructure/Notifications/IntegrationEventProvider.cs @@ -0,0 +1,69 @@ +// Copyright 2020 Energinet DataHub A/S +// +// Licensed under the Apache License, Version 2.0 (the "License2"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Energinet.DataHub.Core.Messaging.Communication; +using Energinet.DataHub.Core.Messaging.Communication.Publisher; +using Energinet.DataHub.SettlementReport.Application.SettlementReports_v2; +using Google.Protobuf.WellKnownTypes; + +namespace Energinet.DataHub.SettlementReport.Infrastructure.Notifications; + +public sealed class IntegrationEventProvider : IIntegrationEventProvider +{ + private readonly ISettlementReportRepository _settlementReportRepository; + + public IntegrationEventProvider(ISettlementReportRepository settlementReportRepository) + { + _settlementReportRepository = settlementReportRepository; + } + + public async IAsyncEnumerable GetAsync() + { + var reportsForNotifications = await _settlementReportRepository + .GetNeedsNotificationSent() + .ConfigureAwait(false); + + foreach (var reportForNotification in reportsForNotifications) + { + yield return await CreateAsync(reportForNotification).ConfigureAwait(false); + + reportForNotification.MarkAsNotificationSent(); + + await _settlementReportRepository.AddOrUpdateAsync(reportForNotification) + .ConfigureAwait(false); + } + } + + private Task CreateAsync(Application.SettlementReports_v2.SettlementReport reportForNotification) + { + ArgumentNullException.ThrowIfNull(reportForNotification); + + var now = DateTime.UtcNow; + + var integrationEvent = new IntegrationEvent( + Guid.Parse(reportForNotification.RequestId), + Contracts.UserNotificationTriggered.EventName, + Contracts.UserNotificationTriggered.CurrentMinorVersion, + new Contracts.UserNotificationTriggered + { + ReasonIdentifier = "SettlementReportFinished", + TargetActorId = reportForNotification.ActorId.ToString(), + RelatedId = reportForNotification.Id.ToString(), + OccurredAt = now.ToTimestamp(), + ExpiresAt = now.AddHours(23).ToTimestamp(), + }); + + return Task.FromResult(integrationEvent); + } +} diff --git a/source/settlement-report/SettlementReports.Infrastructure/Persistence/SettlementReportRequest/SettlementReportRepository.cs b/source/settlement-report/SettlementReports.Infrastructure/Persistence/SettlementReportRequest/SettlementReportRepository.cs index d689cac..fc18968 100644 --- a/source/settlement-report/SettlementReports.Infrastructure/Persistence/SettlementReportRequest/SettlementReportRepository.cs +++ b/source/settlement-report/SettlementReports.Infrastructure/Persistence/SettlementReportRequest/SettlementReportRepository.cs @@ -89,4 +89,13 @@ public async Task DeleteAsync(Application.SettlementReports_v2.SettlementReport .ToListAsync() .ConfigureAwait(false); } + + public async Task> GetNeedsNotificationSent() + { + return await _context.SettlementReports + .Where(x => x.IsNotficationSent == false) + .OrderBy(x => x.EndedDateTime) + .ToListAsync() + .ConfigureAwait(false); + } } diff --git a/source/settlement-report/SettlementReports.Infrastructure/SettlementReports.Infrastructure.csproj b/source/settlement-report/SettlementReports.Infrastructure/SettlementReports.Infrastructure.csproj index 3f3bedb..df676c8 100644 --- a/source/settlement-report/SettlementReports.Infrastructure/SettlementReports.Infrastructure.csproj +++ b/source/settlement-report/SettlementReports.Infrastructure/SettlementReports.Infrastructure.csproj @@ -11,6 +11,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -23,4 +28,15 @@ + + + + None + Public + True + True + obj\contracts + MSBuild:Compile + +