Skip to content

Commit

Permalink
Merge branch 'main' into fix/dont-cache-invalid-parties-response
Browse files Browse the repository at this point in the history
  • Loading branch information
elsand authored Sep 27, 2024
2 parents 7ee6bcc + d5729de commit ef60109
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/action-deploy-apps.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Deploy apps
env:
AZ_CLI_VERSION: 2.61.0
AZ_CLI_VERSION: 2.64.0
on:
workflow_call:
secrets:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/action-deploy-infra.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Deploy infrastructure

env:
AZ_CLI_VERSION: 2.61.0
AZ_CLI_VERSION: 2.64.0

on:
workflow_call:
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.401",
"version": "8.0.402",
"rollForward": "disable"
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
2 changes: 1 addition & 1 deletion src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.ResourceRegistry;

internal sealed class ResourceRegistryClient : IResourceRegistry
{
private const string ServiceResourceInformationByOrgCacheKey = "ServiceResourceInformationByOrgCacheKey";
private const string ServiceResourceInformationByResourceIdCacheKey = "ServiceResourceInformationByResourceIdCacheKey";
private const string ServiceResourceInformationCacheKey = "ServiceResourceInformationCacheKey";
private const string ResourceTypeGenericAccess = "GenericAccessResource";
private const string ResourceTypeAltinnApp = "AltinnApp";
private const string ResourceTypeCorrespondence = "CorrespondenceService";
private const string ResourceRegistryResourceEndpoint = "resourceregistry/api/v1/resource/";

private readonly IFusionCache _cache;
private readonly HttpClient _client;
Expand All @@ -26,27 +26,24 @@ public async Task<IReadOnlyCollection<ServiceResourceInformation>> GetResourceIn
string orgNumber,
CancellationToken cancellationToken)
{
var dic = await GetOrSetResourceInformationByOrg(cancellationToken);
if (!dic.TryGetValue(orgNumber, out var resources))
{
resources = [];
}

return resources.AsReadOnly();
var resources = await FetchServiceResourceInformation(cancellationToken);
return resources
.Where(x => x.OwnerOrgNumber == orgNumber)
.ToList();
}

public async Task<ServiceResourceInformation?> GetResourceInformation(
string serviceResourceId,
CancellationToken cancellationToken)
{
var dic = await GetOrSetResourceInformationByResourceId(cancellationToken);
dic.TryGetValue(serviceResourceId, out var resource);
return resource;
var resources = await FetchServiceResourceInformation(cancellationToken);
return resources
.FirstOrDefault(x => x.ResourceId == serviceResourceId);
}

public async IAsyncEnumerable<List<UpdatedSubjectResource>> GetUpdatedSubjectResources(DateTimeOffset since, int batchSize, [EnumeratorCancellation] CancellationToken cancellationToken)
{
const string searchEndpoint = "resourceregistry/api/v1/resource/updated";
const string searchEndpoint = $"{ResourceRegistryResourceEndpoint}updated";
var nextUrl = searchEndpoint + $"?since={Uri.EscapeDataString(since.ToString("O"))}&limit={batchSize}";

do
Expand All @@ -66,56 +63,33 @@ public async IAsyncEnumerable<List<UpdatedSubjectResource>> GetUpdatedSubjectRes
} while (nextUrl is not null);
}


private async Task<Dictionary<string, ServiceResourceInformation[]>> GetOrSetResourceInformationByOrg(
CancellationToken cancellationToken)
private async Task<ServiceResourceInformation[]> FetchServiceResourceInformation(CancellationToken cancellationToken)
{
return await _cache.GetOrSetAsync(
ServiceResourceInformationByOrgCacheKey,
async cToken =>
{
var resources = await FetchServiceResourceInformation(cToken);
return resources
.GroupBy(x => x.OwnerOrgNumber)
.ToDictionary(x => x.Key, x => x.ToArray());
},
token: cancellationToken);
}
const string searchEndpoint = $"{ResourceRegistryResourceEndpoint}resourcelist";

private async Task<Dictionary<string, ServiceResourceInformation>> GetOrSetResourceInformationByResourceId(
CancellationToken cancellationToken)
{
return await _cache.GetOrSetAsync(
ServiceResourceInformationByResourceIdCacheKey,
ServiceResourceInformationCacheKey,
async cToken =>
{
var resources = await FetchServiceResourceInformation(cToken);
return resources.ToDictionary(x => x.ResourceId);
var response = await _client
.GetFromJsonEnsuredAsync<List<ResourceListResponse>>(searchEndpoint,
cancellationToken: cToken);
return response
.Where(x => !string.IsNullOrWhiteSpace(x.HasCompetentAuthority.Organization))
.Where(x => x.ResourceType is
ResourceTypeGenericAccess or
ResourceTypeAltinnApp or
ResourceTypeCorrespondence)
.Select(x => new ServiceResourceInformation(
$"{Constants.ServiceResourcePrefix}{x.Identifier}",
x.ResourceType,
x.HasCompetentAuthority.Organization!))
.ToArray();
},
token: cancellationToken);
}

private async Task<ServiceResourceInformation[]> FetchServiceResourceInformation(CancellationToken cancellationToken)
{
const string searchEndpoint = "resourceregistry/api/v1/resource/resourcelist";

var response = await _client
.GetFromJsonEnsuredAsync<List<ResourceListResponse>>(searchEndpoint,
cancellationToken: cancellationToken);

return response
.Where(x => !string.IsNullOrWhiteSpace(x.HasCompetentAuthority.Organization))
.Where(x => x.ResourceType is
ResourceTypeGenericAccess or
ResourceTypeAltinnApp or
ResourceTypeCorrespondence)
.Select(x => new ServiceResourceInformation(
$"{Constants.ServiceResourcePrefix}{x.Identifier}",
x.ResourceType,
x.HasCompetentAuthority.Organization!))
.ToArray();
}

private sealed class ResourceListResponse
{
public required string Identifier { get; init; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
2 changes: 1 addition & 1 deletion src/Digdir.Domain.Dialogporten.Janitor/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
2 changes: 1 addition & 1 deletion src/Digdir.Domain.Dialogporten.Service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
2 changes: 1 addition & 1 deletion src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build
WORKDIR /src

COPY [".editorconfig", "."]
Expand Down
16 changes: 10 additions & 6 deletions src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public static CreateDialogCommand GenerateFakeDialog(
List<CreateDialogDialogAttachmentDto>? attachments = null,
List<CreateDialogDialogGuiActionDto>? guiActions = null,
List<CreateDialogDialogApiActionDto>? apiActions = null,
List<CreateDialogDialogActivityDto>? activities = null)
List<CreateDialogDialogActivityDto>? activities = null,
List<CreateDialogDialogTransmissionDto>? transmissions = null)
{
return GenerateFakeDialogs(
seed,
Expand All @@ -60,12 +61,12 @@ public static CreateDialogCommand GenerateFakeDialog(
attachments,
guiActions,
apiActions,
activities
activities,
transmissions
)[0];
}

public static List<CreateDialogCommand> GenerateFakeDialogs(
int? seed = null,
public static List<CreateDialogCommand> GenerateFakeDialogs(int? seed = null,
int count = 1,
Guid? id = null,
string? serviceResource = null,
Expand All @@ -86,7 +87,8 @@ public static List<CreateDialogCommand> GenerateFakeDialogs(
List<CreateDialogDialogAttachmentDto>? attachments = null,
List<CreateDialogDialogGuiActionDto>? guiActions = null,
List<CreateDialogDialogApiActionDto>? apiActions = null,
List<CreateDialogDialogActivityDto>? activities = null)
List<CreateDialogDialogActivityDto>? activities = null,
List<CreateDialogDialogTransmissionDto>? transmissions = null)
{
Randomizer.Seed = seed.HasValue ? new Random(seed.Value) : new Random();
return new Faker<CreateDialogCommand>()
Expand All @@ -108,6 +110,7 @@ public static List<CreateDialogCommand> GenerateFakeDialogs(
.RuleFor(o => o.ApiActions, _ => apiActions ?? GenerateFakeDialogApiActions())
.RuleFor(o => o.Activities, _ => activities ?? GenerateFakeDialogActivities())
.RuleFor(o => o.Process, f => process ?? GenerateFakeProcessUri())
.RuleFor(o => o.Transmissions, f => transmissions ?? GenerateFakeDialogTransmissions())
.Generate(count);
}

Expand All @@ -121,7 +124,8 @@ public static CreateDialogCommand GenerateSimpleFakeDialog(Guid? id = null)
attachments: [],
guiActions: [],
apiActions: [],
searchTags: []);
searchTags: [],
transmissions: []);
}

public static string GenerateFakeResource(Func<string?>? generator = null)
Expand Down
8 changes: 2 additions & 6 deletions src/Digdir.Tool.Dialogporten.GenerateFakeData/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ private static async Task RunAsync(Options options)
Randomizer.Seed = new Random(options.Seed);
var dialogs = DialogGenerator.GenerateFakeDialogs(
seed: new Randomizer().Number(int.MaxValue),
serviceResourceGenerator: () => MaybeGetRandomResource(options),
partyGenerator: () => MaybeGetRandomParty(options),
count: options.Count);
count: options.Count, serviceResourceGenerator: () => MaybeGetRandomResource(options), partyGenerator: () => MaybeGetRandomParty(options));
var serialized = JsonSerializer.Serialize(dialogs, JsonSerializerOptions);
Console.WriteLine(serialized);
return;
Expand Down Expand Up @@ -118,9 +116,7 @@ private static void ProduceDialogs(Options options, BlockingCollection<(int, Cre
var dialogsToGenerate = Math.Min(DialogsPerBatch, totalDialogs - dialogCounter);
var dialogs = DialogGenerator.GenerateFakeDialogs(
seed: new Randomizer().Number(int.MaxValue),
serviceResourceGenerator: () => MaybeGetRandomResource(options),
partyGenerator: () => MaybeGetRandomParty(options),
count: dialogsToGenerate).Take(dialogsToGenerate);
count: dialogsToGenerate, serviceResourceGenerator: () => MaybeGetRandomResource(options), partyGenerator: () => MaybeGetRandomParty(options)).Take(dialogsToGenerate);
foreach (var dialog in dialogs)
{
dialogQueue.Add((dialogCounter + 1, dialog), cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
using Castle.Core.Logging;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
Expand Down Expand Up @@ -203,4 +206,70 @@ public async Task Can_Create_Dialog_With_UpdatedAt_And_CreatedAt_Being_Equal()
response.TryPickT0(out var success, out _).Should().BeTrue();
success.Should().NotBeNull();
}

[Fact]
public async Task Cannot_Create_Transmission_Without_Content()
{
// Arrange
var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0];
transmission.Content = null!;

var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
createDialogCommand.Transmissions = [transmission];

// Act
var response = await Application.Send(createDialogCommand);

// Assert
response.TryPickT2(out var validationError, out _).Should().BeTrue();
validationError.Should().NotBeNull();
validationError.Errors.Should().HaveCount(1);
validationError.Errors.First().ErrorMessage.Should().Contain("'Content' must not be empty");
}

[Fact]
public async Task Cannot_Create_Transmission_Without_Content_Value()
{
// Arrange
var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0];
transmission.Content.Summary.Value = [];
transmission.Content.Title.Value = [];

var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
createDialogCommand.Transmissions = [transmission];

// Act
var response = await Application.Send(createDialogCommand);

// Assert
response.TryPickT2(out var validationError, out _).Should().BeTrue();
validationError.Should().NotBeNull();
validationError.Errors
.Count(e => e.PropertyName.Contains(nameof(createDialogCommand.Content)))
.Should()
.Be(2);
}

[Fact]
public async Task Cannot_Create_Transmission_With_Empty_Content_Localization_Values()
{
// Arrange
var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0];
transmission.Content.Summary.Value = [new LocalizationDto { LanguageCode = "nb", Value = "" }];
transmission.Content.Title.Value = [new LocalizationDto { LanguageCode = "nb", Value = "" }];

var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
createDialogCommand.Transmissions = [transmission];

// Act
var response = await Application.Send(createDialogCommand);

// Assert
response.TryPickT2(out var validationError, out _).Should().BeTrue();
validationError.Should().NotBeNull();
validationError.Errors
.Count(e => e.PropertyName.Contains(nameof(createDialogCommand.Content)))
.Should()
.Be(2);
}
}

0 comments on commit ef60109

Please sign in to comment.