From af35e95f7cc986acd5349dd29fa3b6b82dc141e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Dybvik=20Langfors?= Date: Sun, 21 Jul 2024 02:19:56 +0200 Subject: [PATCH 1/4] Initial implementation of actor entity --- .../ApplicationExtensions.cs | 1 - .../Common/IStringHasher.cs | 35 - .../Common/IUserRegistry.cs | 17 +- .../Common/IdentifierMasker.cs | 36 + .../Externals/IPartyNameRegistry.cs | 6 + .../Externals/IPersonNameRegistry.cs | 6 - .../Queries/Get/GetDialogActivityDto.cs | 12 +- .../Queries/Get/GetDialogActivityQuery.cs | 3 + .../Queries/Get/MappingProfile.cs | 6 + .../Queries/Get/GetDialogSeenLogDto.cs | 14 +- .../Queries/Get/GetDialogSeenLogQuery.cs | 7 +- .../Queries/Get/MappingProfile.cs | 6 + .../Queries/Search/MappingProfile.cs | 5 + .../Queries/Search/SearchDialogSeenLogDto.cs | 13 +- .../Search/SearchDialogSeenLogQuery.cs | 10 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 23 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 16 +- .../Dialogs/Queries/Get/MappingProfile.cs | 10 + .../Dialogs/Queries/Search/MappingProfile.cs | 11 +- .../Dialogs/Queries/Search/SearchDialogDto.cs | 23 +- .../Queries/Search/SearchDialogQuery.cs | 9 +- .../Queries/Get/GetDialogActivityDto.cs | 10 +- .../Queries/Get/GetDialogActivityQuery.cs | 3 + .../Queries/Get/MappingProfile.cs | 5 + .../Queries/Get/GetDialogSeenLogDto.cs | 12 +- .../Queries/Get/GetDialogSeenLogQuery.cs | 5 +- .../Queries/Get/MappingProfile.cs | 3 + .../Queries/Search/SearchDialogSeenLogDto.cs | 12 +- .../Search/SearchDialogSeenLogQuery.cs | 5 - .../Commands/Create/CreateDialogCommand.cs | 16 +- .../Create/CreateDialogCommandValidator.cs | 28 +- .../Commands/Create/CreateDialogDto.cs | 10 +- .../Dialogs/Commands/Create/MappingProfile.cs | 5 + .../Dialogs/Commands/Update/MappingProfile.cs | 6 + .../Update/UpdateDialogCommandValidator.cs | 28 +- .../Commands/Update/UpdateDialogDto.cs | 10 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 24 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 8 +- .../Dialogs/Queries/Get/MappingProfile.cs | 4 + .../Dialogs/Queries/Search/MappingProfile.cs | 7 +- .../Dialogs/Queries/Search/SearchDialogDto.cs | 16 +- .../Queries/Search/SearchDialogQuery.cs | 9 +- .../Entities/Activities/DialogActivity.cs | 5 +- .../Dialogs/Entities/Actors/DialogActor.cs | 12 + .../Entities/Actors/DialogActorType.cs | 15 + .../Dialogs/Entities/DialogEntity.cs | 11 +- .../Dialogs/Entities/DialogSeenLog.cs | 16 +- .../Parties/NorwegianPersonIdentifier.cs | 5 +- ...ryClient.cs => PartyNameRegistryClient.cs} | 38 +- .../InfrastructureExtensions.cs | 2 +- .../Persistence/DialogDbContext.cs | 2 + .../20240720163626_AddActor.Designer.cs | 1388 +++++++++++++++++ .../Migrations/20240720163626_AddActor.cs | 213 +++ .../DialogDbContextModelSnapshot.cs | 136 +- .../Update/UpdateDialogSwaggerConfig.cs | 6 +- .../DialogGenerator.cs | 3 +- .../Common/DialogApplication.cs | 6 +- .../EndUser/Dialogs/Queries/SeenLogTests.cs | 22 +- .../Dialogs/Queries/SeenLogTests.cs | 20 +- .../Dialogs/Commands/CreateDialogTests.cs | 7 +- 60 files changed, 2169 insertions(+), 233 deletions(-) delete mode 100644 src/Digdir.Domain.Dialogporten.Application/Common/IStringHasher.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Externals/IPartyNameRegistry.cs delete mode 100644 src/Digdir.Domain.Dialogporten.Application/Externals/IPersonNameRegistry.cs create mode 100644 src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActor.cs create mode 100644 src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActorType.cs rename src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/{PersonNameRegistryClient.cs => PartyNameRegistryClient.cs} (53%) create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.Designer.cs create mode 100644 src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs index dad0484c8..6c506d501 100644 --- a/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs @@ -39,7 +39,6 @@ public static IServiceCollection AddApplication(this IServiceCollection services .AddScoped() // Transient - .AddTransient() .AddTransient() .AddTransient() .AddTransient() diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/IStringHasher.cs b/src/Digdir.Domain.Dialogporten.Application/Common/IStringHasher.cs deleted file mode 100644 index c9a6e04d6..000000000 --- a/src/Digdir.Domain.Dialogporten.Application/Common/IStringHasher.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text; - -namespace Digdir.Domain.Dialogporten.Application.Common; - -internal interface IStringHasher -{ - [return: NotNullIfNotNull(nameof(personIdentifier))] - string? Hash(string? personIdentifier); -} - -internal class PersistentRandomSaltStringHasher : IStringHasher -{ - public const int StringLength = 10; - private const int SaltSize = 16; - private readonly Lazy _lazySalt = new(() => RandomNumberGenerator.GetBytes(SaltSize)); - - public string? Hash(string? personIdentifier) - { - if (string.IsNullOrWhiteSpace(personIdentifier)) - { - return null; - } - - var identifierBytes = Encoding.UTF8.GetBytes(personIdentifier); - Span buffer = stackalloc byte[identifierBytes.Length + _lazySalt.Value.Length]; - identifierBytes.CopyTo(buffer); - _lazySalt.Value.CopyTo(buffer[identifierBytes.Length..]); - - var hashBytes = SHA256.HashData(buffer); - - return BitConverter.ToString(hashBytes, 0, StringLength / 2).Replace("-", "").ToLowerInvariant(); - } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/IUserRegistry.cs b/src/Digdir.Domain.Dialogporten.Application/Common/IUserRegistry.cs index c70d95bd9..7dfdfcd48 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/IUserRegistry.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/IUserRegistry.cs @@ -2,6 +2,7 @@ using Digdir.Domain.Dialogporten.Application.Common.Extensions; using Digdir.Domain.Dialogporten.Application.Externals; using Digdir.Domain.Dialogporten.Application.Externals.Presentation; +using Digdir.Domain.Dialogporten.Domain.Parties; using UserIdType = Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType.Values; namespace Digdir.Domain.Dialogporten.Application.Common; @@ -16,6 +17,14 @@ public sealed class UserId { public required UserIdType Type { get; set; } public required string ExternalId { get; init; } + public string ExternalIdWithPrefix => (Type switch + { + UserIdType.Person or UserIdType.ServiceOwnerOnBehalfOfPerson => NorwegianPersonIdentifier.PrefixWithSeparator, + UserIdType.SystemUser => SystemUserIdentifier.PrefixWithSeparator, + UserIdType.ServiceOwner => NorwegianOrganizationIdentifier.PrefixWithSeparator, + UserIdType.Unknown or UserIdType.LegacySystemUser => string.Empty, + _ => throw new UnreachableException("Unknown UserIdType") + }) + ExternalId; } public sealed class UserInformation @@ -27,14 +36,14 @@ public sealed class UserInformation public class UserRegistry : IUserRegistry { private readonly IUser _user; - private readonly IPersonNameRegistry _personNameRegistry; + private readonly IPartyNameRegistry _partyNameRegistry; public UserRegistry( IUser user, - IPersonNameRegistry personNameRegistry) + IPartyNameRegistry partyNameRegistry) { _user = user ?? throw new ArgumentNullException(nameof(user)); - _personNameRegistry = personNameRegistry ?? throw new ArgumentNullException(nameof(personNameRegistry)); + _partyNameRegistry = partyNameRegistry ?? throw new ArgumentNullException(nameof(partyNameRegistry)); } public UserId GetCurrentUserId() @@ -57,7 +66,7 @@ public async Task GetCurrentUserInformation(CancellationToken c { case UserIdType.Person: case UserIdType.ServiceOwnerOnBehalfOfPerson: - name = await _personNameRegistry.GetName(userId.ExternalId, cancellationToken); + name = await _partyNameRegistry.GetName(userId.ExternalIdWithPrefix, cancellationToken); break; case UserIdType.LegacySystemUser: diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs b/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs new file mode 100644 index 000000000..f5b47f921 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs @@ -0,0 +1,36 @@ + +using System.Security.Cryptography; +using System.Text; +using Digdir.Domain.Dialogporten.Domain.Parties; + +namespace Digdir.Domain.Dialogporten.Application.Common; + +public static class IdentifierMasker +{ + public const int StringLength = 10; + private const int SaltSize = 16; + private static readonly byte[] Salt = RandomNumberGenerator.GetBytes(SaltSize); + + public static string? Hash(string? plaintext) + { + if (string.IsNullOrWhiteSpace(plaintext)) + { + return null; + } + + var identifierBytes = Encoding.UTF8.GetBytes(plaintext); + Span buffer = stackalloc byte[identifierBytes.Length + Salt.Length]; + identifierBytes.CopyTo(buffer); + Salt.CopyTo(buffer[identifierBytes.Length..]); + + var hashBytes = SHA256.HashData(buffer); + + return BitConverter.ToString(hashBytes, 0, StringLength / 2).Replace("-", "").ToLowerInvariant(); + } + + public static string? GetMaybeMaskedIdentifier(string? identifier) => + // We only care about masking norwegian person identifiers + identifier is null || !identifier.StartsWith(NorwegianPersonIdentifier.Prefix, StringComparison.Ordinal) + ? identifier + : NorwegianPersonIdentifier.HashPrefixWithSeparator + Hash(identifier); +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Externals/IPartyNameRegistry.cs b/src/Digdir.Domain.Dialogporten.Application/Externals/IPartyNameRegistry.cs new file mode 100644 index 000000000..9cee0aace --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Externals/IPartyNameRegistry.cs @@ -0,0 +1,6 @@ +namespace Digdir.Domain.Dialogporten.Application.Externals; + +public interface IPartyNameRegistry +{ + Task GetName(string externalIdWithPrefix, CancellationToken cancellationToken); +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Externals/IPersonNameRegistry.cs b/src/Digdir.Domain.Dialogporten.Application/Externals/IPersonNameRegistry.cs deleted file mode 100644 index 284bb9226..000000000 --- a/src/Digdir.Domain.Dialogporten.Application/Externals/IPersonNameRegistry.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Digdir.Domain.Dialogporten.Application.Externals; - -public interface IPersonNameRegistry -{ - Task GetName(string personalIdentificationNumber, CancellationToken cancellationToken); -} 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 4b5e32fbf..08d1194a3 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 @@ -1,5 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; @@ -11,8 +12,17 @@ public sealed class GetDialogActivityDto public DialogActivityType.Values Type { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public GetDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } + +public sealed class GetDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { 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 1c71c9c2f..c73218bac 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 @@ -40,6 +40,9 @@ public async Task Handle(GetDialogActivityQuery request { var dialog = await _dbContext.Dialogs .Include(x => x.Activities.Where(x => x.Id == request.ActivityId)) + .ThenInclude(x => x.PerformedBy) + .Include(x => x.Activities.Where(x => x.Id == request.ActivityId)) + .ThenInclude(x => x.Description!.Localizations) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken: cancellationToken); 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 0d686fade..a27a1e20a 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 @@ -1,5 +1,7 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; @@ -9,5 +11,9 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs index 8d5def578..a6a6130a6 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs @@ -1,13 +1,17 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; -public class GetDialogSeenLogDto +public sealed class GetDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } + public GetDialogSeenLogActorDto SeenBy { get; set; } = null!; - public string EndUserIdHash { get; set; } = null!; - - public string? EndUserName { get; set; } - + public bool IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } + +public sealed class GetDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs index 2ae343806..a89b1e9cd 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs @@ -24,20 +24,17 @@ internal sealed class GetDialogSeenLogQueryHandler : IRequestHandler Handle(GetDialogSeenLogQuery request, var dialog = await _dbContext.Dialogs .AsNoTracking() .Include(x => x.SeenLog.Where(x => x.Id == request.SeenLogId)) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken: cancellationToken); @@ -80,8 +78,7 @@ public async Task Handle(GetDialogSeenLogQuery request, } var dto = _mapper.Map(seenLog); - dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalId == seenLog.EndUserId; - dto.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserId); + dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalIdWithPrefix == seenLog.SeenBy.ActorId; return dto; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs index 1bfcae98f..426c0d858 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs @@ -1,5 +1,7 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; @@ -9,5 +11,9 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + + CreateMap() + .ForMember(dest => dest.ActorId, + opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs index f57b3f427..1c4b6e82c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs @@ -1,5 +1,7 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search; @@ -9,5 +11,8 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + + CreateMap() + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs index a09d24123..22e62bfbc 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs @@ -1,13 +1,18 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search; -public class SearchDialogSeenLogDto +public sealed class SearchDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; - - public string? EndUserName { get; set; } + public SearchDialogSeenLogSeenByDto SeenBy { get; set; } = null!; + public bool IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } + +public sealed class SearchDialogSeenLogSeenByDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs index eefd261f3..f5f740e78 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs @@ -24,20 +24,17 @@ internal sealed class SearchDialogSeenLogQueryHandler : IRequestHandler Handle(SearchDialogSeenLogQuery request, CancellationToken cancellationToken) @@ -47,7 +44,7 @@ public async Task Handle(SearchDialogSeenLogQuery req var dialog = await _db.Dialogs .AsNoTracking() .Include(x => x.SeenLog) - .ThenInclude(x => x.Via!.Localizations) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken: cancellationToken); @@ -76,8 +73,7 @@ public async Task Handle(SearchDialogSeenLogQuery req .Select(x => { var dto = _mapper.Map(x); - dto.IsCurrentEndUser = x.EndUserId == currentUserInformation.UserId.ExternalId; - dto.EndUserIdHash = _stringHasher.Hash(x.EndUserId); + dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalId == x.SeenBy.ActorId; return dto; }) .ToList(); 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 b394c14ed..867662fce 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 @@ -2,6 +2,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; @@ -38,18 +39,23 @@ public sealed class GetDialogDto } -public class GetDialogDialogSeenLogDto +public sealed class GetDialogDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; - - public string? EndUserName { get; set; } + public GetDialogDialogSeenLogActorDto SeenBy { get; set; } = new(); + public bool? IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } +public sealed class GetDialogDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + public sealed class GetDialogContentDto { public DialogContentType.Values Type { get; set; } @@ -67,10 +73,17 @@ public sealed class GetDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public GetDialogDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } +public sealed class GetDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} + public sealed class GetDialogDialogApiActionDto { public Guid Id { 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 06c2b03c9..d3740eb7b 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 @@ -6,6 +6,7 @@ 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.Parties; using MediatR; using Microsoft.EntityFrameworkCore; using OneOf; @@ -29,7 +30,6 @@ internal sealed class GetDialogQueryHandler : IRequestHandler Handle(GetDialogQuery request, CancellationToken cancellationToken) @@ -73,9 +71,11 @@ public async Task Handle(GetDialogQuery request, CancellationTo .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.Description!.Localizations) + .Include(x => x.Activities).ThenInclude(x => x.PerformedBy) .Include(x => x.SeenLog .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) .OrderBy(x => x.CreatedAt)) + .ThenInclude(x => x.SeenBy) .Where(x => !x.VisibleFrom.HasValue || x.VisibleFrom < _clock.UtcNowOffset) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken); @@ -101,7 +101,10 @@ public async Task Handle(GetDialogQuery request, CancellationTo // TODO: What if name lookup fails // https://github.com/digdir/dialogporten/issues/387 - dialog.UpdateSeenAt(currentUserInformation.UserId.ExternalId, currentUserInformation.UserId.Type, currentUserInformation.Name); + dialog.UpdateSeenAt( + currentUserInformation.UserId.ExternalIdWithPrefix, + currentUserInformation.UserId.Type, + currentUserInformation.Name); var saveResult = await _unitOfWork .WithoutAuditableSideEffects() @@ -118,8 +121,7 @@ public async Task Handle(GetDialogQuery request, CancellationTo .Select(log => { var logDto = _mapper.Map(log); - logDto.IsCurrentEndUser = log.EndUserId == currentUserInformation.UserId.ExternalId; - logDto.EndUserIdHash = _stringHasher.Hash(log.EndUserId); + logDto.IsCurrentEndUser = currentUserInformation.UserId.ExternalIdWithPrefix == log.SeenBy.ActorId; return logDto; }) .ToList(); 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 b226cfe6b..f00ef9afa 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 @@ -1,7 +1,9 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; @@ -19,9 +21,17 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + CreateMap() + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); + CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); + CreateMap(); 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 674b67960..e4ca70439 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 @@ -1,6 +1,8 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; @@ -30,10 +32,15 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); CreateMap() - .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)) - .ForMember(dest => dest.EndUserIdHash, opt => opt.MapFrom(src => src.EndUserId)); + .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + + CreateMap(); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } 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 9d42dabe1..4383ef050 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 @@ -1,6 +1,7 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search; @@ -27,18 +28,23 @@ public sealed class SearchDialogDto public List SeenSinceLastUpdate { get; set; } = []; } -public class SearchDialogDialogSeenLogDto +public sealed class SearchDialogDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; - - public string? EndUserName { get; set; } + public SearchDialogDialogSeenLogActorDto SeenBy { get; set; } = null!; + public bool? IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } +public sealed class SearchDialogDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + public sealed class SearchDialogContentDto { public DialogContentType.Values Type { get; set; } @@ -55,6 +61,13 @@ public sealed class SearchDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } + +public sealed class SearchDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { 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 27e31e817..137d8496e 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 @@ -116,22 +116,19 @@ internal sealed class SearchDialogQueryHandler : IRequestHandler Handle(SearchDialogQuery request, CancellationToken cancellationToken) @@ -176,9 +173,7 @@ public async Task Handle(SearchDialogQuery request, Cancella 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.IsCurrentEndUser = currentUserInfo.UserId.ExternalId == seenLog.EndUserIdHash; - seenLog.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserIdHash); + seenLog.IsCurrentEndUser = currentUserInfo.UserId.ExternalIdWithPrefix == seenLog.SeenBy.ActorId; } return paginatedList; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs index 6474b3b75..a517c6ef5 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs @@ -1,5 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogActivities.Queries.Get; @@ -15,6 +16,13 @@ public sealed class GetDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public GetDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } + +public sealed class GetDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityQuery.cs index d16d49e86..f394d0452 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityQuery.cs @@ -39,6 +39,9 @@ public async Task Handle(GetDialogActivityQuery request var dialog = await _dbContext.Dialogs .Include(x => x.Activities.Where(x => x.Id == request.ActivityId)) + .ThenInclude(x => x.PerformedBy) + .Include(x => x.Activities.Where(x => x.Id == request.ActivityId)) + .ThenInclude(x => x.Description!.Localizations) .IgnoreQueryFilters() .Where(x => resourceIds.Contains(x.ServiceResource)) .FirstOrDefaultAsync(x => x.Id == request.DialogId, diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs index affc6eac0..a816e018d 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogActivities.Queries.Get; @@ -10,5 +11,9 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); + } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs index 698e28a46..adacd81e4 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs @@ -1,11 +1,17 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Get; -public class GetDialogSeenLogDto +public sealed class GetDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; + public GetDialogSeenLogActorDto SeenBy { get; set; } = null!; - public string? EndUserName { get; set; } + public bool IsViaServiceOwner { get; set; } +} + +public sealed class GetDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs index d57eb6591..70713af0f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs @@ -22,18 +22,15 @@ internal sealed class GetDialogSeenLogQueryHandler : IRequestHandler Handle(GetDialogSeenLogQuery request, var dialog = await _dbContext.Dialogs .AsNoTracking() .Include(x => x.SeenLog.Where(x => x.Id == request.SeenLogId)) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .Where(x => resourceIds.Contains(x.ServiceResource)) .FirstOrDefaultAsync(x => x.Id == request.DialogId, @@ -62,7 +60,6 @@ public async Task Handle(GetDialogSeenLogQuery request, } var dto = _mapper.Map(seenLog); - dto.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserId); return dto; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs index 617328809..3af80c4af 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Get; @@ -9,5 +10,7 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + + CreateMap(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs index a68f7a7dc..e548a545b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs @@ -1,11 +1,17 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Search; -public class SearchDialogSeenLogDto +public sealed class SearchDialogSeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; + public SearchDialogSeenLogSeenByDto SeenBy { get; set; } = null!; - public string? EndUserName { get; set; } + public bool? IsViaServiceOwner { get; set; } +} + +public sealed class SearchDialogSeenLogSeenByDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs index c6937053c..439660110 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs @@ -21,18 +21,15 @@ internal sealed class SearchDialogSeenLogQueryHandler : IRequestHandler Handle(SearchDialogSeenLogQuery req var dialog = await _db.Dialogs .AsNoTracking() .Include(x => x.SeenLog) - .ThenInclude(x => x.Via!.Localizations) .IgnoreQueryFilters() .Where(x => resourceIds.Contains(x.ServiceResource)) .FirstOrDefaultAsync(x => x.Id == request.DialogId, @@ -58,7 +54,6 @@ public async Task Handle(SearchDialogSeenLogQuery req .Select(x => { var dto = _mapper.Map(x); - dto.EndUserIdHash = _stringHasher.Hash(x.EndUserId); return dto; }) .ToList(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs index de9014d49..e7e2c868a 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs @@ -29,6 +29,7 @@ internal sealed class CreateDialogCommandHandler : IRequestHandler Handle(CreateDialogCommand request, CancellationToken cancellationToken) @@ -71,6 +74,17 @@ public async Task Handle(CreateDialogCommand request, Cancel return new ValidationError(ProgressValidationFailure); } + foreach (var activity in request.Activities) + { + if (activity.PerformedBy.ActorId is null) continue; + + activity.PerformedBy.ActorName = await _partyNameRegistry.GetName(activity.PerformedBy.ActorId, cancellationToken); + if (activity.PerformedBy.ActorName == null) + { + return new DomainError(new DomainFailure(nameof(activity.PerformedBy.ActorId), $"Unable to look up actor id: {activity.PerformedBy.ActorId}")); + } + } + var dialog = _mapper.Map(request); dialog.ServiceResourceType = serviceResourceType; 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 6cba21c9f..3599f2b3a 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 @@ -4,6 +4,7 @@ using Digdir.Domain.Dialogporten.Domain; using Digdir.Domain.Dialogporten.Domain.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; using FluentValidation; @@ -258,7 +259,8 @@ public CreateDialogDialogApiActionEndpointDtoValidator() internal sealed class CreateDialogDialogActivityDtoValidator : AbstractValidator { public CreateDialogDialogActivityDtoValidator( - IValidator> localizationsValidator) + IValidator> localizationsValidator, + IValidator actorValidator) { RuleFor(x => x.Id) .NotEqual(default(Guid)); @@ -273,9 +275,31 @@ public CreateDialogDialogActivityDtoValidator( .NotEqual(x => x.Id) .When(x => x.RelatedActivityId.HasValue); RuleFor(x => x.PerformedBy) - .MaximumLength(Constants.DefaultMaxStringLength); + .SetValidator(actorValidator); RuleFor(x => x.Description) .NotEmpty() .SetValidator(localizationsValidator); } } + +internal sealed class CreateDialogDialogActivityActorDtoValidator : AbstractValidator +{ + public CreateDialogDialogActivityActorDtoValidator() + { + RuleFor(x => x.ActorType) + .IsInEnum(); + + RuleFor(x => x.ActorId) + .Must((dto, value) => value is null || dto.ActorName is null) + .WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both."); + + RuleFor(x => x.ActorType) + .Must((dto, value) => (value == DialogActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || + (value != DialogActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))) + .WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set."); + + RuleFor(x => x.ActorId!) + .IsValidPartyIdentifier() + .When(x => x.ActorId is not null); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs index 332958113..d69054640 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs @@ -3,6 +3,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; @@ -54,10 +55,17 @@ public sealed class CreateDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public CreateDialogDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } +public sealed class CreateDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} + public sealed class CreateDialogDialogApiActionDto { public string Action { get; set; } = null!; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs index 0a0fc63cb..ec3808eec 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs @@ -2,6 +2,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; @@ -39,6 +40,10 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.Ignore()) + .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); + CreateMap() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs index 6c2cbe8b2..eb04503e6 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs @@ -4,6 +4,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; @@ -63,6 +64,10 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.Ignore()) + .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); + // =========================================== // ================== Patch ================== // =========================================== @@ -72,6 +77,7 @@ public MappingProfile() .ForMember(dest => dest.Activities, opt => opt.Ignore()); CreateMap(); CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); 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 0bab7d84a..abab4aec8 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 @@ -4,6 +4,7 @@ using Digdir.Domain.Dialogporten.Domain; using Digdir.Domain.Dialogporten.Domain.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; using FluentValidation; @@ -257,7 +258,8 @@ public UpdateDialogDialogApiActionEndpointDtoValidator() internal sealed class UpdateDialogDialogActivityDtoValidator : AbstractValidator { public UpdateDialogDialogActivityDtoValidator( - IValidator> localizationsValidator) + IValidator> localizationsValidator, + IValidator actorValidator) { RuleFor(x => x.Id) .NotEqual(default(Guid)); @@ -272,9 +274,31 @@ public UpdateDialogDialogActivityDtoValidator( .NotEqual(x => x.Id) .When(x => x.RelatedActivityId.HasValue); RuleFor(x => x.PerformedBy) - .MaximumLength(Constants.DefaultMaxStringLength); + .SetValidator(actorValidator); RuleFor(x => x.Description) .NotEmpty() .SetValidator(localizationsValidator); } } + +internal sealed class UpdateDialogDialogActivityActorDtoValidator : AbstractValidator +{ + public UpdateDialogDialogActivityActorDtoValidator() + { + RuleFor(x => x.ActorType) + .IsInEnum(); + + RuleFor(x => x.ActorId) + .Must((dto, value) => value is null || dto.ActorName is null) + .WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both."); + + RuleFor(x => x.ActorType) + .Must((dto, value) => (value == DialogActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || + (value != DialogActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))) + .WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set."); + + RuleFor(x => x.ActorId!) + .IsValidPartyIdentifier() + .When(x => x.ActorId is not null); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs index 7dc597130..564aa9090 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs @@ -3,6 +3,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; @@ -52,10 +53,17 @@ public class UpdateDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public UpdateDialogDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } +public sealed class UpdateDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} + public sealed class UpdateDialogDialogApiActionDto { public Guid? Id { get; set; } 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 0ff5b9a44..e60f4b4a0 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 @@ -2,6 +2,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; @@ -39,16 +40,20 @@ public sealed class GetDialogDto public List SeenSinceLastUpdate { get; set; } = []; } -public class GetDialogDialogSeenLogDto +public sealed class GetDialogDialogSeenLogDto { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; + public GetDialogDialogSeenLogActorDto SeenBy { get; set; } = null!; - public string? EndUserName { get; set; } + public bool IsViaServiceOwner { get; set; } +} - public bool? IsCurrentEndUser { get; set; } +public sealed class GetDialogDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; } public sealed class GetDialogContentDto @@ -72,10 +77,17 @@ public sealed class GetDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public GetDialogDialogActivityActorDto PerformedBy { get; set; } = new(); public List Description { get; set; } = []; } +public sealed class GetDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} + public sealed class GetDialogDialogApiActionDto { public Guid Id { get; set; } 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 da6c67986..f13dd1a6f 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 @@ -22,18 +22,15 @@ internal sealed class GetDialogQueryHandler : IRequestHandler Handle(GetDialogQuery request, CancellationToken cancellationToken) @@ -59,9 +56,11 @@ public async Task Handle(GetDialogQuery request, CancellationTo .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.Description!.Localizations) + .Include(x => x.Activities).ThenInclude(x => x.PerformedBy) .Include(x => x.SeenLog .Where(x => x.CreatedAt >= x.Dialog.UpdatedAt) .OrderBy(x => x.CreatedAt)) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .AsNoTracking() // TODO: Remove when #386 is implemented .Where(x => resourceIds.Contains(x.ServiceResource)) @@ -83,7 +82,6 @@ public async Task Handle(GetDialogQuery request, CancellationTo 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(); 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 9b5d3d3bd..202b51bb8 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 @@ -2,6 +2,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; @@ -21,6 +22,9 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); + CreateMap(); CreateMap() 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 580cf9552..94825a095 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 @@ -1,5 +1,7 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Search; @@ -16,8 +18,9 @@ public MappingProfile() .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(); 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 dc94f12b2..276c7f55b 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 @@ -25,15 +25,21 @@ public sealed class SearchDialogDto public List SeenSinceLastUpdate { get; set; } = []; } -public class SearchDialogDialogSeenLogDto +public sealed class SearchDialogDialogSeenLogDto { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset SeenAt { get; set; } + + public SearchDialogDialogSeenLogActorDto SeenBy { get; set; } = null!; - public string EndUserIdHash { get; set; } = null!; + public bool? IsViaServiceOwner { get; set; } + public bool IsCurrentEndUser { get; set; } +} - public string? EndUserName { get; set; } - public bool? IsCurrentEndUser { get; set; } +public sealed class SearchDialogDialogSeenLogActorDto +{ + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; } 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 14b7cec05..9cfbba6c0 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 @@ -122,20 +122,17 @@ internal sealed class SearchDialogQueryHandler : IRequestHandler Handle(SearchDialogQuery request, CancellationToken cancellationToken) @@ -184,10 +181,8 @@ public async Task Handle(SearchDialogQuery request, Cancella { if (request.EndUserId is not null) { - seenRecord.IsCurrentEndUser = seenRecord.EndUserIdHash == request.EndUserId; + seenRecord.IsCurrentEndUser = seenRecord.SeenBy.ActorId == request.EndUserId; } - - seenRecord.EndUserIdHash = _stringHasher.Hash(seenRecord.EndUserIdHash); } 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 9784f0d42..71779671d 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivity.cs @@ -1,4 +1,5 @@ -using Digdir.Domain.Dialogporten.Domain.Dialogs.Events.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Events.Activities; using Digdir.Domain.Dialogporten.Domain.Localizations; using Digdir.Library.Entity.Abstractions.Features.Aggregate; using Digdir.Library.Entity.Abstractions.Features.EventPublisher; @@ -27,7 +28,7 @@ public class DialogActivity : IImmutableEntity, IAggregateCreatedHandler, IEvent public DialogActivityDescription? Description { get; set; } [AggregateChild] - public string? PerformedBy { get; set; } + public DialogActor PerformedBy { get; set; } = null!; public List RelatedActivities { get; set; } = []; diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActor.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActor.cs new file mode 100644 index 000000000..3391b3072 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActor.cs @@ -0,0 +1,12 @@ +using Digdir.Library.Entity.Abstractions.Features.Identifiable; + +namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; + +public class DialogActor : IIdentifiableEntity +{ + public Guid Id { get; set; } + public string? ActorId { get; set; } + public DialogActorType ActorType { get; set; } = null!; + public DialogActorType.Values ActorTypeId { get; set; } + public string? ActorName { get; set; } = null!; +} diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActorType.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActorType.cs new file mode 100644 index 000000000..6fed58389 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actors/DialogActorType.cs @@ -0,0 +1,15 @@ +using Digdir.Library.Entity.Abstractions.Features.Lookup; + +namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; + +public class DialogActorType : AbstractLookupEntity +{ + public DialogActorType(Values id) : base(id) { } + public override DialogActorType MapValue(Values id) => new(id); + + public enum Values + { + PartyRepresentative = 1, + ServiceOwner = 2 + } +} diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs index 2ca9acd1a..9ac8adff3 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs @@ -1,5 +1,6 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Dialogs.Events; @@ -84,7 +85,7 @@ public void OnDelete(AggregateNode self, DateTimeOffset utcNow) public void UpdateSeenAt(string endUserId, DialogUserType.Values userTypeId, string? endUserName) { var lastSeenAt = SeenLog - .Where(x => x.EndUserId == endUserId) + .Where(x => x.SeenBy.ActorId == endUserId) .MaxBy(x => x.CreatedAt) ?.CreatedAt ?? DateTimeOffset.MinValue; @@ -96,9 +97,13 @@ public void UpdateSeenAt(string endUserId, DialogUserType.Values userTypeId, str SeenLog.Add(new() { - EndUserId = endUserId, EndUserTypeId = userTypeId, - EndUserName = endUserName + SeenBy = new DialogActor + { + ActorTypeId = DialogActorType.Values.PartyRepresentative, + ActorId = endUserId, + ActorName = endUserName + } }); _domainEvents.Add(new DialogSeenDomainEvent(Id, ServiceResource, Party)); diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenLog.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenLog.cs index bb4fcab49..65508deab 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenLog.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSeenLog.cs @@ -1,5 +1,4 @@ -using Digdir.Domain.Dialogporten.Domain.Localizations; -using Digdir.Library.Entity.Abstractions.Features.Aggregate; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Library.Entity.Abstractions.Features.Immutable; namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -9,12 +8,9 @@ public class DialogSeenLog : IImmutableEntity public Guid Id { get; set; } public DateTimeOffset CreatedAt { get; set; } - public string EndUserId { get; set; } = null!; + public DialogActor SeenBy { get; set; } = null!; - public string? EndUserName { get; set; } - - [AggregateChild] - public DialogSeenLogVia? Via { get; set; } + public bool? IsViaServiceOwner { get; set; } // === Dependent relationships === public Guid DialogId { get; set; } @@ -23,9 +19,3 @@ public class DialogSeenLog : IImmutableEntity public DialogUserType.Values EndUserTypeId { get; set; } public DialogUserType EndUserType { get; set; } = null!; } - -public class DialogSeenLogVia : LocalizationSet -{ - public DialogSeenLog DialogSeenLog { get; set; } = null!; - public Guid DialogSeenLogId { get; set; } -} diff --git a/src/Digdir.Domain.Dialogporten.Domain/Parties/NorwegianPersonIdentifier.cs b/src/Digdir.Domain.Dialogporten.Domain/Parties/NorwegianPersonIdentifier.cs index 645b5703a..54c32c518 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Parties/NorwegianPersonIdentifier.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Parties/NorwegianPersonIdentifier.cs @@ -10,8 +10,11 @@ public class NorwegianPersonIdentifier : IPartyIdentifier private static readonly int[] SocialSecurityNumberWeights1 = [3, 7, 6, 1, 8, 9, 4, 5, 2, 1]; private static readonly int[] SocialSecurityNumberWeights2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2, 1]; - public static string Prefix => "urn:altinn:person:identifier-no"; + private static string PersonPrefix => "urn:altinn:person:identifier-"; + public static string Prefix => PersonPrefix + "no"; public static string PrefixWithSeparator => Prefix + PartyIdentifier.Separator; + public static string HashPrefix => PersonPrefix + "ephemeral"; + public static string HashPrefixWithSeparator => HashPrefix + PartyIdentifier.Separator; public string FullId { get; } public string Id { get; } diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PersonNameRegistryClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PartyNameRegistryClient.cs similarity index 53% rename from src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PersonNameRegistryClient.cs rename to src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PartyNameRegistryClient.cs index 804fa9497..f3fb5d52d 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PersonNameRegistryClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/NameRegistry/PartyNameRegistryClient.cs @@ -1,11 +1,12 @@ using System.Text.Json; using System.Text.Json.Serialization; using Digdir.Domain.Dialogporten.Application.Externals; +using Digdir.Domain.Dialogporten.Domain.Parties; using ZiggyCreatures.Caching.Fusion; namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.NameRegistry; -internal class PersonNameRegistryClient : IPersonNameRegistry +internal class PartyNameRegistryClient : IPartyNameRegistry { private readonly IFusionCache _cache; private readonly HttpClient _client; @@ -16,29 +17,41 @@ internal class PersonNameRegistryClient : IPersonNameRegistry DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; - public PersonNameRegistryClient(HttpClient client, IFusionCacheProvider cacheProvider) + public PartyNameRegistryClient(HttpClient client, IFusionCacheProvider cacheProvider) { _client = client ?? throw new ArgumentNullException(nameof(client)); _cache = cacheProvider.GetCache(nameof(NameRegistry)) ?? throw new ArgumentNullException(nameof(cacheProvider)); } - public async Task GetName(string personalIdentificationNumber, CancellationToken cancellationToken) + public async Task GetName(string externalIdWithPrefix, CancellationToken cancellationToken) { return await _cache.GetOrSetAsync( - $"Name_{personalIdentificationNumber}", - ct => GetNameFromRegister(personalIdentificationNumber, ct), + $"Name_{externalIdWithPrefix}", + ct => GetNameFromRegister(externalIdWithPrefix, ct), token: cancellationToken); } - private async Task GetNameFromRegister(string personalIdentificationNumber, CancellationToken cancellationToken) + private async Task GetNameFromRegister(string externalIdWithPrefix, CancellationToken cancellationToken) { const string apiUrl = "register/api/v1/parties/nameslookup"; + var nameLookupParty = new NameLookupParty(); + if (NorwegianPersonIdentifier.TryParse(externalIdWithPrefix, out var personIdentifier)) + { + nameLookupParty.Ssn = personIdentifier.Id; + } + else if (NorwegianOrganizationIdentifier.TryParse(externalIdWithPrefix, out var organizationIdentifier)) + { + nameLookupParty.OrgNo = organizationIdentifier.Id; + } + else + { + return null; + } + var nameLookup = new NameLookup { - Parties = [ - new() { Ssn = personalIdentificationNumber } - ] + Parties = [nameLookupParty] }; var nameLookupResult = await _client.PostAsJsonEnsuredAsync( @@ -52,17 +65,18 @@ public PersonNameRegistryClient(HttpClient client, IFusionCacheProvider cachePro private sealed class NameLookup { - public List Parties { get; set; } = null!; + public List Parties { get; set; } = null!; } private sealed class NameLookupResult { - public List PartyNames { get; set; } = null!; + public List PartyNames { get; set; } = null!; } - private sealed class NameLookupSsn + private sealed class NameLookupParty { public string Ssn { get; set; } = null!; + public string OrgNo { get; set; } = null!; public string? Name { get; set; } } } diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs index 87ea2fa3f..1f2b19985 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs @@ -153,7 +153,7 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi client.BaseAddress = services.GetRequiredService>().Value.AltinnCdn.BaseUri) .AddPolicyHandlerFromRegistry(PollyPolicy.DefaultHttpRetryPolicy); - services.AddMaskinportenHttpClient( + services.AddMaskinportenHttpClient( infrastructureConfigurationSection, x => x.ClientSettings.ExhangeToAltinnToken = true) .ConfigureHttpClient((services, client) => diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs index 58f02c296..344c24458 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/DialogDbContext.cs @@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using System.Linq.Expressions; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence; @@ -33,6 +34,7 @@ public DialogDbContext(DbContextOptions options) : base(options public DbSet DialogSeenLog => Set(); public DbSet DialogContentTypes => Set(); public DbSet DialogContent => Set(); + public DbSet DialogActors => Set(); public DbSet OutboxMessages => Set(); public DbSet OutboxMessageConsumers => Set(); diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.Designer.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.Designer.cs new file mode 100644 index 000000000..a78cfd1c5 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.Designer.cs @@ -0,0 +1,1388 @@ +// +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("20240720163626_AddActor")] + partial class AddActor + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .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("DialogId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + 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("HttpMethodId") + .HasColumnType("integer"); + + b.Property("IsDeleteDialogAction") + .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("HttpMethodId"); + + 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("DialogId") + .HasColumnType("uuid"); + + b.Property("ExtendedType") + .HasMaxLength(1023) + .HasColumnType("character varying(1023)"); + + b.Property("PerformedById") + .HasColumnType("uuid"); + + b.Property("RelatedActivityId") + .HasColumnType("uuid"); + + b.Property("TypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("PerformedById"); + + 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.Actors.DialogActor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ActorId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ActorName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ActorTypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ActorTypeId"); + + b.ToTable("DialogActor"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActorType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogActorType"); + + b.HasData( + new + { + Id = 1, + Name = "PartyRepresentative" + }, + new + { + Id = 2, + Name = "ServiceOwner" + }); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", 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("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.ToTable("DialogAttachment"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachmentUrl", 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("DialogAttachmentId") + .HasColumnType("uuid"); + + b.Property("MediaType") + .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("DialogAttachmentId"); + + b.ToTable("DialogAttachmentUrl"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachmentUrlConsumerType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogAttachmentUrlConsumerType"); + + b.HasData( + new + { + Id = 1, + Name = "Gui" + }, + new + { + Id = 2, + Name = "Api" + }); + }); + + 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("MediaType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + 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("AllowedMediaTypes") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("MaxLength") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("OutputInList") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DialogContentType"); + + b.HasData( + new + { + Id = 1, + AllowedMediaTypes = new[] { "text/plain" }, + MaxLength = 255, + Name = "Title", + OutputInList = true, + Required = true + }, + new + { + Id = 2, + AllowedMediaTypes = new[] { "text/plain" }, + MaxLength = 255, + Name = "SenderName", + OutputInList = true, + Required = false + }, + new + { + Id = 3, + AllowedMediaTypes = new[] { "text/plain" }, + MaxLength = 255, + Name = "Summary", + OutputInList = true, + Required = true + }, + new + { + Id = 4, + AllowedMediaTypes = new[] { "text/html", "text/plain", "text/markdown" }, + MaxLength = 1023, + Name = "AdditionalInfo", + OutputInList = false, + Required = false + }, + new + { + Id = 5, + AllowedMediaTypes = new[] { "text/plain" }, + MaxLength = 20, + Name = "ExtendedStatus", + OutputInList = true, + Required = false + }, + new + { + Id = 6, + AllowedMediaTypes = new[] { "application/vnd.dialogporten.frontchannelembed+json;type=markdown" }, + MaxLength = 1023, + Name = "MainContentReference", + OutputInList = 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("ServiceResourceType") + .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("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.DialogSeenLog", 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("EndUserTypeId") + .HasColumnType("integer"); + + b.Property("IsViaServiceOwner") + .HasColumnType("boolean"); + + b.Property("SeenById") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DialogId"); + + b.HasIndex("EndUserTypeId"); + + b.HasIndex("SeenById"); + + b.ToTable("DialogSeenLog"); + }); + + 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.DialogUserType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogUserType"); + + b.HasData( + new + { + Id = 0, + Name = "Unknown" + }, + new + { + Id = 1, + Name = "Person" + }, + new + { + Id = 2, + Name = "LegacySystemUser" + }, + new + { + Id = 3, + Name = "SystemUser" + }, + new + { + Id = 4, + Name = "ServiceOwner" + }, + new + { + Id = 5, + Name = "ServiceOwnerOnBehalfOfPerson" + }); + }); + + 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("LanguageCode") + .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", "LanguageCode"); + + 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().HasValue("LocalizationSet"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Outboxes.OutboxMessage", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("current_timestamp at time zone 'utc'"); + + 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.DialogGuiActionPrompt", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("GuiActionId") + .HasColumnType("uuid"); + + b.HasIndex("GuiActionId") + .IsUnique(); + + b.ToTable("LocalizationSet", t => + { + t.Property("GuiActionId") + .HasColumnName("DialogGuiActionPrompt_GuiActionId"); + }); + + b.HasDiscriminator().HasValue("DialogGuiActionPrompt"); + }); + + 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.HasDiscriminator().HasValue("DialogActivityDescription"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.AttachmentDisplayName", b => + { + b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet"); + + b.Property("AttachmentId") + .HasColumnType("uuid"); + + b.HasIndex("AttachmentId") + .IsUnique(); + + b.HasDiscriminator().HasValue("AttachmentDisplayName"); + }); + + 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.Actions.DialogApiAction", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("ApiActions") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + 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.Http.HttpVerb", "HttpMethod") + .WithMany() + .HasForeignKey("HttpMethodId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", "Priority") + .WithMany() + .HasForeignKey("PriorityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("HttpMethod"); + + b.Navigation("Priority"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + 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.Actors.DialogActor", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedById") + .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("PerformedBy"); + + b.Navigation("RelatedActivity"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActorType", "ActorType") + .WithMany() + .HasForeignKey("ActorTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ActorType"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("Attachments") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachmentUrl", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachmentUrlConsumerType", "ConsumerType") + .WithMany() + .HasForeignKey("ConsumerTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", "DialogAttachment") + .WithMany("Urls") + .HasForeignKey("DialogAttachmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ConsumerType"); + + b.Navigation("DialogAttachment"); + }); + + 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.DialogSeenLog", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") + .WithMany("SeenLog") + .HasForeignKey("DialogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType", "EndUserType") + .WithMany() + .HasForeignKey("EndUserTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", "SeenBy") + .WithMany() + .HasForeignKey("SeenById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dialog"); + + b.Navigation("EndUserType"); + + b.Navigation("SeenBy"); + }); + + 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.DialogGuiActionPrompt", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction") + .WithOne("Prompt") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPrompt", "GuiActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GuiAction"); + }); + + 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.Attachments.AttachmentDisplayName", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", "Attachment") + .WithOne("DisplayName") + .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.AttachmentDisplayName", "AttachmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Attachment"); + }); + + 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.Actions.DialogApiAction", b => + { + b.Navigation("Endpoints"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b => + { + b.Navigation("Prompt"); + + b.Navigation("Title"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b => + { + b.Navigation("Description"); + + b.Navigation("RelatedActivities"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", b => + { + b.Navigation("DisplayName"); + + b.Navigation("Urls"); + }); + + 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("Attachments"); + + b.Navigation("Content"); + + b.Navigation("GuiActions"); + + b.Navigation("SearchTags"); + + b.Navigation("SeenLog"); + }); + + 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/20240720163626_AddActor.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs new file mode 100644 index 000000000..68dee23e2 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs @@ -0,0 +1,213 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddActor : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_LocalizationSet_DialogSeenLog_DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.DropIndex( + name: "IX_LocalizationSet_DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.DropColumn( + name: "DialogSeenLogId", + table: "LocalizationSet"); + + migrationBuilder.DropColumn( + name: "EndUserId", + table: "DialogSeenLog"); + + migrationBuilder.DropColumn( + name: "EndUserName", + table: "DialogSeenLog"); + + migrationBuilder.DropColumn( + name: "PerformedBy", + table: "DialogActivity"); + + migrationBuilder.AddColumn( + name: "IsViaServiceOwner", + table: "DialogSeenLog", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "SeenById", + table: "DialogSeenLog", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "PerformedById", + table: "DialogActivity", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.CreateTable( + name: "DialogActorType", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false), + Name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DialogActorType", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "DialogActor", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + ActorId = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + ActorTypeId = table.Column(type: "integer", nullable: false), + ActorName = table.Column(type: "character varying(255)", maxLength: 255, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DialogActor", x => x.Id); + table.ForeignKey( + name: "FK_DialogActor_DialogActorType_ActorTypeId", + column: x => x.ActorTypeId, + principalTable: "DialogActorType", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.InsertData( + table: "DialogActorType", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { 1, "PartyRepresentative" }, + { 2, "ServiceOwner" } + }); + + migrationBuilder.CreateIndex( + name: "IX_DialogSeenLog_SeenById", + table: "DialogSeenLog", + column: "SeenById"); + + migrationBuilder.CreateIndex( + name: "IX_DialogActivity_PerformedById", + table: "DialogActivity", + column: "PerformedById"); + + migrationBuilder.CreateIndex( + name: "IX_DialogActor_ActorTypeId", + table: "DialogActor", + column: "ActorTypeId"); + + migrationBuilder.AddForeignKey( + name: "FK_DialogActivity_DialogActor_PerformedById", + table: "DialogActivity", + column: "PerformedById", + principalTable: "DialogActor", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_DialogSeenLog_DialogActor_SeenById", + table: "DialogSeenLog", + column: "SeenById", + principalTable: "DialogActor", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_DialogActivity_DialogActor_PerformedById", + table: "DialogActivity"); + + migrationBuilder.DropForeignKey( + name: "FK_DialogSeenLog_DialogActor_SeenById", + table: "DialogSeenLog"); + + migrationBuilder.DropTable( + name: "DialogActor"); + + migrationBuilder.DropTable( + name: "DialogActorType"); + + migrationBuilder.DropIndex( + name: "IX_DialogSeenLog_SeenById", + table: "DialogSeenLog"); + + migrationBuilder.DropIndex( + name: "IX_DialogActivity_PerformedById", + table: "DialogActivity"); + + migrationBuilder.DropColumn( + name: "IsViaServiceOwner", + table: "DialogSeenLog"); + + migrationBuilder.DropColumn( + name: "SeenById", + table: "DialogSeenLog"); + + migrationBuilder.DropColumn( + name: "PerformedById", + table: "DialogActivity"); + + migrationBuilder.AddColumn( + name: "DialogSeenLogId", + table: "LocalizationSet", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "EndUserId", + table: "DialogSeenLog", + type: "character varying(255)", + maxLength: 255, + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "EndUserName", + table: "DialogSeenLog", + type: "character varying(255)", + maxLength: 255, + nullable: true); + + migrationBuilder.AddColumn( + name: "PerformedBy", + table: "DialogActivity", + type: "character varying(255)", + maxLength: 255, + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_LocalizationSet_DialogSeenLogId", + table: "LocalizationSet", + column: "DialogSeenLogId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_LocalizationSet_DialogSeenLog_DialogSeenLogId", + table: "LocalizationSet", + column: "DialogSeenLogId", + principalTable: "DialogSeenLog", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs index 224989321..39a624a1f 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -222,9 +222,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(1023) .HasColumnType("character varying(1023)"); - b.Property("PerformedBy") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); + b.Property("PerformedById") + .HasColumnType("uuid"); b.Property("RelatedActivityId") .HasColumnType("uuid"); @@ -236,6 +235,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("DialogId"); + b.HasIndex("PerformedById"); + b.HasIndex("RelatedActivityId"); b.HasIndex("TypeId"); @@ -290,6 +291,58 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ActorId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ActorName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ActorTypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ActorTypeId"); + + b.ToTable("DialogActor"); + }); + + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActorType", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("DialogActorType"); + + b.HasData( + new + { + Id = 1, + Name = "PartyRepresentative" + }, + new + { + Id = 2, + Name = "ServiceOwner" + }); + }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", b => { b.Property("Id") @@ -643,24 +696,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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.Property("EndUserTypeId") .HasColumnType("integer"); + b.Property("IsViaServiceOwner") + .HasColumnType("boolean"); + + b.Property("SeenById") + .HasColumnType("uuid"); + b.HasKey("Id"); b.HasIndex("DialogId"); b.HasIndex("EndUserTypeId"); + b.HasIndex("SeenById"); + b.ToTable("DialogSeenLog"); }); @@ -870,7 +922,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("LocalizationSet"); - b.HasDiscriminator("Discriminator").HasValue("LocalizationSet"); + b.HasDiscriminator().HasValue("LocalizationSet"); b.UseTphMappingStrategy(); }); @@ -992,19 +1044,6 @@ 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.Actions.DialogApiAction", b => { b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") @@ -1070,6 +1109,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", "PerformedBy") + .WithMany() + .HasForeignKey("PerformedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "RelatedActivity") .WithMany("RelatedActivities") .HasForeignKey("RelatedActivityId") @@ -1083,11 +1128,24 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Dialog"); + b.Navigation("PerformedBy"); + b.Navigation("RelatedActivity"); b.Navigation("Type"); }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", b => + { + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActorType", "ActorType") + .WithMany() + .HasForeignKey("ActorTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ActorType"); + }); + modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments.DialogAttachment", b => { b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog") @@ -1173,9 +1231,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors.DialogActor", "SeenBy") + .WithMany() + .HasForeignKey("SeenById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.Navigation("Dialog"); b.Navigation("EndUserType"); + + b.Navigation("SeenBy"); }); modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b => @@ -1255,17 +1321,6 @@ 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.DialogSeenLog", "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.Actions.DialogApiAction", b => { b.Navigation("Endpoints"); @@ -1315,11 +1370,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("SeenLog"); }); - modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLog", b => - { - b.Navigation("Via"); - }); - modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b => { b.Navigation("Localizations"); 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 f1096c891..d4088781d 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 @@ -4,6 +4,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; using Digdir.Domain.Dialogporten.WebApi.Common; @@ -152,7 +153,10 @@ public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) => { Id = Guid.Parse("8b95d42d-d2b6-4c01-8ca0-a817a4b3c50d"), Type = DialogActivityType.Values.Information, - PerformedBy = "Some performed", + PerformedBy = new UpdateDialogDialogActivityActorDto + { + ActorType = DialogActorType.Values.ServiceOwner + }, Description = [ new LocalizationDto diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs index ff5f69c2e..f124753fa 100644 --- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs +++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs @@ -5,6 +5,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; using Digdir.Domain.Dialogporten.Domain.Http; @@ -215,7 +216,7 @@ public static List GenerateFakeDialogActivities(i .RuleFor(o => o.CreatedAt, f => f.Date.Past()) .RuleFor(o => o.ExtendedType, f => new Uri(f.Internet.UrlWithPath())) .RuleFor(o => o.Type, f => type ?? f.PickRandom()) - .RuleFor(o => o.PerformedBy, f => f.Name.FullName()) + .RuleFor(o => o.PerformedBy, f => new CreateDialogDialogActivityActorDto { ActorType = DialogActorType.Values.PartyRepresentative, ActorName = f.Name.FullName() }) .RuleFor(o => o.Description, f => GenerateFakeLocalizations(f.Random.Number(4, 8))) .Generate(count ?? new Randomizer().Number(1, 4)); } 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 eef0ef3f5..7adf3bc51 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs @@ -61,7 +61,7 @@ public async Task InitializeAsync() .AddScoped() .AddScoped() .AddScoped(_ => CreateServiceOwnerNameRegistrySubstitute()) - .AddScoped(_ => CreateNameRegistrySubstitute()) + .AddScoped(_ => CreateNameRegistrySubstitute()) .AddScoped>(_ => CreateApplicationSettingsSubstitute()) .AddScoped() .AddScoped() @@ -75,9 +75,9 @@ public async Task InitializeAsync() await BuildRespawnState(); } - private static IPersonNameRegistry CreateNameRegistrySubstitute() + private static IPartyNameRegistry CreateNameRegistrySubstitute() { - var nameRegistrySubstitute = Substitute.For(); + var nameRegistrySubstitute = Substitute.For(); nameRegistrySubstitute .GetName(Arg.Any(), Arg.Any()) diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs index d6ac9a305..6991b623c 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs @@ -13,9 +13,9 @@ namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.E public class SeenLogTests(DialogApplication application) : ApplicationCollectionFixture(application) { [Fact] - public async Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() + public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() { - // Arrange + /* var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -31,11 +31,14 @@ public async Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() + public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -60,11 +63,14 @@ public async Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -86,11 +92,14 @@ public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() result.EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -112,5 +121,8 @@ public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + + */ + throw new NotImplementedException(); } } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs index f55925c03..32a0f9904 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs @@ -14,8 +14,9 @@ namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.S public class SeenLogTests(DialogApplication application) : ApplicationCollectionFixture(application) { [Fact] - public async Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() + public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -35,11 +36,14 @@ public async Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() + public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -64,11 +68,14 @@ public async Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -90,11 +97,14 @@ public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() result.EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } [Fact] - public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() { + /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -116,5 +126,7 @@ public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() .EndUserIdHash .Should() .HaveLength(PersistentRandomSaltStringHasher.StringLength); + */ + throw new NotImplementedException(); } } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs index dab9d8f2f..6093490f0 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs @@ -1,6 +1,7 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Common.ResourceRegistry; +using Digdir.Domain.Dialogporten.Application.Externals; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Tool.Dialogporten.GenerateFakeData; using NSubstitute; @@ -25,6 +26,7 @@ public async Task CreateDialogCommand_Should_Return_Forbidden_When_Scope_Is_Miss var domainContextSub = Substitute.For(); var userResourceRegistrySub = Substitute.For(); var userOrganizationRegistrySub = Substitute.For(); + var partyNameRegistrySub = Substitute.For(); var createCommand = DialogGenerator.GenerateSimpleFakeDialog(); @@ -37,7 +39,7 @@ public async Task CreateDialogCommand_Should_Return_Forbidden_When_Scope_Is_Miss var commandHandler = new CreateDialogCommandHandler(dialogDbContextSub, mapper, unitOfWorkSub, domainContextSub, userResourceRegistrySub, - userOrganizationRegistrySub); + userOrganizationRegistrySub, partyNameRegistrySub); // Act var result = await commandHandler.Handle(createCommand, CancellationToken.None); @@ -63,6 +65,7 @@ public async Task CreateDialogCommand_Should_Return_ValidationError_When_Progres var domainContextSub = Substitute.For(); var userResourceRegistrySub = Substitute.For(); var userOrganizationRegistrySub = Substitute.For(); + var partyNameRegistrySub = Substitute.For(); var createCommand = DialogGenerator.GenerateSimpleFakeDialog(); @@ -77,7 +80,7 @@ public async Task CreateDialogCommand_Should_Return_ValidationError_When_Progres var commandHandler = new CreateDialogCommandHandler(dialogDbContextSub, mapper, unitOfWorkSub, domainContextSub, userResourceRegistrySub, - userOrganizationRegistrySub); + userOrganizationRegistrySub, partyNameRegistrySub); // Act var result = await commandHandler.Handle(createCommand, CancellationToken.None); From 0fdb308e8544f5f99a4ba42ad989cba3cf5292ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Dybvik=20Langfors?= Date: Sun, 21 Jul 2024 10:45:55 +0200 Subject: [PATCH 2/4] Fix tests, fix broken mappings, add missing properties to SO get/search DTOs --- docs/schema/V1/swagger.verified.json | 359 +++++++++++++++--- .../Queries/Get/GetDialogSeenLogQuery.cs | 2 +- .../Dialogs/Queries/Search/MappingProfile.cs | 3 +- .../Queries/Search/MappingProfile.cs | 3 + .../Queries/Search/SearchDialogSeenLogDto.cs | 4 +- .../Search/SearchDialogSeenLogQuery.cs | 1 + .../Dialogs/Queries/Get/MappingProfile.cs | 1 + .../Dialogs/Queries/Search/MappingProfile.cs | 26 +- .../Dialogs/Queries/Search/SearchDialogDto.cs | 26 ++ .../Queries/Search/SearchDialogQuery.cs | 5 +- .../DialogGenerator.cs | 4 +- .../Dialogs/Queries/ActivityLogTests.cs | 94 +++++ .../EndUser/Dialogs/Queries/SeenLogTests.cs | 40 +- .../Dialogs/Queries/ActivityLogTests.cs | 94 +++++ .../Dialogs/Queries/SeenLogTests.cs | 38 +- 15 files changed, 582 insertions(+), 118 deletions(-) create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index 663b3a5f6..145a9355c 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -319,7 +319,11 @@ "ExtendedType": null, "Type": 3, "RelatedActivityId": null, - "PerformedBy": "Some performed", + "PerformedBy": { + "ActorType": 2, + "ActorName": null, + "ActorId": null + }, "Description": [ { "Value": "Some description", @@ -1787,15 +1791,27 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/SearchDialogSeenLogActorDtoSO" }, - "endUserName": { - "type": "string", + "isViaServiceOwner": { + "type": "boolean", "nullable": true } } }, + "SearchDialogSeenLogActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, "ProblemDetails": { "type": "object", "additionalProperties": false, @@ -1867,12 +1883,23 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { + "seenBy": { + "$ref": "#/components/schemas/GetDialogSeenLogActorDtoSO" + }, + "isViaServiceOwner": { + "type": "boolean" + } + } + }, + "GetDialogSeenLogActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { "type": "string" }, - "endUserName": { - "type": "string", - "nullable": true + "actorId": { + "type": "string" } } }, @@ -2281,8 +2308,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/UpdateDialogDialogActivityActorDto" }, "description": { "type": "array", @@ -2312,6 +2338,35 @@ "Forwarded" ] }, + "UpdateDialogDialogActivityActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, + "DialogActorType_Values": { + "type": "string", + "description": "", + "x-enumNames": [ + "PartyRepresentative", + "ServiceOwner" + ], + "enum": [ + "PartyRepresentative", + "ServiceOwner" + ] + }, "PaginatedListOfSearchDialogDtoSO": { "type": "object", "additionalProperties": false, @@ -2363,6 +2418,11 @@ "format": "int32", "nullable": true }, + "guiAttachmentCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, "extendedStatus": { "type": "string", "nullable": true @@ -2388,6 +2448,14 @@ "status": { "$ref": "#/components/schemas/DialogStatus_Values" }, + "latestActivity": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/SearchDialogDialogActivityDtoSO" + } + ] + }, "content": { "type": "array", "items": { @@ -2402,6 +2470,60 @@ } } }, + "SearchDialogDialogActivityDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "extendedType": { + "type": "string", + "format": "uri", + "nullable": true + }, + "type": { + "$ref": "#/components/schemas/DialogActivityType_Values" + }, + "relatedActivityId": { + "type": "string", + "format": "guid", + "nullable": true + }, + "performedBy": { + "$ref": "#/components/schemas/SearchDialogDialogActivityActorDtoSO" + }, + "description": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocalizationDto" + } + } + } + }, + "SearchDialogDialogActivityActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, "SearchDialogContentDtoSO": { "type": "object", "additionalProperties": false, @@ -2425,20 +2547,31 @@ "type": "string", "format": "guid" }, - "createdAt": { + "seenAt": { "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/SearchDialogDialogSeenLogActorDtoSO" }, - "endUserName": { - "type": "string", + "isViaServiceOwner": { + "type": "boolean", "nullable": true }, "isCurrentEndUser": { - "type": "boolean", - "nullable": true + "type": "boolean" + } + } + }, + "SearchDialogDialogSeenLogActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" } } }, @@ -2765,8 +2898,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/GetDialogDialogActivityActorDtoSO" }, "description": { "type": "array", @@ -2776,6 +2908,23 @@ } } }, + "GetDialogDialogActivityActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, "GetDialogDialogSeenLogDtoSO": { "type": "object", "additionalProperties": false, @@ -2784,20 +2933,27 @@ "type": "string", "format": "guid" }, - "createdAt": { + "seenAt": { "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/GetDialogDialogSeenLogActorDtoSO" }, - "endUserName": { - "type": "string", - "nullable": true + "isViaServiceOwner": { + "type": "boolean" + } + } + }, + "GetDialogDialogSeenLogActorDtoSO": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" }, - "isCurrentEndUser": { - "type": "boolean", - "nullable": true + "actorId": { + "type": "string" } } }, @@ -3084,8 +3240,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/CreateDialogDialogActivityActorDto" }, "description": { "type": "array", @@ -3095,6 +3250,23 @@ } } }, + "CreateDialogDialogActivityActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, "CreateDialogActivityRequest": { "type": "object", "additionalProperties": false, @@ -3123,8 +3295,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/UpdateDialogDialogActivityActorDto" }, "description": { "type": "array", @@ -3195,18 +3366,29 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/SearchDialogSeenLogSeenByDto" }, - "endUserName": { - "type": "string", - "nullable": true + "isViaServiceOwner": { + "type": "boolean" }, "isCurrentEndUser": { "type": "boolean" } } }, + "SearchDialogSeenLogSeenByDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, "GetDialogSeenLogDto": { "type": "object", "additionalProperties": false, @@ -3219,18 +3401,29 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/GetDialogSeenLogActorDto" }, - "endUserName": { - "type": "string", - "nullable": true + "isViaServiceOwner": { + "type": "boolean" }, "isCurrentEndUser": { "type": "boolean" } } }, + "GetDialogSeenLogActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, "PaginatedListOfSearchDialogDto": { "type": "object", "additionalProperties": false, @@ -3352,8 +3545,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/SearchDialogDialogActivityActorDto" }, "description": { "type": "array", @@ -3363,6 +3555,23 @@ } } }, + "SearchDialogDialogActivityActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, "SearchDialogContentDto": { "type": "object", "additionalProperties": false, @@ -3390,11 +3599,11 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/SearchDialogDialogSeenLogActorDto" }, - "endUserName": { - "type": "string", + "isViaServiceOwner": { + "type": "boolean", "nullable": true }, "isCurrentEndUser": { @@ -3402,6 +3611,18 @@ } } }, + "SearchDialogDialogSeenLogActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, "GetDialogDto": { "type": "object", "additionalProperties": false, @@ -3712,8 +3933,7 @@ "nullable": true }, "performedBy": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/GetDialogDialogActivityActorDto" }, "description": { "type": "array", @@ -3723,6 +3943,23 @@ } } }, + "GetDialogDialogActivityActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string", + "nullable": true + }, + "actorId": { + "type": "string", + "nullable": true + } + } + }, "GetDialogDialogSeenLogDto": { "type": "object", "additionalProperties": false, @@ -3735,11 +3972,11 @@ "type": "string", "format": "date-time" }, - "endUserIdHash": { - "type": "string" + "seenBy": { + "$ref": "#/components/schemas/GetDialogDialogSeenLogActorDto" }, - "endUserName": { - "type": "string", + "isViaServiceOwner": { + "type": "boolean", "nullable": true }, "isCurrentEndUser": { @@ -3747,6 +3984,18 @@ } } }, + "GetDialogDialogSeenLogActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, "Operation": { "type": "object", "additionalProperties": false, diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs index a89b1e9cd..bdf5da5d4 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/GetDialogSeenLogQuery.cs @@ -46,7 +46,7 @@ public async Task Handle(GetDialogSeenLogQuery request, var dialog = await _dbContext.Dialogs .AsNoTracking() .Include(x => x.SeenLog.Where(x => x.Id == request.SeenLogId)) - .ThenInclude(x => x.SeenBy) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .FirstOrDefaultAsync(x => x.Id == request.DialogId, cancellationToken: cancellationToken); 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 e4ca70439..aadfd38c2 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 @@ -34,7 +34,8 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap(); + CreateMap() + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs index 76442824a..1d3ad4da1 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Search; @@ -9,5 +10,7 @@ public MappingProfile() { CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + + CreateMap(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs index e548a545b..4e27c357b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogDto.cs @@ -5,12 +5,12 @@ public sealed class SearchDialogSeenLogDto public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public SearchDialogSeenLogSeenByDto SeenBy { get; set; } = null!; + public SearchDialogSeenLogActorDto SeenBy { get; set; } = null!; public bool? IsViaServiceOwner { get; set; } } -public sealed class SearchDialogSeenLogSeenByDto +public sealed class SearchDialogSeenLogActorDto { public string ActorName { get; set; } = null!; public string ActorId { get; set; } = null!; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs index 439660110..96d9159ed 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs @@ -40,6 +40,7 @@ public async Task Handle(SearchDialogSeenLogQuery req var dialog = await _db.Dialogs .AsNoTracking() .Include(x => x.SeenLog) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .Where(x => resourceIds.Contains(x.ServiceResource)) .FirstOrDefaultAsync(x => x.Id == request.DialogId, 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 202b51bb8..9a1b12f19 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 @@ -18,6 +18,7 @@ public MappingProfile() .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore()); CreateMap(); + 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 94825a095..342db6f01 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 @@ -1,7 +1,8 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Search; @@ -11,20 +12,35 @@ internal sealed class MappingProfile : Profile public MappingProfile() { CreateMap() + .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.SeenSinceLastUpdate, 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.Attachments + .Count(x => x.Urls + .Any(url => url.ConsumerTypeId == DialogAttachmentUrlConsumerType.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)); - CreateMap(); + CreateMap() + .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap(); + CreateMap() + .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap(); + CreateMap() + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId)); - CreateMap() + CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId)); } } 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 276c7f55b..c08fd162f 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 @@ -1,5 +1,7 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Search; @@ -13,6 +15,7 @@ public sealed class SearchDialogDto public string Party { get; set; } = null!; public string? EndUserId { get; set; } public int? Progress { get; set; } + public int? GuiAttachmentCount { get; set; } public string? ExtendedStatus { get; set; } public DateTimeOffset CreatedAt { get; set; } public DateTimeOffset UpdatedAt { get; set; } @@ -21,6 +24,8 @@ public sealed class SearchDialogDto public DialogStatus.Values Status { get; set; } + public SearchDialogDialogActivityDto? LatestActivity { get; set; } + public List Content { get; set; } = []; public List SeenSinceLastUpdate { get; set; } = []; } @@ -52,3 +57,24 @@ public sealed class SearchDialogContentDto public DialogContentType.Values Type { get; set; } public List Value { get; set; } = []; } + +public sealed class SearchDialogDialogActivityDto +{ + public Guid Id { get; set; } + public DateTimeOffset? CreatedAt { get; set; } + public Uri? ExtendedType { get; set; } + + public DialogActivityType.Values Type { get; set; } + + public Guid? RelatedActivityId { get; set; } + + public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public List Description { get; set; } = []; +} + +public sealed class SearchDialogDialogActivityActorDto +{ + public DialogActorType.Values ActorType { get; set; } + public string? ActorName { get; set; } + public string? ActorId { get; set; } +} 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 9cfbba6c0..9577509f3 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 @@ -177,11 +177,12 @@ public async Task Handle(SearchDialogQuery request, Cancella .ProjectTo(_mapper.ConfigurationProvider) .ToPaginatedListAsync(request, cancellationToken: cancellationToken); - foreach (var seenRecord in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) + if (request.EndUserId is not null) { - if (request.EndUserId is not null) + foreach (var seenRecord in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) { seenRecord.IsCurrentEndUser = seenRecord.SeenBy.ActorId == request.EndUserId; + } } diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs index f124753fa..c70cc0498 100644 --- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs +++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs @@ -121,10 +121,10 @@ public static string GenerateFakeResource() return ResourcePrefix + result.ToString("D4", CultureInfo.InvariantCulture); } - public static string GenerateRandomParty() + public static string GenerateRandomParty(bool forcePerson = false) { var r = new Randomizer(); - return r.Bool() ? $"urn:altinn:organization:identifier-no:{GenerateFakeOrgNo()}" : $"urn:altinn:person:identifier-no:{GenerateFakePid()}"; + return r.Bool() && !forcePerson ? $"urn:altinn:organization:identifier-no:{GenerateFakeOrgNo()}" : $"urn:altinn:person:identifier-no:{GenerateFakePid()}"; } private static readonly int[] SocialSecurityNumberWeights1 = [3, 7, 6, 1, 8, 9, 4, 5, 2]; diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs new file mode 100644 index 000000000..c0ea34457 --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs @@ -0,0 +1,94 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; +using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Parties; +using Digdir.Tool.Dialogporten.GenerateFakeData; +using FluentAssertions; + +namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.EndUser.Dialogs.Queries; + +[Collection(nameof(DialogCqrsCollectionFixture))] +public class ActivityLogTests(DialogApplication application) : ApplicationCollectionFixture(application) +{ + [Fact] + public async Task Get_Dialog_ActivityLog_Should_Not_Return_User_Ids_Unhashed() + { + var (_, createCommandResponse) = await GenerateDialogWithActivity(); + + // Act + var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.Activities + .Single() + .PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); + + } + + [Fact] + public async Task Search_Dialog_LatestActivity_Should_Not_Return_User_Ids_Unhashed() + { + // Arrange + var (createDialogCommand, _) = await GenerateDialogWithActivity(); + + // Act + var response = await Application.Send(new SearchDialogQuery + { + ServiceResource = [createDialogCommand.ServiceResource] + }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.Items + .Single() + .LatestActivity! + .PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); + } + + [Fact] + public async Task Get_ActivityLog_Should_Not_Return_User_Ids_Unhashed() + { + // Arrange + var (_, createCommandResponse) = await GenerateDialogWithActivity(); + + var getDialogResult = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }); + var activityId = getDialogResult.AsT0.Activities.First().Id; + + // Act + var response = await Application.Send(new GetDialogActivityQuery() + { + DialogId = createCommandResponse.AsT0.Value, + ActivityId = activityId + }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); + } + + private async Task<(CreateDialogCommand, CreateDialogResult)> GenerateDialogWithActivity() + { + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var activity = DialogGenerator.GenerateFakeDialogActivity(); + activity.PerformedBy.ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true); + activity.PerformedBy.ActorName = null; + createDialogCommand.Activities.Add(activity); + var createCommandResponse = await Application.Send(createDialogCommand); + return (createDialogCommand, createCommandResponse); + } +} diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs index 6991b623c..5b99374ea 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs @@ -4,6 +4,7 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Parties; using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; @@ -13,9 +14,9 @@ namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.E public class SeenLogTests(DialogApplication application) : ApplicationCollectionFixture(application) { [Fact] - public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() + public async Task Get_Dialog_SeenLog_Should_Not_Return_User_Ids_Unhashed() { - /* + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -28,17 +29,15 @@ public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() result.SeenSinceLastUpdate .Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); + } [Fact] - public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() + public async Task Search_Dialog_SeenLog_Should_Not_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -60,17 +59,14 @@ public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() .Single() .SeenSinceLastUpdate .Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); } [Fact] - public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -89,17 +85,14 @@ public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() response.TryPickT0(out var result, out _).Should().BeTrue(); result.Should().NotBeNull(); - result.EndUserIdHash + result.SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); } [Fact] - public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -118,11 +111,8 @@ public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() result.Should().NotBeNull(); result.Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.HashPrefixWithSeparator); } } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs new file mode 100644 index 000000000..9e4d37b03 --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs @@ -0,0 +1,94 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Search; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogActivities.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; +using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Parties; +using Digdir.Tool.Dialogporten.GenerateFakeData; +using FluentAssertions; + +namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Dialogs.Queries; + +[Collection(nameof(DialogCqrsCollectionFixture))] +public class ActivityLogTests(DialogApplication application) : ApplicationCollectionFixture(application) +{ + [Fact] + public async Task Get_Dialog_ActivityLog_Should_Return_User_Ids_Unhashed() + { + var (_, createCommandResponse) = await GenerateDialogWithActivity(); + + // Act + var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.Activities + .Single() + .PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); + + } + + [Fact] + public async Task Search_Dialog_LatestActivity_Should_Return_User_Ids_Unhashed() + { + // Arrange + var (createDialogCommand, _) = await GenerateDialogWithActivity(); + + // Act + var response = await Application.Send(new SearchDialogQuery + { + ServiceResource = [createDialogCommand.ServiceResource] + }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.Items + .Single() + .LatestActivity! + .PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); + } + + [Fact] + public async Task Get_ActivityLog_Should_Return_User_Ids_Unhashed() + { + // Arrange + var (_, createCommandResponse) = await GenerateDialogWithActivity(); + + var getDialogResult = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }); + var activityId = getDialogResult.AsT0.Activities.First().Id; + + // Act + var response = await Application.Send(new GetDialogActivityQuery() + { + DialogId = createCommandResponse.AsT0.Value, + ActivityId = activityId + }); + + // Assert + response.TryPickT0(out var result, out _).Should().BeTrue(); + result.Should().NotBeNull(); + + result.PerformedBy.ActorId + .Should() + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); + } + + private async Task<(CreateDialogCommand, CreateDialogResult)> GenerateDialogWithActivity() + { + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var activity = DialogGenerator.GenerateFakeDialogActivity(); + activity.PerformedBy.ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true); + activity.PerformedBy.ActorName = null; + createDialogCommand.Activities.Add(activity); + var createCommandResponse = await Application.Send(createDialogCommand); + return (createDialogCommand, createCommandResponse); + } +} diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs index 32a0f9904..c1471fe91 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs @@ -1,8 +1,8 @@ -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Search; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Search; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Parties; using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; using GetDialogQueryEndUser = Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get.GetDialogQuery; @@ -14,9 +14,8 @@ namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.S public class SeenLogTests(DialogApplication application) : ApplicationCollectionFixture(application) { [Fact] - public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() + public async Task Get_Dialog_SeenLog_Should_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -33,17 +32,14 @@ public Task Get_Dialog_Should_Not_Return_User_Ids_Unhashed() result.SeenSinceLastUpdate .Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); } [Fact] - public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() + public async Task Search_Dialog_SeenLog_Should_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -65,17 +61,14 @@ public Task Search_Dialog_Should_Not_Return_User_Ids_Unhashed() .Single() .SeenSinceLastUpdate .Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); } [Fact] - public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public async Task Get_SeenLog_Should_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -94,17 +87,14 @@ public Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed() response.TryPickT0(out var result, out _).Should().BeTrue(); result.Should().NotBeNull(); - result.EndUserIdHash + result.SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); } [Fact] - public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() + public async Task Search_SeenLog_Should_Return_User_Ids_Unhashed() { - /* // Arrange var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); var createCommandResponse = await Application.Send(createDialogCommand); @@ -123,10 +113,8 @@ public Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed() result.Should().NotBeNull(); result.Single() - .EndUserIdHash + .SeenBy.ActorId .Should() - .HaveLength(PersistentRandomSaltStringHasher.StringLength); - */ - throw new NotImplementedException(); + .StartWith(NorwegianPersonIdentifier.PrefixWithSeparator); } } From 5c0799b6ef0e2c762d5bb684a05c817457311f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Dybvik=20Langfors?= Date: Mon, 22 Jul 2024 14:11:05 +0200 Subject: [PATCH 3/4] Fix invalid actorid comparison --- .../DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs | 2 +- .../V1/EndUser/Dialogs/Queries/Search/SearchDialogQuery.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs index f5f740e78..92b211caa 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs @@ -73,7 +73,7 @@ public async Task Handle(SearchDialogSeenLogQuery req .Select(x => { var dto = _mapper.Map(x); - dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalId == x.SeenBy.ActorId; + dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalIdWithPrefix == x.SeenBy.ActorId; return dto; }) .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 137d8496e..fe5723a0d 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 @@ -173,7 +173,7 @@ public async Task Handle(SearchDialogQuery request, Cancella foreach (var seenLog in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) { - seenLog.IsCurrentEndUser = currentUserInfo.UserId.ExternalIdWithPrefix == seenLog.SeenBy.ActorId; + seenLog.IsCurrentEndUser = IdentifierMasker.GetMaybeMaskedIdentifier(currentUserInfo.UserId.ExternalIdWithPrefix) == seenLog.SeenBy.ActorId; } return paginatedList; From 22459cd6a89715674610a697b1755fc6ed1a7007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Mon, 22 Jul 2024 16:33:52 +0200 Subject: [PATCH 4/4] chore: Actor cleanup (#913) --- docs/schema/V1/schema.verified.graphql | 16 ++++++++++++--- docs/schema/V1/swagger.verified.json | 3 ++- .../Common/IdentifierMasker.cs | 2 +- .../Queries/Get/GetDialogActivityDto.cs | 4 +--- .../Dialogs/Queries/Get/GetDialogDto.cs | 4 ++-- .../Dialogs/Queries/Get/GetDialogQuery.cs | 1 - .../Dialogs/Queries/Get/MappingProfile.cs | 1 - .../Dialogs/Queries/Search/SearchDialogDto.cs | 2 +- .../Queries/Get/GetDialogActivityDto.cs | 2 +- .../Queries/Get/GetDialogSeenLogDto.cs | 2 +- .../Search/SearchDialogSeenLogQuery.cs | 2 +- .../Commands/Create/CreateDialogCommand.cs | 13 +++++++++--- .../Create/CreateDialogCommandValidator.cs | 1 + .../Commands/Create/CreateDialogDto.cs | 2 +- .../Update/UpdateDialogCommandValidator.cs | 1 + .../Commands/Update/UpdateDialogDto.cs | 2 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 2 +- .../Dialogs/Queries/Get/GetDialogQuery.cs | 2 +- .../Dialogs/Queries/Get/MappingProfile.cs | 4 +++- .../Dialogs/Queries/Search/SearchDialogDto.cs | 2 +- .../Queries/Search/SearchDialogQuery.cs | 2 +- .../EndUser/Common/MappingProfile.cs | 8 ++++++++ .../EndUser/Common/ObjectTypes.cs | 20 +++++++++++++++---- .../Authorization/DecisionRequestHelper.cs | 1 - .../Migrations/20240720163626_AddActor.cs | 13 ++++++++---- .../Dialogs/Queries/ActivityLogTests.cs | 3 ++- .../EndUser/Dialogs/Queries/SeenLogTests.cs | 1 - .../Dialogs/Queries/ActivityLogTests.cs | 4 +++- 28 files changed, 82 insertions(+), 38 deletions(-) diff --git a/docs/schema/V1/schema.verified.graphql b/docs/schema/V1/schema.verified.graphql index 3e214b904..e914f1029 100644 --- a/docs/schema/V1/schema.verified.graphql +++ b/docs/schema/V1/schema.verified.graphql @@ -16,10 +16,16 @@ type Activity { extendedType: URL type: ActivityType! relatedActivityId: UUID - performedBy: String + performedBy: Actor! description: [Localization!]! } +type Actor { + actorType: ActorType + actorId: String + actorName: String +} + type ApiAction { id: UUID! action: String! @@ -173,8 +179,7 @@ type SearchDialogsPayload { type SeenLog { id: UUID! seenAt: DateTime! - endUserIdHash: String! - endUserName: String + seenBy: Actor! isCurrentEndUser: Boolean! } @@ -224,6 +229,11 @@ enum ActivityType { FORWARDED } +enum ActorType { + PARTY_REPRESENTATIVE + SERVICE_OWNER +} + enum ApplyPolicy { BEFORE_RESOLVER AFTER_RESOLVER diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index 145a9355c..120307510 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -1887,7 +1887,8 @@ "$ref": "#/components/schemas/GetDialogSeenLogActorDtoSO" }, "isViaServiceOwner": { - "type": "boolean" + "type": "boolean", + "nullable": true } } }, diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs b/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs index f5b47f921..70daf3a81 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Common/IdentifierMasker.cs @@ -11,7 +11,7 @@ public static class IdentifierMasker private const int SaltSize = 16; private static readonly byte[] Salt = RandomNumberGenerator.GetBytes(SaltSize); - public static string? Hash(string? plaintext) + private static string? Hash(string? plaintext) { if (string.IsNullOrWhiteSpace(plaintext)) { 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 08d1194a3..78024dea1 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 @@ -12,11 +12,9 @@ public sealed class GetDialogActivityDto public DialogActivityType.Values Type { get; set; } - public DateTimeOffset? DeletedAt { get; set; } - public Guid? RelatedActivityId { get; set; } - public GetDialogActivityActorDto PerformedBy { get; set; } = new(); + public GetDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } 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 867662fce..83b0153b7 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 @@ -44,7 +44,7 @@ public sealed class GetDialogDialogSeenLogDto public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public GetDialogDialogSeenLogActorDto SeenBy { get; set; } = new(); + public GetDialogDialogSeenLogActorDto SeenBy { get; set; } = null!; public bool? IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } @@ -73,7 +73,7 @@ public sealed class GetDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public GetDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public GetDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { 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 d3740eb7b..01abb0fe7 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 @@ -6,7 +6,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.Parties; using MediatR; using Microsoft.EntityFrameworkCore; using OneOf; 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 f00ef9afa..ef9064dea 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 @@ -22,7 +22,6 @@ public MappingProfile() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); CreateMap() - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); CreateMap() 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 4383ef050..f605efaef 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 @@ -61,7 +61,7 @@ public sealed class SearchDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs index a517c6ef5..bffc2eaa7 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/GetDialogActivityDto.cs @@ -16,7 +16,7 @@ public sealed class GetDialogActivityDto public Guid? RelatedActivityId { get; set; } - public GetDialogActivityActorDto PerformedBy { get; set; } = new(); + public GetDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs index adacd81e4..06c037a4f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/GetDialogSeenLogDto.cs @@ -7,7 +7,7 @@ public sealed class GetDialogSeenLogDto public GetDialogSeenLogActorDto SeenBy { get; set; } = null!; - public bool IsViaServiceOwner { get; set; } + public bool? IsViaServiceOwner { get; set; } } public sealed class GetDialogSeenLogActorDto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs index 96d9159ed..a74a94f6c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SearchDialogSeenLogQuery.cs @@ -40,7 +40,7 @@ public async Task Handle(SearchDialogSeenLogQuery req var dialog = await _db.Dialogs .AsNoTracking() .Include(x => x.SeenLog) - .ThenInclude(x => x.SeenBy) + .ThenInclude(x => x.SeenBy) .IgnoreQueryFilters() .Where(x => resourceIds.Contains(x.ServiceResource)) .FirstOrDefaultAsync(x => x.Id == request.DialogId, diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs index e7e2c868a..4ccb7fd14 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs @@ -76,13 +76,20 @@ public async Task Handle(CreateDialogCommand request, Cancel foreach (var activity in request.Activities) { - if (activity.PerformedBy.ActorId is null) continue; + if (activity.PerformedBy.ActorId is null) + { + continue; + } activity.PerformedBy.ActorName = await _partyNameRegistry.GetName(activity.PerformedBy.ActorId, cancellationToken); - if (activity.PerformedBy.ActorName == null) + + if (!string.IsNullOrWhiteSpace(activity.PerformedBy.ActorName)) { - return new DomainError(new DomainFailure(nameof(activity.PerformedBy.ActorId), $"Unable to look up actor id: {activity.PerformedBy.ActorId}")); + continue; } + + var domainFailure = new DomainFailure(nameof(activity.PerformedBy.ActorId), $"Unable to look up name for actor id: {activity.PerformedBy.ActorId}"); + return new DomainError(domainFailure); } var dialog = _mapper.Map(request); 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 3599f2b3a..3a50fee6e 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 @@ -275,6 +275,7 @@ public CreateDialogDialogActivityDtoValidator( .NotEqual(x => x.Id) .When(x => x.RelatedActivityId.HasValue); RuleFor(x => x.PerformedBy) + .NotNull() .SetValidator(actorValidator); RuleFor(x => x.Description) .NotEmpty() diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs index d69054640..e541da881 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs @@ -55,7 +55,7 @@ public sealed class CreateDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public CreateDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public CreateDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } 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 abab4aec8..8a41548de 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 @@ -274,6 +274,7 @@ public UpdateDialogDialogActivityDtoValidator( .NotEqual(x => x.Id) .When(x => x.RelatedActivityId.HasValue); RuleFor(x => x.PerformedBy) + .NotNull() .SetValidator(actorValidator); RuleFor(x => x.Description) .NotEmpty() diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs index 564aa9090..42495c44a 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs @@ -53,7 +53,7 @@ public class UpdateDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public UpdateDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public UpdateDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } 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 e60f4b4a0..d8009eb16 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 @@ -77,7 +77,7 @@ public sealed class GetDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public GetDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public GetDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } 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 f13dd1a6f..2ba826e71 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 @@ -81,7 +81,7 @@ public async Task Handle(GetDialogQuery request, CancellationTo { var logDto = _mapper.Map(log); // TODO: Set when #386 is implemented - // logDto.IsAuthenticatedUser = log.EndUserId == userPid; + // logDto.IsCurrentEndUser = log.EndUserId == userPid; return logDto; }) .ToList(); 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 9a1b12f19..e762f361d 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 @@ -17,7 +17,9 @@ public MappingProfile() .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId)) .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore()); - CreateMap(); + CreateMap() + .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); + 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 c08fd162f..af640f31a 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 @@ -68,7 +68,7 @@ public sealed class SearchDialogDialogActivityDto public Guid? RelatedActivityId { get; set; } - public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = new(); + public SearchDialogDialogActivityActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } 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 9577509f3..be3241622 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 @@ -10,6 +10,7 @@ using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Localizations; +using Digdir.Domain.Dialogporten.Domain.Parties; using MediatR; using Microsoft.EntityFrameworkCore; using OneOf; @@ -182,7 +183,6 @@ public async Task Handle(SearchDialogQuery request, Cancella foreach (var seenRecord in paginatedList.Items.SelectMany(x => x.SeenSinceLastUpdate)) { seenRecord.IsCurrentEndUser = seenRecord.SeenBy.ActorId == request.EndUserId; - } } diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs index 8924c36d8..756771323 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs @@ -12,7 +12,10 @@ public MappingProfile() CreateMap(); CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); @@ -21,7 +24,12 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorType)); + CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorType)); } } diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/ObjectTypes.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/ObjectTypes.cs index 313dc1d45..dfc761928 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/ObjectTypes.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/ObjectTypes.cs @@ -28,9 +28,7 @@ public sealed class SeenLog public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public string EndUserIdHash { get; set; } = null!; - - public string? EndUserName { get; set; } + public Actor SeenBy { get; set; } = null!; public bool IsCurrentEndUser { get; set; } } @@ -45,10 +43,24 @@ public sealed class Activity public Guid? RelatedActivityId { get; set; } - public string? PerformedBy { get; set; } + public Actor PerformedBy { get; set; } = null!; + public List Description { get; set; } = []; } +public sealed class Actor +{ + public ActorType? ActorType { get; set; } + public string? ActorId { get; set; } + public string? ActorName { get; set; } +} + +public enum ActorType +{ + PartyRepresentative = 1, + ServiceOwner = 2 +} + public enum ActivityType { [GraphQLDescription("Refers to a submission made by a party that has been received by the service provider.")] diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/DecisionRequestHelper.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/DecisionRequestHelper.cs index 3b744d849..36a26cdce 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/DecisionRequestHelper.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/DecisionRequestHelper.cs @@ -1,6 +1,5 @@ using Altinn.Authorization.ABAC.Xacml.JsonProfile; using System.Security.Claims; -using System.Text.Json; using Digdir.Domain.Dialogporten.Application.Common.Authorization; using Digdir.Domain.Dialogporten.Application.Common.Extensions; using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs index 68dee23e2..11061c86a 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20240720163626_AddActor.cs @@ -13,6 +13,13 @@ public partial class AddActor : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.Sql(""" + DELETE FROM public."DialogActivity" + """); + migrationBuilder.Sql(""" + DELETE FROM public."DialogSeenLog" + """); + migrationBuilder.DropForeignKey( name: "FK_LocalizationSet_DialogSeenLog_DialogSeenLogId", table: "LocalizationSet"); @@ -47,15 +54,13 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "SeenById", table: "DialogSeenLog", type: "uuid", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + nullable: false); migrationBuilder.AddColumn( name: "PerformedById", table: "DialogActivity", type: "uuid", - nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + nullable: false); migrationBuilder.CreateTable( name: "DialogActorType", diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs index c0ea34457..4106b3be0 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs @@ -3,6 +3,7 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Parties; using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; @@ -84,7 +85,7 @@ public async Task Get_ActivityLog_Should_Not_Return_User_Ids_Unhashed() private async Task<(CreateDialogCommand, CreateDialogResult)> GenerateDialogWithActivity() { var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); - var activity = DialogGenerator.GenerateFakeDialogActivity(); + var activity = DialogGenerator.GenerateFakeDialogActivity(type: DialogActivityType.Values.Information); activity.PerformedBy.ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true); activity.PerformedBy.ActorName = null; createDialogCommand.Activities.Add(activity); diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs index 5b99374ea..37578216e 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs @@ -1,4 +1,3 @@ -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search; using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs index 9e4d37b03..7ae32eb66 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs @@ -3,6 +3,7 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogActivities.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Parties; using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; @@ -15,6 +16,7 @@ public class ActivityLogTests(DialogApplication application) : ApplicationCollec [Fact] public async Task Get_Dialog_ActivityLog_Should_Return_User_Ids_Unhashed() { + // Arrange var (_, createCommandResponse) = await GenerateDialogWithActivity(); // Act @@ -84,7 +86,7 @@ public async Task Get_ActivityLog_Should_Return_User_Ids_Unhashed() private async Task<(CreateDialogCommand, CreateDialogResult)> GenerateDialogWithActivity() { var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); - var activity = DialogGenerator.GenerateFakeDialogActivity(); + var activity = DialogGenerator.GenerateFakeDialogActivity(type: DialogActivityType.Values.Information); activity.PerformedBy.ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true); activity.PerformedBy.ActorName = null; createDialogCommand.Activities.Add(activity);