Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce actor and consolidate seenby, performedby and sender #901

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Digdir.Domain.Dialogporten.Domain.Parties;
using UserIdType = Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType.Values;
using UserIdType = Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.ActorType.Values;

namespace Digdir.Domain.Dialogporten.Application.Common.Extensions;

Expand Down Expand Up @@ -236,7 +236,7 @@ public static (UserIdType, string externalId) GetUserType(this ClaimsPrincipal c
// TODO: This needs to be fixed when implementing https://github.com/digdir/dialogporten/issues/386
// F.ex. a middleware that runs before UserTypeValidationMiddleware that adds the PID claim
return (claimsPrincipal.HasScope(ServiceProviderScope)
? UserIdType.ServiceOwnerOnBehalfOfPerson
? UserIdType.UserViaServiceOwner
: UserIdType.Person, externalId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Externals;
using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
using UserIdType = Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType.Values;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Parties;

namespace Digdir.Domain.Dialogporten.Application.Common;

Expand All @@ -14,14 +15,16 @@ public interface IUserRegistry

public sealed class UserId
{
public required UserIdType Type { get; set; }
public required ActorType.Values Type { get; set; }
public required string ExternalId { get; init; }
public required string URNId { get; init; }
}

public sealed class UserInformation
{
public required UserId UserId { get; init; }
public string? Name { get; init; }
public string? URNId { get; init; }
}

public class UserRegistry : IUserRegistry
Expand All @@ -40,38 +43,44 @@ public UserRegistry(
public UserId GetCurrentUserId()
{
var (userType, externalId) = _user.GetPrincipal().GetUserType();
if (userType == UserIdType.Unknown)
if (userType == ActorType.Values.Unknown)
{
throw new InvalidOperationException("User external id not found");
}

return new() { Type = userType, ExternalId = externalId };
return new() { Type = userType, ExternalId = externalId, URNId = $"{NorwegianPersonIdentifier.PrefixWithSeparator}{externalId}" };
}

public async Task<UserInformation> GetCurrentUserInformation(CancellationToken cancellationToken)
{
var userId = GetCurrentUserId();
string? name;
string? urnId;

switch (userId.Type)
{
case UserIdType.Person:
case UserIdType.ServiceOwnerOnBehalfOfPerson:
case ActorType.Values.Person:
case ActorType.Values.UserViaServiceOwner:
name = await _personNameRegistry.GetName(userId.ExternalId, cancellationToken);
// todo: would it be correct to set the URNId here? Or does it belong elsewhere...
urnId = $"{NorwegianPersonIdentifier.PrefixWithSeparator}{userId.ExternalId}";
break;

case UserIdType.LegacySystemUser:
case ActorType.Values.LegacySystemUser:
_user.TryGetLegacySystemUserName(out var legacyUserName);
name = legacyUserName;
urnId = $"{NorwegianPersonIdentifier.PrefixWithSeparator}{userId.ExternalId}";
break;

case UserIdType.SystemUser:
// TODO: Implement when SystemUsers are introduced?
case ActorType.Values.SystemUser:
// TODO: Implement when SystemUsers are introduced? https://docs.altinn.studio/nb/authentication/what-do-you-get/systemuser/
// TODO: What would be the URN of the system user?
name = "System User";
urnId = $"{NorwegianPersonIdentifier.PrefixWithSeparator}{userId.ExternalId}";
break;

case UserIdType.ServiceOwner:
case UserIdType.Unknown:
case ActorType.Values.ServiceOwner:
case ActorType.Values.Unknown:
default:
throw new UnreachableException();
}
Expand All @@ -80,6 +89,7 @@ public async Task<UserInformation> GetCurrentUserInformation(CancellationToken c
{
UserId = userId,
Name = name,
URNId = urnId
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments;

namespace Digdir.Domain.Dialogporten.Application.Externals;
Expand All @@ -24,7 +25,7 @@ public interface IDialogDbContext

DbSet<OutboxMessage> OutboxMessages { get; }
DbSet<OutboxMessageConsumer> OutboxMessageConsumers { get; }
DbSet<DialogSeenLog> DialogSeenLog { get; }
DbSet<Actor> DialogSeenLog { get; }

/// <summary>
/// Validate a property on the <typeparamref name="TEntity"/> using a lambda
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get;

public class GetDialogSeenLogDto
public class GetDialogActorDto
{
public Guid Id { get; set; }
public DateTimeOffset SeenAt { get; set; }

public string EndUserIdHash { get; set; } = null!;

public string? EndUserName { get; set; }

public string ActorId { get; set; } = null!;
public string? ActorName { get; set; }
public bool IsCurrentEndUser { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
using Digdir.Domain.Dialogporten.Application.Externals;
using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Parties;
using MediatR;
using Microsoft.EntityFrameworkCore;
using OneOf;
Expand All @@ -17,7 +19,7 @@ public sealed class GetDialogSeenLogQuery : IRequest<GetDialogSeenLogResult>
}

[GenerateOneOf]
public partial class GetDialogSeenLogResult : OneOfBase<GetDialogSeenLogDto, EntityNotFound, EntityDeleted, Forbidden>;
public partial class GetDialogSeenLogResult : OneOfBase<GetDialogActorDto, EntityNotFound, EntityDeleted, Forbidden>;

internal sealed class GetDialogSeenLogQueryHandler : IRequestHandler<GetDialogSeenLogQuery, GetDialogSeenLogResult>
{
Expand Down Expand Up @@ -76,12 +78,15 @@ public async Task<GetDialogSeenLogResult> Handle(GetDialogSeenLogQuery request,
var seenLog = dialog.SeenLog.FirstOrDefault();
if (seenLog is null)
{
return new EntityNotFound<DialogSeenLog>(request.SeenLogId);
return new EntityNotFound<Actor>(request.SeenLogId);
}

var dto = _mapper.Map<GetDialogSeenLogDto>(seenLog);
dto.IsCurrentEndUser = currentUserInformation.UserId.ExternalId == seenLog.EndUserId;
dto.EndUserIdHash = _stringHasher.Hash(seenLog.EndUserId);
var dto = _mapper.Map<GetDialogActorDto>(seenLog);
dto.IsCurrentEndUser = currentUserInformation.UserId.URNId == seenLog.ActorId;

var actorId = dto.ActorId.Split(':');
var pid = actorId.Last();
dto.ActorId = NorwegianPersonIdentifier.HashPrefixWithSeparator + _stringHasher.Hash(pid);

return dto;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get;
Expand All @@ -7,7 +8,7 @@ public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<DialogSeenLog, GetDialogSeenLogDto>()
CreateMap<Actor, GetDialogActorDto>()
.ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search;
Expand All @@ -7,7 +8,7 @@ public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<DialogSeenLog, SearchDialogSeenLogDto>()
CreateMap<Actor, SearchDialogActorDto>()
.ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search;

public class SearchDialogSeenLogDto
public class SearchDialogActorDto
{
public Guid Id { get; set; }
public DateTimeOffset SeenAt { get; set; }

public string EndUserIdHash { get; set; } = null!;

public string? EndUserName { get; set; }

public string ActorId { get; set; } = null!;
public string? ActorName { get; set; }
// todo: include ActorType? 🤔
public bool IsCurrentEndUser { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using OneOf;
using Microsoft.EntityFrameworkCore;
using Digdir.Domain.Dialogporten.Application.Common;
using Digdir.Domain.Dialogporten.Domain.Parties;

namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search;

Expand All @@ -16,7 +17,7 @@ public sealed class SearchDialogSeenLogQuery : IRequest<SearchDialogSeenLogResul
}

[GenerateOneOf]
public partial class SearchDialogSeenLogResult : OneOfBase<List<SearchDialogSeenLogDto>, EntityNotFound, EntityDeleted, Forbidden>;
public partial class SearchDialogSeenLogResult : OneOfBase<List<SearchDialogActorDto>, EntityNotFound, EntityDeleted, Forbidden>;

internal sealed class SearchDialogSeenLogQueryHandler : IRequestHandler<SearchDialogSeenLogQuery, SearchDialogSeenLogResult>
{
Expand Down Expand Up @@ -47,7 +48,7 @@ public async Task<SearchDialogSeenLogResult> Handle(SearchDialogSeenLogQuery req
var dialog = await _db.Dialogs
.AsNoTracking()
.Include(x => x.SeenLog)
.ThenInclude(x => x.Via!.Localizations)
// todo: cannot use via here. That is interpreted through the DialogActor entity if type is UserViaServiceOwner(?).
.IgnoreQueryFilters()
.FirstOrDefaultAsync(x => x.Id == request.DialogId,
cancellationToken: cancellationToken);
Expand Down Expand Up @@ -75,9 +76,11 @@ public async Task<SearchDialogSeenLogResult> Handle(SearchDialogSeenLogQuery req
return dialog.SeenLog
.Select(x =>
{
var dto = _mapper.Map<SearchDialogSeenLogDto>(x);
dto.IsCurrentEndUser = x.EndUserId == currentUserInformation.UserId.ExternalId;
dto.EndUserIdHash = _stringHasher.Hash(x.EndUserId);
var dto = _mapper.Map<SearchDialogActorDto>(x);
dto.IsCurrentEndUser = x.ActorId == currentUserInformation.UserId.URNId;
var actorId = x.ActorId!.Split(':');
var pid = actorId.Last();
dto.ActorId = NorwegianPersonIdentifier.HashPrefixWithSeparator + _stringHasher.Hash(pid);
return dto;
})
.ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,22 @@ public sealed class GetDialogDto
public List<GetDialogDialogGuiActionDto> GuiActions { get; set; } = [];
public List<GetDialogDialogApiActionDto> ApiActions { get; set; } = [];
public List<GetDialogDialogActivityDto> Activities { get; set; } = [];
public List<GetDialogDialogSeenLogDto> SeenSinceLastUpdate { get; set; } = [];
// todo: SeenByEndUserSinceLastUpdate?
public List<GetDialogDialogActorDto> SeenSinceLastUpdate { get; set; } = [];

}

public class GetDialogDialogSeenLogDto
public class GetDialogDialogActorDto
{
public Guid Id { get; set; }
public DateTimeOffset SeenAt { get; set; }

public string EndUserIdHash { get; set; } = null!;
public string ActorId { get; set; } = null!; // ActorId

public string? EndUserName { get; set; }
public string? ActorName { get; set; } // ActorName

public bool IsCurrentEndUser { get; set; }
// todo: IsCurrentEndUser? If userViaSystemOwner, then we probably need to name it IsCurrentEndUser to avoid confusion?
public bool IsCurrentEndUser { get; set; } // IsCurrentActor(?)
}

public sealed class GetDialogContentDto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -99,9 +100,9 @@ public async Task<GetDialogResult> Handle(GetDialogQuery request, CancellationTo
return new EntityDeleted<DialogEntity>(request.DialogId);
}

// TODO: What if name lookup fails
// https://github.com/digdir/dialogporten/issues/387
dialog.UpdateSeenAt(currentUserInformation.UserId.ExternalId, currentUserInformation.UserId.Type, currentUserInformation.Name);
// TODO: What if name lookup fails: https://github.com/digdir/dialogporten/issues/387 & https://github.com/digdir/dialogporten/issues/851
// todo: UpdateSeenAtByEndUser(?)
dialog.UpdateSeenAt(currentUserInformation.UserId.URNId, currentUserInformation.UserId.Type, currentUserInformation.Name);

var saveResult = await _unitOfWork
.WithoutAuditableSideEffects()
Expand All @@ -117,9 +118,14 @@ public async Task<GetDialogResult> Handle(GetDialogQuery request, CancellationTo
dialogDto.SeenSinceLastUpdate = dialog.SeenLog
.Select(log =>
{
var logDto = _mapper.Map<GetDialogDialogSeenLogDto>(log);
logDto.IsCurrentEndUser = log.EndUserId == currentUserInformation.UserId.ExternalId;
logDto.EndUserIdHash = _stringHasher.Hash(log.EndUserId);
var logDto = _mapper.Map<GetDialogDialogActorDto>(log);
logDto.IsCurrentEndUser = log.ActorId == currentUserInformation.UserId.URNId;
// todo: actorId here has to be defined because it is a user. But seems harsh
// todo: hash the pid and use the ephemeral urn
// todo: We know that ActorId always has a value here, but 🤫🤫 dangerous. Consider separating the two by extending DialogActor? 🤔
var actorId = log.ActorId!.Split(':');
var pid = actorId.Last();
logDto.ActorId = NorwegianPersonIdentifier.HashPrefixWithSeparator + _stringHasher.Hash(pid);
return logDto;
})
.ToList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
Expand All @@ -16,7 +17,8 @@ public MappingProfile()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusId))
.ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore());

CreateMap<DialogSeenLog, GetDialogDialogSeenLogDto>()
// todo: here we need to do additional mapping
CreateMap<Actor, GetDialogDialogActorDto>()
.ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));

CreateMap<DialogActivity, GetDialogDialogActivityDto>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments;
Expand Down Expand Up @@ -29,9 +30,9 @@ public MappingProfile()
CreateMap<DialogContent, SearchDialogContentDto>()
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId));

CreateMap<DialogSeenLog, SearchDialogDialogSeenLogDto>()
CreateMap<Actor, SearchDialogDialogActorDto>()
.ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt))
.ForMember(dest => dest.EndUserIdHash, opt => opt.MapFrom(src => src.EndUserId));
.ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId));

CreateMap<DialogActivity, SearchDialogDialogActivityDto>()
.ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ public sealed class SearchDialogDto
public SearchDialogDialogActivityDto? LatestActivity { get; set; }

public List<SearchDialogContentDto> Content { get; set; } = [];
public List<SearchDialogDialogSeenLogDto> SeenSinceLastUpdate { get; set; } = [];
public List<SearchDialogDialogActorDto> SeenSinceLastUpdate { get; set; } = [];
}

public class SearchDialogDialogSeenLogDto
// todo: maybe this should be named SearchDialogDialogSeenLogDto? And rather have an actor object?
public class SearchDialogDialogActorDto
{
public Guid Id { get; set; }
public DateTimeOffset SeenAt { get; set; }

public string EndUserIdHash { get; set; } = null!;
public string ActorId { get; set; } = null!;

public string? EndUserName { get; set; }
public string? ActorName { get; set; }

public bool IsCurrentEndUser { get; set; }
}
Expand Down
Loading
Loading