Skip to content

Commit

Permalink
sqwsh
Browse files Browse the repository at this point in the history
  • Loading branch information
oskogstad committed Sep 9, 2024
1 parent 0029f46 commit f514164
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 10 deletions.
5 changes: 5 additions & 0 deletions docs/schema/V1/schema.verified.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
schema {
query: Queries
subscription: Subscriptions
}

interface DialogByIdError {
Expand Down Expand Up @@ -201,6 +202,10 @@ type SeenLog {
isCurrentEndUser: Boolean!
}

type Subscriptions @authorize(policy: "enduser") {
dialogUpdated(dialogId: UUID!): UUID!
}

type Transmission {
id: UUID!
createdAt: DateTime!
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
17 changes: 14 additions & 3 deletions src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,21 @@ static void BuildAndRun(string[] args)
.AddApplicationInsightsTelemetry()
.AddScoped<IUser, ApplicationUser>()
.AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true)
.AddAzureAppConfiguration()
.AddAzureAppConfiguration();

// Graph QL
.AddDialogportenGraphQl()
var infrastructureConfigurationSection =
builder.Configuration.GetSection(InfrastructureSettings.ConfigurationSectionName);

builder.Services.AddOptions<InfrastructureSettings>()
.Bind(infrastructureConfigurationSection)
.ValidateFluently()
.ValidateOnStart();

var infrastructureSettings = infrastructureConfigurationSection.Get<InfrastructureSettings>() ?? throw new InvalidOperationException("Infrastructure settings must not be null.");

// Graph QL
builder.Services
.AddDialogportenGraphQl(infrastructureSettings.Redis.ConnectionString)

// Auth
.AddDialogportenAuthentication(builder.Configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Subscriptions>()
.AddAuthorization()
.RegisterDbContext<DialogDbContext>()
.AddDiagnosticEventListener<ApplicationInsightEventListener>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<ItemGroup>
<PackageReference Include="Altinn.ApiClients.Maskinporten" Version="9.1.0"/>
<PackageReference Include="HotChocolate.Subscriptions.Redis" Version="13.9.11" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.8" />
<PackageReference Include="Altinn.Authorization.ABAC" Version="0.0.8"/>
<PackageReference Include="Bogus" Version="35.6.0"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IDomainEvent> _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<InterceptionResult<int>> SavingChangesAsync(
Expand All @@ -26,24 +34,45 @@ public override ValueTask<InterceptionResult<int>> 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();

dbContext.Set<OutboxMessage>().AddRange(outboxMessages);

return base.SavingChangesAsync(eventData, result, cancellationToken);
}

public override async ValueTask<int> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using HotChocolate.Execution.Configuration;
using HotChocolate.Subscriptions;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;

namespace Digdir.Domain.Dialogporten.Infrastructure.GraphQl;

/// <summary>
/// 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.
/// </summary>
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -72,6 +73,7 @@ public async Task InitializeAsync()
.AddScoped<IServiceOwnerNameRegistry>(_ => CreateServiceOwnerNameRegistrySubstitute())
.AddScoped<IPartyNameRegistry>(_ => CreateNameRegistrySubstitute())
.AddScoped<IOptions<ApplicationSettings>>(_ => CreateApplicationSettingsSubstitute())
.AddScoped<ITopicEventSender>(_ => Substitute.For<ITopicEventSender>())
.AddScoped<IUnitOfWork, UnitOfWork>()
.AddScoped<IAltinnAuthorization, LocalDevelopmentAltinnAuthorization>()
.AddSingleton<ICloudEventBus, IntegrationTestCloudBus>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down

0 comments on commit f514164

Please sign in to comment.