Skip to content

Commit

Permalink
fix: ensure performed by is set for activities (#628)
Browse files Browse the repository at this point in the history
<!--- Provide a general summary of your changes in the Title above -->

Before refactoring more, let's do a review on this if this is the best
approach or not..

## Description
<!--- Describe your changes in detail -->

- Adds a new function to get the long names of the organization
- Caches the result with a new cache key

## Related Issue(s)

- #404

## Verification

- [X] **Your** code builds clean without any errors or warnings
- [X] Manual testing done (required)
- [ ] Relevant automated test added (if you find this hard, leave it and
we'll help out)

## Documentation

- [X] Documentation is updated (either in `docs`-directory, Altinnpedia
or a separate linked PR in
[altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if
applicable)

---------

Co-authored-by: Ole Jørgen Skogstad <[email protected]>
Co-authored-by: Magnus Sandgren <[email protected]>
  • Loading branch information
3 people authored Apr 22, 2024
1 parent 434dd43 commit 1adf075
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Digdir.Domain.Dialogporten.Application.Common.Behaviours;
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.OptionExtensions;
using Digdir.Domain.Dialogporten.Application.Common.Services;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -43,6 +44,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services
.AddTransient<IUserOrganizationRegistry, UserOrganizationRegistry>()
.AddTransient<IUserResourceRegistry, UserResourceRegistry>()
.AddTransient<IUserNameRegistry, UserNameRegistry>()
.AddTransient<IDialogActivityService, DialogActivityService>()
.AddTransient<IClock, Clock>()
.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>))
.AddTransient(typeof(IPipelineBehavior<,>), typeof(DomainContextBehaviour<,>));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Digdir.Domain.Dialogporten.Application.Common;
public interface IUserOrganizationRegistry
{
Task<string?> GetCurrentUserOrgShortName(CancellationToken cancellationToken);
Task<IList<OrganizationLongName>?> GetCurrentUserOrgLongNames(CancellationToken cancellationToken);
}

public class UserOrganizationRegistry : IUserOrganizationRegistry
Expand All @@ -32,7 +33,21 @@ public UserOrganizationRegistry(IUser user, IOrganizationRegistry organizationRe
return null;
}

return await _organizationRegistry.GetOrgShortName(orgNumber, cancellationToken);
var orgInfo = await _organizationRegistry.GetOrgInfo(orgNumber, cancellationToken);

return orgInfo?.ShortName;
}

public async Task<IList<OrganizationLongName>?> GetCurrentUserOrgLongNames(CancellationToken cancellationToken)
{
if (!_user.TryGetOrgNumber(out var orgNumber))
{
return null;
}

var orgInfo = await _organizationRegistry.GetOrgInfo(orgNumber, cancellationToken);

return orgInfo?.LongNames.ToArray();
}
}

Expand All @@ -41,4 +56,6 @@ internal sealed class LocalDevelopmentUserOrganizationRegistryDecorator : IUserO
public LocalDevelopmentUserOrganizationRegistryDecorator(IUserOrganizationRegistry _) { }

public Task<string?> GetCurrentUserOrgShortName(CancellationToken cancellationToken) => Task.FromResult("digdir")!;
public Task<IList<OrganizationLongName>?> GetCurrentUserOrgLongNames(CancellationToken cancellationToken) =>
Task.FromResult<IList<OrganizationLongName>?>(new[] { new OrganizationLongName { LongName = "Digdir", Language = "nb" } });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
using Digdir.Domain.Dialogporten.Domain.Localizations;

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

public interface IDialogActivityService
{
Task EnsurePerformedByIsSetForActivities(IEnumerable<DialogActivity> activities, CancellationToken cancellationToken);
}

public class DialogActivityService : IDialogActivityService
{
private readonly IUserOrganizationRegistry _userOrganizationRegistry;

public DialogActivityService(
IUserOrganizationRegistry userOrganizationRegistry
)
{
_userOrganizationRegistry = userOrganizationRegistry;
}

public async Task EnsurePerformedByIsSetForActivities(IEnumerable<DialogActivity> activities, CancellationToken cancellationToken)
{
// TODO: if organization cannot be found we need to handle this. Put on a queue to be retried later(?) https://github.com/digdir/dialogporten/issues/639
foreach (var activity in activities)
{
var organizationLongNames = await _userOrganizationRegistry.GetCurrentUserOrgLongNames(cancellationToken);
activity.PerformedBy ??= new DialogActivityPerformedBy
{
Localizations = organizationLongNames?.Select(x => new Localization { Value = x.LongName, CultureCode = x.Language }).ToList() ?? new List<Localization>(),
};
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,18 @@ namespace Digdir.Domain.Dialogporten.Application.Externals;

public interface IOrganizationRegistry
{
Task<string?> GetOrgShortName(string orgNumber, CancellationToken cancellationToken);
Task<OrganizationInfo?> GetOrgInfo(string orgNumber, CancellationToken cancellationToken);
}

public sealed class OrganizationLongName
{
public required string LongName { get; init; }
public required string Language { get; init; }
}

public sealed class OrganizationInfo
{
public required string OrgNumber { get; init; }
public required string ShortName { get; init; }
public required IList<OrganizationLongName> LongNames { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
using AutoMapper;
using Digdir.Domain.Dialogporten.Application.Common;
using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
using Digdir.Domain.Dialogporten.Application.Common.Services;
using Digdir.Domain.Dialogporten.Application.Externals;
using Digdir.Domain.Dialogporten.Domain.Common;
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 @@ -26,21 +28,24 @@ internal sealed class CreateDialogCommandHandler : IRequestHandler<CreateDialogC
private readonly IDomainContext _domainContext;
private readonly IUserResourceRegistry _userResourceRegistry;
private readonly IUserOrganizationRegistry _userOrganizationRegistry;
private readonly IDialogActivityService _dialogActivityService;

public CreateDialogCommandHandler(
IDialogDbContext db,
IMapper mapper,
IUnitOfWork unitOfWork,
IDomainContext domainContext,
IUserResourceRegistry userResourceRegistry,
IUserOrganizationRegistry userOrganizationRegistry)
IUserOrganizationRegistry userOrganizationRegistry,
IDialogActivityService dialogActivityService)
{
_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));
_userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry));
_userOrganizationRegistry = userOrganizationRegistry ?? throw new ArgumentNullException(nameof(userOrganizationRegistry));
_dialogActivityService = dialogActivityService ?? throw new ArgumentNullException(nameof(dialogActivityService));
}

public async Task<CreateDialogResult> Handle(CreateDialogCommand request, CancellationToken cancellationToken)
Expand Down Expand Up @@ -77,6 +82,8 @@ public async Task<CreateDialogResult> Handle(CreateDialogCommand request, Cancel
_domainContext.AddError(DomainFailure.EntityExists<DialogElement>(existingElementIds));
}

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

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

var saveResult = await _unitOfWork.SaveChangesAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
using Digdir.Domain.Dialogporten.Application.Common;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables;
using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
using Digdir.Domain.Dialogporten.Application.Common.Services;
using Digdir.Domain.Dialogporten.Application.Externals;
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.Content;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Elements;
using Digdir.Domain.Dialogporten.Domain.Localizations;
using MediatR;
using Microsoft.EntityFrameworkCore;
using OneOf;
Expand All @@ -32,19 +34,22 @@ internal sealed class UpdateDialogCommandHandler : IRequestHandler<UpdateDialogC
private readonly IUnitOfWork _unitOfWork;
private readonly IDomainContext _domainContext;
private readonly IUserResourceRegistry _userResourceRegistry;
private readonly IDialogActivityService _dialogActivityService;

public UpdateDialogCommandHandler(
IDialogDbContext db,
IMapper mapper,
IUnitOfWork unitOfWork,
IDomainContext domainContext,
IUserResourceRegistry userResourceRegistry)
IUserResourceRegistry userResourceRegistry,
IDialogActivityService dialogActivityService)
{
_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));
_userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry));
_dialogActivityService = dialogActivityService ?? throw new ArgumentNullException(nameof(dialogActivityService));
}

public async Task<UpdateDialogResult> Handle(UpdateDialogCommand request, CancellationToken cancellationToken)
Expand Down Expand Up @@ -165,6 +170,9 @@ private async Task AppendActivity(DialogEntity dialog, UpdateDialogDto dto, Canc
{
var newDialogActivities = _mapper.Map<List<DialogActivity>>(dto.Activities);


await _dialogActivityService.EnsurePerformedByIsSetForActivities(newDialogActivities, cancellationToken);

var existingIds = await _db.GetExistingIds(newDialogActivities, cancellationToken);
if (existingIds.Count != 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.OrganizationRegistry;

internal class OrganizationRegistryClient : IOrganizationRegistry
{
private const string OrgShortNameReferenceCacheKey = "OrgShortNameReference";
private const string OrgNameReferenceCacheKey = "OrgNameReference";

private static readonly DistributedCacheEntryOptions OneDayCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1) };
private static readonly DistributedCacheEntryOptions ZeroCacheDuration = new() { AbsoluteExpiration = DateTimeOffset.MinValue };

Expand All @@ -20,28 +21,35 @@ 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)

public async Task<OrganizationInfo?> GetOrgInfo(string orgNumber, CancellationToken cancellationToken)
{
var orgShortNameByOrgNumber = await _cache.GetOrSetAsync(OrgShortNameReferenceCacheKey, async token => await GetOrgShortNameByOrgNumber(token), token: cancellationToken);
orgShortNameByOrgNumber.TryGetValue(orgNumber, out var orgShortName);
var orgInfoByOrgNumber = await _cache.GetOrSetAsync(OrgNameReferenceCacheKey, GetOrgInfo, token: cancellationToken);
orgInfoByOrgNumber.TryGetValue(orgNumber, out var orgInfo);

return orgShortName;
return orgInfo;
}

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

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

return orgShortNameByOrgNumber;
return orgInfoByOrgNumber;
}

private sealed class OrganizationRegistryResponse
Expand All @@ -57,4 +65,4 @@ private sealed class OrganizationDetails
public string? Homepage { get; init; }
public IList<string>? Environments { get; init; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,13 @@ private static IOrganizationRegistry CreateOrganizationRegistrySubstitute()
var organizationRegistrySubstitute = Substitute.For<IOrganizationRegistry>();

organizationRegistrySubstitute
.GetOrgShortName(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns("digdir");
.GetOrgInfo(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(new OrganizationInfo
{
OrgNumber = "991825827",
ShortName = "digdir",
LongNames = new[] { new OrganizationLongName { LongName = "Digitaliseringsdirektoratet", Language = "nb" } }
});

return organizationRegistrySubstitute;
}
Expand Down

0 comments on commit 1adf075

Please sign in to comment.