From 66d12e540647715356da571ad464b6c1e378b056 Mon Sep 17 00:00:00 2001 From: Magnus Sandgren <5285192+MagnusSandgren@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:22:55 +0200 Subject: [PATCH 01/13] chore: Make ef aware of index and collation changes (#590) --- .../Dialogs/DialogEntityConfiguration.cs | 8 +- ...wareOfIndexAndCollationChanges.Designer.cs | 1214 +++++++++++++++++ ...2_MakeEfAwareOfIndexAndCollationChanges.cs | 133 ++ .../DialogDbContextModelSnapshot.cs | 15 +- 4 files changed, 1366 insertions(+), 4 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.Designer.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.cs diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/DialogEntityConfiguration.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/DialogEntityConfiguration.cs index 59181f3de..db456e3bd 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/DialogEntityConfiguration.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/DialogEntityConfiguration.cs @@ -15,5 +15,11 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(x => x.CreatedAt); builder.HasIndex(x => x.DueAt); builder.HasIndex(x => x.UpdatedAt); + builder.Property(x => x.Org).UseCollation("C"); + builder.Property(x => x.ServiceResource).UseCollation("C"); + builder.Property(x => x.Party).UseCollation("C"); + builder.HasIndex(x => x.Org); + builder.HasIndex(x => x.ServiceResource); + builder.HasIndex(x => x.Party); } -} \ No newline at end of file +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.Designer.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.Designer.cs new file mode 100644 index 000000000..071d9984b --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.Designer.cs @@ -0,0 +1,1214 @@ +// +using System; +using Digdir.Domain.Dialogporten.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(DialogDbContext))] + [Migration("20240404075422_MakeEfAwareOfIndexAndCollationChanges")] + partial class MakeEfAwareOfIndexAndCollationChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogApiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ActionId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deprecated") + .HasColumnType("boolean"); + + b.Property("DocumentationUrl") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("HttpMethodId") + .HasColumnType("integer"); + + b.Property("RequestSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("ResponseSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("SunsetAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("Version") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("ActionId"); + + b.HasIndex("HttpMethodId"); + + b.ToTable("DialogApiActionEndpoint"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("IsBackChannel") + .HasColumnType("boolean"); + + b.Property("IsDeleteAction") + .HasColumnType("boolean"); + + b.Property("PriorityId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("PriorityId"); + + b.ToTable("DialogGuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogGuiActionPriority"); + + b.HasData( + new + { + Id = 1, + Name = "Primary" + }, + new + { + Id = 2, + Name = "Secondary" + }, + new + { + Id = 3, + Name = "Tertiary" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExtendedType") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("RelatedActivityId") + .HasColumnType("uuid"); + + b.Property("SeenByEndUserId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedActivityId"); + + b.HasIndex("TypeId"); + + b.ToTable("DialogActivity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogActivityType"); + + b.HasData( + new + { + Id = 1, + Name = "Submission" + }, + new + { + Id = 2, + Name = "Feedback" + }, + new + { + Id = 3, + Name = "Information" + }, + new + { + Id = 4, + Name = "Error" + }, + new + { + Id = 5, + Name = "Closed" + }, + new + { + Id = 6, + Name = "Seen" + }, + new + { + Id = 7, + Name = "Forwarded" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("TypeId"); + + b.HasIndex("DialogId", "TypeId") + .IsUnique(); + + b.ToTable("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("MaxLength") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("OutputInList") + .HasColumnType("boolean"); + + b.Property("RenderAsHtml") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DialogContentType"); + + b.HasData( + new + { + Id = 1, + MaxLength = 255, + Name = "Title", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 2, + MaxLength = 255, + Name = "SenderName", + OutputInList = true, + RenderAsHtml = false, + Required = false + }, + new + { + Id = 3, + MaxLength = 255, + Name = "Summary", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 4, + MaxLength = 1023, + Name = "AdditionalInfo", + OutputInList = false, + RenderAsHtml = true, + Required = false + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deleted") + .HasColumnType("boolean"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtendedStatus") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Org") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("Party") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("Progress") + .HasColumnType("integer"); + + b.Property("Revision") + .IsConcurrencyToken() + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ServiceResource") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("StatusId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("VisibleFrom") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("DueAt"); + + b.HasIndex("Org"); + + b.HasIndex("Party"); + + b.HasIndex("ServiceResource"); + + b.HasIndex("StatusId"); + + b.HasIndex("UpdatedAt"); + + b.ToTable("Dialog", (string)null); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(63) + .HasColumnType("character varying(63)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId", "Value") + .IsUnique(); + + b.ToTable("DialogSearchTag"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogStatus"); + + b.HasData( + new + { + Id = 1, + Name = "New" + }, + new + { + Id = 2, + Name = "InProgress" + }, + new + { + Id = 3, + Name = "Waiting" + }, + new + { + Id = 4, + Name = "Signing" + }, + new + { + Id = 5, + Name = "Cancelled" + }, + new + { + Id = 6, + Name = "Completed" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RelatedDialogElementId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedDialogElementId"); + + b.ToTable("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConsumerTypeId") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("ConsumerTypeId"); + + b.HasIndex("DialogElementId"); + + b.ToTable("DialogElementUrl"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogElementUrlConsumerType"); + + b.HasData( + new + { + Id = 1, + Name = "Gui" + }, + new + { + Id = 2, + Name = "Api" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("HttpVerb"); + + b.HasData( + new + { + Id = 1, + Name = "GET" + }, + new + { + Id = 2, + Name = "POST" + }, + new + { + Id = 3, + Name = "PUT" + }, + new + { + Id = 4, + Name = "PATCH" + }, + new + { + Id = 5, + Name = "DELETE" + }, + new + { + Id = 6, + Name = "HEAD" + }, + new + { + Id = 7, + Name = "OPTIONS" + }, + new + { + Id = 8, + Name = "TRACE" + }, + new + { + Id = 9, + Name = "CONNECT" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.Property("LocalizationSetId") + .HasColumnType("uuid"); + + b.Property("CultureCode") + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4095) + .HasColumnType("character varying(4095)"); + + b.HasKey("LocalizationSetId", "CultureCode"); + + b.ToTable("Localization"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("LocalizationSet"); + + b.HasDiscriminator("Discriminator").HasValue("LocalizationSet"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventPayload") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId"); + + b.ToTable("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ConsumerName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId", "ConsumerName"); + + b.ToTable("OutboxMessageConsumer"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("GuiActionId") + .HasColumnType("uuid"); + + b.HasIndex("GuiActionId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogGuiActionTitle"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.ToTable("LocalizationSet", t => + { + t.Property("ActivityId") + .HasColumnName("DialogActivityDescription_ActivityId"); + }); + + b.HasDiscriminator().HasValue("DialogActivityDescription"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogActivityPerformedBy"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("DialogContentId") + .HasColumnType("uuid"); + + b.HasIndex("DialogContentId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogContentValue"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ElementId") + .HasColumnType("uuid"); + + b.HasIndex("ElementId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogElementDisplayName"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("ApiActions") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("ApiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", "Action") + .WithMany("Endpoints") + .HasForeignKey("ActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", "HttpMethod") + .WithMany() + .HasForeignKey("HttpMethodId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Action"); + + b.Navigation("HttpMethod"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("GuiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", "Priority") + .WithMany() + .HasForeignKey("PriorityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Priority"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Activities") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Activities") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "RelatedActivity") + .WithMany("RelatedActivities") + .HasForeignKey("RelatedActivityId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + + b.Navigation("RelatedActivity"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Content") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SearchTags") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Elements") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "RelatedDialogElement") + .WithMany("RelatedDialogElements") + .HasForeignKey("RelatedDialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Dialog"); + + b.Navigation("RelatedDialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", "ConsumerType") + .WithMany() + .HasForeignKey("ConsumerTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Urls") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ConsumerType"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", "LocalizationSet") + .WithMany("Localizations") + .HasForeignKey("LocalizationSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LocalizationSet"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", "OutboxMessage") + .WithMany("OutboxMessageConsumers") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction") + .WithOne("Title") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", "GuiActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("Description") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("PerformedBy") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", "DialogContent") + .WithOne("Value") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", "DialogContentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "Element") + .WithOne("DisplayName") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", "ElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Element"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Navigation("Endpoints"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Navigation("Title"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Navigation("Description"); + + b.Navigation("PerformedBy"); + + b.Navigation("RelatedActivities"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Navigation("Value") + .IsRequired(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("Content"); + + b.Navigation("Elements"); + + b.Navigation("GuiActions"); + + b.Navigation("SearchTags"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("DisplayName"); + + b.Navigation("RelatedDialogElements"); + + b.Navigation("Urls"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Navigation("Localizations"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Navigation("OutboxMessageConsumers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.cs new file mode 100644 index 000000000..85378db77 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404075422_MakeEfAwareOfIndexAndCollationChanges.cs @@ -0,0 +1,133 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + /// + public partial class MakeEfAwareOfIndexAndCollationChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Remove cowboy changes 🤠 + migrationBuilder.Sql( + """ + DROP INDEX IF EXISTS "IX_Dialog_Org"; + DROP INDEX IF EXISTS "IX_Dialog_ServiceResource"; + DROP INDEX IF EXISTS "IX_Dialog_Party"; + ALTER TABLE "Dialog" ALTER COLUMN "Org" TYPE character varying(255) USING "Org"::varchar; + ALTER TABLE "Dialog" ALTER COLUMN "ServiceResource" TYPE character varying(255) USING "ServiceResource"::varchar; + ALTER TABLE "Dialog" ALTER COLUMN "Party" TYPE character varying(255) USING "Party"::varchar; + """); + + migrationBuilder.AlterColumn( + name: "ServiceResource", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + collation: "C", + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255); + + migrationBuilder.AlterColumn( + name: "Party", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + collation: "C", + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255); + + migrationBuilder.AlterColumn( + name: "Org", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + collation: "C", + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255); + + migrationBuilder.CreateIndex( + name: "IX_Dialog_Org", + table: "Dialog", + column: "Org"); + + migrationBuilder.CreateIndex( + name: "IX_Dialog_Party", + table: "Dialog", + column: "Party"); + + migrationBuilder.CreateIndex( + name: "IX_Dialog_ServiceResource", + table: "Dialog", + column: "ServiceResource"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Dialog_Org", + table: "Dialog"); + + migrationBuilder.DropIndex( + name: "IX_Dialog_Party", + table: "Dialog"); + + migrationBuilder.DropIndex( + name: "IX_Dialog_ServiceResource", + table: "Dialog"); + + migrationBuilder.AlterColumn( + name: "ServiceResource", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255, + oldCollation: "C"); + + migrationBuilder.AlterColumn( + name: "Party", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255, + oldCollation: "C"); + + migrationBuilder.AlterColumn( + name: "Org", + table: "Dialog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(255)", + oldMaxLength: 255, + oldCollation: "C"); + + migrationBuilder.Sql( + """ + ALTER TABLE "Dialog" ALTER COLUMN "Org" TYPE character varying(255) COLLATE "C" USING "Org"::varchar; + ALTER TABLE "Dialog" ALTER COLUMN "ServiceResource" TYPE character varying(255) COLLATE "C" USING "ServiceResource"::varchar; + ALTER TABLE "Dialog" ALTER COLUMN "Party" TYPE character varying(255) COLLATE "C" USING "Party"::varchar; + + CREATE INDEX IF NOT EXISTS "IX_Dialog_ServiceResource" ON "Dialog" USING btree ("ServiceResource"); + CREATE INDEX IF NOT EXISTS "IX_Dialog_Party" ON "Dialog" USING btree ("Party"); + CREATE INDEX IF NOT EXISTS "IX_Dialog_Org" ON "Dialog" USING btree ("Org"); + """); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs index 8cf92c460..e4cda3af2 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs @@ -436,12 +436,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Org") .IsRequired() .HasMaxLength(255) - .HasColumnType("character varying(255)"); + .HasColumnType("character varying(255)") + .UseCollation("C"); b.Property("Party") .IsRequired() .HasMaxLength(255) - .HasColumnType("character varying(255)"); + .HasColumnType("character varying(255)") + .UseCollation("C"); b.Property("Progress") .HasColumnType("integer"); @@ -455,7 +457,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ServiceResource") .IsRequired() .HasMaxLength(255) - .HasColumnType("character varying(255)"); + .HasColumnType("character varying(255)") + .UseCollation("C"); b.Property("StatusId") .HasColumnType("integer"); @@ -474,6 +477,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DueAt"); + b.HasIndex("Org"); + + b.HasIndex("Party"); + + b.HasIndex("ServiceResource"); + b.HasIndex("StatusId"); b.HasIndex("UpdatedAt"); From a9f10b09ee58453766e1de46acd2166be48fd0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Thu, 4 Apr 2024 10:43:47 +0200 Subject: [PATCH 02/13] feat: Add ExtendedStatus content type (#589) #568 --- docs/swagger/V1/swagger.verified.json | 6 +- .../Entities/Content/DialogContentType.cs | 10 +- ...0_AddExtendedStatusContentType.Designer.cs | 1214 +++++++++++++++++ ...0404082820_AddExtendedStatusContentType.cs | 28 + .../DialogDbContextModelSnapshot.cs | 9 + 5 files changed, 1264 insertions(+), 3 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.Designer.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.cs diff --git a/docs/swagger/V1/swagger.verified.json b/docs/swagger/V1/swagger.verified.json index 4a4cfe054..c721b4c3b 100644 --- a/docs/swagger/V1/swagger.verified.json +++ b/docs/swagger/V1/swagger.verified.json @@ -2200,13 +2200,15 @@ "Title", "SenderName", "Summary", - "AdditionalInfo" + "AdditionalInfo", + "ExtendedStatus" ], "enum": [ "Title", "SenderName", "Summary", - "AdditionalInfo" + "AdditionalInfo", + "ExtendedStatus" ] }, "LocalizationDto": { diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Content/DialogContentType.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Content/DialogContentType.cs index 9e02a86e0..2bf12a710 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Content/DialogContentType.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Content/DialogContentType.cs @@ -11,7 +11,8 @@ public enum Values Title = 1, SenderName = 2, Summary = 3, - AdditionalInfo = 4 + AdditionalInfo = 4, + ExtendedStatus = 5 } public bool Required { get; private init; } @@ -49,6 +50,13 @@ public enum Values MaxLength = 1023, OutputInList = false }, + Values.ExtendedStatus => new(id) + { + Required = false, + RenderAsHtml = false, + MaxLength = 20, + OutputInList = true + }, _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) }; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.Designer.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.Designer.cs new file mode 100644 index 000000000..55eda4975 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.Designer.cs @@ -0,0 +1,1214 @@ +// +using System; +using Digdir.Domain.Dialogporten.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(DialogDbContext))] + [Migration("20240404082820_AddExtendedStatusContentType")] + partial class AddExtendedStatusContentType + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogApiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ActionId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deprecated") + .HasColumnType("boolean"); + + b.Property("DocumentationUrl") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("HttpMethodId") + .HasColumnType("integer"); + + b.Property("RequestSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("ResponseSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("SunsetAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("Version") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("ActionId"); + + b.HasIndex("HttpMethodId"); + + b.ToTable("DialogApiActionEndpoint"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("IsBackChannel") + .HasColumnType("boolean"); + + b.Property("IsDeleteAction") + .HasColumnType("boolean"); + + b.Property("PriorityId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("PriorityId"); + + b.ToTable("DialogGuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogGuiActionPriority"); + + b.HasData( + new + { + Id = 1, + Name = "Primary" + }, + new + { + Id = 2, + Name = "Secondary" + }, + new + { + Id = 3, + Name = "Tertiary" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExtendedType") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("RelatedActivityId") + .HasColumnType("uuid"); + + b.Property("SeenByEndUserId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedActivityId"); + + b.HasIndex("TypeId"); + + b.ToTable("DialogActivity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogActivityType"); + + b.HasData( + new + { + Id = 1, + Name = "Submission" + }, + new + { + Id = 2, + Name = "Feedback" + }, + new + { + Id = 3, + Name = "Information" + }, + new + { + Id = 4, + Name = "Error" + }, + new + { + Id = 5, + Name = "Closed" + }, + new + { + Id = 6, + Name = "Seen" + }, + new + { + Id = 7, + Name = "Forwarded" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("TypeId"); + + b.HasIndex("DialogId", "TypeId") + .IsUnique(); + + b.ToTable("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("MaxLength") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("OutputInList") + .HasColumnType("boolean"); + + b.Property("RenderAsHtml") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DialogContentType"); + + b.HasData( + new + { + Id = 1, + MaxLength = 255, + Name = "Title", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 2, + MaxLength = 255, + Name = "SenderName", + OutputInList = true, + RenderAsHtml = false, + Required = false + }, + new + { + Id = 3, + MaxLength = 255, + Name = "Summary", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 4, + MaxLength = 1023, + Name = "AdditionalInfo", + OutputInList = false, + RenderAsHtml = true, + Required = false + }, + new + { + Id = 5, + MaxLength = 20, + Name = "ExtendedStatus", + OutputInList = true, + RenderAsHtml = false, + Required = false + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deleted") + .HasColumnType("boolean"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtendedStatus") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Org") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Party") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Progress") + .HasColumnType("integer"); + + b.Property("Revision") + .IsConcurrencyToken() + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ServiceResource") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("StatusId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("VisibleFrom") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("DueAt"); + + b.HasIndex("StatusId"); + + b.HasIndex("UpdatedAt"); + + b.ToTable("Dialog", (string)null); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(63) + .HasColumnType("character varying(63)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId", "Value") + .IsUnique(); + + b.ToTable("DialogSearchTag"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogStatus"); + + b.HasData( + new + { + Id = 1, + Name = "New" + }, + new + { + Id = 2, + Name = "InProgress" + }, + new + { + Id = 3, + Name = "Waiting" + }, + new + { + Id = 4, + Name = "Signing" + }, + new + { + Id = 5, + Name = "Cancelled" + }, + new + { + Id = 6, + Name = "Completed" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RelatedDialogElementId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedDialogElementId"); + + b.ToTable("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConsumerTypeId") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("ConsumerTypeId"); + + b.HasIndex("DialogElementId"); + + b.ToTable("DialogElementUrl"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogElementUrlConsumerType"); + + b.HasData( + new + { + Id = 1, + Name = "Gui" + }, + new + { + Id = 2, + Name = "Api" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("HttpVerb"); + + b.HasData( + new + { + Id = 1, + Name = "GET" + }, + new + { + Id = 2, + Name = "POST" + }, + new + { + Id = 3, + Name = "PUT" + }, + new + { + Id = 4, + Name = "PATCH" + }, + new + { + Id = 5, + Name = "DELETE" + }, + new + { + Id = 6, + Name = "HEAD" + }, + new + { + Id = 7, + Name = "OPTIONS" + }, + new + { + Id = 8, + Name = "TRACE" + }, + new + { + Id = 9, + Name = "CONNECT" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.Property("LocalizationSetId") + .HasColumnType("uuid"); + + b.Property("CultureCode") + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4095) + .HasColumnType("character varying(4095)"); + + b.HasKey("LocalizationSetId", "CultureCode"); + + b.ToTable("Localization"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("LocalizationSet"); + + b.HasDiscriminator("Discriminator").HasValue("LocalizationSet"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventPayload") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId"); + + b.ToTable("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ConsumerName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId", "ConsumerName"); + + b.ToTable("OutboxMessageConsumer"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("GuiActionId") + .HasColumnType("uuid"); + + b.HasIndex("GuiActionId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogGuiActionTitle"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.ToTable("LocalizationSet", t => + { + t.Property("ActivityId") + .HasColumnName("DialogActivityDescription_ActivityId"); + }); + + b.HasDiscriminator().HasValue("DialogActivityDescription"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogActivityPerformedBy"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("DialogContentId") + .HasColumnType("uuid"); + + b.HasIndex("DialogContentId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogContentValue"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ElementId") + .HasColumnType("uuid"); + + b.HasIndex("ElementId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogElementDisplayName"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("ApiActions") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("ApiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", "Action") + .WithMany("Endpoints") + .HasForeignKey("ActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", "HttpMethod") + .WithMany() + .HasForeignKey("HttpMethodId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Action"); + + b.Navigation("HttpMethod"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("GuiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", "Priority") + .WithMany() + .HasForeignKey("PriorityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Priority"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Activities") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Activities") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "RelatedActivity") + .WithMany("RelatedActivities") + .HasForeignKey("RelatedActivityId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + + b.Navigation("RelatedActivity"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Content") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SearchTags") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Elements") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "RelatedDialogElement") + .WithMany("RelatedDialogElements") + .HasForeignKey("RelatedDialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Dialog"); + + b.Navigation("RelatedDialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", "ConsumerType") + .WithMany() + .HasForeignKey("ConsumerTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Urls") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ConsumerType"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", "LocalizationSet") + .WithMany("Localizations") + .HasForeignKey("LocalizationSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LocalizationSet"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", "OutboxMessage") + .WithMany("OutboxMessageConsumers") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction") + .WithOne("Title") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", "GuiActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("Description") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("PerformedBy") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", "DialogContent") + .WithOne("Value") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", "DialogContentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "Element") + .WithOne("DisplayName") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", "ElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Element"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Navigation("Endpoints"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Navigation("Title"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Navigation("Description"); + + b.Navigation("PerformedBy"); + + b.Navigation("RelatedActivities"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Navigation("Value") + .IsRequired(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("Content"); + + b.Navigation("Elements"); + + b.Navigation("GuiActions"); + + b.Navigation("SearchTags"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("DisplayName"); + + b.Navigation("RelatedDialogElements"); + + b.Navigation("Urls"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Navigation("Localizations"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Navigation("OutboxMessageConsumers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.cs new file mode 100644 index 000000000..b607267a9 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240404082820_AddExtendedStatusContentType.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddExtendedStatusContentType : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.InsertData( + table: "DialogContentType", + columns: new[] { "Id", "MaxLength", "Name", "OutputInList", "RenderAsHtml", "Required" }, + values: new object[] { 5, 20, "ExtendedStatus", true, false, false }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "DialogContentType", + keyColumn: "Id", + keyValue: 5); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs index e4cda3af2..c0e061e3e 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs @@ -398,6 +398,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) OutputInList = false, RenderAsHtml = true, Required = false + }, + new + { + Id = 5, + MaxLength = 20, + Name = "ExtendedStatus", + OutputInList = true, + RenderAsHtml = false, + Required = false }); }); From e66087086d0f2d175f47fac108bd9d311d11e238 Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Fri, 5 Apr 2024 09:53:51 +0100 Subject: [PATCH 03/13] chore(azure): test recreating migration job --- .../web-api-migration-job-aretest/main.bicep | 38 ++++++ .../staging.bicepparam | 9 ++ .../test.bicepparam | 9 ++ .github/workflows/action-deploy-apps.yml | 116 +++++++++--------- 4 files changed, 112 insertions(+), 60 deletions(-) create mode 100644 .azure/applications/web-api-migration-job-aretest/main.bicep create mode 100644 .azure/applications/web-api-migration-job-aretest/staging.bicepparam create mode 100644 .azure/applications/web-api-migration-job-aretest/test.bicepparam diff --git a/.azure/applications/web-api-migration-job-aretest/main.bicep b/.azure/applications/web-api-migration-job-aretest/main.bicep new file mode 100644 index 000000000..a2afca97f --- /dev/null +++ b/.azure/applications/web-api-migration-job-aretest/main.bicep @@ -0,0 +1,38 @@ +targetScope = 'resourceGroup' + +@minLength(3) +param imageTag string +@minLength(3) +param environment string +@minLength(3) +param location string + +@minLength(3) +@secure() +param containerAppEnvironmentName string +@minLength(3) +@secure() +param adoConnectionStringSecretUri string + +var namePrefix = 'dp-be-${environment}' +var baseImageUrl = 'ghcr.io/digdir/dialogporten-' + +var name = '${namePrefix}-db-migration-job-aretest' + +resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = { + name: containerAppEnvironmentName +} + +module migrationJob '../../modules/containerAppJob/main.bicep' = { + name: name + params: { + name: name + location: location + image: '${baseImageUrl}migration-bundle:${imageTag}' + containerAppEnvId: containerAppEnvironment.id + adoConnectionStringSecretUri: adoConnectionStringSecretUri + } +} + +output identityPrincipalId string = migrationJob.outputs.identityPrincipalId +output name string = migrationJob.outputs.name diff --git a/.azure/applications/web-api-migration-job-aretest/staging.bicepparam b/.azure/applications/web-api-migration-job-aretest/staging.bicepparam new file mode 100644 index 000000000..99220851c --- /dev/null +++ b/.azure/applications/web-api-migration-job-aretest/staging.bicepparam @@ -0,0 +1,9 @@ +using './main.bicep' + +param environment = 'staging' +param location = 'norwayeast' +param imageTag = readEnvironmentVariable('IMAGE_TAG') + +//secrets +param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME') +param adoConnectionStringSecretUri = readEnvironmentVariable('ADO_CONNECTION_STRING_SECRET_URI') diff --git a/.azure/applications/web-api-migration-job-aretest/test.bicepparam b/.azure/applications/web-api-migration-job-aretest/test.bicepparam new file mode 100644 index 000000000..2f5ac2de3 --- /dev/null +++ b/.azure/applications/web-api-migration-job-aretest/test.bicepparam @@ -0,0 +1,9 @@ +using './main.bicep' + +param environment = 'test' +param location = 'norwayeast' +param imageTag = readEnvironmentVariable('IMAGE_TAG') + +//secrets +param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME') +param adoConnectionStringSecretUri = readEnvironmentVariable('ADO_CONNECTION_STRING_SECRET_URI') diff --git a/.github/workflows/action-deploy-apps.yml b/.github/workflows/action-deploy-apps.yml index cb43c11e6..5697c32f2 100644 --- a/.github/workflows/action-deploy-apps.yml +++ b/.github/workflows/action-deploy-apps.yml @@ -46,75 +46,71 @@ concurrency: # if the dryrun input is true, we want to cancel any running deployments in order to not block the pipeline e.g for environment approvals cancel-in-progress: ${{ inputs.dryRun }} jobs: - # deploy-migration-job: - # name: Deploy migration job to ${{ inputs.environment }} - # runs-on: ubuntu-latest - # # todo: Enabled for staging only. Remove once working in the test environment - # if: ${{ inputs.environment == 'staging' }} - # environment: ${{inputs.environment}} - # permissions: - # id-token: write - # contents: read - # steps: - # - name: "Checkout GitHub Action" - # uses: actions/checkout@v4 + deploy-migration-job: + name: Deploy migration job to ${{ inputs.environment }} + runs-on: ubuntu-latest + environment: ${{inputs.environment}} + permissions: + id-token: write + contents: read + steps: + - name: "Checkout GitHub Action" + uses: actions/checkout@v4 - # - name: OIDC Login to Azure Public Cloud - # uses: azure/login@v1 - # with: - # client-id: ${{ secrets.AZURE_CLIENT_ID }} - # tenant-id: ${{ secrets.AZURE_TENANT_ID }} - # subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: OIDC Login to Azure Public Cloud + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - # - name: Deploy migration job (${{ inputs.environment }}) - # uses: azure/arm-deploy@v1 - # id: deploy - # env: - # # parameters - # IMAGE_TAG: ${{ inputs.gitShortSha }} - # # secrets - # ADO_CONNECTION_STRING_SECRET_URI: ${{ secrets.AZURE_ADO_CONNECTION_STRING_SECRET_URI }} - # CONTAINER_APP_ENVIRONMENT_NAME: ${{ secrets.AZURE_CONTAINER_APP_ENVIRONMENT_NAME }} - # with: - # scope: resourcegroup - # template: ./.azure/applications/web-api-migration-job/main.bicep - # resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - # deploymentMode: Incremental - # deploymentName: "dp-be-${{ inputs.environment }}-web-api-migration-job-${{ inputs.gitShortSha }}" - # region: ${{ inputs.region }} - # failOnStdErr: false - # additionalArguments: "${{inputs.dryRun && '--what-if'}}" - # parameters: ./.azure/applications/web-api-migration-job/${{ inputs.environment }}.bicepparam + - name: Deploy migration job (${{ inputs.environment }}) + uses: azure/arm-deploy@v1 + id: deploy + env: + # parameters + IMAGE_TAG: ${{ inputs.version }} + # secrets + ADO_CONNECTION_STRING_SECRET_URI: ${{ secrets.AZURE_ADO_CONNECTION_STRING_SECRET_URI }} + CONTAINER_APP_ENVIRONMENT_NAME: ${{ secrets.AZURE_CONTAINER_APP_ENVIRONMENT_NAME }} + with: + scope: resourcegroup + template: ./.azure/applications/web-api-migration-job-aretest/main.bicep + resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + deploymentMode: Incremental + deploymentName: "dp-be-${{ inputs.environment }}-web-api-migration-job-aretest-${{ inputs.version }}" + region: ${{ inputs.region }} + failOnStdErr: false + additionalArguments: "${{inputs.dryRun && '--what-if'}}" + parameters: ./.azure/applications/web-api-migration-job-aretest/${{ inputs.environment }}.bicepparam - # - name: Start migration job - # uses: azure/CLI@v1 - # if: ${{!inputs.dryRun}} - # with: - # azcliversion: 2.56.0 - # inlineScript: | - # az containerapp job start -n ${{ steps.deploy.outputs.name }} -g ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + - name: Start migration job + uses: azure/CLI@v1 + if: ${{!inputs.dryRun}} + with: + azcliversion: 2.56.0 + inlineScript: | + az containerapp job start -n ${{ steps.deploy.outputs.name }} -g ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - # - name: Verify migration - # uses: azure/CLI@v1 - # if: ${{!inputs.dryRun}} - # id: verify-migration - # timeout-minutes: 3 - # with: - # azcliversion: ${{ env.AZ_CLI_VERSION }} - # inlineScript: | - # ./.github/tools/containerAppJobVerifier.sh ${{ steps.deploy.outputs.name }} ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} ${{ inputs.gitShortSha }} + - name: Verify migration + uses: azure/CLI@v1 + if: ${{!inputs.dryRun}} + id: verify-migration + timeout-minutes: 3 + with: + azcliversion: ${{ env.AZ_CLI_VERSION }} + inlineScript: | + ./.github/tools/containerAppJobVerifier.sh ${{ steps.deploy.outputs.name }} ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} ${{ inputs.version }} - # - name: Logout from azure - # if: ${{failure() || success()}} - # continue-on-error: true - # run: az logout + - name: Logout from azure + if: ${{failure() || success()}} + continue-on-error: true + run: az logout deploy-apps: name: Deploy ${{ matrix.name }} to ${{ inputs.environment }} runs-on: ubuntu-latest - # todo: remove once migration job is working in the test environment - # if: ${{ always() }} - # needs: deploy-migration-job + needs: deploy-migration-job strategy: fail-fast: true matrix: From a04693fd04126a1913f1e217f03008b438098fbb Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Fri, 5 Apr 2024 10:14:50 +0100 Subject: [PATCH 04/13] Revert "chore(azure): test recreating migration job" This reverts commit e66087086d0f2d175f47fac108bd9d311d11e238. --- .../web-api-migration-job-aretest/main.bicep | 38 ------ .../staging.bicepparam | 9 -- .../test.bicepparam | 9 -- .github/workflows/action-deploy-apps.yml | 116 +++++++++--------- 4 files changed, 60 insertions(+), 112 deletions(-) delete mode 100644 .azure/applications/web-api-migration-job-aretest/main.bicep delete mode 100644 .azure/applications/web-api-migration-job-aretest/staging.bicepparam delete mode 100644 .azure/applications/web-api-migration-job-aretest/test.bicepparam diff --git a/.azure/applications/web-api-migration-job-aretest/main.bicep b/.azure/applications/web-api-migration-job-aretest/main.bicep deleted file mode 100644 index a2afca97f..000000000 --- a/.azure/applications/web-api-migration-job-aretest/main.bicep +++ /dev/null @@ -1,38 +0,0 @@ -targetScope = 'resourceGroup' - -@minLength(3) -param imageTag string -@minLength(3) -param environment string -@minLength(3) -param location string - -@minLength(3) -@secure() -param containerAppEnvironmentName string -@minLength(3) -@secure() -param adoConnectionStringSecretUri string - -var namePrefix = 'dp-be-${environment}' -var baseImageUrl = 'ghcr.io/digdir/dialogporten-' - -var name = '${namePrefix}-db-migration-job-aretest' - -resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = { - name: containerAppEnvironmentName -} - -module migrationJob '../../modules/containerAppJob/main.bicep' = { - name: name - params: { - name: name - location: location - image: '${baseImageUrl}migration-bundle:${imageTag}' - containerAppEnvId: containerAppEnvironment.id - adoConnectionStringSecretUri: adoConnectionStringSecretUri - } -} - -output identityPrincipalId string = migrationJob.outputs.identityPrincipalId -output name string = migrationJob.outputs.name diff --git a/.azure/applications/web-api-migration-job-aretest/staging.bicepparam b/.azure/applications/web-api-migration-job-aretest/staging.bicepparam deleted file mode 100644 index 99220851c..000000000 --- a/.azure/applications/web-api-migration-job-aretest/staging.bicepparam +++ /dev/null @@ -1,9 +0,0 @@ -using './main.bicep' - -param environment = 'staging' -param location = 'norwayeast' -param imageTag = readEnvironmentVariable('IMAGE_TAG') - -//secrets -param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME') -param adoConnectionStringSecretUri = readEnvironmentVariable('ADO_CONNECTION_STRING_SECRET_URI') diff --git a/.azure/applications/web-api-migration-job-aretest/test.bicepparam b/.azure/applications/web-api-migration-job-aretest/test.bicepparam deleted file mode 100644 index 2f5ac2de3..000000000 --- a/.azure/applications/web-api-migration-job-aretest/test.bicepparam +++ /dev/null @@ -1,9 +0,0 @@ -using './main.bicep' - -param environment = 'test' -param location = 'norwayeast' -param imageTag = readEnvironmentVariable('IMAGE_TAG') - -//secrets -param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME') -param adoConnectionStringSecretUri = readEnvironmentVariable('ADO_CONNECTION_STRING_SECRET_URI') diff --git a/.github/workflows/action-deploy-apps.yml b/.github/workflows/action-deploy-apps.yml index 5697c32f2..cb43c11e6 100644 --- a/.github/workflows/action-deploy-apps.yml +++ b/.github/workflows/action-deploy-apps.yml @@ -46,71 +46,75 @@ concurrency: # if the dryrun input is true, we want to cancel any running deployments in order to not block the pipeline e.g for environment approvals cancel-in-progress: ${{ inputs.dryRun }} jobs: - deploy-migration-job: - name: Deploy migration job to ${{ inputs.environment }} - runs-on: ubuntu-latest - environment: ${{inputs.environment}} - permissions: - id-token: write - contents: read - steps: - - name: "Checkout GitHub Action" - uses: actions/checkout@v4 + # deploy-migration-job: + # name: Deploy migration job to ${{ inputs.environment }} + # runs-on: ubuntu-latest + # # todo: Enabled for staging only. Remove once working in the test environment + # if: ${{ inputs.environment == 'staging' }} + # environment: ${{inputs.environment}} + # permissions: + # id-token: write + # contents: read + # steps: + # - name: "Checkout GitHub Action" + # uses: actions/checkout@v4 - - name: OIDC Login to Azure Public Cloud - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + # - name: OIDC Login to Azure Public Cloud + # uses: azure/login@v1 + # with: + # client-id: ${{ secrets.AZURE_CLIENT_ID }} + # tenant-id: ${{ secrets.AZURE_TENANT_ID }} + # subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Deploy migration job (${{ inputs.environment }}) - uses: azure/arm-deploy@v1 - id: deploy - env: - # parameters - IMAGE_TAG: ${{ inputs.version }} - # secrets - ADO_CONNECTION_STRING_SECRET_URI: ${{ secrets.AZURE_ADO_CONNECTION_STRING_SECRET_URI }} - CONTAINER_APP_ENVIRONMENT_NAME: ${{ secrets.AZURE_CONTAINER_APP_ENVIRONMENT_NAME }} - with: - scope: resourcegroup - template: ./.azure/applications/web-api-migration-job-aretest/main.bicep - resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - deploymentMode: Incremental - deploymentName: "dp-be-${{ inputs.environment }}-web-api-migration-job-aretest-${{ inputs.version }}" - region: ${{ inputs.region }} - failOnStdErr: false - additionalArguments: "${{inputs.dryRun && '--what-if'}}" - parameters: ./.azure/applications/web-api-migration-job-aretest/${{ inputs.environment }}.bicepparam + # - name: Deploy migration job (${{ inputs.environment }}) + # uses: azure/arm-deploy@v1 + # id: deploy + # env: + # # parameters + # IMAGE_TAG: ${{ inputs.gitShortSha }} + # # secrets + # ADO_CONNECTION_STRING_SECRET_URI: ${{ secrets.AZURE_ADO_CONNECTION_STRING_SECRET_URI }} + # CONTAINER_APP_ENVIRONMENT_NAME: ${{ secrets.AZURE_CONTAINER_APP_ENVIRONMENT_NAME }} + # with: + # scope: resourcegroup + # template: ./.azure/applications/web-api-migration-job/main.bicep + # resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + # deploymentMode: Incremental + # deploymentName: "dp-be-${{ inputs.environment }}-web-api-migration-job-${{ inputs.gitShortSha }}" + # region: ${{ inputs.region }} + # failOnStdErr: false + # additionalArguments: "${{inputs.dryRun && '--what-if'}}" + # parameters: ./.azure/applications/web-api-migration-job/${{ inputs.environment }}.bicepparam - - name: Start migration job - uses: azure/CLI@v1 - if: ${{!inputs.dryRun}} - with: - azcliversion: 2.56.0 - inlineScript: | - az containerapp job start -n ${{ steps.deploy.outputs.name }} -g ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} + # - name: Start migration job + # uses: azure/CLI@v1 + # if: ${{!inputs.dryRun}} + # with: + # azcliversion: 2.56.0 + # inlineScript: | + # az containerapp job start -n ${{ steps.deploy.outputs.name }} -g ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} - - name: Verify migration - uses: azure/CLI@v1 - if: ${{!inputs.dryRun}} - id: verify-migration - timeout-minutes: 3 - with: - azcliversion: ${{ env.AZ_CLI_VERSION }} - inlineScript: | - ./.github/tools/containerAppJobVerifier.sh ${{ steps.deploy.outputs.name }} ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} ${{ inputs.version }} + # - name: Verify migration + # uses: azure/CLI@v1 + # if: ${{!inputs.dryRun}} + # id: verify-migration + # timeout-minutes: 3 + # with: + # azcliversion: ${{ env.AZ_CLI_VERSION }} + # inlineScript: | + # ./.github/tools/containerAppJobVerifier.sh ${{ steps.deploy.outputs.name }} ${{ secrets.AZURE_RESOURCE_GROUP_NAME }} ${{ inputs.gitShortSha }} - - name: Logout from azure - if: ${{failure() || success()}} - continue-on-error: true - run: az logout + # - name: Logout from azure + # if: ${{failure() || success()}} + # continue-on-error: true + # run: az logout deploy-apps: name: Deploy ${{ matrix.name }} to ${{ inputs.environment }} runs-on: ubuntu-latest - needs: deploy-migration-job + # todo: remove once migration job is working in the test environment + # if: ${{ always() }} + # needs: deploy-migration-job strategy: fail-fast: true matrix: From 71b77d25ca464e4194712e45a19d16d30d17e4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 5 Apr 2024 15:47:18 +0200 Subject: [PATCH 05/13] feat: Split SeenLog from activities (#598) Co-authored-by: Magnus Sandgren <5285192+MagnusSandgren@users.noreply.github.com> --- .../Common/MappingUtils.cs | 12 +- .../Externals/IDialogDbContext.cs | 1 + .../V1/Common/Events/CloudEventTypes.cs | 1 - .../Queries/Get/GetDialogActivityDto.cs | 1 - .../Queries/Get/GetDialogActivityQuery.cs | 6 - .../Queries/Get/MappingProfile.cs | 1 - .../Queries/Search/MappingProfile.cs | 1 - .../Search/SearchDialogActivityQuery.cs | 7 - .../Dialogs/Queries/Get/GetDialogDto.cs | 14 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 31 +- .../Dialogs/Queries/Get/MappingProfile.cs | 6 +- .../Dialogs/Queries/Search/MappingProfile.cs | 18 +- .../Dialogs/Queries/Search/SearchDialogDto.cs | 18 +- .../Queries/Search/SearchDialogQuery.cs | 66 +- .../Entities/Activities/DialogActivity.cs | 1 - .../Entities/Activities/DialogActivityType.cs | 14 +- .../Dialogs/Entities/DialogEntity.cs | 35 +- .../Dialogs/Entities/DialogSeenRecord.cs | 28 + .../Persistence/DialogDbContext.cs | 4 + ...ogFromActivityToSeparateEntity.Designer.cs | 1287 +++++++++++++++++ ...plitSeenLogFromActivityToSeparateEntity.cs | 106 ++ .../DialogDbContextModelSnapshot.cs | 120 +- 22 files changed, 1611 insertions(+), 167 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenRecord.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.Designer.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs b/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs index effa6a705..2e53a9b4d 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs @@ -7,13 +7,8 @@ internal static class MappingUtils { internal static byte[] GetHashSalt(int size = 16) => RandomNumberGenerator.GetBytes(size); - internal static string? HashPid(string? personIdentifier, byte[] salt) + internal static string HashPid(string personIdentifier, byte[] salt) { - if (string.IsNullOrWhiteSpace(personIdentifier)) - { - return null; - } - var identifierBytes = Encoding.UTF8.GetBytes(personIdentifier); Span buffer = stackalloc byte[identifierBytes.Length + salt.Length]; identifierBytes.CopyTo(buffer); @@ -21,6 +16,9 @@ internal static class MappingUtils var hashBytes = SHA256.HashData(buffer); - return BitConverter.ToString(hashBytes, 0, 5).Replace("-", "").ToLowerInvariant(); + return BitConverter + .ToString(hashBytes, 0, 5) + .Replace("-", "") + .ToLowerInvariant(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Externals/IDialogDbContext.cs b/src/Digdir.Domain.Dialogporten.Application/Externals/IDialogDbContext.cs index fd6cbb3eb..daa937047 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Externals/IDialogDbContext.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Externals/IDialogDbContext.cs @@ -24,6 +24,7 @@ public interface IDialogDbContext DbSet OutboxMessages { get; } DbSet OutboxMessageConsumers { get; } + DbSet DialogSeenLog { get; } /// /// Validate a property on the using a lambda diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs index 452b14a5e..d2830a857 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs @@ -25,7 +25,6 @@ internal static class CloudEventTypes nameof(DialogActivityType.Values.Information) => "dialogporten.dialog.activity.information.v1", nameof(DialogActivityType.Values.Error) => "dialogporten.dialog.activity.error.v1", nameof(DialogActivityType.Values.Closed) => "dialogporten.dialog.activity.closed.v1", - nameof(DialogActivityType.Values.Seen) => "dialogporten.dialog.activity.seen.v1", nameof(DialogActivityType.Values.Forwarded) => "dialogporten.dialog.activity.forwarded.v1", _ => throw new ArgumentOutOfRangeException(nameof(eventName), eventName, null) diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityDto.cs index 11fd505e4..bb757113f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityDto.cs @@ -8,7 +8,6 @@ public sealed class GetDialogActivityDto public Guid Id { get; set; } public DateTimeOffset? CreatedAt { get; set; } public Uri? ExtendedType { get; set; } - public string? SeenByEndUserIdHash { get; init; } public DialogActivityType.Values Type { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs index d2982a015..bd6545728 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs @@ -72,12 +72,6 @@ public async Task Handle(GetDialogActivityQuery request return new EntityNotFound(request.ActivityId); } - // Hash end user id - if (activity.SeenByEndUserId is not null) - { - activity.SeenByEndUserId = MappingUtils.HashPid(activity.SeenByEndUserId, MappingUtils.GetHashSalt()); - } - return _mapper.Map(activity); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs index d4116efbf..0d686fade 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs @@ -8,7 +8,6 @@ internal sealed class MappingProfile : Profile public MappingProfile() { CreateMap() - .ForMember(dest => dest.SeenByEndUserIdHash, opt => opt.MapFrom(src => src.SeenByEndUserId)) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/MappingProfile.cs index 1040028e5..b363412f8 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/MappingProfile.cs @@ -8,7 +8,6 @@ internal sealed class MappingProfile : Profile public MappingProfile() { CreateMap() - .ForMember(dest => dest.SeenByEndUserIdHash, opt => opt.MapFrom(src => src.SeenByEndUserId)) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs index ea2bd4c37..a46226480 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs @@ -62,13 +62,6 @@ public async Task Handle(SearchDialogActivityQuery r return new EntityDeleted(request.DialogId); } - // hash end user ids - var salt = MappingUtils.GetHashSalt(); - foreach (var activity in dialog.Activities) - { - activity.SeenByEndUserId = MappingUtils.HashPid(activity.SeenByEndUserId, salt); - } - return _mapper.Map>(dialog.Activities); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs index 71ab808ad..3d7a04d56 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs @@ -33,6 +33,19 @@ public sealed class GetDialogDto public List GuiActions { get; set; } = []; public List ApiActions { get; set; } = []; public List Activities { get; set; } = []; + public List SeenLog { get; set; } = []; +} + +public class GetDialogDialogSeenRecordDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + + public string EndUserIdHash { get; set; } = null!; + + public string? EndUserName { get; set; } + + public bool IsAuthenticatedUser { get; set; } } public sealed class GetDialogContentDto @@ -46,7 +59,6 @@ public sealed class GetDialogDialogActivityDto public Guid Id { get; set; } public DateTimeOffset? CreatedAt { get; set; } public Uri? ExtendedType { get; set; } - public string? SeenByEndUserIdHash { get; init; } public DialogActivityType.Values Type { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs index 895c47b46..906019d20 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs @@ -73,6 +73,9 @@ public async Task Handle(GetDialogQuery request, CancellationTo .ThenInclude(x => x.Endpoints.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) .Include(x => x.Activities).ThenInclude(x => x.PerformedBy!.Localizations) .Include(x => x.Activities).ThenInclude(x => x.Description!.Localizations) + .Include(x => x.SeenLog + .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) + .OrderBy(x => x.CreatedAt)) .Where(x => !x.VisibleFrom.HasValue || x.VisibleFrom < _clock.UtcNowOffset) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken); @@ -110,25 +113,29 @@ public async Task Handle(GetDialogQuery request, CancellationTo domainError => throw new UnreachableException("Should not get domain error when updating SeenAt."), concurrencyError => throw new UnreachableException("Should not get concurrencyError when updating SeenAt.")); - // hash end user ids - var salt = MappingUtils.GetHashSalt(); - foreach (var activity in dialog.Activities) - { - activity.SeenByEndUserId = MappingUtils.HashPid(activity.SeenByEndUserId, salt); - } + var dialogDto = _mapper.Map(dialog); - var dto = _mapper.Map(dialog); - - dto.DialogToken = _dialogTokenGenerator.GetDialogToken( + var salt = MappingUtils.GetHashSalt(); + dialogDto.SeenLog = dialog.SeenLog + .Select(log => + { + var logDto = _mapper.Map(log); + logDto.IsAuthenticatedUser = log.EndUserId == userPid; + logDto.EndUserIdHash = MappingUtils.HashPid(log.EndUserId, salt); + return logDto; + }) + .ToList(); + + dialogDto.DialogToken = _dialogTokenGenerator.GetDialogToken( dialog, authorizationResult, "api/v1" ); - DecorateWithAuthorization(dto, authorizationResult); - ReplaceUnauthorizedUrls(dto); + DecorateWithAuthorization(dialogDto, authorizationResult); + ReplaceUnauthorizedUrls(dialogDto); - return dto; + return dialogDto; } private static void DecorateWithAuthorization(GetDialogDto dto, diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs index 4b72eecd3..c3ca7b3b4 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs @@ -13,10 +13,12 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.Revision, opt => opt.MapFrom(src => src.Revision)) - .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)); + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)) + .ForMember(dest => dest.SeenLog, opt => opt.Ignore()); + + CreateMap(); CreateMap() - .ForMember(dest => dest.SeenByEndUserIdHash, opt => opt.MapFrom(src => src.SeenByEndUserId)) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); CreateMap(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs index 3b842eb66..a4cc6015b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs @@ -11,9 +11,17 @@ internal sealed class MappingProfile : Profile public MappingProfile() { CreateMap() - .ForMember(dest => dest.LatestActivities, opt => opt.Ignore()) - .ForMember(dest => dest.GuiAttachmentCount, opt => - opt.MapFrom(src => src.Elements.Count(x => x.Urls + .ForMember(dest => dest.LatestActivity, opt => opt.MapFrom(src => src.Activities + .Where(activity => activity.TypeId != DialogActivityType.Values.Forwarded) + .OrderByDescending(activity => activity.CreatedAt).ThenByDescending(activity => activity.Id) + .FirstOrDefault() + )) + .ForMember(dest => dest.SeenLog, opt => opt.MapFrom(src => src.SeenLog + .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) + .OrderByDescending(x => x.CreatedAt) + )) + .ForMember(dest => dest.GuiAttachmentCount, opt => opt.MapFrom(src => src.Elements + .Count(x => x.Urls .Any(url => url.ConsumerTypeId == DialogElementUrlConsumerType.Values.Gui)))) .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.Content.Where(x => x.Type.OutputInList))) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)); @@ -21,8 +29,10 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + CreateMap() + .ForMember(dest => dest.EndUserIdHash, opt => opt.MapFrom(src => src.EndUserId)); + CreateMap() - .ForMember(dest => dest.SeenByEndUserIdHash, opt => opt.MapFrom(src => src.SeenByEndUserId)) .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs index 2f4ce1bc6..0e62cf7a8 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs @@ -20,8 +20,22 @@ public sealed class SearchDialogDto public DialogStatus.Values Status { get; set; } + public SearchDialogDialogActivityDto? LatestActivity { get; set; } + public List Content { get; set; } = []; - public List LatestActivities { get; set; } = []; + public List SeenLog { get; set; } = []; +} + +public class SearchDialogDialogSeenRecordDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + + public string EndUserIdHash { get; set; } = null!; + + public string? EndUserName { get; set; } + + public bool IsAuthenticatedUser { get; set; } } public sealed class SearchDialogContentDto @@ -35,8 +49,6 @@ public sealed class SearchDialogDialogActivityDto public Guid Id { get; set; } public DateTimeOffset? CreatedAt { get; set; } public Uri? ExtendedType { get; set; } - public string? SeenByEndUserIdHash { get; set; } - public bool? SeenActivityIsCurrentEndUser { get; set; } public DialogActivityType.Values Type { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs index 8de373359..a77d9b12a 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs @@ -175,66 +175,16 @@ public async Task Handle(SearchDialogQuery request, Cancella .ProjectTo(_mapper.ConfigurationProvider) .ToPaginatedListAsync(request, cancellationToken: cancellationToken); - await FetchRelevantActivities(paginatedList, userPid, cancellationToken); - - return paginatedList; - } - - private async Task FetchRelevantActivities(PaginatedList paginatedList, string userPid, CancellationToken cancellationToken) - { - var dialogIds = paginatedList.Items - .Select(x => x.Id) - .ToList(); - - var latestActivityByDialogIdTask = await _db.DialogActivities - .AsNoTracking() - .Include(x => x.Description!.Localizations) - .Include(x => x.PerformedBy!.Localizations) - .Where(x => - dialogIds.Contains(x.DialogId) - && x.TypeId != DialogActivityType.Values.Forwarded - && x.TypeId != DialogActivityType.Values.Seen) - .GroupBy(x => x.DialogId) - .ToDictionaryAsync( - x => x.Key, - x => x.OrderByDescending(x => x.CreatedAt) - .ThenBy(x => x.Id) - .First(), - cancellationToken); - - var latestSeenActivityByDialogIdTask = await _db.DialogActivities - .AsNoTracking() - .Include(x => x.Description!.Localizations) - .Include(x => x.PerformedBy!.Localizations) - .Where(x => - dialogIds.Contains(x.DialogId) - && x.TypeId == DialogActivityType.Values.Seen - && x.CreatedAt > x.Dialog.UpdatedAt) - .GroupBy(x => x.DialogId) - .ToDictionaryAsync(x => x.Key, x => x.ToList(), cancellationToken); - var salt = MappingUtils.GetHashSalt(); - foreach (var dialog in paginatedList.Items) + foreach (var seenLog in paginatedList.Items.SelectMany(x => x.SeenLog)) { - var activities = latestSeenActivityByDialogIdTask.TryGetValue(dialog.Id, out var seenActivities) - ? seenActivities - : []; - - if (latestActivityByDialogIdTask.TryGetValue(dialog.Id, out var latestNonSeenActivity)) - { - activities.Add(latestNonSeenActivity); - } - - dialog.LatestActivities = _mapper.Map>(activities); - - foreach (var activity in dialog.LatestActivities - .Where(x => !string.IsNullOrWhiteSpace(x.SeenByEndUserIdHash))) - { - // Before we hash the end user id, check if the seen activity is for the current user - activity.SeenActivityIsCurrentEndUser = userPid == activity.SeenByEndUserIdHash; - // Hash end user ids - activity.SeenByEndUserIdHash = MappingUtils.HashPid(activity.SeenByEndUserIdHash, salt); - } + // Before we hash the end user id, check if the seen log entry is for the current user + seenLog.IsAuthenticatedUser = userPid == seenLog.EndUserIdHash; + // TODO: Add test to not expose unhashed end user id to the client + // https://github.com/digdir/dialogporten/issues/596 + seenLog.EndUserIdHash = MappingUtils.HashPid(seenLog.EndUserIdHash, salt); } + + return paginatedList; } } diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs index 21dd90b00..0aff54e29 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs @@ -12,7 +12,6 @@ public class DialogActivity : IImmutableEntity, IAggregateCreatedHandler, IEvent public Guid Id { get; set; } public DateTimeOffset CreatedAt { get; set; } public Uri? ExtendedType { get; set; } - public string? SeenByEndUserId { get; set; } // === Dependent relationships === public DialogActivityType.Values TypeId { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs index 10454913c..90ced2afd 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs @@ -15,18 +15,18 @@ public enum Values Submission = 1, /// - /// Indikerer en tilbakemelding fra tjenestetilbyder på en innsending. Inneholder + /// Indikerer en tilbakemelding fra tjenestetilbyder på en innsending. Inneholder /// referanse til den aktuelle innsendingen. /// Feedback = 2, /// - /// Informasjon fra tjenestetilbyder, ikke (direkte) relatert til noen innsending. + /// Informasjon fra tjenestetilbyder, ikke (direkte) relatert til noen innsending. /// Information = 3, /// - /// Brukes for å indikere en feilsituasjon, typisk på en innsending. Inneholder en + /// Brukes for å indikere en feilsituasjon, typisk på en innsending. Inneholder en /// tjenestespesifikk activityErrorCode. /// Error = 4, @@ -37,14 +37,6 @@ public enum Values /// Closed = 5, - /// - /// Når dialogen først ble hentet og av hvem. Kan brukes for å avgjøre om purring - /// skal sendes ut, eller internt i virksomheten for å tracke tilganger/bruker. - /// Merk at dette ikke er det samme som "lest", dette må tjenestetilbyder selv håndtere - /// i egne løsninger. - /// - Seen = 6, - /// /// Når dialogen blir videresendt (tilgang delegert) av noen med tilgang til andre. /// diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs index 73cbb0742..d0bdd2149 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs @@ -57,6 +57,9 @@ public class DialogEntity : [AggregateChild] public List Activities { get; set; } = []; + [AggregateChild] + public List SeenLog { get; set; } = []; + public void SoftDelete() { foreach (var dialogElement in Elements) @@ -90,37 +93,23 @@ public void OnDelete(AggregateNode self, DateTimeOffset utcNow) _domainEvents.Add(new DialogDeletedDomainEvent(Id, ServiceResource, Party)); } - public void UpdateSeenAt(string seenByEndUserId, string? seenByEndUserName) + public void UpdateSeenAt(string endUserId, string? endUserName) { - var lastSeenByAt = Activities - .Where(x => x.SeenByEndUserId == seenByEndUserId) + var lastSeenAt = SeenLog + .Where(x => x.EndUserId == endUserId) .MaxBy(x => x.CreatedAt) - ?.CreatedAt; + ?.CreatedAt + ?? DateTimeOffset.MinValue; - if ((lastSeenByAt ?? DateTimeOffset.MinValue) >= UpdatedAt) + if (lastSeenAt >= UpdatedAt) { return; } - var performedBy = seenByEndUserName is not null - ? new DialogActivityPerformedBy - { - Localizations = - [ - new Localization - { - CultureCode = "nb-no", - Value = seenByEndUserName - } - ] - } - : null; - - Activities.Add(new DialogActivity + SeenLog.Add(new() { - PerformedBy = performedBy, - SeenByEndUserId = seenByEndUserId, - TypeId = DialogActivityType.Values.Seen + EndUserId = endUserId, + EndUserName = endUserName }); _domainEvents.Add(new DialogSeenDomainEvent(Id, ServiceResource, Party)); diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenRecord.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenRecord.cs new file mode 100644 index 000000000..a6e751589 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenRecord.cs @@ -0,0 +1,28 @@ +using Digdir.Domain.Dialogporten.Domain.Localizations; +using Digdir.Library.Entity.Abstractions.Features.Aggregate; +using Digdir.Library.Entity.Abstractions.Features.Immutable; + +namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; + +public class DialogSeenRecord : IImmutableEntity +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + + public string EndUserId { get; set; } = null!; + + public string? EndUserName { get; set; } + + [AggregateChild] + public DialogSeenLogVia? Via { get; set; } + + // === Dependent relationships === + public Guid DialogId { get; set; } + public DialogEntity Dialog { get; set; } = null!; +} + +public class DialogSeenLogVia : LocalizationSet +{ + public DialogSeenRecord DialogSeenLog { get; set; } = null!; + public Guid DialogSeenLogId { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs index 1439d6b47..c7f3057ef 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs @@ -30,12 +30,16 @@ public DialogDbContext(DbContextOptions options) : base(options public DbSet DialogElementUrls => Set(); public DbSet DialogGuiActionTypes => Set(); public DbSet DialogActivityTypes => Set(); + public DbSet DialogSeenLog => Set(); public DbSet DialogContentTypes => Set(); public DbSet DialogContent => Set(); public DbSet OutboxMessages => Set(); public DbSet OutboxMessageConsumers => Set(); + //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => + // optionsBuilder.LogTo(Console.WriteLine); + internal bool TrySetOriginalRevision( TEntity? entity, Guid? revision) diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.Designer.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.Designer.cs new file mode 100644 index 000000000..4d9258e53 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.Designer.cs @@ -0,0 +1,1287 @@ +// +using System; +using Digdir.Domain.Dialogporten.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(DialogDbContext))] + [Migration("20240405113521_SplitSeenLogFromActivityToSeparateEntity")] + partial class SplitSeenLogFromActivityToSeparateEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogApiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ActionId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deprecated") + .HasColumnType("boolean"); + + b.Property("DocumentationUrl") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("HttpMethodId") + .HasColumnType("integer"); + + b.Property("RequestSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("ResponseSchema") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("SunsetAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("Version") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("ActionId"); + + b.HasIndex("HttpMethodId"); + + b.ToTable("DialogApiActionEndpoint"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("IsBackChannel") + .HasColumnType("boolean"); + + b.Property("IsDeleteAction") + .HasColumnType("boolean"); + + b.Property("PriorityId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("PriorityId"); + + b.ToTable("DialogGuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogGuiActionPriority"); + + b.HasData( + new + { + Id = 1, + Name = "Primary" + }, + new + { + Id = 2, + Name = "Secondary" + }, + new + { + Id = 3, + Name = "Tertiary" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExtendedType") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("RelatedActivityId") + .HasColumnType("uuid"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DialogElementId"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedActivityId"); + + b.HasIndex("TypeId"); + + b.ToTable("DialogActivity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogActivityType"); + + b.HasData( + new + { + Id = 1, + Name = "Submission" + }, + new + { + Id = 2, + Name = "Feedback" + }, + new + { + Id = 3, + Name = "Information" + }, + new + { + Id = 4, + Name = "Error" + }, + new + { + Id = 5, + Name = "Closed" + }, + new + { + Id = 7, + Name = "Forwarded" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("TypeId"); + + b.HasIndex("DialogId", "TypeId") + .IsUnique(); + + b.ToTable("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("MaxLength") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("OutputInList") + .HasColumnType("boolean"); + + b.Property("RenderAsHtml") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DialogContentType"); + + b.HasData( + new + { + Id = 1, + MaxLength = 255, + Name = "Title", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 2, + MaxLength = 255, + Name = "SenderName", + OutputInList = true, + RenderAsHtml = false, + Required = false + }, + new + { + Id = 3, + MaxLength = 255, + Name = "Summary", + OutputInList = true, + RenderAsHtml = false, + Required = true + }, + new + { + Id = 4, + MaxLength = 1023, + Name = "AdditionalInfo", + OutputInList = false, + RenderAsHtml = true, + Required = false + }, + new + { + Id = 5, + MaxLength = 20, + Name = "ExtendedStatus", + OutputInList = true, + RenderAsHtml = false, + Required = false + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Deleted") + .HasColumnType("boolean"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtendedStatus") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Org") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("Party") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("Progress") + .HasColumnType("integer"); + + b.Property("Revision") + .IsConcurrencyToken() + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ServiceResource") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .UseCollation("C"); + + b.Property("StatusId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("VisibleFrom") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("DueAt"); + + b.HasIndex("Org"); + + b.HasIndex("Party"); + + b.HasIndex("ServiceResource"); + + b.HasIndex("StatusId"); + + b.HasIndex("UpdatedAt"); + + b.ToTable("Dialog", (string)null); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(63) + .HasColumnType("character varying(63)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId", "Value") + .IsUnique(); + + b.ToTable("DialogSearchTag"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("EndUserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("EndUserName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogSeenRecord"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogStatus"); + + b.HasData( + new + { + Id = 1, + Name = "New" + }, + new + { + Id = 2, + Name = "InProgress" + }, + new + { + Id = 3, + Name = "Waiting" + }, + new + { + Id = 4, + Name = "Signing" + }, + new + { + Id = 5, + Name = "Cancelled" + }, + new + { + Id = 6, + Name = "Completed" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AuthorizationAttribute") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("ExternalReference") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RelatedDialogElementId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("RelatedDialogElementId"); + + b.ToTable("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ConsumerTypeId") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogElementId") + .HasColumnType("uuid"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.HasKey("Id"); + + b.HasIndex("ConsumerTypeId"); + + b.HasIndex("DialogElementId"); + + b.ToTable("DialogElementUrl"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogElementUrlConsumerType"); + + b.HasData( + new + { + Id = 1, + Name = "Gui" + }, + new + { + Id = 2, + Name = "Api" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("HttpVerb"); + + b.HasData( + new + { + Id = 1, + Name = "GET" + }, + new + { + Id = 2, + Name = "POST" + }, + new + { + Id = 3, + Name = "PUT" + }, + new + { + Id = 4, + Name = "PATCH" + }, + new + { + Id = 5, + Name = "DELETE" + }, + new + { + Id = 6, + Name = "HEAD" + }, + new + { + Id = 7, + Name = "OPTIONS" + }, + new + { + Id = 8, + Name = "TRACE" + }, + new + { + Id = 9, + Name = "CONNECT" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.Property("LocalizationSetId") + .HasColumnType("uuid"); + + b.Property("CultureCode") + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4095) + .HasColumnType("character varying(4095)"); + + b.HasKey("LocalizationSetId", "CultureCode"); + + b.ToTable("Localization"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("LocalizationSet"); + + b.HasDiscriminator("Discriminator").HasValue("LocalizationSet"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventPayload") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId"); + + b.ToTable("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ConsumerName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("EventId", "ConsumerName"); + + b.ToTable("OutboxMessageConsumer"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("GuiActionId") + .HasColumnType("uuid"); + + b.HasIndex("GuiActionId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogGuiActionTitle"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.ToTable("LocalizationSet", t => + { + t.Property("ActivityId") + .HasColumnName("DialogActivityDescription_ActivityId"); + }); + + b.HasDiscriminator().HasValue("DialogActivityDescription"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ActivityId") + .HasColumnType("uuid"); + + b.HasIndex("ActivityId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogActivityPerformedBy"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("DialogContentId") + .HasColumnType("uuid"); + + b.HasIndex("DialogContentId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogContentValue"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("DialogSeenLogId") + .HasColumnType("uuid"); + + b.HasIndex("DialogSeenLogId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogSeenLogVia"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("ElementId") + .HasColumnType("uuid"); + + b.HasIndex("ElementId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogElementDisplayName"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("ApiActions") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("ApiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", "Action") + .WithMany("Endpoints") + .HasForeignKey("ActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", "HttpMethod") + .WithMany() + .HasForeignKey("HttpMethodId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Action"); + + b.Navigation("HttpMethod"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("GuiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", "Priority") + .WithMany() + .HasForeignKey("PriorityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Priority"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Activities") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Activities") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "RelatedActivity") + .WithMany("RelatedActivities") + .HasForeignKey("RelatedActivityId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("DialogElement"); + + b.Navigation("RelatedActivity"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Content") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SearchTags") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SeenLog") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Elements") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "RelatedDialogElement") + .WithMany("RelatedDialogElements") + .HasForeignKey("RelatedDialogElementId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Dialog"); + + b.Navigation("RelatedDialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", "ConsumerType") + .WithMany() + .HasForeignKey("ConsumerTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "DialogElement") + .WithMany("Urls") + .HasForeignKey("DialogElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ConsumerType"); + + b.Navigation("DialogElement"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", "LocalizationSet") + .WithMany("Localizations") + .HasForeignKey("LocalizationSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LocalizationSet"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", "OutboxMessage") + .WithMany("OutboxMessageConsumers") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OutboxMessage"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction") + .WithOne("Title") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", "GuiActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuiAction"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("Description") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity") + .WithOne("PerformedBy") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedBy", "ActivityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Activity"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", "DialogContent") + .WithOne("Value") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentValue", "DialogContentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DialogContent"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", "DialogSeenLog") + .WithOne("Via") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", "DialogSeenLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DialogSeenLog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "Element") + .WithOne("DisplayName") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", "ElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Element"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b => + { + b.Navigation("Endpoints"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Navigation("Title"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Navigation("Description"); + + b.Navigation("PerformedBy"); + + b.Navigation("RelatedActivities"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContent", b => + { + b.Navigation("Value") + .IsRequired(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("Content"); + + b.Navigation("Elements"); + + b.Navigation("GuiActions"); + + b.Navigation("SearchTags"); + + b.Navigation("SeenLog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.Navigation("Via"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => + { + b.Navigation("Activities"); + + b.Navigation("ApiActions"); + + b.Navigation("DisplayName"); + + b.Navigation("RelatedDialogElements"); + + b.Navigation("Urls"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => + { + b.Navigation("Localizations"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Navigation("OutboxMessageConsumers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.cs new file mode 100644 index 000000000..c796306f4 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240405113521_SplitSeenLogFromActivityToSeparateEntity.cs @@ -0,0 +1,106 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + /// + public partial class SplitSeenLogFromActivityToSeparateEntity : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "DialogActivity", + keyColumn: "TypeId", + keyValue: 6); + + migrationBuilder.DeleteData( + table: "DialogActivityType", + keyColumn: "Id", + keyValue: 6); + + migrationBuilder.DropColumn( + name: "SeenByEndUserId", + table: "DialogActivity"); + + migrationBuilder.AddColumn( + name: "DialogSeenLogId", + table: "LocalizationSet", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "DialogSeenRecord", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "current_timestamp at time zone 'utc'"), + EndUserId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + EndUserName = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + DialogId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DialogSeenRecord", x => x.Id); + table.ForeignKey( + name: "FK_DialogSeenRecord_Dialog_DialogId", + column: x => x.DialogId, + principalTable: "Dialog", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_LocalizationSet_DialogSeenLogId", + table: "LocalizationSet", + column: "DialogSeenLogId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_DialogSeenRecord_DialogId", + table: "DialogSeenRecord", + column: "DialogId"); + + migrationBuilder.AddForeignKey( + name: "FK_LocalizationSet_DialogSeenRecord_DialogSeenLogId", + table: "LocalizationSet", + column: "DialogSeenLogId", + principalTable: "DialogSeenRecord", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_LocalizationSet_DialogSeenRecord_DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.DropTable( + name: "DialogSeenRecord"); + + migrationBuilder.DropIndex( + name: "IX_LocalizationSet_DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.DropColumn( + name: "DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.AddColumn( + name: "SeenByEndUserId", + table: "DialogActivity", + type: "character varying(255)", + maxLength: 255, + nullable: true); + + migrationBuilder.InsertData( + table: "DialogActivityType", + columns: new[] { "Id", "Name" }, + values: new object[] { 6, "Seen" }); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs index c0e061e3e..589455e6f 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs @@ -60,7 +60,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DialogId"); - b.ToTable("DialogApiAction"); + b.ToTable("DialogApiAction", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b => @@ -119,7 +119,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("HttpMethodId"); - b.ToTable("DialogApiActionEndpoint"); + b.ToTable("DialogApiActionEndpoint", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => @@ -171,7 +171,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PriorityId"); - b.ToTable("DialogGuiAction"); + b.ToTable("DialogGuiAction", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", b => @@ -186,7 +186,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("DialogGuiActionPriority"); + b.ToTable("DialogGuiActionPriority", (string)null); b.HasData( new @@ -231,10 +231,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("RelatedActivityId") .HasColumnType("uuid"); - b.Property("SeenByEndUserId") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - b.Property("TypeId") .HasColumnType("integer"); @@ -248,7 +244,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("TypeId"); - b.ToTable("DialogActivity"); + b.ToTable("DialogActivity", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", b => @@ -263,7 +259,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("DialogActivityType"); + b.ToTable("DialogActivityType", (string)null); b.HasData( new @@ -292,11 +288,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) Name = "Closed" }, new - { - Id = 6, - Name = "Seen" - }, - new { Id = 7, Name = "Forwarded" @@ -333,7 +324,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DialogId", "TypeId") .IsUnique(); - b.ToTable("DialogContent"); + b.ToTable("DialogContent", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content.DialogContentType", b => @@ -360,7 +351,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("DialogContentType"); + b.ToTable("DialogContentType", (string)null); b.HasData( new @@ -524,7 +515,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DialogId", "Value") .IsUnique(); - b.ToTable("DialogSearchTag"); + b.ToTable("DialogSearchTag", (string)null); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.Property("DialogId") + .HasColumnType("uuid"); + + b.Property("EndUserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("EndUserName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogSeenRecord", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", b => @@ -539,7 +561,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("DialogStatus"); + b.ToTable("DialogStatus", (string)null); b.HasData( new @@ -615,7 +637,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RelatedDialogElementId"); - b.ToTable("DialogElement"); + b.ToTable("DialogElement", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrl", b => @@ -656,7 +678,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DialogElementId"); - b.ToTable("DialogElementUrl"); + b.ToTable("DialogElementUrl", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementUrlConsumerType", b => @@ -671,7 +693,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("DialogElementUrlConsumerType"); + b.ToTable("DialogElementUrlConsumerType", (string)null); b.HasData( new @@ -698,7 +720,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("HttpVerb"); + b.ToTable("HttpVerb", (string)null); b.HasData( new @@ -774,7 +796,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("LocalizationSetId", "CultureCode"); - b.ToTable("Localization"); + b.ToTable("Localization", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => @@ -796,7 +818,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("LocalizationSet"); + b.ToTable("LocalizationSet", (string)null); b.HasDiscriminator("Discriminator").HasValue("LocalizationSet"); @@ -820,7 +842,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("EventId"); - b.ToTable("OutboxMessage"); + b.ToTable("OutboxMessage", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessageConsumer", b => @@ -834,7 +856,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("EventId", "ConsumerName"); - b.ToTable("OutboxMessageConsumer"); + b.ToTable("OutboxMessageConsumer", (string)null); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b => @@ -860,7 +882,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ActivityId") .IsUnique(); - b.ToTable("LocalizationSet", t => + b.ToTable("LocalizationSet", null, t => { t.Property("ActivityId") .HasColumnName("DialogActivityDescription_ActivityId"); @@ -895,6 +917,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("DialogContentValue"); }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("DialogSeenLogId") + .HasColumnType("uuid"); + + b.HasIndex("DialogSeenLogId") + .IsUnique(); + + b.HasDiscriminator().HasValue("DialogSeenLogVia"); + }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => { b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); @@ -1038,6 +1073,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Dialog"); }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SeenLog") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => { b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") @@ -1141,6 +1187,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("DialogContent"); }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", "DialogSeenLog") + .WithOne("Via") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogVia", "DialogSeenLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DialogSeenLog"); + }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElementDisplayName", b => { b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", "Element") @@ -1190,6 +1247,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("GuiActions"); b.Navigation("SearchTags"); + + b.Navigation("SeenLog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenRecord", b => + { + b.Navigation("Via"); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements.DialogElement", b => From 14ee4a1a1cb57014d7d6d6ae385681fa5b3c690c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 5 Apr 2024 16:08:04 +0200 Subject: [PATCH 06/13] fix: Add PartyIdentifier.Separator to party validation error (#595) Was lost in refactoring --- .../FluentValidation_PartyIdentifier_Extensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs index b5b2edcd4..7cbd9d3b6 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs @@ -15,7 +15,7 @@ public static IRuleBuilderOptions IsValidPartyIdentifier(this IRul && id is NorwegianPersonIdentifier or NorwegianOrganizationIdentifier )) .WithMessage( - $"'{{PropertyName}}' must be on format '{NorwegianOrganizationIdentifier.Prefix}{{norwegian org-nr}}' or " + - $"'{NorwegianPersonIdentifier.Prefix}{{{{norwegian f-nr/d-nr}}}}' with valid numbers respectively."); + $"'{{PropertyName}}' must be on format '{NorwegianOrganizationIdentifier.Prefix}{PartyIdentifier.Separator}{{norwegian org-nr}}' or " + + $"'{NorwegianPersonIdentifier.Prefix}{PartyIdentifier.Separator}{{{{norwegian f-nr/d-nr}}}}' with valid numbers respectively."); } } From b8dae9d95d407d67e7edfb68de9266990e867d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 5 Apr 2024 17:08:58 +0200 Subject: [PATCH 07/13] chore: Fix swagger test (#600) --- docs/swagger/V1/swagger.verified.json | 110 ++++++++++++++++++-------- 1 file changed, 79 insertions(+), 31 deletions(-) diff --git a/docs/swagger/V1/swagger.verified.json b/docs/swagger/V1/swagger.verified.json index c721b4c3b..5faf7ca90 100644 --- a/docs/swagger/V1/swagger.verified.json +++ b/docs/swagger/V1/swagger.verified.json @@ -2513,7 +2513,6 @@ "Information", "Error", "Closed", - "Seen", "Forwarded" ], "enum": [ @@ -2522,7 +2521,6 @@ "Information", "Error", "Closed", - "Seen", "Forwarded" ] }, @@ -3459,31 +3457,24 @@ "status": { "$ref": "#/components/schemas/DialogStatus_Values" }, + "latestActivity": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/SearchDialogDialogActivityDto" + } + ] + }, "content": { "type": "array", "items": { "$ref": "#/components/schemas/SearchDialogContentDto" } }, - "latestActivities": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchDialogDialogActivityDto" - } - } - } - }, - "SearchDialogContentDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "$ref": "#/components/schemas/DialogContentType_Values" - }, - "value": { + "seenLog": { "type": "array", "items": { - "$ref": "#/components/schemas/LocalizationDto" + "$ref": "#/components/schemas/SearchDialogDialogSeenRecordDto" } } } @@ -3506,14 +3497,6 @@ "format": "uri", "nullable": true }, - "seenByEndUserIdHash": { - "type": "string", - "nullable": true - }, - "seenActivityIsCurrentEndUser": { - "type": "boolean", - "nullable": true - }, "type": { "$ref": "#/components/schemas/DialogActivityType_Values" }, @@ -3542,6 +3525,45 @@ } } }, + "SearchDialogContentDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "$ref": "#/components/schemas/DialogContentType_Values" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocalizationDto" + } + } + } + }, + "SearchDialogDialogSeenRecordDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "endUserIdHash": { + "type": "string" + }, + "endUserName": { + "type": "string", + "nullable": true + }, + "isAuthenticatedUser": { + "type": "boolean" + } + } + }, "GetDialogDto": { "type": "object", "additionalProperties": false, @@ -3635,6 +3657,12 @@ "items": { "$ref": "#/components/schemas/GetDialogDialogActivityDto" } + }, + "seenLog": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetDialogDialogSeenRecordDto" + } } } }, @@ -3849,10 +3877,6 @@ "format": "uri", "nullable": true }, - "seenByEndUserIdHash": { - "type": "string", - "nullable": true - }, "type": { "$ref": "#/components/schemas/DialogActivityType_Values" }, @@ -3881,6 +3905,30 @@ } } }, + "GetDialogDialogSeenRecordDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "endUserIdHash": { + "type": "string" + }, + "endUserName": { + "type": "string", + "nullable": true + }, + "isAuthenticatedUser": { + "type": "boolean" + } + } + }, "Operation": { "type": "object", "additionalProperties": false, From b3a9129ab3835906d15daf737036338a098ca92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 5 Apr 2024 17:23:59 +0200 Subject: [PATCH 08/13] chore: Refactor MappingUtils (#599) * Removed salt input, weird behavior to take input that the tool itself has generated * Changed old name, it used to be related to mapping * Put it in the DI container --- .../ApplicationExtensions.cs | 1 + .../Common/ICompactJwsGenerator.cs | 1 - .../Common/MappingUtils.cs | 28 +++++++++++++------ .../Queries/Get/GetDialogActivityQuery.cs | 1 - .../Search/SearchDialogActivityQuery.cs | 1 - .../Dialogs/Queries/Get/GetDialogQuery.cs | 9 +++--- .../Queries/Search/SearchDialogQuery.cs | 11 ++++---- .../Dialogs/Entities/DialogEntity.cs | 1 - .../InfrastructureExtensions.cs | 6 ---- .../Get/GetDialogActivityEndpoint.cs | 1 - .../Search/SearchDialogActivityEndpoint.cs | 1 - .../Get/GetDialogElementEndpoint.cs | 1 - .../Search/SearchDialogElementEndpoint.cs | 1 - .../Update/UpdateDialogElementEndpoint.cs | 1 - .../Dialogs/Purge/PurgeDialogEndpoint.cs | 1 - 15 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs index d37f9ca13..8b248668d 100644 --- a/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs @@ -39,6 +39,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services .AddScoped() // Transient + .AddTransient() .AddTransient() .AddTransient() .AddTransient() diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/ICompactJwsGenerator.cs b/src/Digdir.Domain.Dialogporten.Application/Common/ICompactJwsGenerator.cs index 8d988cffe..a4f002bfc 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/ICompactJwsGenerator.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/ICompactJwsGenerator.cs @@ -1,7 +1,6 @@ using System.Buffers.Text; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using Microsoft.Extensions.Options; using NSec.Cryptography; diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs b/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs index 2e53a9b4d..4a3ca8f22 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/MappingUtils.cs @@ -1,24 +1,34 @@ +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using System.Text; namespace Digdir.Domain.Dialogporten.Application.Common; -internal static class MappingUtils +internal interface IStringHasher { - internal static byte[] GetHashSalt(int size = 16) => RandomNumberGenerator.GetBytes(size); + [return: NotNullIfNotNull(nameof(personIdentifier))] + string? Hash(string? personIdentifier); +} + +internal class RandomSaltStringHasher : IStringHasher +{ + private const int SaltSize = 16; + private readonly Lazy _lazySalt = new(() => RandomNumberGenerator.GetBytes(SaltSize)); - internal static string HashPid(string personIdentifier, byte[] salt) + public string? Hash(string? personIdentifier) { + if (string.IsNullOrWhiteSpace(personIdentifier)) + { + return null; + } + var identifierBytes = Encoding.UTF8.GetBytes(personIdentifier); - Span buffer = stackalloc byte[identifierBytes.Length + salt.Length]; + Span buffer = stackalloc byte[identifierBytes.Length + _lazySalt.Value.Length]; identifierBytes.CopyTo(buffer); - salt.CopyTo(buffer[identifierBytes.Length..]); + _lazySalt.Value.CopyTo(buffer[identifierBytes.Length..]); var hashBytes = SHA256.HashData(buffer); - return BitConverter - .ToString(hashBytes, 0, 5) - .Replace("-", "") - .ToLowerInvariant(); + return BitConverter.ToString(hashBytes, 0, 5).Replace("-", "").ToLowerInvariant(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs index bd6545728..1c71c9c2f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/GetDialogActivityQuery.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; using Digdir.Domain.Dialogporten.Application.Externals; using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs index a46226480..6c5765204 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Search/SearchDialogActivityQuery.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; using Digdir.Domain.Dialogporten.Application.Externals; using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs index 906019d20..23a6f99b8 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs @@ -8,7 +8,6 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using MediatR; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; using OneOf; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get; @@ -30,6 +29,7 @@ internal sealed class GetDialogQueryHandler : IRequestHandler Handle(GetDialogQuery request, CancellationToken cancellationToken) @@ -115,13 +117,12 @@ public async Task Handle(GetDialogQuery request, CancellationTo var dialogDto = _mapper.Map(dialog); - var salt = MappingUtils.GetHashSalt(); dialogDto.SeenLog = dialog.SeenLog .Select(log => { var logDto = _mapper.Map(log); logDto.IsAuthenticatedUser = log.EndUserId == userPid; - logDto.EndUserIdHash = MappingUtils.HashPid(log.EndUserId, salt); + logDto.EndUserIdHash = _stringHasher.Hash(log.EndUserId); return logDto; }) .ToList(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs index a77d9b12a..2bcdf6c75 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs @@ -10,7 +10,6 @@ using Digdir.Domain.Dialogporten.Application.Externals; using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; -using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Localizations; using MediatR; using Microsoft.EntityFrameworkCore; @@ -117,19 +116,22 @@ internal sealed class SearchDialogQueryHandler : IRequestHandler Handle(SearchDialogQuery request, CancellationToken cancellationToken) @@ -175,14 +177,13 @@ public async Task Handle(SearchDialogQuery request, Cancella .ProjectTo(_mapper.ConfigurationProvider) .ToPaginatedListAsync(request, cancellationToken: cancellationToken); - var salt = MappingUtils.GetHashSalt(); foreach (var seenLog in paginatedList.Items.SelectMany(x => x.SeenLog)) { // Before we hash the end user id, check if the seen log entry is for the current user seenLog.IsAuthenticatedUser = userPid == seenLog.EndUserIdHash; - // TODO: Add test to not expose unhashed end user id to the client + // TODO: Add test to not expose un-hashed end user id to the client // https://github.com/digdir/dialogporten/issues/596 - seenLog.EndUserIdHash = MappingUtils.HashPid(seenLog.EndUserIdHash, salt); + seenLog.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserIdHash); } return paginatedList; diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs index d0bdd2149..e883422a8 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs @@ -3,7 +3,6 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements; using Digdir.Domain.Dialogporten.Domain.Dialogs.Events; -using Digdir.Domain.Dialogporten.Domain.Localizations; using Digdir.Library.Entity.Abstractions; using Digdir.Library.Entity.Abstractions.Features.Aggregate; using Digdir.Library.Entity.Abstractions.Features.EventPublisher; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs index 32f1a7791..bf9aab711 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs @@ -25,13 +25,7 @@ using Digdir.Domain.Dialogporten.Infrastructure.Altinn.Events; using Digdir.Domain.Dialogporten.Infrastructure.Altinn.OrganizationRegistry; using Digdir.Domain.Dialogporten.Infrastructure.Altinn.ResourceRegistry; -using System.Text.Json; -using Microsoft.Extensions.Caching.StackExchangeRedis; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Caching.Memory; using ZiggyCreatures.Caching.Fusion; -using ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis; using Digdir.Domain.Dialogporten.Infrastructure.Altinn.NameRegistry; namespace Digdir.Domain.Dialogporten.Infrastructure; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Get/GetDialogActivityEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Get/GetDialogActivityEndpoint.cs index 9423f7ec1..e5eaebcbe 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Get/GetDialogActivityEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Get/GetDialogActivityEndpoint.cs @@ -1,7 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Search/SearchDialogActivityEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Search/SearchDialogActivityEndpoint.cs index dd29e6059..d2a2fab61 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Search/SearchDialogActivityEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogActivities/Search/SearchDialogActivityEndpoint.cs @@ -1,7 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Search; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Get/GetDialogElementEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Get/GetDialogElementEndpoint.cs index d07088722..05517ea3a 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Get/GetDialogElementEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Get/GetDialogElementEndpoint.cs @@ -1,7 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogElements.Queries.Get; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Search/SearchDialogElementEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Search/SearchDialogElementEndpoint.cs index a93a23f98..b0b7ae4ff 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Search/SearchDialogElementEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogElements/Search/SearchDialogElementEndpoint.cs @@ -1,7 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogElements.Queries.Search; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogElements/Update/UpdateDialogElementEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogElements/Update/UpdateDialogElementEndpoint.cs index 441e97be9..7a738206e 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogElements/Update/UpdateDialogElementEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogElements/Update/UpdateDialogElementEndpoint.cs @@ -6,7 +6,6 @@ using Digdir.Domain.Dialogporten.WebApi.Common; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; using IMapper = AutoMapper.IMapper; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Purge/PurgeDialogEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Purge/PurgeDialogEndpoint.cs index b3d5be7c4..6690ec7de 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Purge/PurgeDialogEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Purge/PurgeDialogEndpoint.cs @@ -2,7 +2,6 @@ using Digdir.Domain.Dialogporten.WebApi.Common; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; -using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; using FastEndpoints; using MediatR; From bbe27d740947a550dd05a80b7108a1f93ff7ca2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 8 Apr 2024 11:14:06 +0200 Subject: [PATCH 09/13] chore: code cleaning (#605) * Naming conventions * Typos * Trailing commas * Collection expressions * Expression bodies --- .../Extensions/ClaimsPrincipalExtensions.cs | 2 +- ...uentValidationDateTimeOffsetExtensions.cs} | 4 ++-- ...> FluentValidationEnumerableExtensions.cs} | 2 +- ...entValidationLocalizationDtoExtensions.cs} | 0 ...entValidationPartyIdentifierExtensions.cs} | 0 ...cs => FluentValidationStringExtensions.cs} | 4 ++-- ...ns.cs => FluentValidationUriExtensions.cs} | 0 .../Common/Extensions/OneOfExtensions.cs | 12 +++++----- .../Common/Extensions/QueryableExtensions.cs | 4 +--- .../Common/Extensions/TypeExtensions.cs | 4 +--- .../Pagination/Extensions/OrderExtensions.cs | 22 +++++++------------ .../Extensions/TryParseExtensions.cs | 16 +++++++------- .../Pagination/OrderOption/IOrderOptions.cs | 4 +--- .../Common/Pagination/PaginationExtensions.cs | 4 ++-- .../Common/ReturnTypes/ConcurrencyError.cs | 2 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 7 ++++-- .../Create/CreateDialogCommandValidator.cs | 6 ++--- .../Update/UpdateDialogCommandValidator.cs | 6 ++--- .../Dialogs/Queries/Search/SearchDialogDto.cs | 2 +- .../Common/AzureAppConfigurationExtensions.cs | 2 +- .../Program.cs | 4 ++-- .../Dialogs/Entities/DialogEntity.cs | 8 ++----- .../AltinnAuthorizationClient.cs | 6 ++--- .../NameRegistryClient.cs | 12 +++++----- .../OrganizationRegistryClient.cs | 4 ++-- .../ResourceRegistryClient.cs | 4 ++-- .../Common/Serialization/SerializerOptions.cs | 5 ++--- .../InfrastructureExtensions.cs | 6 ++--- .../Persistence/ContextDesignTimeFactory.cs | 1 + .../OutboxMessageConsumerDefinition.cs | 5 +---- .../Program.cs | 4 ++-- .../Common/Authentication/TokenIssuerCache.cs | 17 +++++++++----- ...nApiDocumentGeneratorSettingsExtensions.cs | 2 +- .../AzureAppConfigurationExtensions.cs | 2 +- .../Common/Extensions/EndpointExtensions.cs | 4 +++- .../Common/Extensions/StringExtensions.cs | 4 +--- .../Json/DateTimeNotSupportedConverter.cs | 11 ++++------ .../Endpoints/V1/MetadataGroup.cs | 2 +- .../Update/UpdateDialogSwaggerConfig.cs | 2 +- .../V1/WellKnown/Jwks/Get/GetJwksEndpoint.cs | 2 +- .../Features/Aggregate/AggregateNode.cs | 10 ++++----- .../Features/Lookup/AbstractLookupEntity.cs | 10 ++------- .../Features/Updatable/UpdatableExtensions.cs | 4 +--- .../EntityLibraryEfCoreExtensions.cs | 6 ++--- .../Features/Lookup/LookupEntityExtensions.cs | 4 ++-- .../SoftDeletable/SoftDeletableExtensions.cs | 10 ++------- .../Common/DialogApplication.cs | 2 +- .../V1/Dialogs/Commands/CreateDialogTests.cs | 4 ++-- .../Ed25519GeneratorTests.cs | 2 +- .../DecisionRequestHelperTests.cs | 10 +++------ 50 files changed, 118 insertions(+), 152 deletions(-) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_DateTimeOffset_Extensions.cs => FluentValidationDateTimeOffsetExtensions.cs} (95%) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_Enumerable_Extensions.cs => FluentValidationEnumerableExtensions.cs} (97%) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_LocalizationDto_Extensions.cs => FluentValidationLocalizationDtoExtensions.cs} (100%) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_PartyIdentifier_Extensions.cs => FluentValidationPartyIdentifierExtensions.cs} (100%) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_String_Extensions.cs => FluentValidationStringExtensions.cs} (73%) rename src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/{FluentValidation_Uri_Extensions.cs => FluentValidationUriExtensions.cs} (100%) diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ClaimsPrincipalExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ClaimsPrincipalExtensions.cs index d1872b0b2..0a7746098 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ClaimsPrincipalExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ClaimsPrincipalExtensions.cs @@ -86,7 +86,7 @@ public static bool TryGetOrgNumber(this Claim? consumerClaim, [NotNullWhen(true) public static bool TryGetAuthenticationLevel(this ClaimsPrincipal claimsPrincipal, [NotNullWhen(true)] out int? authenticationLevel) { - string[] claimTypes = { "acr", "urn:altinn:authlevel" }; + string[] claimTypes = ["acr", "urn:altinn:authlevel"]; foreach (var claimType in claimTypes) { diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_DateTimeOffset_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationDateTimeOffsetExtensions.cs similarity index 95% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_DateTimeOffset_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationDateTimeOffsetExtensions.cs index 367475be0..91f2f8e2b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_DateTimeOffset_Extensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationDateTimeOffsetExtensions.cs @@ -2,7 +2,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; -internal static class FluentValidation_DateTimeOffset_Extensions +internal static class FluentValidationDateTimeOffsetExtensions { public const string InPastMessage = "'{PropertyName}' must be in the past."; public const string InFutureMessage = "'{PropertyName}' must be in the future."; @@ -37,4 +37,4 @@ public static IRuleBuilderOptions IsInPast(this IRuleBuild .LessThanOrEqualTo(DateTimeOffset.UtcNow) .WithMessage(InPastMessage); } -} \ No newline at end of file +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_Enumerable_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationEnumerableExtensions.cs similarity index 97% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_Enumerable_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationEnumerableExtensions.cs index 0d41e7e56..5d9205023 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_Enumerable_Extensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationEnumerableExtensions.cs @@ -5,7 +5,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; -internal static class FluentValidation_Enumerable_Extensions +internal static class FluentValidationEnumerableExtensions { public static IRuleBuilderOptions> UniqueBy( this IRuleBuilder> ruleBuilder, diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_LocalizationDto_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationLocalizationDtoExtensions.cs similarity index 100% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_LocalizationDto_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationLocalizationDtoExtensions.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationPartyIdentifierExtensions.cs similarity index 100% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_PartyIdentifier_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationPartyIdentifierExtensions.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_String_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationStringExtensions.cs similarity index 73% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_String_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationStringExtensions.cs index be25899bc..1a4f27e5b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_String_Extensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationStringExtensions.cs @@ -2,12 +2,12 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; -internal static class FluentValidation_String_Extensions +internal static class FluentValidationStringExtensions { public static IRuleBuilderOptions IsValidUri(this IRuleBuilder ruleBuilder) { return ruleBuilder .Must(uri => uri is null || Uri.IsWellFormedUriString(uri, UriKind.RelativeOrAbsolute)) - .WithMessage("'{PropertyName}' is not a well formated URI."); + .WithMessage("'{PropertyName}' is not a well formatted URI."); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_Uri_Extensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationUriExtensions.cs similarity index 100% rename from src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidation_Uri_Extensions.cs rename to src/Digdir.Domain.Dialogporten.Application/Common/Extensions/FluentValidation/FluentValidationUriExtensions.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/OneOfExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/OneOfExtensions.cs index 3471e0d74..cfc146ca1 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/OneOfExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/OneOfExtensions.cs @@ -6,12 +6,12 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Extensions; internal static class OneOfExtensions { - private static readonly ConcurrentDictionary<(Type, Type), MethodInfo?> _oneOfFactoryByType = new(); + private static readonly ConcurrentDictionary<(Type, Type), MethodInfo?> OneOfFactoryByType = new(); public static bool TryConvertToOneOf(object value, [NotNullWhen(true)] out TOneOf? result) { ArgumentNullException.ThrowIfNull(value); - var oneOfFactory = _oneOfFactoryByType.GetOrAdd( + var oneOfFactory = OneOfFactoryByType.GetOrAdd( key: new(typeof(TOneOf), value.GetType()), valueFactory: GetOneOfFactoryOrNull); result = (TOneOf)oneOfFactory?.Invoke(null, [value])! ?? default; @@ -20,12 +20,12 @@ public static bool TryConvertToOneOf(object value, [NotNullWhen(true)] o private static MethodInfo? GetOneOfFactoryOrNull((Type, Type) arg) { - const string ImplicitOperatorName = "op_Implicit"; - const BindingFlags ImplicitOperatorFlags = BindingFlags.Static | BindingFlags.Public; + const string implicitOperatorName = "op_Implicit"; + const BindingFlags implicitOperatorFlags = BindingFlags.Static | BindingFlags.Public; var (oneOf, type) = arg; var oneOfImplicitOperator = oneOf.GetMethod( - name: ImplicitOperatorName, - bindingAttr: ImplicitOperatorFlags, + name: implicitOperatorName, + bindingAttr: implicitOperatorFlags, types: [type]); return oneOfImplicitOperator; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/QueryableExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/QueryableExtensions.cs index 7364abb80..da2006fb5 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/QueryableExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/QueryableExtensions.cs @@ -8,9 +8,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Extensions; public static class QueryableExtensions { public static IQueryable WhereIf(this IQueryable source, bool predicate, Expression> queryPredicate) - { - return predicate ? source.Where(queryPredicate) : source; - } + => predicate ? source.Where(queryPredicate) : source; private static readonly Type DialogType = typeof(DialogEntity); private static readonly PropertyInfo DialogIdPropertyInfo = DialogType.GetProperty(nameof(DialogEntity.Id))!; diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/TypeExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/TypeExtensions.cs index ab67eccde..69da536c2 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/TypeExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/TypeExtensions.cs @@ -3,7 +3,5 @@ internal static class TypeExtensions { public static bool IsNullableType(this Type type) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } + => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/OrderExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/OrderExtensions.cs index 413c816f5..37edf00cf 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/OrderExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/OrderExtensions.cs @@ -9,10 +9,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.Extensions; internal static class OrderExtensions { public static OrderSet DefaultIfNull(this OrderSet? orderSet) - where TOrderDefinition : IOrderDefinition - { - return orderSet ?? OrderSet.Default; - } + where TOrderDefinition : IOrderDefinition => orderSet ?? OrderSet.Default; public static IQueryable ApplyOrder(this IQueryable query, IOrderSet orderSet) { @@ -81,9 +78,9 @@ public static IQueryable ApplyCondition(this IQueryable query, IOrderSe // where a, b, c, ... are the order by expressions and an, bn, cn, ... // are the values from the continuation token. It's a repeating pattern // where the previous properties are equal and the next property is less than - // or greater than the next value, depending on the sort order. + // or greater than the next value, depending on the sort order. // See https://phauer.com/2018/web-api-pagination-timestamp-id-continuation-token/ for more info. - // The algorithm is as follows: + // The algorithm is as follows: // equalParst = [ a = an, b = bn, c = cn, ... ] // ltGtParts = [ a < an, b < bn, c < cn, ... ] // lgGtEqualParst = [ a < an, a = an AND b < bn, a = an AND b = bn AND c < cn, ... ] @@ -139,8 +136,8 @@ when x.orderBody.Type.IsNullableType() } /// - /// The not null "block" is added when the continuation token is null in descending - /// order, meaning we are still in the beginning where the null values are and + /// The not null "block" is added when the continuation token is null in descending + /// order, meaning we are still in the beginning where the null values are and /// have not reached the set where the not null values start. /// private static BinaryExpression IncludeNotNullsBlock(Expression orderBody) @@ -150,8 +147,8 @@ private static BinaryExpression IncludeNotNullsBlock(Expression orderBody) } /// - /// The null "block" is added when a nullable continuation token is not null in - /// ascending order, meaning we have not reached the end of the pagination + /// The null "block" is added when a nullable continuation token is not null in + /// ascending order, meaning we have not reached the end of the pagination /// where the null values are. /// private static BinaryExpression IncludeNullsBlock(Expression orderBody, Expression valueExpression) @@ -171,9 +168,6 @@ public ParameterReplacer(ParameterExpression newParameter) _newParameter = newParameter; } - protected override Expression VisitParameter(ParameterExpression node) - { - return _newParameter; - } + protected override Expression VisitParameter(ParameterExpression node) => _newParameter; } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/TryParseExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/TryParseExtensions.cs index c3f3c4568..a82ed9a3c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/TryParseExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Extensions/TryParseExtensions.cs @@ -7,7 +7,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.Extensions; public static class TryParseExtensions { - private static readonly ConcurrentDictionary _tryParseByType = new(); + private static readonly ConcurrentDictionary TryParseByType = new(); public static bool TryParse(Type type, string? value, out object? result) { @@ -27,7 +27,7 @@ public static bool TryParse(Type type, string? value, out object? result) return true; } - var method = _tryParseByType.GetOrAdd(type, t => TryFindTryParseMethod(t, out var m) ? m : null); + var method = TryParseByType.GetOrAdd(type, t => TryFindTryParseMethod(t, out var m) ? m : null); if (method is null) { return false; @@ -53,11 +53,11 @@ private static bool TryFindTryParseMethod(Type type, [NotNullWhen(true)] out Met var method = (MethodInfo)m; if (method.Name != tryParseMethodName) return false; if (method.ReturnParameter.ParameterType != typeof(bool)) return false; - var parms = method.GetParameters(); - if (parms.Length != 2) return false; - if (parms[0].ParameterType != typeof(string)) return false; - if (parms[1].ParameterType != type.MakeByRefType()) return false; - if (!parms[1].IsOut) return false; + var parameters = method.GetParameters(); + if (parameters.Length != 2) return false; + if (parameters[0].ParameterType != typeof(string)) return false; + if (parameters[1].ParameterType != type.MakeByRefType()) return false; + if (!parameters[1].IsOut) return false; return true; @@ -70,4 +70,4 @@ private static bool TryFindTryParseMethod(Type type, [NotNullWhen(true)] out Met return method is not null; } -} \ No newline at end of file +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/IOrderOptions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/IOrderOptions.cs index bebbb7a73..57ca1c2bb 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/IOrderOptions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/IOrderOptions.cs @@ -19,7 +19,6 @@ internal static class OrderOptions internal class OrderOptions : IOrderOptions { - private readonly string _defaultKey; private readonly Dictionary> _optionByKey; public Order DefaultOrder { get; } @@ -27,9 +26,8 @@ internal class OrderOptions : IOrderOptions public OrderOptions(string defaultKey, Dictionary> optionByKey) { - _defaultKey = defaultKey; _optionByKey = optionByKey; - DefaultOrder = new(_defaultKey, _optionByKey[_defaultKey]); + DefaultOrder = new(defaultKey, _optionByKey[defaultKey]); IdOrder = new(PaginationConstants.OrderIdKey, _optionByKey[PaginationConstants.OrderIdKey]); } diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/PaginationExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/PaginationExtensions.cs index 1d0e98e76..1515624c1 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/PaginationExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/PaginationExtensions.cs @@ -41,12 +41,12 @@ private static async Task> CreateAsync( { ArgumentNullException.ThrowIfNull(source); - const int OneMore = 1; + const int oneMore = 1; var items = await source .ApplyOrder(orderSet) .ApplyCondition(orderSet, continuationTokenSet) - .Take(limit + OneMore) + .Take(limit + oneMore) .ToArrayAsync(cancellationToken); // Fetch one more item than requested to determine if there is a next page diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ConcurrencyError.cs b/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ConcurrencyError.cs index fd3f7c640..86119bf38 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ConcurrencyError.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ConcurrencyError.cs @@ -1,3 +1,3 @@ namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; -public record ConcurrencyError(); \ No newline at end of file +public record ConcurrencyError; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs index 23a6f99b8..cc1ebeb10 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs @@ -164,8 +164,11 @@ private static void DecorateWithAuthorization(GetDialogDto dto, // Simple "read" on the main resource will give access to a dialog element, unless a authorization attribute is set, // in which case an "elementread" action is required - foreach (var dialogElement in dto.Elements.Where(dialogElement => (dialogElement.AuthorizationAttribute is null && action == Constants.ReadAction) - || (dialogElement.AuthorizationAttribute is not null && action == Constants.ElementReadAction))) + var elements = dto.Elements.Where(dialogElement => + (dialogElement.AuthorizationAttribute is null && action == Constants.ReadAction) || + (dialogElement.AuthorizationAttribute is not null && action == Constants.ElementReadAction)); + + foreach (var dialogElement in elements) { dialogElement.IsAuthorized = true; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs index 924c57a69..c4676b4f9 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs @@ -47,15 +47,15 @@ public CreateDialogCommandValidator( RuleFor(x => x.ExpiresAt) .IsInFuture() .GreaterThanOrEqualTo(x => x.DueAt) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.DueAt.HasValue, ApplyConditionTo.CurrentValidator) .GreaterThanOrEqualTo(x => x.VisibleFrom) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.VisibleFrom.HasValue, ApplyConditionTo.CurrentValidator); RuleFor(x => x.DueAt) .IsInFuture() .GreaterThanOrEqualTo(x => x.VisibleFrom) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.VisibleFrom.HasValue, ApplyConditionTo.CurrentValidator); RuleFor(x => x.VisibleFrom) .IsInFuture(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs index b5cb6fce7..a9cc68534 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs @@ -43,14 +43,14 @@ public UpdateDialogDtoValidator( RuleFor(x => x.ExpiresAt) .GreaterThanOrEqualTo(x => x.DueAt) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.DueAt.HasValue, ApplyConditionTo.CurrentValidator) .GreaterThanOrEqualTo(x => x.VisibleFrom) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.VisibleFrom.HasValue, ApplyConditionTo.CurrentValidator); RuleFor(x => x.DueAt) .GreaterThanOrEqualTo(x => x.VisibleFrom) - .WithMessage(FluentValidation_DateTimeOffset_Extensions.InFutureOfMessage) + .WithMessage(FluentValidationDateTimeOffsetExtensions.InFutureOfMessage) .When(x => x.VisibleFrom.HasValue, ApplyConditionTo.CurrentValidator); RuleFor(x => x.Status) diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs index 6b5e2111d..53f3ab060 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs @@ -10,7 +10,7 @@ public sealed class SearchDialogDto public string Org { get; set; } = null!; public string ServiceResource { get; set; } = null!; public string Party { get; set; } = null!; - public string? EndUserId { get; set; } = null!; + public string? EndUserId { get; set; } public int? Progress { get; set; } public string? ExtendedStatus { get; set; } public DateTimeOffset CreatedAt { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Common/AzureAppConfigurationExtensions.cs b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Common/AzureAppConfigurationExtensions.cs index 1ffadfe9b..d2314c6b8 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Common/AzureAppConfigurationExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Common/AzureAppConfigurationExtensions.cs @@ -6,7 +6,7 @@ namespace Digdir.Domain.Dialogporten.ChangeDataCapture.Common; /// -/// Wrapper around azure app configuration bootstrapping such that azure app +/// Wrapper around azure app configuration bootstrapping such that azure app /// config is activated through the environment variable AZURE_APPCONFIG_URI. /// internal static class AzureAppConfigurationExtensions diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs index 734261833..95576462f 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs @@ -45,7 +45,7 @@ static void BuildAndRun(string[] args) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Conditional( - condition: x => builder.Environment.IsDevelopment(), + condition: _ => builder.Environment.IsDevelopment(), configureSink: x => x.Console(formatProvider: CultureInfo.InvariantCulture)) .WriteTo.ApplicationInsights( services.GetRequiredService(), @@ -69,7 +69,7 @@ static void BuildAndRun(string[] args) }); }); }) - .AddSingleton(x => new PostgresCdcSSubscriptionOptions + .AddSingleton(_ => new PostgresCdcSSubscriptionOptions ( ConnectionString: builder.Configuration["Infrastructure:DialogDbConnectionString"]!, ReplicationSlotName: builder.Configuration["ReplicationSlotName"]!, diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs index e883422a8..4888be2ca 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs @@ -68,9 +68,7 @@ public void SoftDelete() } public void OnCreate(AggregateNode self, DateTimeOffset utcNow) - { - _domainEvents.Add(new DialogCreatedDomainEvent(Id, ServiceResource, Party)); - } + => _domainEvents.Add(new DialogCreatedDomainEvent(Id, ServiceResource, Party)); public void OnUpdate(AggregateNode self, DateTimeOffset utcNow) { @@ -88,9 +86,7 @@ x.Entity is not DialogSearchTag && } public void OnDelete(AggregateNode self, DateTimeOffset utcNow) - { - _domainEvents.Add(new DialogDeletedDomainEvent(Id, ServiceResource, Party)); - } + => _domainEvents.Add(new DialogDeletedDomainEvent(Id, ServiceResource, Party)); public void UpdateSeenAt(string endUserId, string? endUserName) { diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs index 1c6bd08d6..eeb272953 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs @@ -121,7 +121,7 @@ private List GetOrCreateClaimsBasedOnEndUserId(string? endUserId) return claims; } - private static readonly JsonSerializerOptions _serializerOptions = new() + private static readonly JsonSerializerOptions SerializerOptions = new() { PropertyNameCaseInsensitive = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault @@ -130,7 +130,7 @@ private List GetOrCreateClaimsBasedOnEndUserId(string? endUserId) private async Task SendRequest(XacmlJsonRequestRoot xacmlJsonRequest, CancellationToken cancellationToken) { const string apiUrl = "authorization/api/v1/authorize"; - var requestJson = JsonSerializer.Serialize(xacmlJsonRequest, _serializerOptions); + var requestJson = JsonSerializer.Serialize(xacmlJsonRequest, SerializerOptions); _logger.LogDebug("Generated XACML request: {RequestJson}", requestJson); var httpContent = new StringContent(requestJson, Encoding.UTF8, "application/json"); @@ -147,6 +147,6 @@ private List GetOrCreateClaimsBasedOnEndUserId(string? endUserId) } var responseData = await response.Content.ReadAsStringAsync(cancellationToken); - return JsonSerializer.Deserialize(responseData, _serializerOptions); + return JsonSerializer.Deserialize(responseData, SerializerOptions); } } diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/NameRegistryClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/NameRegistryClient.cs index e8e385470..da0579782 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/NameRegistryClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/NameRegistryClient.cs @@ -11,14 +11,14 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.NameRegistry; internal class NameRegistryClient : INameRegistry { - private static readonly DistributedCacheEntryOptions _oneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; - private static readonly DistributedCacheEntryOptions _zeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; + private static readonly DistributedCacheEntryOptions OneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; + private static readonly DistributedCacheEntryOptions ZeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; private readonly IFusionCache _cache; private readonly HttpClient _client; private readonly ILogger _logger; - private static readonly JsonSerializerOptions _serializerOptions = new() + private static readonly JsonSerializerOptions SerializerOptions = new() { PropertyNameCaseInsensitive = true, @@ -36,7 +36,7 @@ public NameRegistryClient(HttpClient client, IFusionCacheProvider cacheProvider, { return await _cache.GetOrSetAsync( $"Name_{personalIdentificationNumber}", - (ct) => GetNameFromRegister(personalIdentificationNumber, ct), + ct => GetNameFromRegister(personalIdentificationNumber, ct), token: cancellationToken); } @@ -51,7 +51,7 @@ public NameRegistryClient(HttpClient client, IFusionCacheProvider cacheProvider, ] }; - var requestJson = JsonSerializer.Serialize(nameLookup, _serializerOptions); + var requestJson = JsonSerializer.Serialize(nameLookup, SerializerOptions); var httpContent = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await _client.PostAsync(apiUrl, httpContent, cancellationToken); @@ -67,7 +67,7 @@ public NameRegistryClient(HttpClient client, IFusionCacheProvider cacheProvider, } var responseData = await response.Content.ReadAsStringAsync(cancellationToken); - var nameLookupResult = JsonSerializer.Deserialize(responseData, _serializerOptions); + var nameLookupResult = JsonSerializer.Deserialize(responseData, SerializerOptions); return nameLookupResult?.PartyNames.FirstOrDefault()?.Name; } diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/OrganizationRegistryClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/OrganizationRegistryClient.cs index 8c5c4e349..131f6a322 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/OrganizationRegistryClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/OrganizationRegistry/OrganizationRegistryClient.cs @@ -9,8 +9,8 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.OrganizationRegistry; internal class OrganizationRegistryClient : IOrganizationRegistry { private const string OrgShortNameReferenceCacheKey = "OrgShortNameReference"; - private static readonly DistributedCacheEntryOptions _oneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; - private static readonly DistributedCacheEntryOptions _zeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; + private static readonly DistributedCacheEntryOptions OneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; + private static readonly DistributedCacheEntryOptions ZeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; private readonly IFusionCache _cache; private readonly HttpClient _client; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs index 0e9c81d5c..7d4595eff 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs @@ -10,8 +10,8 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.ResourceRegistry; internal sealed class ResourceRegistryClient : IResourceRegistry { private const string OrgResourceReferenceCacheKey = "OrgResourceReference"; - private static readonly DistributedCacheEntryOptions _oneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; - private static readonly DistributedCacheEntryOptions _zeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; + private static readonly DistributedCacheEntryOptions OneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) }; + private static readonly DistributedCacheEntryOptions ZeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue }; private readonly IFusionCache _cache; private readonly HttpClient _client; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Serialization/SerializerOptions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Serialization/SerializerOptions.cs index b07ae7b3c..fcf6e1f2d 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Serialization/SerializerOptions.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Serialization/SerializerOptions.cs @@ -7,7 +7,7 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Common.Serialization; internal static class SerializerOptions { - private static readonly Lazy _polymorphismOptions = new(() => + private static readonly Lazy PolymorphismOptions = new(() => { var options = new JsonPolymorphismOptions(); var domainEventType = typeof(IDomainEvent); @@ -45,7 +45,7 @@ private static void DomainEventModifier(JsonTypeInfo typeInfo) return; } - typeInfo.PolymorphismOptions = _polymorphismOptions.Value; + typeInfo.PolymorphismOptions = PolymorphismOptions.Value; } } @@ -54,4 +54,3 @@ internal class LowerCaseNamingPolicy : JsonNamingPolicy public override string ConvertName(string name) => name.ToLowerInvariant(); } - diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs index bf9aab711..0fa59ac00 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs @@ -74,15 +74,15 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi services.ConfigureFusionCache(nameof(Altinn.NameRegistry), new() { - Duration = TimeSpan.FromDays(1), + Duration = TimeSpan.FromDays(1) }) .ConfigureFusionCache(nameof(Altinn.ResourceRegistry), new() { - Duration = TimeSpan.FromMinutes(20), + Duration = TimeSpan.FromMinutes(20) }) .ConfigureFusionCache(nameof(Altinn.OrganizationRegistry), new() { - Duration = TimeSpan.FromDays(1), + Duration = TimeSpan.FromDays(1) }); services.AddDbContext((services, options) => diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/ContextDesignTimeFactory.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/ContextDesignTimeFactory.cs index 59aa577c1..204c47f7b 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/ContextDesignTimeFactory.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/ContextDesignTimeFactory.cs @@ -14,6 +14,7 @@ public DialogDbContext CreateDbContext(string[] args) var localPostgresConnectionString = new ConfigurationBuilder() .AddUserSecrets(Assembly.GetExecutingAssembly(), true) .Build()[ConnectionStringConfigName]; + return new(new DbContextOptionsBuilder() .UseNpgsql(localPostgresConnectionString) .Options); diff --git a/src/Digdir.Domain.Dialogporten.Service/Consumers/OutboxMessages/OutboxMessageConsumerDefinition.cs b/src/Digdir.Domain.Dialogporten.Service/Consumers/OutboxMessages/OutboxMessageConsumerDefinition.cs index 44f53771d..69925382a 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Consumers/OutboxMessages/OutboxMessageConsumerDefinition.cs +++ b/src/Digdir.Domain.Dialogporten.Service/Consumers/OutboxMessages/OutboxMessageConsumerDefinition.cs @@ -7,8 +7,5 @@ public sealed class OutboxMessageConsumerDefinition : ConsumerDefinition consumerConfigurator, - IRegistrationContext context) - { - endpointConfigurator.ConfigureConsumeTopology = false; - } + IRegistrationContext context) => endpointConfigurator.ConfigureConsumeTopology = false; } diff --git a/src/Digdir.Domain.Dialogporten.Service/Program.cs b/src/Digdir.Domain.Dialogporten.Service/Program.cs index bd96a720b..aab622cba 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Program.cs +++ b/src/Digdir.Domain.Dialogporten.Service/Program.cs @@ -9,7 +9,7 @@ using Digdir.Domain.Dialogporten.Service; // TODO: Add AppConfiguration and key vault -// TODO: Configure RabbitMQ connection settings +// TODO: Configure RabbitMQ connection settings // TODO: Configure Postgres connection settings // TODO: Improve exceptions thrown in this assembly @@ -49,7 +49,7 @@ static void BuildAndRun(string[] args) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Conditional( - condition: x => builder.Environment.IsDevelopment(), + condition: _ => builder.Environment.IsDevelopment(), configureSink: x => x.Console(formatProvider: CultureInfo.InvariantCulture)) .WriteTo.ApplicationInsights( services.GetRequiredService(), diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/TokenIssuerCache.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/TokenIssuerCache.cs index f582ca58d..d6f9b84fd 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/TokenIssuerCache.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/TokenIssuerCache.cs @@ -35,9 +35,17 @@ public TokenIssuerCache(IOptions apiSettings) private async Task EnsureInitializedAsync() { - if (_initialized) return; + if (_initialized) + { + return; + } + await _initializationSemaphore.WaitAsync(); - if (_initialized) return; + + if (_initialized) + { + return; + } try { @@ -57,8 +65,5 @@ private async Task EnsureInitializedAsync() } } - public void Dispose() - { - _initializationSemaphore.Dispose(); - } + public void Dispose() => _initializationSemaphore.Dispose(); } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AspNetCoreOpenApiDocumentGeneratorSettingsExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AspNetCoreOpenApiDocumentGeneratorSettingsExtensions.cs index ae636709d..22f251eb3 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AspNetCoreOpenApiDocumentGeneratorSettingsExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AspNetCoreOpenApiDocumentGeneratorSettingsExtensions.cs @@ -15,7 +15,7 @@ public static AspNetCoreOpenApiDocumentGeneratorSettings CleanupPaginatedLists( settings.OperationProcessors.Add(new PaginatedListParametersProcessor()); // Attempt to remove the definitions that NSwag generates for this - foreach (var ignoreType in new Type[] + foreach (var ignoreType in new[] { typeof(ContinuationTokenSet<,>), typeof(Order<>), diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AzureAppConfigurationExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AzureAppConfigurationExtensions.cs index 39211d8de..a0c7112e0 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AzureAppConfigurationExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/AzureAppConfigurationExtensions.cs @@ -6,7 +6,7 @@ namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions; /// -/// Wrapper around azure app configuration bootstrapping such that azure app +/// Wrapper around azure app configuration bootstrapping such that azure app /// config is activated through the environment variable AZURE_APPCONFIG_URI. /// internal static class AzureAppConfigurationExtensions diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/EndpointExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/EndpointExtensions.cs index 281d35ac0..911d21bd6 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/EndpointExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/EndpointExtensions.cs @@ -8,8 +8,10 @@ public static class EndpointExtensions { public static Task BadRequestAsync(this IEndpoint ep, ValidationError failure, CancellationToken cancellationToken = default) => ep.BadRequestAsync(failure.Errors, cancellationToken); + public static Task BadRequestAsync(this IEndpoint ep, IEnumerable failures, CancellationToken cancellationToken = default) - => ep.HttpContext.Response.SendErrorsAsync(failures.ToList() ?? [], StatusCodes.Status400BadRequest, cancellation: cancellationToken); + => ep.HttpContext.Response.SendErrorsAsync(failures.ToList(), cancellation: cancellationToken); + public static Task BadRequestAsync(this IEndpoint ep, BadRequest badRequest, CancellationToken cancellationToken = default) => ep.HttpContext.Response.SendErrorsAsync( badRequest.ToValidationResults(), diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/StringExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/StringExtensions.cs index 0c2ed2974..888723e46 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/StringExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/StringExtensions.cs @@ -5,7 +5,5 @@ namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions; internal static class StringExtensions { public static string FormatInvariant(this string pattern, params object[] args) - { - return string.Format(CultureInfo.InvariantCulture, pattern, args); - } + => string.Format(CultureInfo.InvariantCulture, pattern, args); } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Json/DateTimeNotSupportedConverter.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Json/DateTimeNotSupportedConverter.cs index a5e57b8b0..8ff6b1dbd 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Json/DateTimeNotSupportedConverter.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Json/DateTimeNotSupportedConverter.cs @@ -12,11 +12,8 @@ internal sealed class DateTimeNotSupportedConverter : JsonConverter "error as a consumer."; public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - throw new NotSupportedException(ErrorMessage); - } + => throw new NotSupportedException(ErrorMessage); + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - throw new NotSupportedException(ErrorMessage); - } -} \ No newline at end of file + => throw new NotSupportedException(ErrorMessage); +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/MetadataGroup.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/MetadataGroup.cs index 5c1882870..e12c50162 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/MetadataGroup.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/MetadataGroup.cs @@ -7,7 +7,7 @@ public sealed class MetadataGroup : Group private const string GroupName = "Metadata"; public MetadataGroup() { - Configure(String.Empty, ep => + Configure(string.Empty, ep => { ep.DontAutoTag(); ep.Description(x => x.WithTags(GroupName)); diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs index 443b5e5ec..b2dc22d22 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs @@ -144,7 +144,7 @@ public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) => Url = new Uri("https://example.com/some-api-action"), DocumentationUrl = new Uri("https://example.com/some-api-action-doc"), RequestSchema = new Uri("https://example.com/some-api-action-request-schema"), - ResponseSchema = new Uri("https://example.com/some-api-action-response-schema"), + ResponseSchema = new Uri("https://example.com/some-api-action-response-schema") } ] } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/WellKnown/Jwks/Get/GetJwksEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/WellKnown/Jwks/Get/GetJwksEndpoint.cs index 86c8ed655..0191583f4 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/WellKnown/Jwks/Get/GetJwksEndpoint.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/WellKnown/Jwks/Get/GetJwksEndpoint.cs @@ -26,7 +26,7 @@ public override async Task HandleAsync(CancellationToken ct) { var result = await _sender.Send(new GetJwksQuery(), ct); - HttpContext.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + HttpContext.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue { Public = true, MaxAge = TimeSpan.FromHours(24) diff --git a/src/Digdir.Library.Entity.Abstractions/Features/Aggregate/AggregateNode.cs b/src/Digdir.Library.Entity.Abstractions/Features/Aggregate/AggregateNode.cs index 14f02f63a..300597e6a 100644 --- a/src/Digdir.Library.Entity.Abstractions/Features/Aggregate/AggregateNode.cs +++ b/src/Digdir.Library.Entity.Abstractions/Features/Aggregate/AggregateNode.cs @@ -7,9 +7,9 @@ namespace Digdir.Library.Entity.Abstractions.Features.Aggregate; /// public abstract class AggregateNode { - private static readonly Type _openGenericAggregateNodeType = typeof(AggregateNode<>); - private readonly HashSet _children = new(); - private readonly HashSet _parents = new(); + private static readonly Type OpenGenericAggregateNodeType = typeof(AggregateNode<>); + private readonly HashSet _children = []; + private readonly HashSet _parents = []; private readonly List _modifiedProperties; /// @@ -25,7 +25,7 @@ public abstract class AggregateNode /// /// A collection of modified children. A child node is modified if it itself /// is modified, or one of its children are modified. Modified in this - /// context meens added, modified, or deleted. + /// context means added, modified, or deleted. /// public IReadOnlyCollection Children => _children; @@ -78,7 +78,7 @@ internal static AggregateNode Create(Type type, object entity, AggregateNodeStat } var nodeArguments = new[] { entity, state, modifiedProperties }; - var genericType = _openGenericAggregateNodeType.MakeGenericType(type); + var genericType = OpenGenericAggregateNodeType.MakeGenericType(type); var node = (AggregateNode)Activator.CreateInstance(genericType, BindingFlags.NonPublic | BindingFlags.Instance, null, nodeArguments, null)!; return node; diff --git a/src/Digdir.Library.Entity.Abstractions/Features/Lookup/AbstractLookupEntity.cs b/src/Digdir.Library.Entity.Abstractions/Features/Lookup/AbstractLookupEntity.cs index 90701c066..33ca30877 100644 --- a/src/Digdir.Library.Entity.Abstractions/Features/Lookup/AbstractLookupEntity.cs +++ b/src/Digdir.Library.Entity.Abstractions/Features/Lookup/AbstractLookupEntity.cs @@ -102,10 +102,7 @@ public override bool Equals(object? obj) } /// - public override int GetHashCode() - { - return Id.GetHashCode(); - } + public override int GetHashCode() => Id.GetHashCode(); /// public static explicit operator TEnum(AbstractLookupEntity entity) @@ -119,8 +116,5 @@ public static explicit operator AbstractLookupEntity(TEnum id) return GetUninitializedSelf().MapValue(id); } - private static TSelf GetUninitializedSelf() - { - return (TSelf)RuntimeHelpers.GetUninitializedObject(typeof(TSelf)); - } + private static TSelf GetUninitializedSelf() => (TSelf)RuntimeHelpers.GetUninitializedObject(typeof(TSelf)); } diff --git a/src/Digdir.Library.Entity.Abstractions/Features/Updatable/UpdatableExtensions.cs b/src/Digdir.Library.Entity.Abstractions/Features/Updatable/UpdatableExtensions.cs index abcb04525..349b60f43 100644 --- a/src/Digdir.Library.Entity.Abstractions/Features/Updatable/UpdatableExtensions.cs +++ b/src/Digdir.Library.Entity.Abstractions/Features/Updatable/UpdatableExtensions.cs @@ -11,7 +11,5 @@ public static class UpdatableExtensions /// The to update. /// The update time in UTC. public static void Update(this IUpdateableEntity updateable, DateTimeOffset utcNow) - { - updateable.UpdatedAt = utcNow; - } + => updateable.UpdatedAt = utcNow; } diff --git a/src/Digdir.Library.Entity.EntityFrameworkCore/EntityLibraryEfCoreExtensions.cs b/src/Digdir.Library.Entity.EntityFrameworkCore/EntityLibraryEfCoreExtensions.cs index 687b334b0..afcbf1da2 100644 --- a/src/Digdir.Library.Entity.EntityFrameworkCore/EntityLibraryEfCoreExtensions.cs +++ b/src/Digdir.Library.Entity.EntityFrameworkCore/EntityLibraryEfCoreExtensions.cs @@ -115,10 +115,8 @@ public static PropertyBuilder HasUnlimitedLength(this PropertyBuilder(this ModelBuilder modelBuilder, Action buildAction) where TEntity : class - { - return modelBuilder.EntitiesOfType(typeof(TEntity), buildAction); - } + internal static ModelBuilder EntitiesOfType(this ModelBuilder modelBuilder, Action buildAction) + where TEntity : class => modelBuilder.EntitiesOfType(typeof(TEntity), buildAction); internal static ModelBuilder EntitiesOfType(this ModelBuilder modelBuilder, Type type, Action buildAction) diff --git a/src/Digdir.Library.Entity.EntityFrameworkCore/Features/Lookup/LookupEntityExtensions.cs b/src/Digdir.Library.Entity.EntityFrameworkCore/Features/Lookup/LookupEntityExtensions.cs index 27a1e20dd..a629c3ece 100644 --- a/src/Digdir.Library.Entity.EntityFrameworkCore/Features/Lookup/LookupEntityExtensions.cs +++ b/src/Digdir.Library.Entity.EntityFrameworkCore/Features/Lookup/LookupEntityExtensions.cs @@ -9,7 +9,7 @@ namespace Digdir.Library.Entity.EntityFrameworkCore.Features.Lookup; internal static class LookupEntityExtensions { - private static readonly ConcurrentDictionary _lookupEntityMethodCache = new(); + private static readonly ConcurrentDictionary LookupEntityMethodCache = new(); public static ModelBuilder AddLookupEntities(this ModelBuilder modelBuilder) { @@ -54,7 +54,7 @@ private static IEnumerable GetLookupEntityValues(Type type) private static bool TryGetLookupValueMethodInfo(this Type type, [NotNullWhen(true)] out MethodInfo? methodInfo) { - methodInfo = _lookupEntityMethodCache.GetOrAdd(type, GetLookupEntityMethod); + methodInfo = LookupEntityMethodCache.GetOrAdd(type, GetLookupEntityMethod); return methodInfo is not null; } diff --git a/src/Digdir.Library.Entity.EntityFrameworkCore/Features/SoftDeletable/SoftDeletableExtensions.cs b/src/Digdir.Library.Entity.EntityFrameworkCore/Features/SoftDeletable/SoftDeletableExtensions.cs index 465299af7..26c2c75ce 100644 --- a/src/Digdir.Library.Entity.EntityFrameworkCore/Features/SoftDeletable/SoftDeletableExtensions.cs +++ b/src/Digdir.Library.Entity.EntityFrameworkCore/Features/SoftDeletable/SoftDeletableExtensions.cs @@ -26,10 +26,7 @@ public static class SoftDeletableExtensions /// access to change tracking information and operations for the entity. /// public static EntityEntry HardRemove(this DbSet set, TSoftDeletableEntity entity) - where TSoftDeletableEntity : class, ISoftDeletableEntity - { - return set.Remove(entity); - } + where TSoftDeletableEntity : class, ISoftDeletableEntity => set.Remove(entity); /// /// Marks a as hard deleted. @@ -146,8 +143,5 @@ private static List> AssertNoModifiedSoftDelet } private static void EnableSoftDeletableQueryFilter_Internal(ModelBuilder modelBuilder) - where T : class, ISoftDeletableEntity - { - modelBuilder.Entity().HasQueryFilter(x => !x.Deleted); - } + where T : class, ISoftDeletableEntity => modelBuilder.Entity().HasQueryFilter(x => !x.Deleted); } 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 d9932bea4..b8e7a7483 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs @@ -106,7 +106,7 @@ private static IOptions CreateApplicationSettingsSubstitute Kid = "kid2", PrivateComponent = "private2", PublicComponent = "public2" - }, + } } } }); diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Dialogs/Commands/CreateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Dialogs/Commands/CreateDialogTests.cs index 109c15fce..34bd91a49 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Dialogs/Commands/CreateDialogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Dialogs/Commands/CreateDialogTests.cs @@ -20,7 +20,7 @@ public async Task Create_CreatesDialog_WhenDialogIsSimple() var response = await Application.Send(createCommand); // Assert - response.TryPickT0(out var success, out var _).Should().BeTrue(); + response.TryPickT0(out var success, out _).Should().BeTrue(); success.Value.Should().Be(expectedDialogId); } @@ -36,7 +36,7 @@ public async Task Create_CreateDialog_WhenDialogIsComplex() var result = await Application.Send(createDialogCommand); // Assert - result.TryPickT0(out var success, out var _).Should().BeTrue(); + result.TryPickT0(out var success, out _).Should().BeTrue(); success.Value.Should().Be(expectedDialogId); } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Ed25519GeneratorTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Ed25519GeneratorTests.cs index ab6c78bc8..9c2231deb 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Ed25519GeneratorTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Ed25519GeneratorTests.cs @@ -27,7 +27,7 @@ public void ValidJwsIsGenerated() Kid = "unittestkeypair2", PrivateComponent = "", PublicComponent = "" - }, + } } } }; diff --git a/tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests/DecisionRequestHelperTests.cs b/tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests/DecisionRequestHelperTests.cs index 6d4eceb31..56bf62603 100644 --- a/tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests/DecisionRequestHelperTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests/DecisionRequestHelperTests.cs @@ -183,12 +183,8 @@ private static XacmlJsonResponse CreateMockedXamlJsonResponse(XacmlJsonRequestRo } private static List GetAsClaims(params (string, string)[] claims) - { - return claims.Select(c => new Claim(c.Item1, c.Item2)).ToList(); - } + => claims.Select(c => new Claim(c.Item1, c.Item2)).ToList(); - private static bool ContainsSameElements(IEnumerable collection, IEnumerable expectedElements) - { - return expectedElements.All(expected => collection.Contains(expected)) && collection.Count() == expectedElements.Count(); - } + private static bool ContainsSameElements(IEnumerable collection, IEnumerable expectedElements) => + expectedElements.All(collection.Contains) && collection.Count() == expectedElements.Count(); } From 85fdf92778d0e008de4cd9c4eaee741bf91ecc18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 8 Apr 2024 11:14:59 +0200 Subject: [PATCH 10/13] chore: Add seen log to service owner (#604) Not marking as feat., there is already a PR merged that adds the SeenLog split in the changelog. Will edit changelog with more notes before merging 1.4.0 --- docs/swagger/V1/swagger.verified.json | 70 +++++++++++++++++-- .../Dialogs/Queries/Get/GetDialogDto.cs | 4 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 4 +- .../Dialogs/Queries/Get/MappingProfile.cs | 2 +- .../Dialogs/Queries/Search/MappingProfile.cs | 2 +- .../Dialogs/Queries/Search/SearchDialogDto.cs | 4 +- .../Queries/Search/SearchDialogQuery.cs | 4 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 13 ++++ .../Dialogs/Queries/Get/GetDialogQuery.cs | 32 +++++++-- .../Dialogs/Queries/Get/MappingProfile.cs | 5 +- .../Dialogs/Queries/Search/MappingProfile.cs | 7 ++ .../Dialogs/Queries/Search/SearchDialogDto.cs | 12 ++++ .../Queries/Search/SearchDialogQuery.cs | 21 +++++- 13 files changed, 158 insertions(+), 22 deletions(-) diff --git a/docs/swagger/V1/swagger.verified.json b/docs/swagger/V1/swagger.verified.json index 5faf7ca90..882c5df4d 100644 --- a/docs/swagger/V1/swagger.verified.json +++ b/docs/swagger/V1/swagger.verified.json @@ -2602,6 +2602,12 @@ "items": { "$ref": "#/components/schemas/SearchDialogContentDtoSO" } + }, + "seenSinceLastUpdate": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchDialogDialogSeenRecordDtoSO" + } } } }, @@ -2620,6 +2626,31 @@ } } }, + "SearchDialogDialogSeenRecordDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "endUserIdHash": { + "type": "string" + }, + "endUserName": { + "type": "string", + "nullable": true + }, + "isCurrentEndUser": { + "type": "boolean", + "nullable": true + } + } + }, "GetDialogDtoSO": { "type": "object", "additionalProperties": false, @@ -2721,6 +2752,12 @@ "items": { "$ref": "#/components/schemas/GetDialogDialogActivityDtoSO" } + }, + "seenSinceLastUpdate": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetDialogDialogSeenRecordDtoSO" + } } } }, @@ -2961,6 +2998,31 @@ } } }, + "GetDialogDialogSeenRecordDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "endUserIdHash": { + "type": "string" + }, + "endUserName": { + "type": "string", + "nullable": true + }, + "isCurrentEndUser": { + "type": "boolean", + "nullable": true + } + } + }, "CreateDialogCommand": { "type": "object", "additionalProperties": false, @@ -3471,7 +3533,7 @@ "$ref": "#/components/schemas/SearchDialogContentDto" } }, - "seenLog": { + "seenSinceLastUpdate": { "type": "array", "items": { "$ref": "#/components/schemas/SearchDialogDialogSeenRecordDto" @@ -3559,7 +3621,7 @@ "type": "string", "nullable": true }, - "isAuthenticatedUser": { + "isCurrentEndUser": { "type": "boolean" } } @@ -3658,7 +3720,7 @@ "$ref": "#/components/schemas/GetDialogDialogActivityDto" } }, - "seenLog": { + "seenSinceLastUpdate": { "type": "array", "items": { "$ref": "#/components/schemas/GetDialogDialogSeenRecordDto" @@ -3924,7 +3986,7 @@ "type": "string", "nullable": true }, - "isAuthenticatedUser": { + "isCurrentEndUser": { "type": "boolean" } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs index 3d7a04d56..e4056533f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogDto.cs @@ -33,7 +33,7 @@ public sealed class GetDialogDto public List GuiActions { get; set; } = []; public List ApiActions { get; set; } = []; public List Activities { get; set; } = []; - public List SeenLog { get; set; } = []; + public List SeenSinceLastUpdate { get; set; } = []; } public class GetDialogDialogSeenRecordDto @@ -45,7 +45,7 @@ public class GetDialogDialogSeenRecordDto public string? EndUserName { get; set; } - public bool IsAuthenticatedUser { get; set; } + public bool IsCurrentEndUser { get; set; } } public sealed class GetDialogContentDto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs index cc1ebeb10..b5ade7164 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/GetDialogQuery.cs @@ -117,11 +117,11 @@ public async Task Handle(GetDialogQuery request, CancellationTo var dialogDto = _mapper.Map(dialog); - dialogDto.SeenLog = dialog.SeenLog + dialogDto.SeenSinceLastUpdate = dialog.SeenLog .Select(log => { var logDto = _mapper.Map(log); - logDto.IsAuthenticatedUser = log.EndUserId == userPid; + logDto.IsCurrentEndUser = log.EndUserId == userPid; logDto.EndUserIdHash = _stringHasher.Hash(log.EndUserId); return logDto; }) diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs index c3ca7b3b4..44528f860 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs @@ -14,7 +14,7 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Revision, opt => opt.MapFrom(src => src.Revision)) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)) - .ForMember(dest => dest.SeenLog, opt => opt.Ignore()); + .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore()); CreateMap(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs index a4cc6015b..8987b0c99 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs @@ -16,7 +16,7 @@ public MappingProfile() .OrderByDescending(activity => activity.CreatedAt).ThenByDescending(activity => activity.Id) .FirstOrDefault() )) - .ForMember(dest => dest.SeenLog, opt => opt.MapFrom(src => src.SeenLog + .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.MapFrom(src => src.SeenLog .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) .OrderByDescending(x => x.CreatedAt) )) diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs index 0e62cf7a8..fafb9fff0 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDto.cs @@ -23,7 +23,7 @@ public sealed class SearchDialogDto public SearchDialogDialogActivityDto? LatestActivity { get; set; } public List Content { get; set; } = []; - public List SeenLog { get; set; } = []; + public List SeenSinceLastUpdate { get; set; } = []; } public class SearchDialogDialogSeenRecordDto @@ -35,7 +35,7 @@ public class SearchDialogDialogSeenRecordDto public string? EndUserName { get; set; } - public bool IsAuthenticatedUser { get; set; } + public bool IsCurrentEndUser { get; set; } } public sealed class SearchDialogContentDto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs index 2bcdf6c75..f30f26dc8 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs @@ -177,10 +177,10 @@ public async Task Handle(SearchDialogQuery request, Cancella .ProjectTo(_mapper.ConfigurationProvider) .ToPaginatedListAsync(request, cancellationToken: cancellationToken); - foreach (var seenLog in paginatedList.Items.SelectMany(x => x.SeenLog)) + foreach (var seenLog in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) { // Before we hash the end user id, check if the seen log entry is for the current user - seenLog.IsAuthenticatedUser = userPid == seenLog.EndUserIdHash; + seenLog.IsCurrentEndUser = userPid == seenLog.EndUserIdHash; // TODO: Add test to not expose un-hashed end user id to the client // https://github.com/digdir/dialogporten/issues/596 seenLog.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserIdHash); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs index 683ec16c3..caf584dfd 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs @@ -35,6 +35,19 @@ public sealed class GetDialogDto public List GuiActions { get; set; } = []; public List ApiActions { get; set; } = []; public List Activities { get; set; } = []; + public List SeenSinceLastUpdate { get; set; } = []; +} + +public class GetDialogDialogSeenRecordDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + + public string EndUserIdHash { get; set; } = null!; + + public string? EndUserName { get; set; } + + public bool? IsCurrentEndUser { get; set; } } public sealed class GetDialogContentDto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs index 33898db80..a946a0a19 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogQuery.cs @@ -1,5 +1,4 @@ using AutoMapper; -using AutoMapper.QueryableExtensions; using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; using Digdir.Domain.Dialogporten.Application.Externals; @@ -23,15 +22,18 @@ internal sealed class GetDialogQueryHandler : IRequestHandler Handle(GetDialogQuery request, CancellationToken cancellationToken) @@ -54,10 +56,14 @@ public async Task Handle(GetDialogQuery request, CancellationTo .ThenInclude(x => x.Title!.Localizations.OrderBy(x => x.CreatedAt).ThenBy(x => x.CultureCode)) .Include(x => x.ApiActions.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) .ThenInclude(x => x.Endpoints.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .Include(x => x.Activities).ThenInclude(x => x.PerformedBy!.Localizations) + .Include(x => x.Activities).ThenInclude(x => x.Description!.Localizations) + .Include(x => x.SeenLog + .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) + .OrderBy(x => x.CreatedAt)) .IgnoreQueryFilters() - .AsNoTracking() + .AsNoTracking() // TODO: Remove when #386 is implemented .Where(x => resourceIds.Contains(x.ServiceResource)) - .ProjectTo(_mapper.ConfigurationProvider) .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken); if (dialog is null) @@ -65,6 +71,22 @@ public async Task Handle(GetDialogQuery request, CancellationTo return new EntityNotFound(request.DialogId); } - return dialog; + // TODO: Add SeenLog if optional parameter pid on behalf of end user is present + // https://github.com/digdir/dialogporten/issues/386 + + var dialogDto = _mapper.Map(dialog); + + dialogDto.SeenSinceLastUpdate = dialog.SeenLog + .Select(log => + { + var logDto = _mapper.Map(log); + // TODO: Set when #386 is implemented + // logDto.IsAuthenticatedUser = log.EndUserId == userPid; + logDto.EndUserIdHash = _stringHasher.Hash(log.EndUserId); + return logDto; + }) + .ToList(); + + return dialogDto; } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs index 081e4bbb5..1500a9f9b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs @@ -13,7 +13,10 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.Revision, opt => opt.MapFrom(src => src.Revision)) - .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)); + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)) + .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore()); + + CreateMap(); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs index a7cd3871a..09f3a90dc 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs @@ -9,9 +9,16 @@ internal sealed class MappingProfile : Profile public MappingProfile() { CreateMap() + .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.MapFrom(src => src.SeenLog + .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) + .OrderByDescending(x => x.CreatedAt) + )) .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.Content.Where(x => x.Type.OutputInList))) .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)); + CreateMap() + .ForMember(dest => dest.EndUserIdHash, opt => opt.MapFrom(src => src.EndUserId)); + CreateMap(); CreateMap() diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs index 53f3ab060..2e4135f04 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDto.cs @@ -21,6 +21,18 @@ public sealed class SearchDialogDto public DialogStatus.Values Status { get; set; } public List Content { get; set; } = []; + public List SeenSinceLastUpdate { get; set; } = []; +} + +public class SearchDialogDialogSeenRecordDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + + public string EndUserIdHash { get; set; } = null!; + + public string? EndUserName { get; set; } + public bool? IsCurrentEndUser { get; set; } } public sealed class SearchDialogSearchTagDto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogQuery.cs index fbc69ca43..724c504d4 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogQuery.cs @@ -123,17 +123,20 @@ internal sealed class SearchDialogQueryHandler : IRequestHandler Handle(SearchDialogQuery request, CancellationToken cancellationToken) @@ -174,8 +177,22 @@ public async Task Handle(SearchDialogQuery request, Cancella query = query.WhereUserIsAuthorizedFor(authorizedResources); } - return await query + var paginatedList = await query .ProjectTo(_mapper.ConfigurationProvider) .ToPaginatedListAsync(request, cancellationToken: cancellationToken); + + foreach (var seenRecord in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) + { + if (request.EndUserId is not null) + { + seenRecord.IsCurrentEndUser = seenRecord.EndUserIdHash == request.EndUserId; + } + + // TODO: Add test to not expose un-hashed end user id to the client + // https://github.com/digdir/dialogporten/issues/596 + seenRecord.EndUserIdHash = _stringHasher.Hash(seenRecord.EndUserIdHash); + } + + return paginatedList; } } From dc339e77e16c4e1014145a6811474ba15fef2b20 Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Mon, 8 Apr 2024 11:18:37 +0100 Subject: [PATCH 11/13] feat(service): use in-memory transport instead of rabbitmq (#602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to #50 - Remove all traces of RabbitMQ - Add in-memory transport if specified in settings --------- Co-authored-by: Ole Jørgen Skogstad --- .env | 4 --- .github/workflows/action-publish.yml | 2 -- RabbitMq/Dockerfile | 8 ----- RabbitMq/enabled_plugins | 1 - docker-compose-no-webapi.yml | 24 -------------- docker-compose.yml | 1 + enabled_plugins | 1 - ...main.Dialogporten.ChangeDataCapture.csproj | 9 +++-- .../ICdcSink.cs | 9 ----- .../Program.cs | 18 ++++++---- .../appsettings.Development.json | 6 ++-- .../Digdir.Domain.Dialogporten.Service.csproj | 9 +++-- .../Program.cs | 33 +++++++------------ .../appsettings.Development.json | 6 ++-- 14 files changed, 36 insertions(+), 95 deletions(-) delete mode 100644 RabbitMq/Dockerfile delete mode 100644 RabbitMq/enabled_plugins delete mode 100644 enabled_plugins diff --git a/.env b/.env index dfb9acb1c..cbd2e3101 100644 --- a/.env +++ b/.env @@ -4,8 +4,4 @@ POSTGRES_PASSWORD=supersecret POSTGRES_DB=Dialogporten DB_CONNECTION_STRING=Server=dialogporten-postgres;Port=5432;Database=${POSTGRES_DB};User ID=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}; -RABBITMQ_USER=guest -RABBITMQ_PASSWORD=guest -RABBITMQ_HOST=dialogporten-rabbitmq - COMPOSE_PROJECT_NAME=digdir diff --git a/.github/workflows/action-publish.yml b/.github/workflows/action-publish.yml index 050603f05..8725ea3c4 100644 --- a/.github/workflows/action-publish.yml +++ b/.github/workflows/action-publish.yml @@ -33,8 +33,6 @@ jobs: imageName: cdc - dockerfile: ./src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile imageName: migration-bundle - - dockerfile: ./RabbitMq/Dockerfile - imageName: rabbitmq permissions: contents: read diff --git a/RabbitMq/Dockerfile b/RabbitMq/Dockerfile deleted file mode 100644 index b7df6977c..000000000 --- a/RabbitMq/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM rabbitmq:3-management - -COPY ./RabbitMq/enabled_plugins /etc/rabbitmq/enabled_plugins - -RUN chown -R rabbitmq:rabbitmq /etc/rabbitmq/enabled_plugins - -# What ports should be exposed? -EXPOSE 4369 5671 5672 15671 15672 15692 25672 35672-35682 diff --git a/RabbitMq/enabled_plugins b/RabbitMq/enabled_plugins deleted file mode 100644 index 085db6b54..000000000 --- a/RabbitMq/enabled_plugins +++ /dev/null @@ -1 +0,0 @@ -[rabbitmq_management, rabbitmq_prometheus, rabbitmq_shovel, rabbitmq_shovel_management]. \ No newline at end of file diff --git a/docker-compose-no-webapi.yml b/docker-compose-no-webapi.yml index 1fe466a04..5883d4a89 100644 --- a/docker-compose-no-webapi.yml +++ b/docker-compose-no-webapi.yml @@ -16,20 +16,6 @@ services: interval: 2s timeout: 20s retries: 5 - - dialogporten-rabbitmq: - build: - context: . - dockerfile: RabbitMq/Dockerfile - restart: always - ports: - - "5672:5672" - - "15672:15672" - healthcheck: - test: [ "CMD-SHELL", "rabbitmq-diagnostics check_running" ] - interval: 5s - timeout: 20s - retries: 5 dialogporten-redis: image: redis:6.0-alpine @@ -50,14 +36,9 @@ services: depends_on: dialogporten-postgres: condition: service_healthy - dialogporten-rabbitmq: - condition: service_healthy environment: - Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING} - ASPNETCORE_ENVIRONMENT=Development - - RabbitMq:Host=${RABBITMQ_HOST} - - RabbitMq:Username=${RABBITMQ_USERNAME} - - RabbitMq:Password=${RABBITMQ_PASSWORD} dialogporten-cdc: build: @@ -67,14 +48,9 @@ services: depends_on: dialogporten-postgres: condition: service_healthy - dialogporten-rabbitmq: - condition: service_healthy environment: - ASPNETCORE_ENVIRONMENT=Development - Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING} - ReplicationSlotName=outboxmessage_replication_slot - PublicationName=outboxmessage_publication - TableName=OutboxMessage - - RabbitMq:Host=${RABBITMQ_HOST} - - RabbitMq:Username=${RABBITMQ_USERNAME} - - RabbitMq:Password=${RABBITMQ_PASSWORD} diff --git a/docker-compose.yml b/docker-compose.yml index 2e304d8d4..f67750305 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,7 @@ services: environment: - Infrastructure:Redis:ConnectionString=dialogporten-redis:6379 - Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING} + - Application:Dialogporten:BaseUri=http://localhost:7214 - Serilog__WriteTo__0__Name=Console - Serilog__MinimumLevel__Default=Debug - ASPNETCORE_URLS=http://+:8080 diff --git a/enabled_plugins b/enabled_plugins deleted file mode 100644 index 085db6b54..000000000 --- a/enabled_plugins +++ /dev/null @@ -1 +0,0 @@ -[rabbitmq_management, rabbitmq_prometheus, rabbitmq_shovel, rabbitmq_shovel_management]. \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj index 435ca4e42..c17edd073 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj @@ -5,14 +5,13 @@ net8.0 enable enable - 750256a4-f332-4783-8802-8a7d9566f9ca + 750256a4-f332-4783-8802-8a7d9566f9ca - + - + - @@ -23,4 +22,4 @@ - + \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/ICdcSink.cs b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/ICdcSink.cs index 18272eb25..5d065d994 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/ICdcSink.cs +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/ICdcSink.cs @@ -23,15 +23,6 @@ public async Task Send(OutboxMessage outboxMessage, CancellationToken cancellati var endpoint = await _sender.GetSendEndpoint(new Uri("exchange:Digdir.Domain.Dialogporten.Service")); await endpoint.Send( outboxMessage, - context => - { - if (context is not RabbitMqSendContext rabbitMqSendContext) - { - throw new ArgumentException("The context is not a RabbitMQ send context.", nameof(context)); - } - - rabbitMqSendContext.Mandatory = true; - }, cancellationToken); } } diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs index 95576462f..1758fcb5e 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Program.cs @@ -7,7 +7,7 @@ using Microsoft.ApplicationInsights.Extensibility; using Serilog; -// TODO: Configure RabbitMQ connection settings and endpoint exchange +// TODO: Configure Azure Service Bus connection settings and endpoint exchange // TODO: Configure Postgres connection settings // TODO: Improve exceptions thrown in this assembly @@ -59,15 +59,19 @@ static void BuildAndRun(string[] args) .AddHostedService() .AddMassTransit(x => { - x.UsingRabbitMq((context, cfg) => + var useInMemoryTransport = builder.Configuration.GetValue("MassTransit:UseInMemoryTransport"); + + if (useInMemoryTransport) { - const string rabbitMqSection = "RabbitMq"; - cfg.Host(builder.Configuration[$"{rabbitMqSection}:Host"], "/", h => + x.UsingInMemory((context, cfg) => { - h.Username(builder.Configuration[$"{rabbitMqSection}:Username"]); - h.Password(builder.Configuration[$"{rabbitMqSection}:Password"]); + cfg.ConfigureEndpoints(context); }); - }); + } + else + { + // todo: Configure for using Azure Service Bus + } }) .AddSingleton(_ => new PostgresCdcSSubscriptionOptions ( diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/appsettings.Development.json b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/appsettings.Development.json index 818106014..368f04c1c 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/appsettings.Development.json +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/appsettings.Development.json @@ -2,9 +2,7 @@ "ReplicationSlotName": "outboxmessage_replication_slot", "PublicationName": "outboxmessage_publication", "TableName": "OutboxMessage", - "RabbitMq": { - "Host": "localhost", - "Username": "guest", - "Password": "guest" + "MassTransit": { + "UseInMemoryTransport": true } } diff --git a/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj b/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj index 1d51daa54..51d9c0182 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj +++ b/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj @@ -9,16 +9,15 @@ - - + - + - - + + diff --git a/src/Digdir.Domain.Dialogporten.Service/Program.cs b/src/Digdir.Domain.Dialogporten.Service/Program.cs index aab622cba..3a0b4332d 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Program.cs +++ b/src/Digdir.Domain.Dialogporten.Service/Program.cs @@ -9,7 +9,7 @@ using Digdir.Domain.Dialogporten.Service; // TODO: Add AppConfiguration and key vault -// TODO: Configure RabbitMQ connection settings +// TODO: Configure Service bus connection settings // TODO: Configure Postgres connection settings // TODO: Improve exceptions thrown in this assembly @@ -60,29 +60,20 @@ static void BuildAndRun(string[] args) .AddMassTransit(x => { x.AddConsumers(thisAssembly); - x.UsingRabbitMq((context, cfg) => + + var useInMemoryTransport = builder.Configuration.GetValue("MassTransit:UseInMemoryTransport"); + + if (useInMemoryTransport) { - const string rabbitMqSection = "RabbitMq"; - cfg.Host(builder.Configuration[$"{rabbitMqSection}:Host"], "/", h => + x.UsingInMemory((context, cfg) => { - h.Username(builder.Configuration[$"{rabbitMqSection}:Username"]); - h.Password(builder.Configuration[$"{rabbitMqSection}:Password"]); + cfg.ConfigureEndpoints(context); }); - cfg.ReceiveEndpoint(thisAssembly.GetName().Name!, x => - { - x.UseMessageRetry(r => r.Intervals( - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(10))); - // TODO: Add delayed redelivery - but we need a rabbitmq plugin for this - //x.UseDelayedRedelivery(r => r.Intervals( - // TimeSpan.FromMinutes(1), - // TimeSpan.FromMinutes(3), - // TimeSpan.FromMinutes(10))); - x.SetQuorumQueue(); - x.ConfigureConsumers(context); - }); - }); + } + else + { + // todo: Configure for using Azure Service Bus + } }) .AddApplication(builder.Configuration, builder.Environment) .AddInfrastructure(builder.Configuration, builder.Environment) diff --git a/src/Digdir.Domain.Dialogporten.Service/appsettings.Development.json b/src/Digdir.Domain.Dialogporten.Service/appsettings.Development.json index 00042fc37..7544150eb 100644 --- a/src/Digdir.Domain.Dialogporten.Service/appsettings.Development.json +++ b/src/Digdir.Domain.Dialogporten.Service/appsettings.Development.json @@ -5,10 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "RabbitMq": { - "Host": "localhost", - "Username": "guest", - "Password": "guest" + "MassTransit": { + "UseInMemoryTransport": true }, "Infrastructure": { "Redis": { From e7dc1e7a43955cb8c93b7a12c492bf1716b51b3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:37:01 +0200 Subject: [PATCH 12/13] chore(deps): update dependency htmlagilitypack to v1.11.60 (#606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [HtmlAgilityPack](http://html-agility-pack.net/) ([source](https://togithub.com/zzzprojects/html-agility-pack)) | `1.11.59` -> `1.11.60` | [![age](https://developer.mend.io/api/mc/badges/age/nuget/HtmlAgilityPack/1.11.60?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/HtmlAgilityPack/1.11.60?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/HtmlAgilityPack/1.11.59/1.11.60?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/HtmlAgilityPack/1.11.59/1.11.60?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
zzzprojects/html-agility-pack (HtmlAgilityPack) ### [`v1.11.60`](https://togithub.com/zzzprojects/html-agility-pack/releases/tag/v1.11.60) #### Download the library **[here](https://www.nuget.org/packages/HtmlAgilityPack/)** - **FIXED:** Performance optimization with Avoid creating new strings when parsing PcData [#​541](https://togithub.com/zzzprojects/html-agility-pack/issues/541) - **FIXED:** The html rendering result is different from the html output result when we have two unclosed tbody tags [#​540](https://togithub.com/zzzprojects/html-agility-pack/issues/540)
--- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/digdir/dialogporten). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../Digdir.Domain.Dialogporten.Application.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj index 64ee47764..f44fd16c0 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj +++ b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj @@ -13,7 +13,7 @@ - + From 02746c5003ced9d1abe1d9c143cc6491af8085fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Dybvik=20Langfors?= Date: Tue, 9 Apr 2024 11:07:52 +0200 Subject: [PATCH 13/13] chore: create pull request template (#609) --- .github/pull_request_template.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..ede8fa9de --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ + + +## Description + + + +## Related Issue(s) + +- #{issue number} + +## Verification + +- [ ] **Your** code builds clean without any errors or warnings +- [ ] Manual testing done (required) +- [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) + +## Documentation + +- [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable)