From 0c0a4daa80995b45a2fcc48b7b83c760fb9cbdb9 Mon Sep 17 00:00:00 2001 From: Knut Haug <154342485+knuhau@users.noreply.github.com> Date: Fri, 25 Oct 2024 07:58:35 +0200 Subject: [PATCH 1/3] chore(graphql): revert to old app insights config (#1347) Revert to old app insights config for graphql to fix ApplicationInsightsEventListener. --- src/Digdir.Domain.Dialogporten.GraphQL/Program.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs index 8c191dfc3..10040bcad 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs @@ -19,17 +19,16 @@ using Microsoft.Extensions.Options; // Using two-stage initialization to catch startup errors. -var telemetryConfiguration = TelemetryConfiguration.CreateDefault(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Warning() .Enrich.FromLogContext() .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) - .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces) + .WriteTo.ApplicationInsights(TelemetryConfiguration.CreateDefault(), TelemetryConverter.Traces) .CreateBootstrapLogger(); try { - BuildAndRun(args, telemetryConfiguration); + BuildAndRun(args); } catch (Exception ex) when (ex is not OperationCanceledException) { @@ -41,7 +40,7 @@ Log.CloseAndFlush(); } -static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfiguration) +static void BuildAndRun(string[] args) { var builder = WebApplication.CreateBuilder(args); @@ -51,7 +50,7 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) - .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces)); + .WriteTo.ApplicationInsights(services.GetRequiredService(), TelemetryConverter.Traces)); builder.Configuration .AddAzureConfiguration(builder.Environment.EnvironmentName) @@ -77,6 +76,7 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura .WithPubCapabilities() .Build() .AddAutoMapper(Assembly.GetExecutingAssembly()) + .AddApplicationInsightsTelemetry() .AddScoped() .AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true) .AddAzureAppConfiguration() From e3d53cafbbb7157d8439c23745d6b23cbbaeea17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 25 Oct 2024 12:56:22 +0200 Subject: [PATCH 2/3] feat: Add restrictions to Transmissions reference hierarchy (#1310) ## Description This restricts transmissions reference hierarchies to: * only have a width of 1 (two separate transmissions cannot have the same relativeTransmissionId) * have a chain length/depth of max 100 * not allow cyclical references (A => B => C => A) Added tests for the command handlers making sure things are ready before using the new validator. Added tests for general rule violations, varying depth/width etc., ## Related Issue(s) - #1225 ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [x] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a method for validating reference hierarchies in collections, enhancing error handling for hierarchical structures. - Enhanced dialog creation and update processes to ensure proper transmission ID assignment and validation. - **Bug Fixes** - Improved validation logic to prevent depth, width, and cyclic reference violations in hierarchical data. - **Tests** - Added new unit tests to validate reference hierarchy functionality, including scenarios for depth, width, and circular reference violations. - Introduced integration tests for creating and updating transmissions within dialogs, ensuring robust error handling and validation. - Added tests for handling related transmissions with null IDs and ensuring valid updates. --------- Co-authored-by: Magnus Sandgren <5285192+MagnusSandgren@users.noreply.github.com> --- .../ReadOnlyCollectionExtensions.cs | 162 ++++++++++++++ .../Commands/Create/CreateDialogCommand.cs | 15 ++ .../Commands/Update/UpdateDialogCommand.cs | 41 ++-- .../Commands/CreateTransmissionTests.cs | 24 ++- .../Commands/UpdateTransmissionTests.cs | 134 ++++++++++++ .../Common/HierarchyTestNode.cs | 63 ++++++ .../Common/IHierarchyTestNodeBuilder.cs | 83 ++++++++ ...Dialogporten.Application.Unit.Tests.csproj | 1 + .../ValidateReferenceHierarchyTests.cs | 197 ++++++++++++++++++ 9 files changed, 692 insertions(+), 28 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ReadOnlyCollectionExtensions.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/HierarchyTestNode.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/IHierarchyTestNodeBuilder.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Validators/ValidateReferenceHierarchyTests.cs diff --git a/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ReadOnlyCollectionExtensions.cs b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ReadOnlyCollectionExtensions.cs new file mode 100644 index 000000000..6b027751b --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/ReadOnlyCollectionExtensions.cs @@ -0,0 +1,162 @@ +using Digdir.Domain.Dialogporten.Domain.Common; + +namespace Digdir.Domain.Dialogporten.Application.Common.Extensions; + +internal static class ReadOnlyCollectionExtensions +{ + private const int Cycle = int.MaxValue; + + /// + /// Validates the reference hierarchy in a collection of entities, checking for depth, cyclic references, and width violations. + /// + /// The type of the entities in the collection. + /// The type of the key used to identify entities. + /// The collection of entities to validate. + /// A function to select the key for each entity. + /// A function to select the parent key for each entity. + /// The name of the property being validated. + /// The maximum allowed depth of the hierarchy. Default is 100. + /// The maximum allowed width of the hierarchy. Default is 1. + /// A list of objects representing any validation errors found. + /// Thrown if an entity's parent key is not found in the collection. + /// Thrown if an entity's returns default . + public static List ValidateReferenceHierarchy( + this IReadOnlyCollection entities, + Func keySelector, + Func parentKeySelector, + string propertyName, + int maxDepth = 100, + int maxWidth = 1) + where TKey : struct + { + entities.Select(keySelector).EnsureNonDefaultTKey(); + + var maxDepthViolation = maxDepth + 1; + var type = typeof(TEntity); + var errors = new List(); + + var invalidReferences = GetInvalidReferences(entities, keySelector, parentKeySelector); + if (invalidReferences.Count > 0) + { + var ids = $"[{string.Join(",", invalidReferences)}]"; + errors.Add(new DomainFailure(propertyName, + $"Hierarchy reference violation found. " + + $"{type.Name} with the following referenced ids does not exist: {ids}.")); + + return errors; + } + + var depthByKey = entities + .ToDictionary(keySelector) + .ToDepthByKey(keySelector, parentKeySelector); + + var depthErrors = depthByKey + .Where(x => x.Value == maxDepthViolation) + .ToList(); + + var cycleErrors = depthByKey + .Where(x => x.Value == Cycle) + .ToList(); + + var widthErrors = entities + .Where(x => parentKeySelector(x) is not null) + .GroupBy(parentKeySelector) + .Where(x => x.Count() > maxWidth) + .ToList(); + + if (depthErrors.Count > 0) + { + var ids = $"[{string.Join(",", depthErrors.Select(x => x.Key))}]"; + errors.Add(new DomainFailure(propertyName, + $"Hierarchy depth violation found. {type.Name} with the following " + + $"ids is at depth {maxDepthViolation}, exceeding the max allowed depth of {maxDepth}. " + + $"It, and all its referencing children is in violation of the depth constraint. {ids}.")); + } + + if (cycleErrors.Count > 0) + { + var firstTenFailedIds = cycleErrors.Take(10).Select(x => x.Key).ToList(); + var cycleCutOffInfo = cycleErrors.Count > 10 ? " (showing first 10)" : string.Empty; + + var joinedIds = $"[{string.Join(",", firstTenFailedIds)}]"; + errors.Add(new DomainFailure(propertyName, + $"Hierarchy cyclic reference violation found. {type.Name} with the " + + $"following ids is part of a cyclic reference chain{cycleCutOffInfo}: {joinedIds}.")); + } + + if (widthErrors.Count > 0) + { + var ids = $"[{string.Join(",", widthErrors.Select(x => x.Key))}]"; + errors.Add(new DomainFailure(propertyName, + $"Hierarchy width violation found. '{type.Name}' with the following " + + $"ids has to many referring {type.Name}, exceeding the max " + + $"allowed width of {maxWidth}: {ids}.")); + } + + return errors; + } + + private static List GetInvalidReferences(IReadOnlyCollection entities, + Func keySelector, + Func parentKeySelector) where TKey : struct => entities + .Where(x => parentKeySelector(x).HasValue) + .Select(x => parentKeySelector(x)!.Value) + .Except(entities.Select(keySelector)) + .ToList(); + + private static Dictionary ToDepthByKey( + this Dictionary transmissionById, + Func keySelector, + Func parentKeySelector) + where TKey : struct + { + var depthByKey = new Dictionary(); + var breadCrumbs = new HashSet(); + foreach (var (_, current) in transmissionById) + { + GetDepth(current, transmissionById, keySelector, parentKeySelector, depthByKey, breadCrumbs); + } + + return depthByKey; + } + + private static int GetDepth(TEntity current, + Dictionary entitiesById, + Func keySelector, + Func parentKeySelector, + Dictionary cachedDepthByVisited, + HashSet breadCrumbs) + where TKey : struct + { + var key = keySelector(current); + if (breadCrumbs.Contains(key)) + { + return Cycle; + } + + if (cachedDepthByVisited.TryGetValue(key, out var cachedDepth)) + { + return cachedDepth; + } + + breadCrumbs.Add(key); + var parentKey = parentKeySelector(current); + var parentDepth = !parentKey.HasValue ? 0 + : entitiesById.TryGetValue(parentKey.Value, out var parent) + ? GetDepth(parent, entitiesById, keySelector, parentKeySelector, cachedDepthByVisited, breadCrumbs) + : throw new InvalidOperationException( + $"{nameof(entitiesById)} does not contain expected " + + $"key '{parentKey.Value}'."); + + breadCrumbs.Remove(key); + return cachedDepthByVisited[key] = parentDepth == Cycle ? Cycle : ++parentDepth; + } + + private static void EnsureNonDefaultTKey(this IEnumerable keys) where TKey : struct + { + if (keys.Any(key => EqualityComparer.Default.Equals(key, default))) + { + throw new InvalidOperationException("All keys must be non-default."); + } + } +} 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 e8dd6fc0e..f16b28f29 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 @@ -12,6 +12,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; using Digdir.Domain.Dialogporten.Domain.Parties; +using Digdir.Library.Entity.Abstractions.Features.Identifiable; using MediatR; using OneOf; using OneOf.Types; @@ -70,6 +71,20 @@ public async Task Handle(CreateDialogCommand request, Cancel } CreateDialogEndUserContext(request, dialog); await EnsureNoExistingUserDefinedIds(dialog, cancellationToken); + + // Ensure transmissions have a UUIDv7 ID, needed for the transmission hierarchy validation. + foreach (var transmission in dialog.Transmissions) + { + transmission.Id = transmission.Id.CreateVersion7IfDefault(); + } + + _domainContext.AddErrors(dialog.Transmissions.ValidateReferenceHierarchy( + keySelector: x => x.Id, + parentKeySelector: x => x.RelatedTransmissionId, + propertyName: nameof(CreateDialogCommand.Transmissions), + maxDepth: 100, + maxWidth: 1)); + await _db.Dialogs.AddAsync(dialog, cancellationToken); var saveResult = await _unitOfWork.SaveChangesAsync(cancellationToken); return saveResult.Match( diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs index cc53c5df0..f247a3f81 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs @@ -15,6 +15,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; using Digdir.Domain.Dialogporten.Domain.Parties; +using Digdir.Library.Entity.Abstractions.Features.Identifiable; using MediatR; using Microsoft.EntityFrameworkCore; using OneOf; @@ -97,6 +98,12 @@ public async Task Handle(UpdateDialogCommand request, Cancel return new BadRequest($"Entity '{nameof(DialogEntity)}' with key '{request.Id}' is removed, and cannot be updated."); } + // Ensure transmissions have a UUIDv7 ID, needed for the transmission hierarchy validation. + foreach (var transmission in request.Dto.Transmissions) + { + transmission.Id = transmission.Id.CreateVersion7IfDefault(); + } + // Update primitive properties _mapper.Map(request.Dto, dialog); ValidateTimeFields(dialog); @@ -104,7 +111,13 @@ public async Task Handle(UpdateDialogCommand request, Cancel await AppendActivity(dialog, request.Dto, cancellationToken); await AppendTransmission(dialog, request.Dto, cancellationToken); - VerifyTransmissionRelations(dialog); + + _domainContext.AddErrors(dialog.Transmissions.ValidateReferenceHierarchy( + keySelector: x => x.Id, + parentKeySelector: x => x.RelatedTransmissionId, + propertyName: nameof(UpdateDialogDto.Transmissions), + maxDepth: 100, + maxWidth: 1)); VerifyActivityTransmissionRelations(dialog); @@ -270,32 +283,6 @@ private async Task AppendTransmission(DialogEntity dialog, UpdateDialogDto dto, _db.DialogTransmissions.AddRange(newDialogTransmissions); } - private void VerifyTransmissionRelations(DialogEntity dialog) - { - var relatedTransmissionIds = dialog.Transmissions - .Where(x => x.RelatedTransmissionId is not null) - .Select(x => x.RelatedTransmissionId) - .ToList(); - - if (relatedTransmissionIds.Count == 0) - { - return; - } - - var transmissionIds = dialog.Transmissions.Select(x => x.Id).ToList(); - - var invalidRelatedTransmissionIds = relatedTransmissionIds - .Where(id => !transmissionIds.Contains(id!.Value)) - .ToList(); - - if (invalidRelatedTransmissionIds.Count != 0) - { - _domainContext.AddError( - nameof(UpdateDialogDto.Transmissions), - $"Invalid '{nameof(DialogTransmission.RelatedTransmissionId)}, entity '{nameof(DialogTransmission)}' with the following key(s) does not exist: ({string.Join(", ", invalidRelatedTransmissionIds)})."); - } - } - private IEnumerable CreateApiActions(IEnumerable creatables) { return creatables.Select(x => diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs index fb93e5b9d..27cfeb4a9 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs @@ -1,6 +1,5 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; using Digdir.Domain.Dialogporten.Domain; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; @@ -101,4 +100,27 @@ public async Task Cannot_Create_Transmission_Embeddable_Content_With_Http_Url() validationError.Errors.First().ErrorMessage.Should().Contain("HTTPS"); } + + [Fact] + public async Task Can_Create_Related_Transmission_With_Null_Id() + { + // Arrange + var createCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var transmissions = DialogGenerator.GenerateFakeDialogTransmissions(2); + + transmissions[0].RelatedTransmissionId = transmissions[1].Id; + + // This test assures that the Create-handler will use CreateVersion7IfDefault + // on all transmissions before validating the hierarchy. + transmissions[0].Id = null; + + createCommand.Transmissions = transmissions; + + // Act + var response = await Application.Send(createCommand); + + // Assert + response.TryPickT0(out var success, out _).Should().BeTrue(); + success.Should().NotBeNull(); + } } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs new file mode 100644 index 000000000..95371beca --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs @@ -0,0 +1,134 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; +using Digdir.Tool.Dialogporten.GenerateFakeData; +using FluentAssertions; +using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils; + +namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Transmissions.Commands; + +[Collection(nameof(DialogCqrsCollectionFixture))] +public class UpdateTransmissionTests : ApplicationCollectionFixture +{ + public UpdateTransmissionTests(DialogApplication application) : base(application) { } + + [Fact] + public async Task Can_Create_Simple_Transmission_In_Update() + { + // Arrange + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var existingTransmission = DialogGenerator.GenerateFakeDialogTransmissions(1).First(); + + createDialogCommand.Transmissions.Add(existingTransmission); + var createCommandResponse = await Application.Send(createDialogCommand); + + var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }; + var getDialogDto = await Application.Send(getDialogQuery); + + var mapper = Application.GetMapper(); + var updateDialogDto = mapper.Map(getDialogDto.AsT0); + + var newTransmission = UpdateDialogDialogTransmissionDto(); + updateDialogDto.Transmissions.Add(newTransmission); + + // Act + var updateResponse = await Application.Send(new UpdateDialogCommand + { + Id = createCommandResponse.AsT0.Value, + Dto = updateDialogDto + }); + + // Assert + updateResponse.TryPickT0(out var success, out _).Should().BeTrue(); + success.Should().NotBeNull(); + + var transmissionEntities = await Application.GetDbEntities(); + transmissionEntities.Should().HaveCount(2); + transmissionEntities.Single(x => x.Id == newTransmission.Id).Should().NotBeNull(); + } + + [Fact] + public async Task Can_Update_Related_Transmission_With_Null_Id() + { + // Arrange + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var existingTransmission = DialogGenerator.GenerateFakeDialogTransmissions(1).First(); + createDialogCommand.Transmissions.Add(existingTransmission); + var createCommandResponse = await Application.Send(createDialogCommand); + + var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }; + var getDialogDto = await Application.Send(getDialogQuery); + + var mapper = Application.GetMapper(); + var updateDialogDto = mapper.Map(getDialogDto.AsT0); + + // Add new transmission with null Id + // This test assures that the Update-handler will use CreateVersion7IfDefault + // on all transmissions before validating the hierarchy. + var newTransmission = UpdateDialogDialogTransmissionDto(); + newTransmission.RelatedTransmissionId = existingTransmission.Id; + newTransmission.Id = null; + + updateDialogDto.Transmissions.Add(newTransmission); + + // Act + var updateResponse = await Application.Send(new UpdateDialogCommand + { + Id = createCommandResponse.AsT0.Value, + Dto = updateDialogDto + }); + + // Assert + updateResponse.TryPickT0(out var success, out _).Should().BeTrue(); + success.Should().NotBeNull(); + var transmissionEntities = await Application.GetDbEntities(); + transmissionEntities.Should().HaveCount(2); + transmissionEntities.Single(x => x.Id == newTransmission.Id).Should().NotBeNull(); + } + + [Fact] + public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand() + { + // Arrange + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + var existingTransmission = DialogGenerator.GenerateFakeDialogTransmissions(count: 1).First(); + createDialogCommand.Transmissions.Add(existingTransmission); + var createCommandResponse = await Application.Send(createDialogCommand); + + var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value }; + var getDialogDto = await Application.Send(getDialogQuery); + + var mapper = Application.GetMapper(); + var updateDialogDto = mapper.Map(getDialogDto.AsT0); + + var newTransmission = UpdateDialogDialogTransmissionDto(); + newTransmission.Id = existingTransmission.Id; + updateDialogDto.Transmissions.Add(newTransmission); + + // Act + var updateResponse = await Application.Send(new UpdateDialogCommand + { + Id = createCommandResponse.AsT0.Value, + Dto = updateDialogDto + }); + + // Assert + updateResponse.TryPickT5(out var domainError, out _).Should().BeTrue(); + domainError.Should().NotBeNull(); + domainError.Errors.Should().Contain(e => e.ErrorMessage.Contains(existingTransmission.Id.ToString()!)); + } + + private static UpdateDialogDialogTransmissionDto UpdateDialogDialogTransmissionDto() => new() + { + Id = GenerateBigEndianUuidV7(), + Type = DialogTransmissionType.Values.Information, + Sender = new() { ActorType = ActorType.Values.ServiceOwner }, + Content = new() + { + Title = new() { Value = DialogGenerator.GenerateFakeLocalizations(1) }, + Summary = new() { Value = DialogGenerator.GenerateFakeLocalizations(1) } + } + }; +} diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/HierarchyTestNode.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/HierarchyTestNode.cs new file mode 100644 index 000000000..89876275c --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/HierarchyTestNode.cs @@ -0,0 +1,63 @@ +namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Common; + +internal sealed class HierarchyTestNode +{ + private readonly List _children = []; + + public Guid Id { get; } + public Guid? ParentId => Parent?.Id; + public HierarchyTestNode? Parent { get; private set; } + public IReadOnlyCollection Children => _children; + + private HierarchyTestNode(Guid? id = null, HierarchyTestNode? parent = null) + { + Id = id ?? Guid.NewGuid(); + Parent = parent; + } + + public IEnumerable CreateChildrenWidth(int width, params Guid?[] ids) => CreateWidth(width, this, ids); + + public IEnumerable CreateChildrenDepth(int depth, params Guid?[] ids) => CreateDepth(depth, this, ids); + + public static HierarchyTestNode Create(Guid? id = null, HierarchyTestNode? parent = null) + { + var node = new HierarchyTestNode(id, parent); + parent?._children.Add(node); + return node; + } + + public static IEnumerable CreateDepth(int depth, HierarchyTestNode? from = null, params Guid?[] ids) + { + for (var i = 0; i < depth; i++) + { + yield return from = Create(ids.ElementAtOrDefault(i), from); + } + } + + public static IEnumerable CreateWidth(int width, HierarchyTestNode from, params Guid?[] ids) + { + for (var i = 0; i < width; i++) + { + yield return Create(ids.ElementAtOrDefault(i), from); + } + } + + public static IEnumerable CreateCyclicDepth(int depth, params Guid?[] ids) + { + if (depth < 1) + { + yield break; + } + + var last = Create(ids.ElementAtOrDefault(depth - 1)); + var current = last; + foreach (var element in CreateDepth(depth - 1, from: current, ids)) + { + yield return current = element; + } + + last.Parent = current; + current._children.Add(last); + yield return last; + } +} diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/IHierarchyTestNodeBuilder.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/IHierarchyTestNodeBuilder.cs new file mode 100644 index 000000000..c3d97e41d --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/IHierarchyTestNodeBuilder.cs @@ -0,0 +1,83 @@ +namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Common; + +internal interface IHierarchyTestNodeBuilder +{ + IHierarchyTestNodeBuilder CreateNewCyclicHierarchy(int depth, params Guid?[] ids); + IHierarchyTestNodeBuilder CreateNewHierarchy(int depth, params Guid?[] ids); + IHierarchyTestNodeBuilder FromNode(Guid id); + IHierarchyTestNodeBuilder AddWidth(int width, params Guid?[] ids); + IHierarchyTestNodeBuilder AddDepth(int depth, params Guid?[] ids); + IReadOnlyCollection Build(); +} + +internal sealed class HierarchyTestNodeBuilder : IHierarchyTestNodeBuilder +{ + private readonly Dictionary _nodes = []; + private HierarchyTestNode _current; + + private HierarchyTestNodeBuilder(IEnumerable nodes) + { + _current = AddRangeReturnFirst(nodes); + } + + public static IHierarchyTestNodeBuilder CreateNewHierarchy(int depth, params Guid?[] ids) + { + return new HierarchyTestNodeBuilder(HierarchyTestNode.CreateDepth(depth, ids: ids)); + } + + public static IHierarchyTestNodeBuilder CreateNewCyclicHierarchy(int depth, params Guid?[] ids) + { + return new HierarchyTestNodeBuilder(HierarchyTestNode.CreateCyclicDepth(depth, ids)); + } + + IHierarchyTestNodeBuilder IHierarchyTestNodeBuilder.CreateNewHierarchy(int depth, params Guid?[] ids) + { + _current = AddRangeReturnFirst(HierarchyTestNode.CreateDepth(depth, ids: ids)); + return this; + } + + IHierarchyTestNodeBuilder IHierarchyTestNodeBuilder.CreateNewCyclicHierarchy(int depth, params Guid?[] ids) + { + _current = AddRangeReturnFirst(HierarchyTestNode.CreateCyclicDepth(depth, ids)); + return this; + } + + IHierarchyTestNodeBuilder IHierarchyTestNodeBuilder.FromNode(Guid id) + { + _current = _nodes[id]; + return this; + } + + IHierarchyTestNodeBuilder IHierarchyTestNodeBuilder.AddWidth(int width, params Guid?[] ids) + { + if (width == 0) return this; + AddRangeReturnFirst(_current.CreateChildrenWidth(width, ids)); + return this; + } + + IHierarchyTestNodeBuilder IHierarchyTestNodeBuilder.AddDepth(int depth, params Guid?[] ids) + { + AddRangeReturnFirst(_current.CreateChildrenDepth(depth, ids)); + return this; + } + + IReadOnlyCollection IHierarchyTestNodeBuilder.Build() => _nodes.Values; + + private HierarchyTestNode AddRangeReturnFirst(IEnumerable nodes) + { + using var nodeEnumerator = nodes.GetEnumerator(); + if (!nodeEnumerator.MoveNext()) + { + throw new InvalidOperationException("Expected at least one node."); + } + + var first = nodeEnumerator.Current; + _nodes.Add(first.Id, first); + while (nodeEnumerator.MoveNext()) + { + _nodes.Add(nodeEnumerator.Current.Id, nodeEnumerator.Current); + } + + return first; + } +} diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj index 2e6b3b18d..3f62f42de 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj @@ -10,6 +10,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Validators/ValidateReferenceHierarchyTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Validators/ValidateReferenceHierarchyTests.cs new file mode 100644 index 000000000..d2d5bd9cf --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Validators/ValidateReferenceHierarchyTests.cs @@ -0,0 +1,197 @@ +using Digdir.Domain.Dialogporten.Application.Common.Extensions; +using Digdir.Domain.Dialogporten.Application.Unit.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Common; +using FluentAssertions; + +namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Features.V1.Common.Validators; + +public class ValidateReferenceHierarchyTests +{ + [Theory] + [InlineData(1, 1)] + [InlineData(10, 10)] + [InlineData(100, 100)] + public void Cannot_Create_Hierarchy_With_Depth_Violations(int maxDepth, int numberOfViolations) + { + // Arrange + var violatingDepth = maxDepth + 1; + var elements = Enumerable + .Range(1, numberOfViolations) + .Aggregate( + HierarchyTestNodeBuilder.CreateNewHierarchy(violatingDepth), + (current, _) => current.CreateNewHierarchy(violatingDepth)) + .Build(); + + // Act + var domainFailures = Sut(elements, maxDepth: maxDepth, maxWidth: 1); + + // Assert + domainFailures.Should().HaveCount(1); + var domainFailure = domainFailures.First(); + domainFailure.ErrorMessage.Should().Contain("depth violation"); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(10, 10)] + [InlineData(100, 100)] + public void Cannot_Create_Hierarchy_With_Width_Violations(int maxWidth, int numberOfViolations) + { + // Arrange + var violatingWidth = maxWidth + 1; + var elements = Enumerable + .Range(1, numberOfViolations) + .Aggregate( + HierarchyTestNodeBuilder.CreateNewHierarchy(1).AddWidth(violatingWidth), + (current, _) => current.CreateNewHierarchy(1).AddWidth(violatingWidth)) + .Build(); + + // Act + var domainFailures = Sut(elements, maxDepth: 2, maxWidth: maxWidth); + + // Assert + domainFailures.Should().HaveCount(1); + var domainFailure = domainFailures.First(); + domainFailure.ErrorMessage.Should().Contain("width violation"); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(10, 10)] + [InlineData(100, 100)] + public void Cannot_Create_Hierarchy_With_Circular_References(int cycleLength, int numberOfViolations) + { + // Arrange + var elements = Enumerable + .Range(1, numberOfViolations) + .Aggregate(HierarchyTestNodeBuilder.CreateNewCyclicHierarchy(cycleLength), + (current, _) => current.CreateNewCyclicHierarchy(cycleLength)) + .Build(); + + // Act + var domainFailures = Sut(elements, maxDepth: cycleLength, maxWidth: 1); + + // Assert + domainFailures.Should().HaveCount(1); + var domainFailure = domainFailures.First(); + domainFailure.ErrorMessage.Should().Contain("cyclic reference"); + } + + [Theory] + [InlineData(1, 1, 1)] + [InlineData(10, 10, 10)] + [InlineData(100, 100, 100)] + public void Cannot_Create_Hierarchy_With_Multiple_Violations(int maxDepth, int maxWidth, int cycleLength) + { + // Arrange + var violatingDepth = maxDepth + 1; + var violatingWidth = maxWidth + 1; + + var elements = Enumerable + .Range(1, maxDepth) + .Aggregate( + HierarchyTestNodeBuilder.CreateNewHierarchy(violatingDepth), + (current, _) => current.CreateNewHierarchy(violatingDepth)) + .AddWidth(violatingWidth) + .CreateNewCyclicHierarchy(cycleLength) + .Build(); + + // Act + var domainFailures = Sut(elements, maxDepth: maxDepth, maxWidth: maxWidth); + + // Assert + domainFailures.Should().HaveCount(3); + domainFailures.Should().ContainSingle(x => x.ErrorMessage.Contains("depth violation")); + domainFailures.Should().ContainSingle(x => x.ErrorMessage.Contains("width violation")); + domainFailures.Should().ContainSingle(x => x.ErrorMessage.Contains("cyclic reference")); + + } + + [Theory] + [InlineData(1, 1, 1)] + [InlineData(10, 10, 10)] + [InlineData(100, 100, 100)] + public void Can_Create_Valid_Complex_Hierarchy(int numberOfSegments, int maxDepth, int maxWidth) + { + // Arrange + var elements = Enumerable + .Range(1, numberOfSegments) + .Aggregate( + HierarchyTestNodeBuilder.CreateNewHierarchy(maxDepth).AddWidth(maxWidth - 1), + (current, _) => current.CreateNewHierarchy(maxDepth)) + .Build(); + + // Act + var domainFailures = Sut(elements, maxDepth: maxDepth, maxWidth: maxWidth); + + // Assert + domainFailures.Should().BeEmpty(); + } + + [Fact] + public void Cannot_Create_Node_Referencing_Non_Existent_Parent() + { + // Arrange + var unknownParent = HierarchyTestNode.Create(); + var node = HierarchyTestNode.Create(parent: unknownParent); + + // Act + var domainFailures = Sut([node], maxDepth: 1, maxWidth: 1); + + // Assert + domainFailures.Should().HaveCount(1); + var domainFailure = domainFailures.First(); + domainFailure.ErrorMessage.Should().Contain(node.ParentId.ToString()); + domainFailure.ErrorMessage.Should().Contain("reference violation"); + } + + [Fact] + public void Cannot_Create_Node_With_Self_Reference() + { + // Arrange + var id = Guid.NewGuid(); + var nodes = HierarchyTestNodeBuilder + .CreateNewCyclicHierarchy(depth: 1, id) + .Build(); + + // Act + var domainFailures = Sut(nodes, maxDepth: 1, maxWidth: 1); + + // Assert + domainFailures.Should().HaveCount(1); + var domainFailure = domainFailures.First(); + domainFailure.ErrorMessage.Should().Contain(id.ToString()); + domainFailure.ErrorMessage.Should().Contain("cyclic reference"); + } + + [Fact] + public void Sut_Should_Throw_Exception_For_Node_With_Default_Id() + { + // Arrange + var node = HierarchyTestNode.Create(Guid.Empty); + + // Act + var exception = Assert.Throws(() => Sut([node], maxDepth: 1, maxWidth: 1)); + + // Assert + exception.Message.Should().Contain("non-default"); + } + + [Fact] + public void Empty_Hierarchy_Should_Not_Fail() + { + // Arrange/Act + var domainFailures = Sut([], maxDepth: 1, maxWidth: 1); + + // Assert + domainFailures.Should().BeEmpty(); + } + + private static List Sut(IReadOnlyCollection nodes, int maxDepth, int maxWidth) + => nodes.ValidateReferenceHierarchy( + keySelector: x => x.Id, + parentKeySelector: x => x.ParentId, + propertyName: "Reference", + maxDepth: maxDepth, + maxWidth: maxWidth); +} From 866eaae318cf289c97a337b9610d9b95a32b60b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 25 Oct 2024 15:50:00 +0200 Subject: [PATCH 3/3] chore(webapi): Swagger punctuation clean-up (#1351) ## Summary by CodeRabbit - **Documentation Improvements** - Enhanced clarity of XML documentation comments across various classes by adding periods to the end of sentences. - Corrected minor typographical errors in documentation comments for several properties. - **Code Cleanup** - Removed unused import statements in multiple files to streamline the codebase. - **Configuration Updates** - Adjusted logging and application configuration settings in the main program files to maintain effective startup processes. --- docs/schema/V1/swagger.verified.json | 140 +++++++++--------- .../V1/Common/Content/ContentValueDto.cs | 2 +- .../Content/DialogContentInputConverter.cs | 2 +- .../TransmissionContentInputConverter.cs | 2 +- .../Common/Localizations/LocalizationDto.cs | 4 +- .../Queries/Get/GetDialogTransmissionDto.cs | 2 +- .../Search/SearchDialogTransmissionDto.cs | 2 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 28 ++-- .../Dialogs/Queries/Search/SearchDialogDto.cs | 6 +- .../Queries/Search/SearchDialogDtoBase.cs | 12 +- .../Commands/Create/CreateDialogDto.cs | 32 ++-- .../Commands/Update/UpdateDialogDto.cs | 12 +- .../Dialogs/Queries/Get/GetDialogDto.cs | 26 ++-- .../Dialogs/Queries/Search/SearchDialogDto.cs | 8 +- .../Queries/Search/SearchDialogDtoBase.cs | 10 +- .../Program.cs | 1 - .../AltinnAuthorizationClient.cs | 1 - .../HealthChecks/RedisHealthCheck.cs | 3 - .../Program.cs | 1 - .../ClaimsPrincipalExtensionsTests.cs | 15 +- 20 files changed, 150 insertions(+), 159 deletions(-) diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index 6e52b17d8..c13d2b9e3 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -73,7 +73,7 @@ "type": "string" }, "value": { - "description": "A list of localizations for the content", + "description": "A list of localizations for the content.", "items": { "$ref": "#/components/schemas/LocalizationDto" }, @@ -155,14 +155,14 @@ "type": "array" }, "attachments": { - "description": "The attachments associated with the dialog (on an aggregate level)", + "description": "The attachments associated with the dialog (on an aggregate level).", "items": { "$ref": "#/components/schemas/CreateDialogDialogAttachmentDto" }, "type": "array" }, "content": { - "description": "The dialog unstructured text content", + "description": "The dialog unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/CreateDialogContentDto" @@ -170,7 +170,7 @@ ] }, "createdAt": { - "description": "If set, will override the date and time when the dialog is set as created\nIf not supplied, the current date /time will be used.", + "description": "If set, will override the date and time when the dialog is set as created.\nIf not supplied, the current date /time will be used.", "example": "2022-12-31T23:59:59Z", "format": "date-time", "type": "string" @@ -207,14 +207,14 @@ "type": "array" }, "id": { - "description": "A self-defined UUIDv7 may be provided in order to support idempotent creation of dialogs. If not provided, a new UUIDv7 will be generated.", + "description": "A self-defined UUIDv7 may be provided to support idempotent creation of dialogs. If not provided, a new UUIDv7 will be generated.", "example": "01913cd5-784f-7d3b-abef-4c77b1f0972d", "format": "guid", "nullable": true, "type": "string" }, "party": { - "description": "The party code representing the organization or person that the dialog belongs to in URN format", + "description": "The party code representing the organization or person that the dialog belongs to in URN format.", "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", "type": "string" }, @@ -224,7 +224,7 @@ "type": "string" }, "process": { - "description": "Optional process identifier used to indicate a business process this dialog belongs to", + "description": "Optional process identifier used to indicate a business process this dialog belongs to.", "nullable": true, "type": "string" }, @@ -255,7 +255,7 @@ ] }, "systemLabel": { - "description": "Set the system label of the dialog Migration purposes", + "description": "Set the system label of the dialog Migration purposes.", "nullable": true, "oneOf": [ { @@ -264,14 +264,14 @@ ] }, "transmissions": { - "description": "The immutable list of transmissions associated with the dialog", + "description": "The immutable list of transmissions associated with the dialog.", "items": { "$ref": "#/components/schemas/CreateDialogDialogTransmissionDto" }, "type": "array" }, "updatedAt": { - "description": "If set, will override the date and time when the dialog is set as last updated\nIf not supplied, the current date /time will be used.", + "description": "If set, will override the date and time when the dialog is set as last updated.\nIf not supplied, the current date /time will be used.", "example": "2022-12-31T23:59:59Z", "format": "date-time", "type": "string" @@ -367,7 +367,7 @@ "type": "string" }, "id": { - "description": "A self-defined UUIDv7 may be provided in order to support idempotent creation of activities. If not provided, a new UUIDv7 will be generated.", + "description": "A self-defined UUIDv7 may be provided to support idempotent creation of activities. If not provided, a new UUIDv7 will be generated.", "example": "01913cd5-784f-7d3b-abef-4c77b1f0972d", "format": "guid", "nullable": true, @@ -428,7 +428,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog", + "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog.", "example": "write", "type": "string" }, @@ -549,7 +549,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,", + "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy.", "type": "string" }, "authorizationAttribute": { @@ -638,7 +638,7 @@ "additionalProperties": false, "properties": { "attachments": { - "description": "The transmission-level attachments", + "description": "The transmission-level attachments.", "items": { "$ref": "#/components/schemas/CreateDialogTransmissionAttachmentDto" }, @@ -651,7 +651,7 @@ "type": "string" }, "content": { - "description": "The transmission unstructured text content", + "description": "The transmission unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/CreateDialogDialogTransmissionContentDto" @@ -659,7 +659,7 @@ ] }, "createdAt": { - "description": "If supplied, overrides the creating date and time for the transmission\nIf not supplied, the current date /time will be used.", + "description": "If supplied, overrides the creating date and time for the transmission.\nIf not supplied, the current date /time will be used.", "format": "date-time", "type": "string" }, @@ -670,7 +670,7 @@ "type": "string" }, "id": { - "description": "A self-defined UUIDv7 may be provided in order to support idempotent creation of transmissions. If not provided, a new UUIDv7 will be generated.", + "description": "A self-defined UUIDv7 may be provided to support idempotent creation of transmissions. If not provided, a new UUIDv7 will be generated.", "example": "01913cd5-784f-7d3b-abef-4c77b1f0972d", "format": "guid", "nullable": true, @@ -793,7 +793,7 @@ "additionalProperties": false, "properties": { "attachments": { - "description": "The transmission-level attachments", + "description": "The transmission-level attachments.", "items": { "$ref": "#/components/schemas/UpdateDialogTransmissionAttachmentDto" }, @@ -806,7 +806,7 @@ "type": "string" }, "content": { - "description": "The transmission unstructured text content", + "description": "The transmission unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/UpdateDialogDialogTransmissionContentDto" @@ -1329,7 +1329,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog", + "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog.", "example": "write", "type": "string" }, @@ -1362,7 +1362,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog", + "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog.", "example": "write", "type": "string" }, @@ -1623,7 +1623,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,", + "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy.", "type": "string" }, "authorizationAttribute": { @@ -1689,7 +1689,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,", + "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy.", "type": "string" }, "authorizationAttribute": { @@ -1769,12 +1769,12 @@ "type": "boolean" }, "seenAt": { - "description": "The timestamp when the dialog revision was seen", + "description": "The timestamp when the dialog revision was seen.", "format": "date-time", "type": "string" }, "seenBy": { - "description": "The actor that saw the dialog revision", + "description": "The actor that saw the dialog revision.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogDialogSeenLogSeenByActorDto" @@ -1793,7 +1793,7 @@ "type": "string" }, "isCurrentEndUser": { - "description": "Flag indicating whether the seen log entry was created by the current end user, if provided in the query", + "description": "Flag indicating whether the seen log entry was created by the current end user, if provided in the query.", "type": "boolean" }, "isViaServiceOwner": { @@ -1802,12 +1802,12 @@ "type": "boolean" }, "seenAt": { - "description": "The timestamp when the dialog revision was seen", + "description": "The timestamp when the dialog revision was seen.", "format": "date-time", "type": "string" }, "seenBy": { - "description": "The actor that saw the dialog revision", + "description": "The actor that saw the dialog revision.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogDialogSeenLogSeenByActorDtoSO" @@ -2015,7 +2015,7 @@ "additionalProperties": false, "properties": { "attachments": { - "description": "The transmission-level attachments", + "description": "The transmission-level attachments.", "items": { "$ref": "#/components/schemas/GetDialogDialogTransmissionAttachmentDto" }, @@ -2028,7 +2028,7 @@ "type": "string" }, "content": { - "description": "The transmission unstructured text content", + "description": "The transmission unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogDialogTransmissionContentDto" @@ -2052,7 +2052,7 @@ "type": "string" }, "isAuthorized": { - "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available", + "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available.", "type": "boolean" }, "relatedTransmissionId": { @@ -2084,7 +2084,7 @@ "additionalProperties": false, "properties": { "attachments": { - "description": "The transmission-level attachments", + "description": "The transmission-level attachments.", "items": { "$ref": "#/components/schemas/GetDialogDialogTransmissionAttachmentDtoSO" }, @@ -2097,7 +2097,7 @@ "type": "string" }, "content": { - "description": "The transmission unstructured text content", + "description": "The transmission unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogDialogTransmissionContentDtoSO" @@ -2216,14 +2216,14 @@ "type": "array" }, "attachments": { - "description": "The attachments associated with the dialog (on an aggregate level)", + "description": "The attachments associated with the dialog (on an aggregate level).", "items": { "$ref": "#/components/schemas/GetDialogDialogAttachmentDto" }, "type": "array" }, "content": { - "description": "The dialog unstructured text content", + "description": "The dialog unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogContentDto" @@ -2237,7 +2237,7 @@ "type": "string" }, "dialogToken": { - "description": "The dialog token. May be used (if supported) against external URLs referred to in this dialog\u0027s apiActions,\ntransmissions or attachments. Should also be used for front-channel embeds.", + "description": "The dialog token. May be used (if supported) against external URLs referred to in this dialog\u0027s apiActions,\ntransmissions or attachments. It should also be used for front-channel embeds.", "nullable": true, "type": "string" }, @@ -2284,7 +2284,7 @@ "type": "string" }, "party": { - "description": "The party code representing the organization or person that the dialog belongs to in URN format", + "description": "The party code representing the organization or person that the dialog belongs to in URN format.", "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", "type": "string" }, @@ -2294,7 +2294,7 @@ "type": "string" }, "process": { - "description": "Optional process identifier used to indicate a business process this dialog belongs to", + "description": "Optional process identifier used to indicate a business process this dialog belongs to.", "nullable": true, "type": "string" }, @@ -2323,7 +2323,7 @@ "type": "string" }, "serviceResourceType": { - "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType)", + "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType).", "type": "string" }, "status": { @@ -2343,7 +2343,7 @@ ] }, "transmissions": { - "description": "The immutable list of transmissions associated with the dialog", + "description": "The immutable list of transmissions associated with the dialog.", "items": { "$ref": "#/components/schemas/GetDialogDialogTransmissionDto" }, @@ -2376,14 +2376,14 @@ "type": "array" }, "attachments": { - "description": "The attachments associated with the dialog (on an aggregate level)", + "description": "The attachments associated with the dialog (on an aggregate level).", "items": { "$ref": "#/components/schemas/GetDialogDialogAttachmentDtoSO" }, "type": "array" }, "content": { - "description": "The dialog unstructured text content", + "description": "The dialog unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/GetDialogContentDtoSO" @@ -2445,7 +2445,7 @@ "type": "string" }, "party": { - "description": "The party code representing the organization or person that the dialog belongs to in URN format", + "description": "The party code representing the organization or person that the dialog belongs to in URN format.", "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", "type": "string" }, @@ -2455,7 +2455,7 @@ "type": "string" }, "process": { - "description": "Optional process identifier used to indicate a business process this dialog belongs to", + "description": "Optional process identifier used to indicate a business process this dialog belongs to.", "nullable": true, "type": "string" }, @@ -2492,7 +2492,7 @@ "type": "string" }, "serviceResourceType": { - "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType)", + "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType).", "type": "string" }, "status": { @@ -2512,7 +2512,7 @@ ] }, "transmissions": { - "description": "The immutable list of transmissions associated with the dialog", + "description": "The immutable list of transmissions associated with the dialog.", "items": { "$ref": "#/components/schemas/GetDialogDialogTransmissionDtoSO" }, @@ -2840,7 +2840,7 @@ "type": "string" }, "isAuthorized": { - "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available", + "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available.", "type": "boolean" }, "relatedTransmissionId": { @@ -3094,12 +3094,12 @@ "additionalProperties": false, "properties": { "languageCode": { - "description": "The language code of the localization in ISO 639-1 format", + "description": "The language code of the localization in ISO 639-1 format.", "example": "nb", "type": "string" }, "value": { - "description": "The localized text or URI reference", + "description": "The localized text or URI reference.", "type": "string" } }, @@ -3601,12 +3601,12 @@ "type": "boolean" }, "seenAt": { - "description": "The timestamp when the dialog revision was seen", + "description": "The timestamp when the dialog revision was seen.", "format": "date-time", "type": "string" }, "seenBy": { - "description": "The actor that saw the dialog revision", + "description": "The actor that saw the dialog revision.", "oneOf": [ { "$ref": "#/components/schemas/SearchDialogDialogSeenLogSeenByActorDto" @@ -3634,12 +3634,12 @@ "type": "boolean" }, "seenAt": { - "description": "The timestamp when the dialog revision was seen", + "description": "The timestamp when the dialog revision was seen.", "format": "date-time", "type": "string" }, "seenBy": { - "description": "The actor that saw the dialog revision", + "description": "The actor that saw the dialog revision.", "oneOf": [ { "$ref": "#/components/schemas/SearchDialogDialogSeenLogSeenByActorDtoSO" @@ -3683,7 +3683,7 @@ "additionalProperties": false, "properties": { "content": { - "description": "The content of the dialog in search results", + "description": "The content of the dialog in search results.", "oneOf": [ { "$ref": "#/components/schemas/SearchDialogContentDto" @@ -3735,17 +3735,17 @@ "type": "string" }, "party": { - "description": "The party code representing the organization or person that the dialog belongs to in URN format", + "description": "The party code representing the organization or person that the dialog belongs to in URN format.", "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", "type": "string" }, "precedingProcess": { - "description": "Optional preceding process identifier to indicate the business process that preceded the process indicated in the \u0022Process\u0022 field. Cannot be set without also \u0022Process\u0022 being set. ", + "description": "Optional preceding process identifier to indicate the business process that preceded the process indicated in the \u0022Process\u0022 field. Cannot be set without also \u0022Process\u0022 being set.", "nullable": true, "type": "string" }, "process": { - "description": "Optional process identifier used to indicate a business process this dialog belongs to ", + "description": "Optional process identifier used to indicate a business process this dialog belongs to.", "nullable": true, "type": "string" }, @@ -3768,7 +3768,7 @@ "type": "string" }, "serviceResourceType": { - "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType)", + "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType).", "type": "string" }, "status": { @@ -3800,7 +3800,7 @@ "additionalProperties": false, "properties": { "content": { - "description": "The content of the dialog in search results", + "description": "The content of the dialog in search results.", "oneOf": [ { "$ref": "#/components/schemas/SearchDialogContentDtoSO" @@ -3852,7 +3852,7 @@ "type": "string" }, "party": { - "description": "The party code representing the organization or person that the dialog belongs to in URN format", + "description": "The party code representing the organization or person that the dialog belongs to in URN format.", "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", "type": "string" }, @@ -3862,7 +3862,7 @@ "type": "string" }, "process": { - "description": "Optional process identifier used to indicate a business process this dialog belongs to", + "description": "Optional process identifier used to indicate a business process this dialog belongs to.", "nullable": true, "type": "string" }, @@ -3891,7 +3891,7 @@ "type": "string" }, "serviceResourceType": { - "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType)", + "description": "The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType).", "type": "string" }, "status": { @@ -4203,7 +4203,7 @@ "type": "string" }, "isAuthorized": { - "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available", + "description": "Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and\nthe attachments will not be available.", "type": "boolean" }, "relatedTransmissionId": { @@ -4562,7 +4562,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog", + "description": "String identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,\nwhich by default is the policy belonging to the service referred to by \u0022serviceResource\u0022 in the dialog.", "example": "write", "type": "string" }, @@ -4711,7 +4711,7 @@ "additionalProperties": false, "properties": { "action": { - "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy,", + "description": "The action identifier for the action, corresponding to the \u0022action\u0022 attributeId used in the XACML service policy.", "type": "string" }, "authorizationAttribute": { @@ -4807,7 +4807,7 @@ "additionalProperties": false, "properties": { "attachments": { - "description": "The transmission-level attachments", + "description": "The transmission-level attachments.", "items": { "$ref": "#/components/schemas/UpdateDialogTransmissionAttachmentDto" }, @@ -4820,7 +4820,7 @@ "type": "string" }, "content": { - "description": "The transmission unstructured text content", + "description": "The transmission unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/UpdateDialogDialogTransmissionContentDto" @@ -4914,14 +4914,14 @@ "type": "array" }, "attachments": { - "description": "The attachments associated with the dialog (on an aggregate level)", + "description": "The attachments associated with the dialog (on an aggregate level).", "items": { "$ref": "#/components/schemas/UpdateDialogDialogAttachmentDto" }, "type": "array" }, "content": { - "description": "The dialog unstructured text content", + "description": "The dialog unstructured text content.", "oneOf": [ { "$ref": "#/components/schemas/UpdateDialogContentDto" @@ -7438,4 +7438,4 @@ "url": "https://altinn-dev-api.azure-api.net/dialogporten" } ] -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs index 2c6a9f77a..c3db7d6a5 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs @@ -6,7 +6,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; public sealed class ContentValueDto { /// - /// A list of localizations for the content + /// A list of localizations for the content. /// public List Value { get; set; } = []; diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/DialogContentInputConverter.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/DialogContentInputConverter.cs index 8235923ab..6c4333073 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/DialogContentInputConverter.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/DialogContentInputConverter.cs @@ -11,7 +11,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; /// /// TODO: Discuss this with the team later. It works for now -/// This class is used to map bewteen the incoming dto object and the internal dialog content structure. +/// This class is used to map between the incoming dto object and the internal dialog content structure. /// Value needs to be mapped from a list of LocalizationDto in order for merging to work. /// internal sealed class IntermediateDialogContent diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/TransmissionContentInputConverter.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/TransmissionContentInputConverter.cs index 44dd6698b..346cac394 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/TransmissionContentInputConverter.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/TransmissionContentInputConverter.cs @@ -11,7 +11,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; /// /// TODO: Discuss this with the team later. It works for now -/// This class is used to map bewteen the incoming dto object and the internal transmission content structure. +/// This class is used to map between the incoming dto object and the internal transmission content structure. /// Value needs to be mapped from a list of LocalizationDto in order for merging to work. /// /// We might want to consider combining this class with DialogContentInputConverter later. diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Localizations/LocalizationDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Localizations/LocalizationDto.cs index ce209d973..d7d08dc54 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Localizations/LocalizationDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Localizations/LocalizationDto.cs @@ -7,12 +7,12 @@ public sealed class LocalizationDto private readonly string _languageCode = null!; /// - /// The localized text or URI reference + /// The localized text or URI reference. /// public required string Value { get; init; } /// - /// The language code of the localization in ISO 639-1 format + /// The language code of the localization in ISO 639-1 format. /// /// nb public required string LanguageCode diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs index 527df6efa..0beb4312f 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs @@ -25,7 +25,7 @@ public sealed class GetDialogTransmissionDto /// /// Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and - /// the attachments will not be available + /// the attachments will not be available. /// public bool IsAuthorized { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs index 2725078c3..eac474d0b 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs @@ -25,7 +25,7 @@ public sealed class SearchDialogTransmissionDto /// /// Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and - /// the attachments will not be available + /// the attachments will not be available. /// public bool IsAuthorized { 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 a588a0c2a..40ab0988f 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 @@ -39,12 +39,12 @@ public sealed class GetDialogDto public string ServiceResource { get; set; } = null!; /// - /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType) + /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType). /// public string ServiceResourceType { get; set; } = null!; /// - /// The party code representing the organization or person that the dialog belongs to in URN format + /// The party code representing the organization or person that the dialog belongs to in URN format. /// /// /// urn:altinn:person:identifier-no:01125512345 @@ -59,7 +59,7 @@ public sealed class GetDialogDto public int? Progress { get; set; } /// - /// Optional process identifier used to indicate a business process this dialog belongs to + /// Optional process identifier used to indicate a business process this dialog belongs to. /// public string? Process { get; set; } @@ -122,23 +122,23 @@ public sealed class GetDialogDto public SystemLabel.Values SystemLabel { get; set; } /// - /// The dialog unstructured text content + /// The dialog unstructured text content. /// public GetDialogContentDto Content { get; set; } = null!; /// /// The dialog token. May be used (if supported) against external URLs referred to in this dialog's apiActions, - /// transmissions or attachments. Should also be used for front-channel embeds. + /// transmissions or attachments. It should also be used for front-channel embeds. /// public string? DialogToken { get; set; } /// - /// The attachments associated with the dialog (on an aggregate level) + /// The attachments associated with the dialog (on an aggregate level). /// public List Attachments { get; set; } = []; /// - /// The immutable list of transmissions associated with the dialog + /// The immutable list of transmissions associated with the dialog. /// public List Transmissions { get; set; } = []; @@ -193,7 +193,7 @@ public sealed class GetDialogDialogTransmissionDto /// /// Flag indicating if the authenticated user is authorized for this transmission. If not, embedded content and - /// the attachments will not be available + /// the attachments will not be available. /// public bool IsAuthorized { get; set; } @@ -220,12 +220,12 @@ public sealed class GetDialogDialogTransmissionDto public GetDialogDialogTransmissionSenderActorDto Sender { get; set; } = null!; /// - /// The transmission unstructured text content + /// The transmission unstructured text content. /// public GetDialogDialogTransmissionContentDto Content { get; set; } = null!; /// - /// The transmission-level attachments + /// The transmission-level attachments. /// public List Attachments { get; set; } = []; } @@ -238,12 +238,12 @@ public sealed class GetDialogDialogSeenLogDto public Guid Id { get; set; } /// - /// The timestamp when the dialog revision was seen + /// The timestamp when the dialog revision was seen. /// public DateTimeOffset SeenAt { get; set; } /// - /// The actor that saw the dialog revision + /// The actor that saw the dialog revision. /// public GetDialogDialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; @@ -418,7 +418,7 @@ public sealed class GetDialogDialogApiActionDto /// /// String identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, - /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog + /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog. /// /// write public string Action { get; set; } = null!; @@ -518,7 +518,7 @@ public sealed class GetDialogDialogGuiActionDto public Guid Id { get; set; } /// - /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, + /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy. /// public string Action { get; set; } = null!; 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 ea9abc59d..f4c2a854d 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 @@ -7,7 +7,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Que public sealed class SearchDialogDto : SearchDialogDtoBase { /// - /// The content of the dialog in search results + /// The content of the dialog in search results. /// [JsonPropertyOrder(100)] // ILU MAGNUS public SearchDialogContentDto Content { get; set; } = null!; @@ -37,8 +37,8 @@ public sealed class SearchDialogContentDto } /// -/// TOOD: Discuss this with the team later. It works for now -/// This class is used in order to keep using ProjectTo and existing PaginationList code. +/// TODO: Discuss this with the team later. It works for now +/// This class is used to keep using ProjectTo and existing PaginationList code. /// We first map to this using ProjectTo, then map to the new DialogContent structure /// in the SearchDialog handlers, after EF core is done loading the data. /// Then we create a new PaginatedList with the outwards facing dto diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDtoBase.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDtoBase.cs index ee7f9f9f3..92d14120a 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDtoBase.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/SearchDialogDtoBase.cs @@ -28,12 +28,12 @@ public class SearchDialogDtoBase public string ServiceResource { get; set; } = null!; /// - /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType) + /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType). /// public string ServiceResourceType { get; set; } = null!; /// - /// The party code representing the organization or person that the dialog belongs to in URN format + /// The party code representing the organization or person that the dialog belongs to in URN format. /// /// /// urn:altinn:person:identifier-no:01125512345 @@ -48,12 +48,12 @@ public class SearchDialogDtoBase public int? Progress { get; set; } /// - /// Optional process identifier used to indicate a business process this dialog belongs to + /// Optional process identifier used to indicate a business process this dialog belongs to. /// public string? Process { get; set; } /// - /// Optional preceding process identifier to indicate the business process that preceded the process indicated in the "Process" field. Cannot be set without also "Process" being set. + /// Optional preceding process identifier to indicate the business process that preceded the process indicated in the "Process" field. Cannot be set without also "Process" being set. /// public string? PrecedingProcess { get; set; } @@ -117,12 +117,12 @@ public sealed class SearchDialogDialogSeenLogDto public Guid Id { get; set; } /// - /// The timestamp when the dialog revision was seen + /// The timestamp when the dialog revision was seen. /// public DateTimeOffset SeenAt { get; set; } /// - /// The actor that saw the dialog revision + /// The actor that saw the dialog revision. /// public SearchDialogDialogSeenLogSeenByActorDto SeenBy { get; set; } = 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 d49e66524..d7e75b329 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 @@ -14,7 +14,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialog public class CreateDialogDto { /// - /// A self-defined UUIDv7 may be provided in order to support idempotent creation of dialogs. If not provided, a new UUIDv7 will be generated. + /// A self-defined UUIDv7 may be provided to support idempotent creation of dialogs. If not provided, a new UUIDv7 will be generated. /// /// 01913cd5-784f-7d3b-abef-4c77b1f0972d public Guid? Id { get; set; } @@ -28,7 +28,7 @@ public class CreateDialogDto public string ServiceResource { get; set; } = null!; /// - /// The party code representing the organization or person that the dialog belongs to in URN format + /// The party code representing the organization or person that the dialog belongs to in URN format. /// /// /// urn:altinn:person:identifier-no:01125512345 @@ -67,7 +67,7 @@ public class CreateDialogDto public DateTimeOffset? DueAt { get; set; } /// - /// Optional process identifier used to indicate a business process this dialog belongs to + /// Optional process identifier used to indicate a business process this dialog belongs to. /// public string? Process { get; set; } /// @@ -85,14 +85,14 @@ public class CreateDialogDto public DateTimeOffset? ExpiresAt { get; set; } /// - /// If set, will override the date and time when the dialog is set as created + /// If set, will override the date and time when the dialog is set as created. /// If not supplied, the current date /time will be used. /// /// 2022-12-31T23:59:59Z public DateTimeOffset CreatedAt { get; set; } /// - /// If set, will override the date and time when the dialog is set as last updated + /// If set, will override the date and time when the dialog is set as last updated. /// If not supplied, the current date /time will be used. /// /// 2022-12-31T23:59:59Z @@ -104,11 +104,11 @@ public class CreateDialogDto public DialogStatus.Values Status { get; set; } /// - /// Set the system label of the dialog Migration purposes + /// Set the system label of the dialog Migration purposes. /// public SystemLabel.Values? SystemLabel { get; set; } /// - /// The dialog unstructured text content + /// The dialog unstructured text content. /// public CreateDialogContentDto Content { get; set; } = null!; @@ -118,12 +118,12 @@ public class CreateDialogDto public List SearchTags { get; set; } = []; /// - /// The attachments associated with the dialog (on an aggregate level) + /// The attachments associated with the dialog (on an aggregate level). /// public List Attachments { get; set; } = []; /// - /// The immutable list of transmissions associated with the dialog + /// The immutable list of transmissions associated with the dialog. /// public List Transmissions { get; set; } = []; @@ -146,13 +146,13 @@ public class CreateDialogDto public sealed class CreateDialogDialogTransmissionDto { /// - /// A self-defined UUIDv7 may be provided in order to support idempotent creation of transmissions. If not provided, a new UUIDv7 will be generated. + /// A self-defined UUIDv7 may be provided to support idempotent creation of transmissions. If not provided, a new UUIDv7 will be generated. /// /// 01913cd5-784f-7d3b-abef-4c77b1f0972d public Guid? Id { get; set; } /// - /// If supplied, overrides the creating date and time for the transmission + /// If supplied, overrides the creating date and time for the transmission. /// If not supplied, the current date /time will be used. /// public DateTimeOffset CreatedAt { get; set; } @@ -196,12 +196,12 @@ public sealed class CreateDialogDialogTransmissionDto public CreateDialogDialogTransmissionSenderActorDto Sender { get; set; } = null!; /// - /// The transmission unstructured text content + /// The transmission unstructured text content. /// public CreateDialogDialogTransmissionContentDto Content { get; set; } = null!; /// - /// The transmission-level attachments + /// The transmission-level attachments. /// public List Attachments { get; set; } = []; } @@ -275,7 +275,7 @@ public sealed class CreateDialogSearchTagDto public sealed class CreateDialogDialogActivityDto { /// - /// A self-defined UUIDv7 may be provided in order to support idempotent creation of activities. If not provided, a new UUIDv7 will be generated. + /// A self-defined UUIDv7 may be provided to support idempotent creation of activities. If not provided, a new UUIDv7 will be generated. /// /// 01913cd5-784f-7d3b-abef-4c77b1f0972d public Guid? Id { get; set; } @@ -361,7 +361,7 @@ public sealed class CreateDialogDialogApiActionDto { /// /// String identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, - /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog + /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog. /// /// write public string Action { get; set; } = null!; @@ -438,7 +438,7 @@ public sealed class CreateDialogDialogApiActionEndpointDto public sealed class CreateDialogDialogGuiActionDto { /// - /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, + /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy. /// public string Action { get; set; } = 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 5dfe1ab24..fb3cdd3d6 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 @@ -57,7 +57,7 @@ public sealed class UpdateDialogDto public DialogStatus.Values Status { get; set; } /// - /// The dialog unstructured text content + /// The dialog unstructured text content. /// public UpdateDialogContentDto Content { get; set; } = null!; @@ -67,7 +67,7 @@ public sealed class UpdateDialogDto public List SearchTags { get; set; } = []; /// - /// The attachments associated with the dialog (on an aggregate level) + /// The attachments associated with the dialog (on an aggregate level). /// public List Attachments { get; set; } = []; @@ -148,12 +148,12 @@ public class UpdateDialogDialogTransmissionDto public UpdateDialogDialogTransmissionSenderActorDto Sender { get; set; } = null!; /// - /// The transmission unstructured text content + /// The transmission unstructured text content. /// public UpdateDialogDialogTransmissionContentDto Content { get; set; } = null!; /// - /// The transmission-level attachments + /// The transmission-level attachments. /// public List Attachments { get; set; } = []; } @@ -314,7 +314,7 @@ public sealed class UpdateDialogDialogApiActionDto /// /// String identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, - /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog + /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog. /// /// write public string Action { get; set; } = null!; @@ -403,7 +403,7 @@ public sealed class UpdateDialogDialogGuiActionDto public Guid? Id { get; set; } /// - /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, + /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy. /// public string Action { get; set; } = null!; 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 2d9469214..8b067b71f 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 @@ -39,12 +39,12 @@ public sealed class GetDialogDto public string ServiceResource { get; set; } = null!; /// - /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType) + /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType). /// public string ServiceResourceType { get; set; } = null!; /// - /// The party code representing the organization or person that the dialog belongs to in URN format + /// The party code representing the organization or person that the dialog belongs to in URN format. /// /// /// urn:altinn:person:identifier-no:01125512345 @@ -59,7 +59,7 @@ public sealed class GetDialogDto public int? Progress { get; set; } /// - /// Optional process identifier used to indicate a business process this dialog belongs to + /// Optional process identifier used to indicate a business process this dialog belongs to. /// public string? Process { get; set; } @@ -134,7 +134,7 @@ public sealed class GetDialogDto public SystemLabel.Values SystemLabel { get; set; } /// - /// The dialog unstructured text content + /// The dialog unstructured text content. /// public GetDialogContentDto Content { get; set; } = null!; @@ -144,12 +144,12 @@ public sealed class GetDialogDto public List? SearchTags { get; set; } /// - /// The attachments associated with the dialog (on an aggregate level) + /// The attachments associated with the dialog (on an aggregate level). /// public List Attachments { get; set; } = []; /// - /// The immutable list of transmissions associated with the dialog + /// The immutable list of transmissions associated with the dialog. /// public List Transmissions { get; set; } = []; @@ -230,12 +230,12 @@ public sealed class GetDialogDialogTransmissionDto public GetDialogDialogTransmissionSenderActorDto Sender { get; set; } = null!; /// - /// The transmission unstructured text content + /// The transmission unstructured text content. /// public GetDialogDialogTransmissionContentDto Content { get; set; } = null!; /// - /// The transmission-level attachments + /// The transmission-level attachments. /// public List Attachments { get; set; } = []; } @@ -248,12 +248,12 @@ public sealed class GetDialogDialogSeenLogDto public Guid Id { get; set; } /// - /// The timestamp when the dialog revision was seen + /// The timestamp when the dialog revision was seen. /// public DateTimeOffset SeenAt { get; set; } /// - /// The actor that saw the dialog revision + /// The actor that saw the dialog revision. /// public GetDialogDialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; @@ -265,7 +265,7 @@ public sealed class GetDialogDialogSeenLogDto public bool? IsViaServiceOwner { get; set; } /// - /// Flag indicating whether the seen log entry was created by the current end user, if provided in the query + /// Flag indicating whether the seen log entry was created by the current end user, if provided in the query. /// public bool IsCurrentEndUser { get; set; } } @@ -437,7 +437,7 @@ public sealed class GetDialogDialogApiActionDto /// /// String identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, - /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog + /// which by default is the policy belonging to the service referred to by "serviceResource" in the dialog. /// /// write public string Action { get; set; } = null!; @@ -531,7 +531,7 @@ public sealed class GetDialogDialogGuiActionDto public Guid Id { get; set; } /// - /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy, + /// The action identifier for the action, corresponding to the "action" attributeId used in the XACML service policy. /// public string Action { get; set; } = null!; 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 3be09c6eb..398b6cb02 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 @@ -7,7 +7,7 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialog public sealed class SearchDialogDto : SearchDialogDtoBase { /// - /// The content of the dialog in search results + /// The content of the dialog in search results. /// [JsonPropertyOrder(100)] // ILU MAGNUS public SearchDialogContentDto Content { get; set; } = null!; @@ -37,11 +37,11 @@ public sealed class SearchDialogContentDto } /// -/// TOOD: Discuss this with the team later. It works for now -/// This class is used in order to keep using ProjectTo and existing PaginationList code. +/// TODO: Discuss this with the team later. It works for now +/// This class is used to keep using ProjectTo and existing PaginationList code. /// We first map to this using ProjectTo, then map to the new DialogContent structure /// in the SearchDialog handlers, after EF core is done loading the data. -/// Then we create a new PaginatedList with the outwards facing dto +/// Then we create a new PaginatedList with the outwards facing dto. /// public sealed class IntermediateSearchDialogDto : SearchDialogDtoBase { diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDtoBase.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDtoBase.cs index ddde4c035..e2620a405 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDtoBase.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/SearchDialogDtoBase.cs @@ -34,12 +34,12 @@ public class SearchDialogDtoBase public string ServiceResource { get; set; } = null!; /// - /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType) + /// The ServiceResource type, as defined in Altinn Resource Registry (see ResourceType). /// public string ServiceResourceType { get; set; } = null!; /// - /// The party code representing the organization or person that the dialog belongs to in URN format + /// The party code representing the organization or person that the dialog belongs to in URN format. /// /// /// urn:altinn:person:identifier-no:01125512345 @@ -54,7 +54,7 @@ public class SearchDialogDtoBase public int? Progress { get; set; } /// - /// Optional process identifier used to indicate a business process this dialog belongs to + /// Optional process identifier used to indicate a business process this dialog belongs to. /// public string? Process { get; set; } @@ -129,12 +129,12 @@ public sealed class SearchDialogDialogSeenLogDto public Guid Id { get; set; } /// - /// The timestamp when the dialog revision was seen + /// The timestamp when the dialog revision was seen. /// public DateTimeOffset SeenAt { get; set; } /// - /// The actor that saw the dialog revision + /// The actor that saw the dialog revision. /// public SearchDialogDialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs index 10040bcad..fab99b9b2 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs @@ -6,7 +6,6 @@ using Digdir.Domain.Dialogporten.GraphQL.Common; using Digdir.Domain.Dialogporten.GraphQL.Common.Authentication; using Digdir.Domain.Dialogporten.GraphQL.Common.Authorization; -using Digdir.Domain.Dialogporten.GraphQL.Common.Extensions; using Digdir.Domain.Dialogporten.Infrastructure; using Digdir.Domain.Dialogporten.Application.Common.Extensions.OptionExtensions; using Digdir.Domain.Dialogporten.GraphQL; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs index 3d127348e..b676e36c9 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Security.Claims; using System.Text.Json; using System.Text.Json.Serialization; using Altinn.Authorization.ABAC.Xacml.JsonProfile; diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/HealthChecks/RedisHealthCheck.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/HealthChecks/RedisHealthCheck.cs index 4e88b8be6..71983e8e7 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/HealthChecks/RedisHealthCheck.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/HealthChecks/RedisHealthCheck.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; using StackExchange.Redis; using Microsoft.Extensions.Options; diff --git a/src/Digdir.Domain.Dialogporten.Service/Program.cs b/src/Digdir.Domain.Dialogporten.Service/Program.cs index 8844ecd8a..836a3d3e4 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Program.cs +++ b/src/Digdir.Domain.Dialogporten.Service/Program.cs @@ -6,7 +6,6 @@ using Serilog; using Digdir.Domain.Dialogporten.Application.Externals.Presentation; using Digdir.Domain.Dialogporten.Domain; -using Digdir.Domain.Dialogporten.Domain.Common.EventPublisher; using Digdir.Domain.Dialogporten.Service; using Digdir.Domain.Dialogporten.Service.Consumers; using Digdir.Library.Utils.AspNet; diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Extensions/ClaimsPrincipalExtensionsTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Extensions/ClaimsPrincipalExtensionsTests.cs index 8c2204bdc..42ff00431 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Extensions/ClaimsPrincipalExtensionsTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Extensions/ClaimsPrincipalExtensionsTests.cs @@ -9,10 +9,9 @@ public class ClaimsPrincipalExtensionsTests public void TryGetAuthenticationLevel_Should_Parse_Idporten_Acr_Claim_For_Level3() { // Arrange - var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] - { + var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity([ new Claim("acr", "idporten-loa-substantial") - })); + ])); // Act var result = claimsPrincipal.TryGetAuthenticationLevel(out var authenticationLevel); @@ -26,10 +25,9 @@ public void TryGetAuthenticationLevel_Should_Parse_Idporten_Acr_Claim_For_Level3 public void TryGetAuthenticationLevel_Should_Parse_Idporten_Acr_Claim_For_Level4() { // Arrange - var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] - { + var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity([ new Claim("acr", "idporten-loa-high") - })); + ])); // Act var result = claimsPrincipal.TryGetAuthenticationLevel(out var authenticationLevel); @@ -43,11 +41,10 @@ public void TryGetAuthenticationLevel_Should_Parse_Idporten_Acr_Claim_For_Level4 public void TryGetAuthenticationLevel_Should_Parse_Altinn_Authlevel_First() { // Arrange - var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] - { + var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity([ new Claim("acr", "idporten-loa-high"), new Claim("urn:altinn:authlevel", "5") - })); + ])); // Act var result = claimsPrincipal.TryGetAuthenticationLevel(out var authenticationLevel);