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

fix: ensure performed by is set for activities #628

Merged
merged 19 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ namespace Digdir.Domain.Dialogporten.Application.Externals;
public interface IOrganizationRegistry
{
Task<string?> GetOrgShortName(string orgNumber, CancellationToken cancellationToken);
Task<OrganizationLongName[]> GetOrganizationLongNames(string orgNumber, CancellationToken cancellationToken);
}

public sealed class OrganizationLongName
{
public required string LongName { get; init; }
public required string Language { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements;
using Digdir.Domain.Dialogporten.Domain.Localizations;
using MediatR;
using OneOf;
using OneOf.Types;
Expand All @@ -25,6 +26,7 @@ internal sealed class CreateDialogCommandHandler : IRequestHandler<CreateDialogC
private readonly IUnitOfWork _unitOfWork;
private readonly IDomainContext _domainContext;
private readonly IUserResourceRegistry _userResourceRegistry;
private readonly IOrganizationRegistry _organizationRegistry;
private readonly IUserOrganizationRegistry _userOrganizationRegistry;

public CreateDialogCommandHandler(
Expand All @@ -33,12 +35,14 @@ public CreateDialogCommandHandler(
IUnitOfWork unitOfWork,
IDomainContext domainContext,
IUserResourceRegistry userResourceRegistry,
IOrganizationRegistry organizationRegistry,
IUserOrganizationRegistry userOrganizationRegistry)
{
_db = db ?? throw new ArgumentNullException(nameof(db));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
_domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
_organizationRegistry = organizationRegistry ?? throw new ArgumentNullException(nameof(organizationRegistry));
_userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry));
_userOrganizationRegistry = userOrganizationRegistry ?? throw new ArgumentNullException(nameof(userOrganizationRegistry));
}
Expand Down Expand Up @@ -77,6 +81,8 @@ public async Task<CreateDialogResult> Handle(CreateDialogCommand request, Cancel
_domainContext.AddError(DomainFailure.EntityExists<DialogElement>(existingElementIds));
}

await EnsurePerformedByIsSetForActivities(dialog.Activities, dialog.Org, cancellationToken);

await _db.Dialogs.AddAsync(dialog, cancellationToken);

var saveResult = await _unitOfWork.SaveChangesAsync(cancellationToken);
Expand All @@ -85,4 +91,21 @@ public async Task<CreateDialogResult> Handle(CreateDialogCommand request, Cancel
domainError => domainError,
concurrencyError => throw new UnreachableException("Should never get a concurrency error when creating a new dialog"));
}

private async Task EnsurePerformedByIsSetForActivities(IEnumerable<DialogActivity> activities, string org, CancellationToken cancellationToken)
{
foreach (var activity in activities)
{
if (activity.PerformedBy == null)
{
var organizationLongNames = await _organizationRegistry.GetOrganizationLongNames(org, cancellationToken);
activity.PerformedBy = new DialogActivityPerformedBy
{
ActivityId = activity.Id,
Id = Guid.NewGuid(),
Localizations = organizationLongNames.Select(x => new Localization { Value = x.LongName, CultureCode = x.Language }).ToList(),
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Content;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements;
using Digdir.Domain.Dialogporten.Domain.Localizations;
using MediatR;
using Microsoft.EntityFrameworkCore;
using OneOf;
Expand All @@ -31,19 +32,22 @@ internal sealed class UpdateDialogCommandHandler : IRequestHandler<UpdateDialogC
private readonly IMapper _mapper;
private readonly IUnitOfWork _unitOfWork;
private readonly IDomainContext _domainContext;
private readonly IOrganizationRegistry _organizationRegistry;
private readonly IUserResourceRegistry _userResourceRegistry;

public UpdateDialogCommandHandler(
IDialogDbContext db,
IMapper mapper,
IUnitOfWork unitOfWork,
IDomainContext domainContext,
IOrganizationRegistry organizationRegistry,
IUserResourceRegistry userResourceRegistry)
{
_db = db ?? throw new ArgumentNullException(nameof(db));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
_domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
_organizationRegistry = organizationRegistry ?? throw new ArgumentNullException(nameof(organizationRegistry));
_userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry));
}

Expand Down Expand Up @@ -165,6 +169,8 @@ private async Task AppendActivity(DialogEntity dialog, UpdateDialogDto dto, Canc
{
var newDialogActivities = _mapper.Map<List<DialogActivity>>(dto.Activities);

await EnsurePerformedByIsSetForActivities(newDialogActivities, dialog.Org, cancellationToken);

var existingIds = await _db.GetExistingIds(newDialogActivities, cancellationToken);
if (existingIds.Count != 0)
{
Expand All @@ -179,6 +185,23 @@ private async Task AppendActivity(DialogEntity dialog, UpdateDialogDto dto, Canc
_db.DialogActivities.AddRange(newDialogActivities);
}

private async Task EnsurePerformedByIsSetForActivities(IEnumerable<DialogActivity> activities, string org, CancellationToken cancellationToken)
{
foreach (var activity in activities)
{
if (activity.PerformedBy == null)
{
var organizationLongNames = await _organizationRegistry.GetOrganizationLongNames(org, cancellationToken);
activity.PerformedBy = new DialogActivityPerformedBy
{
ActivityId = activity.Id,
Id = Guid.NewGuid(),
Localizations = organizationLongNames.Select(x => new Localization { Value = x.LongName, CultureCode = x.Language }).ToList(),
};
}
}
}

private IEnumerable<DialogApiAction> CreateApiActions(IEnumerable<UpdateDialogDialogApiActionDto> creatables)
{
return creatables.Select(x =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.OrganizationRegistry;
internal class OrganizationRegistryClient : IOrganizationRegistry
{
private const string OrgShortNameReferenceCacheKey = "OrgShortNameReference";
private const string OrgLongNameReferenceCacheKey = "OrgLongNameReference";
private static readonly DistributedCacheEntryOptions OneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) };
private static readonly DistributedCacheEntryOptions ZeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue };

Expand All @@ -20,6 +21,7 @@ public OrganizationRegistryClient(HttpClient client, IFusionCacheProvider cacheP
_client = client ?? throw new ArgumentNullException(nameof(client));
_cache = cacheProvider.GetCache(nameof(OrganizationRegistry)) ?? throw new ArgumentNullException(nameof(cacheProvider));
}

public async Task<string?> GetOrgShortName(string orgNumber, CancellationToken cancellationToken)
{
var orgShortNameByOrgNumber = await _cache.GetOrSetAsync(OrgShortNameReferenceCacheKey, async token => await GetOrgShortNameByOrgNumber(token), token: cancellationToken);
Expand All @@ -28,6 +30,35 @@ public OrganizationRegistryClient(HttpClient client, IFusionCacheProvider cacheP
return orgShortName;
}

public async Task<OrganizationLongName[]> GetOrganizationLongNames(string orgNumber, CancellationToken cancellationToken)
{
var orgLongNamesByOrgNumber = await _cache.GetOrSetAsync(OrgLongNameReferenceCacheKey, async token => await GetOrgLongNamesByOrgNumber(token), token: cancellationToken);
orgLongNamesByOrgNumber.TryGetValue(orgNumber, out var orgLongNames);

return orgLongNames?.LongNames.ToArray() ?? Array.Empty<OrganizationLongName>();
}

private async Task<Dictionary<string, OrganizationLongNames>> GetOrgLongNamesByOrgNumber(CancellationToken cancellationToken)
{
const string searchEndpoint = "orgs/altinn-orgs.json";
var response = await _client
.GetFromJsonAsync<OrganizationRegistryResponse>(searchEndpoint, cancellationToken) ?? throw new UnreachableException();

var orgLongNamesByOrgNumber = response
.Orgs
.ToDictionary(pair => pair.Value.Orgnr, pair => new OrganizationLongNames
{
OrgNumber = pair.Value.Orgnr,
LongNames = pair.Value.Name?.Select(name => new OrganizationLongName
{
LongName = name.Value,
Language = name.Key
}).ToList() ?? new List<OrganizationLongName>()
});

return orgLongNamesByOrgNumber;
}

private async Task<Dictionary<string, string>> GetOrgShortNameByOrgNumber(CancellationToken cancellationToken)
{
const string searchEndpoint = "orgs/altinn-orgs.json";
Expand Down Expand Up @@ -58,3 +89,9 @@ private sealed class OrganizationDetails
public IList<string>? Environments { get; init; }
}
}

public sealed class OrganizationLongNames
{
public required string OrgNumber { get; init; }
public required IList<OrganizationLongName> LongNames { get; init; }
}