From f514164cd66a7944872a0802356addce060c4165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 9 Sep 2024 12:54:12 +0200 Subject: [PATCH 1/5] sqwsh --- docs/schema/V1/schema.verified.graphql | 5 +++ .../EndUser/DialogById/Subscriptions.cs | 20 ++++++++++ .../Program.cs | 17 +++++++-- .../ServiceCollectionExtensions.cs | 13 ++++++- ....Domain.Dialogporten.Infrastructure.csproj | 1 + ...DomainEventsToOutboxMessagesInterceptor.cs | 37 ++++++++++++++++-- .../GraphQL/ServiceCollectionExtensions.cs | 38 +++++++++++++++++++ .../InfrastructureExtensions.cs | 3 ++ .../Common/DialogApplication.cs | 2 + .../Schema/SchemaSnapshotTests.cs | 2 +- 10 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/ServiceCollectionExtensions.cs diff --git a/docs/schema/V1/schema.verified.graphql b/docs/schema/V1/schema.verified.graphql index 2a23d37fb..5cf445aab 100644 --- a/docs/schema/V1/schema.verified.graphql +++ b/docs/schema/V1/schema.verified.graphql @@ -1,5 +1,6 @@ schema { query: Queries + subscription: Subscriptions } interface DialogByIdError { @@ -201,6 +202,10 @@ type SeenLog { isCurrentEndUser: Boolean! } +type Subscriptions @authorize(policy: "enduser") { + dialogUpdated(dialogId: UUID!): UUID! +} + type Transmission { id: UUID! createdAt: DateTime! diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs new file mode 100644 index 000000000..85298d33a --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs @@ -0,0 +1,20 @@ +using Digdir.Domain.Dialogporten.GraphQL.Common.Authorization; +using HotChocolate.Authorization; +using Constants = Digdir.Domain.Dialogporten.Infrastructure.GraphQl.GraphQlSubscriptionConstants; + +// ReSharper disable ClassNeverInstantiated.Global + +namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.DialogById; + +[Authorize(Policy = AuthorizationPolicy.EndUser)] +public sealed class Subscriptions +{ + [Subscribe] + [Topic($"{Constants.DialogUpdatedTopic}{{{nameof(dialogId)}}}")] + public Guid DialogUpdated(Guid dialogId, + [EventMessage] Guid eventMessage) + { + ArgumentNullException.ThrowIfNull(eventMessage); + return dialogId; + } +} diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs index bc73a3cf4..59f246397 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs @@ -76,10 +76,21 @@ static void BuildAndRun(string[] args) .AddApplicationInsightsTelemetry() .AddScoped() .AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true) - .AddAzureAppConfiguration() + .AddAzureAppConfiguration(); - // Graph QL - .AddDialogportenGraphQl() + var infrastructureConfigurationSection = + builder.Configuration.GetSection(InfrastructureSettings.ConfigurationSectionName); + + builder.Services.AddOptions() + .Bind(infrastructureConfigurationSection) + .ValidateFluently() + .ValidateOnStart(); + + var infrastructureSettings = infrastructureConfigurationSection.Get() ?? throw new InvalidOperationException("Infrastructure settings must not be null."); + + // Graph QL + builder.Services + .AddDialogportenGraphQl(infrastructureSettings.Redis.ConnectionString) // Auth .AddDialogportenAuthentication(builder.Configuration) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs index 9d7552ba0..0bdfeecdb 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs @@ -2,16 +2,25 @@ using Digdir.Domain.Dialogporten.GraphQL.EndUser.DialogById; using Digdir.Domain.Dialogporten.GraphQL.EndUser.SearchDialogs; using Digdir.Domain.Dialogporten.Infrastructure.Persistence; +using HotChocolate.Subscriptions; +using StackExchange.Redis; +using Constants = Digdir.Domain.Dialogporten.Infrastructure.GraphQl.GraphQlSubscriptionConstants; namespace Digdir.Domain.Dialogporten.GraphQL; public static class ServiceCollectionExtensions { - public static IServiceCollection AddDialogportenGraphQl( - this IServiceCollection services) + public static IServiceCollection AddDialogportenGraphQl(this IServiceCollection services, + string redisConnectionString) { return services .AddGraphQLServer() + .AddRedisSubscriptions(_ => ConnectionMultiplexer.Connect(redisConnectionString), + new SubscriptionOptions + { + TopicPrefix = Constants.SubscriptionTopicPrefix + }) + .AddSubscriptionType() .AddAuthorization() .RegisterDbContext() .AddDiagnosticEventListener() diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj index cc498524f..a1896bbf7 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj @@ -2,6 +2,7 @@ + diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs index 6d897bdcb..3c41211f4 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs @@ -1,17 +1,25 @@ using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Events; using Digdir.Domain.Dialogporten.Domain.Outboxes; using Digdir.Library.Entity.Abstractions.Features.EventPublisher; +using HotChocolate.Subscriptions; using Microsoft.EntityFrameworkCore.Diagnostics; +using Constants = Digdir.Domain.Dialogporten.Infrastructure.GraphQl.GraphQlSubscriptionConstants; namespace Digdir.Domain.Dialogporten.Infrastructure.DomainEvents.Outbox; internal sealed class ConvertDomainEventsToOutboxMessagesInterceptor : SaveChangesInterceptor { private readonly ITransactionTime _transactionTime; + private readonly ITopicEventSender _topicEventSender; + private List _domainEvents = []; - public ConvertDomainEventsToOutboxMessagesInterceptor(ITransactionTime transactionTime) + public ConvertDomainEventsToOutboxMessagesInterceptor( + ITransactionTime transactionTime, + ITopicEventSender topicEventSender) { _transactionTime = transactionTime ?? throw new ArgumentNullException(nameof(transactionTime)); + _topicEventSender = topicEventSender ?? throw new ArgumentNullException(nameof(topicEventSender)); } public override ValueTask> SavingChangesAsync( @@ -26,19 +34,19 @@ public override ValueTask> SavingChangesAsync( return base.SavingChangesAsync(eventData, result, cancellationToken); } - var domainEvents = dbContext.ChangeTracker.Entries() + _domainEvents = dbContext.ChangeTracker.Entries() .SelectMany(x => x.Entity is IEventPublisher publisher ? publisher.PopDomainEvents() : []) .ToList(); - foreach (var domainEvent in domainEvents) + foreach (var domainEvent in _domainEvents) { domainEvent.OccuredAt = _transactionTime.Value; } - var outboxMessages = domainEvents + var outboxMessages = _domainEvents .Select(OutboxMessage.Create) .ToList(); @@ -46,4 +54,25 @@ x.Entity is IEventPublisher publisher return base.SavingChangesAsync(eventData, result, cancellationToken); } + + public override async ValueTask SavedChangesAsync(SaveChangesCompletedEventData eventData, int result, + CancellationToken cancellationToken = default) + { + foreach (var domainEvent in _domainEvents) + { + switch (domainEvent) + { + case DialogUpdatedDomainEvent dialogUpdatedDomainEvent: + await _topicEventSender.SendAsync( + $"{Constants.DialogUpdatedTopic}{dialogUpdatedDomainEvent.DialogId}", + dialogUpdatedDomainEvent.DialogId, + cancellationToken); + break; + default: + break; + } + } + + return await base.SavedChangesAsync(eventData, result, cancellationToken); + } } diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/ServiceCollectionExtensions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..fe2007412 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/ServiceCollectionExtensions.cs @@ -0,0 +1,38 @@ +using HotChocolate.Execution.Configuration; +using HotChocolate.Subscriptions; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; + +namespace Digdir.Domain.Dialogporten.Infrastructure.GraphQl; + +/// +/// This implementation is a workaround to allow the use of the AddRedisSubscriptions extension method +/// from HotChocolate.Subscriptions.Redis without having to take the entire HotChocolate library as a dependency. +/// +internal sealed class DummyRequestExecutorBuilder : IRequestExecutorBuilder +{ + public string Name => string.Empty; + public IServiceCollection Services { get; init; } = null!; +} + +public static class GraphQlSubscriptionConstants +{ + public const string SubscriptionTopicPrefix = "graphql_subscriptions_"; + public const string DialogUpdatedTopic = "dialogUpdated/"; +} + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddGraphQlRedisSubscriptions(this IServiceCollection services, + string redisConnectionString) + { + var dummyImplementation = new DummyRequestExecutorBuilder { Services = services }; + dummyImplementation.AddRedisSubscriptions(_ => ConnectionMultiplexer.Connect(redisConnectionString), + new SubscriptionOptions + { + TopicPrefix = GraphQlSubscriptionConstants.SubscriptionTopicPrefix + }); + + return services; + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs index 63b0fbddd..77deb03a7 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs @@ -26,6 +26,7 @@ using Digdir.Domain.Dialogporten.Infrastructure.Altinn.NameRegistry; using Digdir.Domain.Dialogporten.Infrastructure.Altinn.OrganizationRegistry; using Digdir.Domain.Dialogporten.Infrastructure.Altinn.ResourceRegistry; +using Digdir.Domain.Dialogporten.Infrastructure.GraphQl; using Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.Actors; using Digdir.Domain.Dialogporten.Infrastructure.Persistence.Repositories; using ZiggyCreatures.Caching.Fusion; @@ -69,6 +70,8 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi { services.AddStackExchangeRedisCache(opt => opt.Configuration = infrastructureSettings.Redis.ConnectionString); services.AddFusionCacheStackExchangeRedisBackplane(opt => opt.Configuration = infrastructureSettings.Redis.ConnectionString); + + services.AddGraphQlRedisSubscriptions(infrastructureSettings.Redis.ConnectionString); } else { diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs index dba387620..ea381992e 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs @@ -14,6 +14,7 @@ using Digdir.Domain.Dialogporten.Infrastructure.Persistence; using Digdir.Library.Entity.Abstractions.Features.Lookup; using FluentAssertions; +using HotChocolate.Subscriptions; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -72,6 +73,7 @@ public async Task InitializeAsync() .AddScoped(_ => CreateServiceOwnerNameRegistrySubstitute()) .AddScoped(_ => CreateNameRegistrySubstitute()) .AddScoped>(_ => CreateApplicationSettingsSubstitute()) + .AddScoped(_ => Substitute.For()) .AddScoped() .AddScoped() .AddSingleton() diff --git a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs index cdf81df36..bc8bc6589 100644 --- a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs +++ b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs @@ -33,7 +33,7 @@ public async Task FailIfGraphQlSchemaSnapshotDoesNotMatch() var builder = WebApplication.CreateBuilder([]); builder.Services .AddSingleton(new TelemetryClient(telemetryConfig)) - .AddDialogportenGraphQl(); + .AddDialogportenGraphQl(string.Empty); var app = builder.Build(); var requestExecutor = From f78e79ce5e3054c0ee94be5e89c98b39015eae4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 9 Sep 2024 13:56:33 +0200 Subject: [PATCH 2/5] fix --- docs/schema/V1/schema.verified.graphql | 6 +++++- .../EndUser/DialogById/ObjectTypes.cs | 5 +++++ .../EndUser/DialogById/Subscriptions.cs | 4 ++-- src/Digdir.Domain.Dialogporten.GraphQL/Program.cs | 12 +----------- .../ServiceCollectionExtensions.cs | 11 +---------- .../Schema/SchemaSnapshotTests.cs | 2 +- 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/docs/schema/V1/schema.verified.graphql b/docs/schema/V1/schema.verified.graphql index 5cf445aab..acfdb679e 100644 --- a/docs/schema/V1/schema.verified.graphql +++ b/docs/schema/V1/schema.verified.graphql @@ -130,6 +130,10 @@ type DialogByIdPayload { errors: [DialogByIdError!]! } +type DialogUpdatedPayload { + id: UUID! +} + type GuiAction { id: UUID! action: String! @@ -203,7 +207,7 @@ type SeenLog { } type Subscriptions @authorize(policy: "enduser") { - dialogUpdated(dialogId: UUID!): UUID! + dialogUpdated(dialogId: UUID!): DialogUpdatedPayload! } type Transmission { diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/ObjectTypes.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/ObjectTypes.cs index ec0417bbf..aba3d964d 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/ObjectTypes.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/ObjectTypes.cs @@ -198,3 +198,8 @@ public enum AttachmentUrlConsumer Gui = 1, Api = 2 } + +public sealed class DialogUpdatedPayload +{ + public Guid Id { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs index 85298d33a..631063e70 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/Subscriptions.cs @@ -11,10 +11,10 @@ public sealed class Subscriptions { [Subscribe] [Topic($"{Constants.DialogUpdatedTopic}{{{nameof(dialogId)}}}")] - public Guid DialogUpdated(Guid dialogId, + public DialogUpdatedPayload DialogUpdated(Guid dialogId, [EventMessage] Guid eventMessage) { ArgumentNullException.ThrowIfNull(eventMessage); - return dialogId; + return new DialogUpdatedPayload { Id = dialogId }; } } diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs index 59f246397..76b6387f5 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs @@ -78,19 +78,9 @@ static void BuildAndRun(string[] args) .AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true) .AddAzureAppConfiguration(); - var infrastructureConfigurationSection = - builder.Configuration.GetSection(InfrastructureSettings.ConfigurationSectionName); - - builder.Services.AddOptions() - .Bind(infrastructureConfigurationSection) - .ValidateFluently() - .ValidateOnStart(); - - var infrastructureSettings = infrastructureConfigurationSection.Get() ?? throw new InvalidOperationException("Infrastructure settings must not be null."); - // Graph QL builder.Services - .AddDialogportenGraphQl(infrastructureSettings.Redis.ConnectionString) + .AddDialogportenGraphQl() // Auth .AddDialogportenAuthentication(builder.Configuration) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs index 0bdfeecdb..299950c9d 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs @@ -2,24 +2,15 @@ using Digdir.Domain.Dialogporten.GraphQL.EndUser.DialogById; using Digdir.Domain.Dialogporten.GraphQL.EndUser.SearchDialogs; using Digdir.Domain.Dialogporten.Infrastructure.Persistence; -using HotChocolate.Subscriptions; -using StackExchange.Redis; -using Constants = Digdir.Domain.Dialogporten.Infrastructure.GraphQl.GraphQlSubscriptionConstants; namespace Digdir.Domain.Dialogporten.GraphQL; public static class ServiceCollectionExtensions { - public static IServiceCollection AddDialogportenGraphQl(this IServiceCollection services, - string redisConnectionString) + public static IServiceCollection AddDialogportenGraphQl(this IServiceCollection services) { return services .AddGraphQLServer() - .AddRedisSubscriptions(_ => ConnectionMultiplexer.Connect(redisConnectionString), - new SubscriptionOptions - { - TopicPrefix = Constants.SubscriptionTopicPrefix - }) .AddSubscriptionType() .AddAuthorization() .RegisterDbContext() diff --git a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs index bc8bc6589..cdf81df36 100644 --- a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs +++ b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Schema/SchemaSnapshotTests.cs @@ -33,7 +33,7 @@ public async Task FailIfGraphQlSchemaSnapshotDoesNotMatch() var builder = WebApplication.CreateBuilder([]); builder.Services .AddSingleton(new TelemetryClient(telemetryConfig)) - .AddDialogportenGraphQl(string.Empty); + .AddDialogportenGraphQl(); var app = builder.Build(); var requestExecutor = From db6e4133d72c8d867a67301abf9ff16b6de1adac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 9 Sep 2024 13:58:09 +0200 Subject: [PATCH 3/5] fx --- src/Digdir.Domain.Dialogporten.GraphQL/Program.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs index 76b6387f5..bc73a3cf4 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs @@ -76,10 +76,9 @@ static void BuildAndRun(string[] args) .AddApplicationInsightsTelemetry() .AddScoped() .AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true) - .AddAzureAppConfiguration(); + .AddAzureAppConfiguration() - // Graph QL - builder.Services + // Graph QL .AddDialogportenGraphQl() // Auth From c6a26ad8abafeba96322d9fc9f61c17e08f30d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 9 Sep 2024 14:03:57 +0200 Subject: [PATCH 4/5] what --- .../ServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs index 299950c9d..e7c0570e6 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/ServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ public static IServiceCollection AddDialogportenGraphQl(this IServiceCollection { return services .AddGraphQLServer() + // This assumes that subscriptions have been set up by the infrastructure .AddSubscriptionType() .AddAuthorization() .RegisterDbContext() From 61363533c444f4aa3eacb65e3131018fb450d682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 9 Sep 2024 14:27:44 +0200 Subject: [PATCH 5/5] todo --- ...DomainEventsToOutboxMessagesInterceptor.cs | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs index 3c41211f4..8bc8b7111 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/DomainEvents/Outbox/ConvertDomainEventsToOutboxMessagesInterceptor.cs @@ -4,6 +4,7 @@ using Digdir.Library.Entity.Abstractions.Features.EventPublisher; using HotChocolate.Subscriptions; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; using Constants = Digdir.Domain.Dialogporten.Infrastructure.GraphQl.GraphQlSubscriptionConstants; namespace Digdir.Domain.Dialogporten.Infrastructure.DomainEvents.Outbox; @@ -12,14 +13,18 @@ internal sealed class ConvertDomainEventsToOutboxMessagesInterceptor : SaveChang { private readonly ITransactionTime _transactionTime; private readonly ITopicEventSender _topicEventSender; + private readonly ILogger _logger; + private List _domainEvents = []; public ConvertDomainEventsToOutboxMessagesInterceptor( ITransactionTime transactionTime, - ITopicEventSender topicEventSender) + ITopicEventSender topicEventSender, + ILogger logger) { _transactionTime = transactionTime ?? throw new ArgumentNullException(nameof(transactionTime)); _topicEventSender = topicEventSender ?? throw new ArgumentNullException(nameof(topicEventSender)); + _logger = logger; } public override ValueTask> SavingChangesAsync( @@ -60,16 +65,24 @@ public override async ValueTask SavedChangesAsync(SaveChangesCompletedEvent { foreach (var domainEvent in _domainEvents) { - switch (domainEvent) + try { - case DialogUpdatedDomainEvent dialogUpdatedDomainEvent: - await _topicEventSender.SendAsync( + // If you are adding any additional cases to this switch, + // please consider making the running of the tasks parallel + var task = domainEvent switch + { + DialogUpdatedDomainEvent dialogUpdatedDomainEvent => _topicEventSender.SendAsync( $"{Constants.DialogUpdatedTopic}{dialogUpdatedDomainEvent.DialogId}", dialogUpdatedDomainEvent.DialogId, - cancellationToken); - break; - default: - break; + cancellationToken), + _ => ValueTask.CompletedTask, + }; + + await task; + } + catch (Exception e) + { + _logger.LogError(e, "Failed to send domain event to graphQL subscription"); } }