Skip to content

Conversation

@elsand
Copy link
Member

@elsand elsand commented Dec 16, 2025

Description

  • Added CreateTransmissionCommand which uses a custom preloader for O(1) adds of transmissions
  • Added ITransmissionHierarchyValidator to encapsulate both full-aggregate and incremental transmission hierarchy checks; registered it in DI and wired CreateDialogCommand, UpdateDialogCommand, and CreateTransmissionCommand to use it instead of bespoke logic.
  • Added DialogTransmissionAppender to centralize counter updates/attachment expiry validation, updated TransmissionEntityExtensions with count helpers, and adjusted command handlers/tests (including new InternalsVisibleTo entry) to accommodate the shared appender/validator.

Similar refactoring for the CreateDialogActivityEndpoint will be a separate PR. When this is done, we can make PUT/PATCH more performant by not hydrating the entire tranmissions/activity graph in UpdateDialogDataLoader

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • 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., if applicable)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

📝 Walkthrough

Walkthrough

Adds a CreateTransmission feature (command, validator, handler, data loader), a transmission appender and hierarchy validator with repository and DI wiring, updates create/update endpoint/handlers and tests, adjusts project InternalsVisibleTo, and edits AGENTS.md guidance and code-style notes.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Rewrote integration-test sandbox guidance (removed Docker workaround; instruct to run tests outside any sandbox), reorganized Swagger/GraphQL lines, and expanded code-style rules (indentation/newline conventions).
Application DI & Project file
src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs, src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
Registered IDialogTransmissionAppender and ITransmissionHierarchyValidator as transient services; added <InternalsVisibleTo Include="DynamicProxyGenAssembly2"/>.
Transmission helpers
src/.../Features/V1/Common/Extensions/TransmissionEntityExtensions.cs
Changed ContainsTransmissionByEndUser to accept IEnumerable<DialogTransmission> and added GetTransmissionCounts() returning (FromParty, FromServiceOwner) counts.
CreateTransmission feature
src/.../ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs, .../CreateTransmissionCommandValidator.cs, .../CreateTransmissionDataLoader.cs
Added CreateTransmissionCommand, OneOf result types and success record, handler (load → validate → append → label → persist), FluentValidation validator, and data loader with admin/org scoping.
Dialog transmission appender
src/.../Dialogs/Common/DialogTransmissionAppender.cs
Added IDialogTransmissionAppender, DialogTransmissionAppender, and DialogTransmissionAppendResult; appender persists new transmissions, updates dialog counters with overflow checks, and validates attachment ExpiresAt via domain errors.
Transmission hierarchy validation & externals
src/.../Dialogs/Common/TransmissionHierarchyValidator.cs, src/.../Externals/ITransmissionHierarchyRepository.cs
Added ITransmissionHierarchyValidator and TransmissionHierarchyValidator for aggregate and new-transmission validation with depth/width limits; added ITransmissionHierarchyRepository and DTO TransmissionHierarchyNode.
Infrastructure repository & DI
src/.../Infrastructure/InfrastructureExtensions.cs, src/.../Infrastructure/Persistence/Repositories/TransmissionHierarchyRepository.cs
Registered ITransmissionHierarchyRepositoryTransmissionHierarchyRepository; repository implements recursive raw-SQL retrieval of hierarchy nodes.
Create/Update flows refactor
src/.../ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs, src/.../ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs, src/.../ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
Injected ITransmissionHierarchyValidator into Create flow; injected IDialogTransmissionAppender and ITransmissionHierarchyValidator into Update flow and made append asynchronous; added clarifying comments in data loader.
Web API endpoint change
src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
Removed IMapper dependency; endpoint now constructs and sends a CreateTransmissionCommand and uses returned TransmissionId to route to GET transmission endpoint.
Tests & test DI/flows
tests/.../Unit.Tests/.../CreateDialogTests.cs, tests/.../Integration.Tests/Common/.../DialogApplication.cs, tests/.../Integration.Tests/.../CreateTransmissionTests.cs, tests/.../Integration.Tests/.../IFlowStepExtensions.cs
Updated unit tests to pass ITransmissionHierarchyValidator mock; registered ITransmissionHierarchyRepository in integration test DI; added integration test for creating a transmission referencing an existing transmission; added flow-step extensions for CreateTransmission results.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding CreateTransmissionCommand and refactoring shared transmission validation/appending logic.
Description check ✅ Passed The description comprehensively covers the main changes, includes a related issue, and verifies all checklist items (build, testing, automated tests). Documentation is appropriately marked as not updated.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/add-create-transmission-command

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@elsand elsand changed the title Add CrateTransmissionCommand, refactor common transmission plumbing fix(perf): Add CrateTransmissionCommand, refactor common transmission plumbing Dec 16, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
AGENTS.md (1)

44-47: Contradictory guidance on lines 45-46.

Lines 45 and 46 provide conflicting advice:

  • Line 45: "Prefer switch expressions over statement-based switch."
  • Line 46: "Prefer if/else chains over statement-based switch."

This creates ambiguity. Based on the coding guidelines, line 46 should likely read "Prefer if with early returns over if/else chains" instead.

 - Prefer pattern matching (`switch` expressions, property patterns) over `if/else` chains.
 - Prefer switch expressions over statement-based `switch`.
-- Prefer `if/else` chains over statement-based `switch`.
+- Prefer `if` with early returns over `if/else` chains.
 - Prefer records or record structs for immutable data models.
🧹 Nitpick comments (8)
src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs (1)

10-28: Consider LINQ alternative for consistency with coding guidelines.

Per guidelines, LINQ is preferred over manual loops for transformations. A single-enumeration LINQ approach:

-    internal static (int FromParty, int FromServiceOwner) GetTransmissionCounts(this IEnumerable<DialogTransmission> transmissions)
-    {
-        var fromParty = 0;
-        var fromServiceOwner = 0;
-
-        foreach (var transmission in transmissions)
-        {
-            if (IsEndUserTransmissionType(transmission.TypeId))
-            {
-                fromParty++;
-            }
-            else
-            {
-                fromServiceOwner++;
-            }
-        }
-
-        return (fromParty, fromServiceOwner);
-    }
+    internal static (int FromParty, int FromServiceOwner) GetTransmissionCounts(this IEnumerable<DialogTransmission> transmissions)
+    {
+        var list = transmissions as IReadOnlyCollection<DialogTransmission> ?? transmissions.ToList();
+        var fromParty = list.Count(x => IsEndUserTransmissionType(x.TypeId));
+        return (fromParty, list.Count - fromParty);
+    }

That said, your current implementation is efficient with a single O(n) pass. If performance is critical here, the imperative approach is acceptable.

tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (1)

56-92: Consider consolidating near-duplicate tests.

Both CreateDialogCommand_Should_Return_Forbidden_When_Scope_Is_Missing and CreateDialogCommand_Should_Return_Forbidden_When_User_Is_Not_Owner have identical arrange/act/assert logic. The test names suggest different scenarios, but both return new Forbidden() from AuthorizeServiceResources and assert result.IsT3.

If they're meant to test distinct scenarios, consider differentiating the setup or assertions. Otherwise, one test may be redundant.

src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (1)

55-57: Consider defensive check for empty collection.

First() will throw if TransmissionIds is unexpectedly empty. Given this endpoint always sends exactly one transmission, the success path should guarantee one ID is returned, but consider using Single() to make the "exactly one" expectation explicit and catch bugs if the contract changes.

-                var transmissionId = success.TransmissionIds.First();
+                var transmissionId = success.TransmissionIds.Single();
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (1)

63-76: Error message lacks transmission/attachment index context.

When validation fails, the error path doesn't indicate which transmission or attachment has the invalid ExpiresAt. Consider including indices in the property path to help identify the specific offending item.

-    private void ValidateTimeFields(DialogTransmissionAttachment attachment)
+    private void ValidateTimeFields(DialogTransmissionAttachment attachment, int transmissionIndex, int attachmentIndex)
     {
         if (!_db.MustWhenAdded(attachment,
                 propertyExpression: x => x.ExpiresAt,
                 predicate: x => x > _clock.UtcNowOffset || x == null))
         {
             var idString = attachment.Id == Guid.Empty ? string.Empty : $" (Id: {attachment.Id})";

-            _domainContext.AddError($"{nameof(DialogEntity.Transmissions)}." +
+            _domainContext.AddError($"{nameof(DialogEntity.Transmissions)}[{transmissionIndex}]." +
-                                    $"{nameof(DialogTransmission.Attachments)}." +
+                                    $"{nameof(DialogTransmission.Attachments)}[{attachmentIndex}]." +
                                     $"{nameof(DialogTransmissionAttachment.ExpiresAt)}",
                 $"Must be in future or null, got '{attachment.ExpiresAt}'.{idString}");
         }
     }

Update the call site in Append to pass indices:

-        foreach (var attachment in newTransmissions.SelectMany(x => x.Attachments))
+        foreach (var (transmission, transmissionIndex) in newTransmissions.Select((t, i) => (t, i)))
+        foreach (var (attachment, attachmentIndex) in transmission.Attachments.Select((a, i) => (a, i)))
         {
-            ValidateTimeFields(attachment);
+            ValidateTimeFields(attachment, transmissionIndex, attachmentIndex);
         }
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)

29-35: Consider using ArgumentNullException.ThrowIfNull() for public API guards.

Per coding guidelines, prefer ArgumentNullException.ThrowIfNull() for public/internal API guards. This is more concise and consistent with modern .NET patterns.

     public TransmissionHierarchyValidator(
         IDialogDbContext dbContext,
         IDomainContext domainContext)
     {
-        _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
-        _domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
+        ArgumentNullException.ThrowIfNull(dbContext);
+        ArgumentNullException.ThrowIfNull(domainContext);
+        _dbContext = dbContext;
+        _domainContext = domainContext;
     }

148-151: Prefer collection expression for empty list.

Per coding guidelines, prefer collection expressions ([]) instead of new List<T>().

         if (parentIds.Count == 0)
         {
-            return Task.FromResult(new List<TransmissionHierarchyNode>());
+            return Task.FromResult<List<TransmissionHierarchyNode>>([]);
         }
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (2)

21-21: Type alias references DTO from another command namespace.

The TransmissionDto is aliased from Update namespace. Consider whether a shared DTO type in the Common namespace would be more appropriate if this DTO is reused across multiple commands (Create, Update, CreateTransmission).


146-159: Duplicated AddSystemLabel method.

This method is identical to the one in UpdateDialogCommandHandler. Consider extracting it to a shared helper or service in the Common namespace to reduce duplication across command handlers.

#!/bin/bash
# Find all occurrences of AddSystemLabel to assess duplication scope
rg -n "AddSystemLabel" --type cs src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 894e531 and 9507105.

📒 Files selected for processing (14)
  • AGENTS.md (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs (2 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (5 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (7 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (2 hunks)
  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.cs: All code must compile with TreatWarningsAsErrors=true and pass .NET analyzers
Use file-scoped namespaces with using directives outside the namespace
Use PascalCase for classes and methods, camelCase for variables and parameters
Prefer expression bodies for single-line members and use var when the type is apparent
Enable nullable reference types and keep entities immutable; use OneOf for union returns when applicable
Prefer LINQ over manual loops for transformations, filtering, grouping, and projections
Prefer FirstOrDefault() / SingleOrDefault() with explicit null handling over index-based access
Use Select, Where, GroupBy, ToDictionary, ToLookup instead of building collections imperatively
Avoid materializing collections early with ToList or ToArray unless required for correctness or performance
Prefer immutable LINQ pipelines over mutating existing collections
Target latest language features available in the solution (.NET 9 / C# 13); prefer pattern matching, switch expressions, records, collection expressions, target-typed new(), and with expressions
Prefer pattern matching (switch expressions, property patterns) over if/else chains
Prefer switch expressions over statement-based switch
Prefer if with early returns over if/else chains
Prefer records or record structs for immutable data models
Use collection expressions ([]) instead of new List<T>() or new[] { } where applicable
Prefer target-typed new() when the type is obvious
Prefer with expressions for non-destructive mutation
Do not suppress nullable warnings (!) unless strictly justified
Prefer pattern matching (is not null) over != null for null checking
Prefer early returns over nested null checks
Use ArgumentNullException.ThrowIfNull() for public API guards

Files:

  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
**/*.{cs,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4-space indentation for C# and 2-space indentation for JSON/YAML with LF line endings

Files:

  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.cs: Organize code by feature folders following Clean Architecture with Domain/Application/Infrastructure/API layers
Apply CQRS, domain events, repository pattern, and use rich domain result objects
Avoid throwing exceptions for domain flow; return domain-specific result objects instead

Files:

  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
🧠 Learnings (10)
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Run tests with `dotnet test Digdir.Domain.Dialogporten.sln` after making changes; skip integration tests in non-Docker environments with `--filter 'FullyQualifiedName!~Integration'`

Applied to files:

  • AGENTS.md
  • src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Build with `dotnet build Digdir.Domain.Dialogporten.sln`

Applied to files:

  • AGENTS.md
  • src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Applies to **/*.Tests.cs : Tests use xUnit with the fixture pattern and Verify for snapshot tests

Applied to files:

  • AGENTS.md
📚 Learning: 2025-10-09T08:50:14.740Z
Learnt from: oskogstad
Repo: Altinn/dialogporten PR: 2841
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20251009083408_AddDialogSearch.cs:9-10
Timestamp: 2025-10-09T08:50:14.740Z
Learning: Files in `src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/*/**` are auto-generated Entity Framework Core migrations and should not be reviewed for formatting or coding style issues.

Applied to files:

  • AGENTS.md
  • src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Applies to **/*.cs : All code must compile with `TreatWarningsAsErrors=true` and pass .NET analyzers

Applied to files:

  • AGENTS.md
📚 Learning: 2025-10-09T10:20:37.692Z
Learnt from: oskogstad
Repo: Altinn/dialogporten PR: 2844
File: src/Digdir.Domain.Dialogporten.GraphQL/Program.cs:104-104
Timestamp: 2025-10-09T10:20:37.692Z
Learning: For the Altinn/dialogporten repository, do not flag compilation errors such as missing NuGet package references, as the team has build pipelines that catch these issues automatically.

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Applies to **/*.cs : Use file-scoped namespaces with `using` directives outside the namespace

Applied to files:

  • AGENTS.md
📚 Learning: 2025-10-15T17:12:11.560Z
Learnt from: knuhau
Repo: Altinn/dialogporten PR: 2872
File: src/Digdir.Domain.Dialogporten.Janitor/CostManagementAggregation/MetricsAggregationOptions.cs:15-23
Timestamp: 2025-10-15T17:12:11.560Z
Learning: In the Dialogporten Janitor application (src/Digdir.Domain.Dialogporten.Janitor), prefer runtime validation over startup validation (e.g., [Required] attributes) for configuration values that are only needed by specific commands. This prevents the application from failing to start when running other commands that don't require those configuration values.

Applied to files:

  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-12T13:51:39.643Z
Learnt from: elsand
Repo: Altinn/dialogporten PR: 3132
File: src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs:40-55
Timestamp: 2025-12-12T13:51:39.643Z
Learning: In src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs, the soft-deletion status of dialogs is intentionally not checked. The EntityDeleted case in the NotificationConditionResult union is unreachable by design, as whether a dialog is soft-deleted is not relevant for determining whether a notification should be sent.

Applied to files:

  • tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-10-06T08:24:02.136Z
Learnt from: MagnusSandgren
Repo: Altinn/dialogporten PR: 2818
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/FusionCache/FeatureMetricServiceResourceCache.cs:12-23
Timestamp: 2025-10-06T08:24:02.136Z
Learning: Before raising concerns about captive dependencies or DI lifetime mismatches in the Altinn/dialogporten repository, verify the actual service registration lifetime by checking the DI configuration files (e.g., InfrastructureExtensions.cs).

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs
🧬 Code graph analysis (7)
src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs (2)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (2)
  • DialogTransmissionAppender (20-77)
  • DialogTransmissionAppender (26-34)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)
  • TransmissionHierarchyValidator (21-162)
  • TransmissionHierarchyValidator (29-35)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs (1)
  • DialogEntity (18-206)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Transmissions/DialogTransmission.cs (1)
  • DialogTransmission (14-67)
src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs (1)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Transmissions/DialogTransmission.cs (1)
  • DialogTransmission (14-67)
src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (2)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1)
  • CreateTransmissionCommand (25-33)
src/Digdir.Domain.Dialogporten.Application/Common/Context/IApplicationContext.cs (1)
  • IsSilentUpdate (13-15)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (2)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)
  • ValidateWholeAggregate (12-12)
  • ValidateWholeAggregate (37-45)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs (1)
  • CreateDialogDto (14-162)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs (4)
src/Digdir.Domain.Dialogporten.Application/Common/Behaviours/DataLoader/IDataLoader.cs (1)
  • TypedDataLoader (64-86)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (2)
  • CreateTransmissionCommand (25-33)
  • Task (74-144)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs (1)
  • DialogEntity (18-206)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs (1)
  • Task (11-12)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (5)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (1)
  • AddSystemLabel (171-184)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs (1)
  • DialogEntity (18-206)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs (1)
  • UpdateDialogDto (13-107)
src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241002134750_AddSystemLabel.cs (1)
  • AddSystemLabel (11-159)
src/Digdir.Domain.Dialogporten.Domain/DialogEndUserContexts/Entities/SystemLabel.cs (2)
  • SystemLabel (6-32)
  • SystemLabel (21-21)
🔇 Additional comments (19)
src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs (2)

7-8: LGTM!

Good refactor to accept IEnumerable<DialogTransmission> for flexibility and use of the extracted helper method for reusability.


30-31: LGTM!

Clean helper using pattern matching with is ... or ... syntax.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs (1)

7-8: LGTM!

Good documentation of the future optimization opportunity. The TODO clearly explains the intent for follow-up work.

src/Digdir.Domain.Dialogporten.Application/ApplicationExtensions.cs (1)

76-77: LGTM!

Transient registrations follow the existing pattern. Both DialogTransmissionAppender and TransmissionHierarchyValidator depend on scoped services (IDialogDbContext, IDomainContext), which is safe when resolved within a request scope.

tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs (1)

34-34: LGTM!

Mock correctly added for the new ITransmissionHierarchyValidator dependency. Since these tests verify the authorization failure path, the validator doesn't need explicit setup.

AGENTS.md (1)

15-22: LGTM!

Clear and helpful guidance for AI agents and developers about build requirements, integration test dependencies, and spec synchronization.

src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj (1)

32-32: LGTM!

Standard addition to enable mocking frameworks (Moq/NSubstitute via Castle.DynamicProxy) to create proxies for internal types like ITransmissionHierarchyValidator and IDialogTransmissionAppender in unit tests.

src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (1)

41-47: LGTM!

Clean refactor to use the new command-based flow. The collection expression [req] is idiomatic for single-element lists.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommandValidator.cs (1)

6-18: LGTM!

Clean validator implementation that reuses the existing TransmissionDto validator via RuleForEach. The validation rules appropriately ensure required fields are present before delegating to per-item validation.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs (2)

22-29: LGTM on admin/org scoping logic.

The conditional org lookup only when non-admin is an efficient optimization. The WhereIf pattern cleanly applies the org filter conditionally.


31-37: No changes needed. The soft-delete handling is already implemented correctly in CreateTransmissionCommand.Handle() at lines 82–85, which explicitly checks if (dialog.Deleted) and returns EntityDeleted before processing. The IgnoreQueryFilters() in the data loader is intentional—it allows the command handler to make explicit domain-level decisions about deleted dialogs rather than relying solely on EF Core's query filter.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (2)

49-49: LGTM!

Clean integration of ITransmissionHierarchyValidator via constructor injection, following the established DI patterns in this handler.

Also applies to: 59-60, 69-69


119-119: LGTM!

Good placement of validation after HasUnopenedContent determination but before persisting. Centralizes transmission hierarchy validation for the create flow.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)

47-98: LGTM!

The incremental validation logic correctly builds an in-memory graph by:

  1. Adding all new transmission nodes
  2. Loading ancestor chains for parents that reference existing transmissions
  3. Loading existing children to properly validate width constraints

The approach efficiently validates hierarchy constraints without loading the full transmission graph.


100-141: LGTM!

The BFS traversal with batching (128 nodes per query) is a good approach to avoid excessive database round-trips while preventing overly large IN clauses. The deduplication via dictionary prevents redundant queries for already-loaded nodes.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (3)

57-58: LGTM!

The new dependencies IDialogTransmissionAppender and ITransmissionHierarchyValidator are properly injected and initialized, following the existing conventions in this handler.


130-137: Potential duplicate hierarchy validation.

The AppendTransmission method at line 130 internally calls _transmissionHierarchyValidator.ValidateNewTransmissionsAsync() (lines 341-344), which validates the new transmissions against the hierarchy constraints. Then lines 132-137 immediately validate the whole dialog.Transmissions collection again.

Given that ValidateNewTransmissionsAsync already loads and validates the entire combined hierarchy, this second full-aggregate validation appears redundant. In contrast, CreateTransmissionCommand only calls ValidateNewTransmissionsAsync once without a follow-up full collection validation. Consider whether the inline ValidateReferenceHierarchy call (lines 132-137) can be removed.


305-353: Unopened content flag is already updated correctly in the update flow.

The HasUnopenedContent flag is recalculated at line 182 via DialogUnopenedContent.HasUnopenedContent(dialog, serviceResourceInformation) right before SaveChangesAsync(). This comprehensive approach checks all transmissions in the dialog; a newly appended service-owner transmission with no activities will correctly result in HasUnopenedContent = true. While the pattern differs from CreateTransmissionCommand (which uses a simple conditional), the update flow's design is more robust for scenarios where dialog state changes multiple times.

Likely an incorrect or invalid review comment.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1)

74-144: LGTM!

The handler flow is well-structured:

  1. Pre-checks (entity existence, deletion, frozen state)
  2. ID normalization for UUIDv7
  3. DTO mapping with dialog association
  4. Hierarchy validation before persistence
  5. Append with conditional label/flag updates
  6. Authorization with proper error handling
  7. Final persistence with concurrency control

The pattern is consistent with UpdateDialogCommand and properly handles all result types.

@elsand elsand changed the title fix(perf): Add CrateTransmissionCommand, refactor common transmission plumbing fix(perf): Add CreateTransmissionCommand, refactor common transmission plumbing Dec 16, 2025
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
elsand and others added 2 commits December 16, 2025 11:43
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (1)

26-34: Consider using ArgumentNullException.ThrowIfNull() for more concise null guards.

The current manual null checks are correct, but the more modern pattern would be slightly more concise.

Apply this diff:

 public DialogTransmissionAppender(
     IDialogDbContext db,
     IClock clock,
     IDomainContext domainContext)
 {
-    _db = db ?? throw new ArgumentNullException(nameof(db));
-    _clock = clock ?? throw new ArgumentNullException(nameof(clock));
-    _domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
+    ArgumentNullException.ThrowIfNull(db);
+    ArgumentNullException.ThrowIfNull(clock);
+    ArgumentNullException.ThrowIfNull(domainContext);
+    _db = db;
+    _clock = clock;
+    _domainContext = domainContext;
 }
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1)

146-159: Consider extracting AddSystemLabel to a shared helper.

This method is duplicated in both CreateTransmissionCommandHandler and CreateDialogCommandHandler (per the relevant code snippets). Extracting it to a shared location would reduce duplication and improve maintainability.

For example, add a static helper class:

internal static class DialogSystemLabelHelper
{
    public static void AddSystemLabel(
        DialogEntity dialog,
        SystemLabel.Values labelToAdd,
        IUser user,
        IDomainContext domainContext)
    {
        if (!user.GetPrincipal().TryGetConsumerOrgNumber(out var organizationNumber))
        {
            domainContext.AddError(new DomainFailure(nameof(organizationNumber), "Cannot find organization number for current user."));
            return;
        }

        dialog.EndUserContext.UpdateSystemLabels(
            addLabels: [labelToAdd],
            removeLabels: [],
            $"{NorwegianOrganizationIdentifier.PrefixWithSeparator}{organizationNumber}",
            ActorType.Values.ServiceOwner);
    }
}

Then both command handlers can call DialogSystemLabelHelper.AddSystemLabel(dialog, labelToAdd, _user, _domainContext).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9507105 and e539754.

📒 Files selected for processing (3)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (5 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.cs: All code must compile with TreatWarningsAsErrors=true and pass .NET analyzers
Use file-scoped namespaces with using directives outside the namespace
Use PascalCase for classes and methods, camelCase for variables and parameters
Prefer expression bodies for single-line members and use var when the type is apparent
Enable nullable reference types and keep entities immutable; use OneOf for union returns when applicable
Prefer LINQ over manual loops for transformations, filtering, grouping, and projections
Prefer FirstOrDefault() / SingleOrDefault() with explicit null handling over index-based access
Use Select, Where, GroupBy, ToDictionary, ToLookup instead of building collections imperatively
Avoid materializing collections early with ToList or ToArray unless required for correctness or performance
Prefer immutable LINQ pipelines over mutating existing collections
Target latest language features available in the solution (.NET 9 / C# 13); prefer pattern matching, switch expressions, records, collection expressions, target-typed new(), and with expressions
Prefer pattern matching (switch expressions, property patterns) over if/else chains
Prefer switch expressions over statement-based switch
Prefer if with early returns over if/else chains
Prefer records or record structs for immutable data models
Use collection expressions ([]) instead of new List<T>() or new[] { } where applicable
Prefer target-typed new() when the type is obvious
Prefer with expressions for non-destructive mutation
Do not suppress nullable warnings (!) unless strictly justified
Prefer pattern matching (is not null) over != null for null checking
Prefer early returns over nested null checks
Use ArgumentNullException.ThrowIfNull() for public API guards

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
**/*.{cs,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4-space indentation for C# and 2-space indentation for JSON/YAML with LF line endings

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.cs: Organize code by feature folders following Clean Architecture with Domain/Application/Infrastructure/API layers
Apply CQRS, domain events, repository pattern, and use rich domain result objects
Avoid throwing exceptions for domain flow; return domain-specific result objects instead

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
🧠 Learnings (5)
📚 Learning: 2025-10-15T17:12:11.560Z
Learnt from: knuhau
Repo: Altinn/dialogporten PR: 2872
File: src/Digdir.Domain.Dialogporten.Janitor/CostManagementAggregation/MetricsAggregationOptions.cs:15-23
Timestamp: 2025-10-15T17:12:11.560Z
Learning: In the Dialogporten Janitor application (src/Digdir.Domain.Dialogporten.Janitor), prefer runtime validation over startup validation (e.g., [Required] attributes) for configuration values that are only needed by specific commands. This prevents the application from failing to start when running other commands that don't require those configuration values.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
📚 Learning: 2025-12-12T13:51:39.643Z
Learnt from: elsand
Repo: Altinn/dialogporten PR: 3132
File: src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs:40-55
Timestamp: 2025-12-12T13:51:39.643Z
Learning: In src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs, the soft-deletion status of dialogs is intentionally not checked. The EntityDeleted case in the NotificationConditionResult union is unreachable by design, as whether a dialog is soft-deleted is not relevant for determining whether a notification should be sent.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Build with `dotnet build Digdir.Domain.Dialogporten.sln`

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Run tests with `dotnet test Digdir.Domain.Dialogporten.sln` after making changes; skip integration tests in non-Docker environments with `--filter 'FullyQualifiedName!~Integration'`

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
📚 Learning: 2025-10-09T08:50:14.740Z
Learnt from: oskogstad
Repo: Altinn/dialogporten PR: 2841
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20251009083408_AddDialogSearch.cs:9-10
Timestamp: 2025-10-09T08:50:14.740Z
Learning: Files in `src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/*/**` are auto-generated Entity Framework Core migrations and should not be reviewed for formatting or coding style issues.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs
🧬 Code graph analysis (3)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (2)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)
  • ValidateWholeAggregate (12-12)
  • ValidateWholeAggregate (37-45)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs (1)
  • CreateDialogDto (14-162)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (5)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (3)
  • Task (72-137)
  • Task (139-152)
  • AddSystemLabel (171-184)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs (3)
  • Task (22-38)
  • CreateTransmissionDataLoader (11-39)
  • CreateTransmissionDataLoader (16-20)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (4)
  • Task (14-18)
  • Task (47-98)
  • Task (100-141)
  • Task (143-159)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs (1)
  • DialogEntity (18-206)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Transmissions/DialogTransmission.cs (1)
  • DialogTransmission (14-67)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (2)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs (1)
  • DialogEntity (18-206)
src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Transmissions/DialogTransmission.cs (2)
  • DialogTransmission (14-67)
  • DialogTransmissionAttachment (75-79)
🪛 GitHub Check: build / build-and-test
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs

[failure] 162-162:
Type or namespace definition, or end-of-file expected


[failure] 162-162:
Type or namespace definition, or end-of-file expected

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Dry run deploy apps / Deploy job reindex-dialogsearch-job to test
  • GitHub Check: Dry run deploy apps / Deploy job sync-subject-resource-mappings-job to test
  • GitHub Check: Dry run deploy apps / Deploy job sync-resource-policy-information-job to test
  • GitHub Check: Dry run deploy apps / Deploy job aggregate-cost-metrics-job to test
🔇 Additional comments (11)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (3)

14-14: LGTM: Dependency injection wiring follows conventions.

The new ITransmissionHierarchyValidator dependency is properly wired through the constructor following standard DI patterns, naming conventions, and null-check practices consistent with the rest of the handler.

Also applies to: 49-49, 59-60, 69-69


119-119: LGTM: Centralized validation improves maintainability.

Replacing inline validation with ITransmissionHierarchyValidator.ValidateWholeAggregate centralizes the transmission hierarchy checks, making the validation logic reusable across CreateDialogCommand, UpdateDialogCommand, and CreateTransmissionCommand as described in the PR objectives.


121-123: LGTM: Checked casts address the previous overflow concern.

The code now uses checked casts when assigning FromPartyTransmissionsCount and FromServiceOwnerTransmissionsCount, which properly addresses the overflow concern raised in the previous review. If the transmission count exceeds short.MaxValue (32,767), an OverflowException will be thrown, which is appropriate since such a scenario indicates a data integrity issue rather than normal domain flow.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/DialogTransmissionAppender.cs (4)

7-7: Consider using file-scoped namespace declaration.

Per the coding guidelines, prefer file-scoped namespaces with a semicolon instead of block-scoped namespaces.

Apply this diff:

-namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Common;
+namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Common;

Wait, looking at the code more carefully, if line 7 shows:

namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Common;

Then it's already file-scoped (ends with semicolon). But if the actual line is without semicolon, then it needs to be changed. Based on the annotations showing line 7~ followed by line 8~ with no closing brace visible before the interface definition on line 9~, I believe this is already file-scoped. Let me skip this comment.


9-18: LGTM!

The interface and result record design is clean and focused. Using IReadOnlyCollection for the parameter appropriately signals that the collection won't be modified during validation.


36-61: LGTM!

The Append method implementation is well-structured:

  • Early return for empty collections avoids unnecessary work
  • Checked arithmetic prevents silent overflow (addressing the previously raised concern)
  • Proper EF Core tracking by adding to both the navigation property and DbSet
  • Efficient attachment validation using SelectMany
  • Clear return value indicating transmission types added

63-76: LGTM!

The ValidateTimeFields method properly validates attachment expiration times using domain error reporting. The structured key path and optional ID in the error message provide good debugging context.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (4)

1-23: LGTM!

File-scoped namespace declaration follows the coding guidelines. The using alias on line 21 appropriately reuses the TransmissionDto from the Update command namespace.


25-38: LGTM!

The command and result type design follows CQRS patterns and coding guidelines. Using OneOf for the result type provides type-safe domain-specific outcomes, and the success record appropriately includes both the dialog revision and newly created transmission IDs.


52-72: Constructor follows established patterns.

The constructor properly injects all necessary dependencies. While the manual null checks are verbose, they're consistent with the codebase pattern. (See earlier comment on DialogTransmissionAppender for a more modern alternative.)


74-144: LGTM!

The Handle method implements a well-structured command flow:

  • Proper validation sequence (existence, deletion status, frozen state, hierarchy)
  • ID normalization ensures consistent UUID version 7 usage
  • Delegation to shared components (ITransmissionHierarchyValidator, IDialogTransmissionAppender) promotes reusability
  • Appropriate state updates based on transmission type (labels, HasUnopenedContent)
  • Authorization with domain context rollback on failure
  • Concurrency control via IfMatchDialogRevision

elsand and others added 2 commits December 16, 2025 13:12
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1)

146-159: Consider extracting shared AddSystemLabel logic.

This method is identical to AddSystemLabel in UpdateDialogCommand. While the duplication is small (3 lines of core logic), extracting it to a shared helper would improve maintainability if system label logic evolves.

Potential approaches:

  1. Extract to a static utility method in a shared class (e.g., DialogSystemLabelHelper.AddSystemLabel)
  2. Create a shared base class for command handlers that need this functionality
  3. Accept the duplication given the small footprint

Given the limited scope, this refactor can be deferred.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e539754 and f865735.

📒 Files selected for processing (1)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.cs: All code must compile with TreatWarningsAsErrors=true and pass .NET analyzers
Use file-scoped namespaces with using directives outside the namespace
Use PascalCase for classes and methods, camelCase for variables and parameters
Prefer expression bodies for single-line members and use var when the type is apparent
Enable nullable reference types and keep entities immutable; use OneOf for union returns when applicable
Prefer LINQ over manual loops for transformations, filtering, grouping, and projections
Prefer FirstOrDefault() / SingleOrDefault() with explicit null handling over index-based access
Use Select, Where, GroupBy, ToDictionary, ToLookup instead of building collections imperatively
Avoid materializing collections early with ToList or ToArray unless required for correctness or performance
Prefer immutable LINQ pipelines over mutating existing collections
Target latest language features available in the solution (.NET 9 / C# 13); prefer pattern matching, switch expressions, records, collection expressions, target-typed new(), and with expressions
Prefer pattern matching (switch expressions, property patterns) over if/else chains
Prefer switch expressions over statement-based switch
Prefer if with early returns over if/else chains
Prefer records or record structs for immutable data models
Use collection expressions ([]) instead of new List<T>() or new[] { } where applicable
Prefer target-typed new() when the type is obvious
Prefer with expressions for non-destructive mutation
Do not suppress nullable warnings (!) unless strictly justified
Prefer pattern matching (is not null) over != null for null checking
Prefer early returns over nested null checks
Use ArgumentNullException.ThrowIfNull() for public API guards

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
**/*.{cs,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4-space indentation for C# and 2-space indentation for JSON/YAML with LF line endings

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.cs: Organize code by feature folders following Clean Architecture with Domain/Application/Infrastructure/API layers
Apply CQRS, domain events, repository pattern, and use rich domain result objects
Avoid throwing exceptions for domain flow; return domain-specific result objects instead

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
🧠 Learnings (1)
📚 Learning: 2025-10-09T08:50:14.740Z
Learnt from: oskogstad
Repo: Altinn/dialogporten PR: 2841
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20251009083408_AddDialogSearch.cs:9-10
Timestamp: 2025-10-09T08:50:14.740Z
Learning: Files in `src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/*/**` are auto-generated Entity Framework Core migrations and should not be reviewed for formatting or coding style issues.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
🧬 Code graph analysis (1)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (6)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (3)
  • Task (72-137)
  • Task (139-152)
  • AddSystemLabel (171-184)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionDataLoader.cs (3)
  • Task (22-38)
  • CreateTransmissionDataLoader (11-39)
  • CreateTransmissionDataLoader (16-20)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (4)
  • Task (14-18)
  • Task (47-98)
  • Task (100-141)
  • Task (143-159)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (3)
  • Task (88-197)
  • Task (305-353)
  • AddSystemLabel (199-212)
src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/DialogUnopenedContent.cs (1)
  • HasUnopenedContent (11-27)
src/Digdir.Domain.Dialogporten.Domain/DialogEndUserContexts/Entities/DialogEndUserContext.cs (1)
  • UpdateSystemLabels (26-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / build-and-test
🔇 Additional comments (3)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (3)

25-38: LGTM! Clean command and result type definitions.

The command structure follows CQRS patterns correctly, uses collection expressions, and implements the appropriate interfaces for silent updates and feature metrics. The OneOf-based result type properly encapsulates all possible outcomes without throwing exceptions for domain flow control.


40-72: LGTM! Proper dependency injection with validation.

All dependencies are validated with explicit null checks. The handler correctly injects the new abstractions (IDialogTransmissionAppender, ITransmissionHierarchyValidator) introduced in this PR.


74-144: LGTM! Well-structured handler with correct performance optimization.

The implementation correctly:

  • Uses the preloaded data pattern to achieve O(1) transmission adds (key performance goal from PR objectives)
  • Follows early-return pattern for error conditions
  • Validates transmission hierarchy before appending
  • Uses the new DialogTransmissionAppender abstraction to centralize counter updates
  • Conditionally applies system labels based on transmission sender type
  • Handles authorization with proper domain context rollback on failure
  • Manages concurrency control through IfMatchDialogRevision

The flow is clean, error handling returns domain result types without exceptions, and the separation of concerns (validator, appender) improves maintainability.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (1)

49-49: Dependency injection wiring is correct.

The validator is properly registered as a readonly field, constructor parameter, and initialized with null checking. The pattern is consistent with other dependencies in this handler.

Optional: The coding guidelines suggest using ArgumentNullException.ThrowIfNull() for parameter validation. You could refactor all constructor guards to use this more concise pattern:

-_transmissionHierarchyValidator = transmissionHierarchyValidator ?? throw new ArgumentNullException(nameof(transmissionHierarchyValidator));
+ArgumentNullException.ThrowIfNull(transmissionHierarchyValidator);
+_transmissionHierarchyValidator = transmissionHierarchyValidator;

However, maintaining consistency with the existing pattern in this file is also reasonable.

Also applies to: 59-60, 69-69

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (2)

20-34: Class initialization is solid.

The constants MaxHierarchyDepth and MaxHierarchyWidth (both 20) provide clear configuration for hierarchy limits. Constructor dependencies are properly guarded.

Optional: Consider using ArgumentNullException.ThrowIfNull() for consistency with coding guidelines:

 public TransmissionHierarchyValidator(
     IDialogDbContext dbContext,
     IDomainContext domainContext)
 {
-    _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
-    _domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
+    ArgumentNullException.ThrowIfNull(dbContext);
+    ArgumentNullException.ThrowIfNull(domainContext);
+    _dbContext = dbContext;
+    _domainContext = domainContext;
 }

98-139: Efficient breadth-first hierarchy loader with appropriate batching.

The method implements a breadth-first traversal of the parent chain, using batching (up to 128 items per query) to avoid N+1 query issues. The logic correctly:

  • Skips already-loaded nodes (line 112)
  • Fetches batches via EF Core (lines 124-127)
  • Enqueues newly discovered parents for subsequent loading (line 133)

Optional: Consider extracting the magic number 128 to a named constant for clarity:

+private const int HierarchyLoadBatchSize = 128;
+
 private async Task<List<TransmissionHierarchyNode>> LoadHierarchyNodes(
     Guid dialogId,
     IReadOnlyCollection<Guid> startIds,
     CancellationToken cancellationToken)
 {
     var nodes = new Dictionary<Guid, TransmissionHierarchyNode>();
     var pending = new Queue<Guid>(startIds);

     while (pending.Count > 0)
     {
         var batch = new HashSet<Guid>();
-        while (pending.Count > 0 && batch.Count < 128)
+        while (pending.Count > 0 && batch.Count < HierarchyLoadBatchSize)
         {
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (1)

84-85: Consider modernizing null checks (optional).

While the current pattern is consistent with the existing codebase, the coding guidelines prefer ArgumentNullException.ThrowIfNull() for newer code. Since this is new code in C# 13, you could modernize:

-_dialogTransmissionAppender = dialogTransmissionAppender ?? throw new ArgumentNullException(nameof(dialogTransmissionAppender));
-_transmissionHierarchyValidator = transmissionHierarchyValidator ?? throw new ArgumentNullException(nameof(transmissionHierarchyValidator));
+ArgumentNullException.ThrowIfNull(dialogTransmissionAppender);
+ArgumentNullException.ThrowIfNull(transmissionHierarchyValidator);
+_dialogTransmissionAppender = dialogTransmissionAppender;
+_transmissionHierarchyValidator = transmissionHierarchyValidator;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f865735 and 945def7.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (5 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (7 hunks)
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.cs: All code must compile with TreatWarningsAsErrors=true and pass .NET analyzers
Use file-scoped namespaces with using directives outside the namespace
Use PascalCase for classes and methods, camelCase for variables and parameters
Prefer expression bodies for single-line members and use var when the type is apparent
Enable nullable reference types and keep entities immutable; use OneOf for union returns when applicable
Prefer LINQ over manual loops for transformations, filtering, grouping, and projections
Prefer FirstOrDefault() / SingleOrDefault() with explicit null handling over index-based access
Use Select, Where, GroupBy, ToDictionary, ToLookup instead of building collections imperatively
Avoid materializing collections early with ToList or ToArray unless required for correctness or performance
Prefer immutable LINQ pipelines over mutating existing collections
Target latest language features available in the solution (.NET 9 / C# 13); prefer pattern matching, switch expressions, records, collection expressions, target-typed new(), and with expressions
Prefer pattern matching (switch expressions, property patterns) over if/else chains
Prefer switch expressions over statement-based switch
Prefer if with early returns over if/else chains
Prefer records or record structs for immutable data models
Use collection expressions ([]) instead of new List<T>() or new[] { } where applicable
Prefer target-typed new() when the type is obvious
Prefer with expressions for non-destructive mutation
Do not suppress nullable warnings (!) unless strictly justified
Prefer pattern matching (is not null) over != null for null checking
Prefer early returns over nested null checks
Use ArgumentNullException.ThrowIfNull() for public API guards

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
**/*.{cs,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4-space indentation for C# and 2-space indentation for JSON/YAML with LF line endings

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.cs: Organize code by feature folders following Clean Architecture with Domain/Application/Infrastructure/API layers
Apply CQRS, domain events, repository pattern, and use rich domain result objects
Avoid throwing exceptions for domain flow; return domain-specific result objects instead

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs
🧠 Learnings (4)
📚 Learning: 2025-10-15T17:12:11.560Z
Learnt from: knuhau
Repo: Altinn/dialogporten PR: 2872
File: src/Digdir.Domain.Dialogporten.Janitor/CostManagementAggregation/MetricsAggregationOptions.cs:15-23
Timestamp: 2025-10-15T17:12:11.560Z
Learning: In the Dialogporten Janitor application (src/Digdir.Domain.Dialogporten.Janitor), prefer runtime validation over startup validation (e.g., [Required] attributes) for configuration values that are only needed by specific commands. This prevents the application from failing to start when running other commands that don't require those configuration values.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-12T13:51:39.643Z
Learnt from: elsand
Repo: Altinn/dialogporten PR: 3132
File: src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs:40-55
Timestamp: 2025-12-12T13:51:39.643Z
Learning: In src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionQuery.cs, the soft-deletion status of dialogs is intentionally not checked. The EntityDeleted case in the NotificationConditionResult union is unreachable by design, as whether a dialog is soft-deleted is not relevant for determining whether a notification should be sent.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Build with `dotnet build Digdir.Domain.Dialogporten.sln`

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Run tests with `dotnet test Digdir.Domain.Dialogporten.sln` after making changes; skip integration tests in non-Docker environments with `--filter 'FullyQualifiedName!~Integration'`

Applied to files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
🔇 Additional comments (16)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateTransmissionCommand.cs (5)

25-33: LGTM! Clean command structure.

The command follows CQRS patterns correctly, uses collection expressions for initialization, and implements the required interfaces with appropriate explicit interface member for DialogId.


35-38: LGTM! Proper use of OneOf for result types.

The result type correctly uses OneOf for union returns with comprehensive error cases, and the success record is appropriately immutable.


40-72: LGTM! Well-structured handler with proper dependency injection.

The handler is correctly scoped as internal sealed, injects all required dependencies, and validates them appropriately. The dependency list reflects the comprehensive workflow involving data loading, validation, appending, authorization, and persistence.


74-143: LGTM! Excellent implementation of the command handler workflow.

The Handle method demonstrates a clean, well-structured flow:

  • Early returns for validation failures (EntityNotFound, EntityDeleted, Frozen)
  • Proper use of preloaded data for performance (O(1) transmission adds)
  • Async hierarchy validation before appending
  • Correct delegation to DialogTransmissionAppender for counter updates and attachment validation
  • Appropriate system label management based on transmission type
  • Authorization with rollback via _domainContext.Pop() on Forbidden
  • Concurrency-safe persistence with revision checking

The implementation adheres to CQRS, domain-driven design, and the coding guidelines—returning domain result objects instead of throwing exceptions for flow control.


145-158: LGTM! Proper domain error handling.

The helper method correctly handles the missing organization number case by adding a domain error and returning early, rather than throwing an exception. This aligns with the domain-driven design principle of using domain result objects for flow control.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs (3)

14-14: LGTM! Using directive properly supports the new validator dependency.

The addition of the Dialogs.Common namespace is necessary for the ITransmissionHierarchyValidator interface used below.


119-119: Excellent refactor to use centralized validation.

Delegating transmission hierarchy validation to ITransmissionHierarchyValidator improves separation of concerns and enables reuse across create/update/transmission commands. The placement after UUID assignment (lines 113-116) is correct.


121-123: Checked cast properly addresses overflow protection.

The use of checked casts ensures that any overflow when converting counts to short will throw an exception rather than silently wrapping. This addresses the overflow concern raised in previous review feedback.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (5)

10-18: Well-defined interface with clear responsibilities.

The interface cleanly separates whole-aggregate validation (for create/update with full graph) from incremental validation (for adding transmissions to existing dialogs). Internal visibility is appropriate for this application-layer component.


36-44: Straightforward delegation to the hierarchy validation extension.

The method correctly delegates to ValidateReferenceHierarchy, passing the transmission collection with appropriate selectors for ID and parent reference. Errors are properly routed to the domain context.


46-96: Well-structured incremental validation with efficient hierarchy loading.

The method correctly:

  1. Builds an in-memory representation of new transmissions
  2. Identifies which parents already exist in the database
  3. Loads the necessary existing hierarchy nodes (parents and their children) in batches
  4. Validates the combined hierarchy against depth/width constraints

The use of HashSet for ID lookups and TryAdd for deduplication ensures efficiency. The separation of new vs. existing nodes prevents unnecessary database queries.


141-157: Clean query for loading existing child transmissions.

The method efficiently loads child nodes for the given parent IDs, with an early return optimization for empty input. The query correctly filters by dialog ID and checks for matching parent references.


159-159: Excellent use of a record for the internal hierarchy node representation.

The TransmissionHierarchyNode record provides a lightweight, immutable structure for building the in-memory hierarchy during validation. The sealed modifier and private scope are appropriate for an internal implementation detail.

src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs (3)

14-14: LGTM! Dependency injection properly implemented.

The new services (IDialogTransmissionAppender and ITransmissionHierarchyValidator) are correctly injected following the existing constructor pattern, with appropriate null checks and field assignments.

Also applies to: 57-58, 70-72, 84-85


130-130: LGTM! Async signature change correctly implemented.

The method signature change to async Task and the corresponding await at the call site are properly implemented to support the new asynchronous validation flow.

Also applies to: 305-305


341-351: Verify potential redundancy with existing validation.

The new validation and append flow follows the existing pattern of adding domain errors to the context. However, the relationship between the new ITransmissionHierarchyValidator.ValidateNewTransmissionsAsync() call (lines 341-344) and the legacy ValidateReferenceHierarchy() call (lines 132-137) needs clarification:

  • Per the PR summary, ITransmissionHierarchyValidator encapsulates "both full-aggregate and incremental transmission-hierarchy checks"
  • The new validator validates new transmissions incrementally
  • The legacy validator runs on the full dialog.Transmissions collection after appending

Confirm whether ValidateReferenceHierarchy is still necessary or fully replaced by the new validator to avoid redundant checks.

@@ -0,0 +1,19 @@
using FluentValidation;
using TransmissionDto = Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.TransmissionDto;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nå tar denne nye vertikalen avhengighet på en annen som skal være isolert.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tror strengt tatt ikke du trenger en dataloader her ettersom det kun er et sted som bryr seg om disse dataene. Så dette kan legges rett inn i CreateTransmissionCommand.Handle


namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update;

// TODO: The hierarchy validator can now fetch only the referenced transmissions,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er dette noe som skal gjøres senere?

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (1)

27-33: Consider using ArgumentNullException.ThrowIfNull() for consistency with modern C# patterns.

While the current null checks are correct, C# 13 provides the more concise ArgumentNullException.ThrowIfNull() helper. Although the coding guideline specifically mentions "public API guards," adopting this pattern for internal APIs improves consistency across the codebase.

🔎 Proposed refactor
     public TransmissionHierarchyValidator(
         ITransmissionHierarchyRepository hierarchyRepository,
         IDomainContext domainContext)
     {
-        _hierarchyRepository = hierarchyRepository ?? throw new ArgumentNullException(nameof(hierarchyRepository));
-        _domainContext = domainContext ?? throw new ArgumentNullException(nameof(domainContext));
+        ArgumentNullException.ThrowIfNull(hierarchyRepository);
+        ArgumentNullException.ThrowIfNull(domainContext);
+        _hierarchyRepository = hierarchyRepository;
+        _domainContext = domainContext;
     }
src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (2)

18-21: Prefer ArgumentNullException.ThrowIfNull() per coding guidelines.

The coding guidelines require using ArgumentNullException.ThrowIfNull() for public API guards. This is terser and aligns with modern .NET conventions.

🔎 Proposed refactor
 public CreateDialogTransmissionEndpoint(ISender sender)
 {
-    _sender = sender ?? throw new ArgumentNullException(nameof(sender));
+    ArgumentNullException.ThrowIfNull(sender);
+    _sender = sender;
 }

54-54: Consider Single() for semantic clarity.

Since the command creates exactly one transmission (line 45), using Single() instead of First() would better express the expectation of exactly one ID in the result.

🔎 Proposed refactor
-var transmissionId = success.TransmissionIds.First();
+var transmissionId = success.TransmissionIds.Single();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b968d38 and 5ce5235.

📒 Files selected for processing (6)
  • src/Digdir.Domain.Dialogporten.Application/Externals/ITransmissionHierarchyRepository.cs
  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Repositories/TransmissionHierarchyRepository.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Repositories/TransmissionHierarchyRepository.cs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.cs: All code must compile with TreatWarningsAsErrors=true and pass .NET analyzers
Use file-scoped namespaces with using directives outside the namespace
Use PascalCase for classes and methods, camelCase for variables and parameters
Prefer expression bodies for single-line members and use var when the type is apparent
Enable nullable reference types and keep entities immutable; use OneOf for union returns when applicable
Prefer LINQ over manual loops for transformations, filtering, grouping, and projections
Prefer FirstOrDefault() / SingleOrDefault() with explicit null handling over index-based access
Use Select, Where, GroupBy, ToDictionary, ToLookup instead of building collections imperatively
Avoid materializing collections early with ToList or ToArray unless required for correctness or performance
Prefer immutable LINQ pipelines over mutating existing collections
Target latest language features available in the solution (.NET 9 / C# 13); prefer pattern matching, switch expressions, records, collection expressions, target-typed new(), and with expressions
Prefer pattern matching (switch expressions, property patterns) over if/else chains
Prefer switch expressions over statement-based switch
Prefer if with early returns over if/else chains
Prefer records or record structs for immutable data models
Use collection expressions ([]) instead of new List<T>() or new[] { } where applicable
Prefer target-typed new() when the type is obvious
Prefer with expressions for non-destructive mutation
Do not suppress nullable warnings (!) unless strictly justified
Prefer pattern matching (is not null) over != null for null checking
Prefer early returns over nested null checks
Use ArgumentNullException.ThrowIfNull() for public API guards

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Externals/ITransmissionHierarchyRepository.cs
**/*.{cs,json,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4-space indentation for C# and 2-space indentation for JSON/YAML with LF line endings

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Externals/ITransmissionHierarchyRepository.cs
src/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.cs: Organize code by feature folders following Clean Architecture with Domain/Application/Infrastructure/API layers
Apply CQRS, domain events, repository pattern, and use rich domain result objects
Avoid throwing exceptions for domain flow; return domain-specific result objects instead

Files:

  • src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs
  • src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs
  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
  • src/Digdir.Domain.Dialogporten.Application/Externals/ITransmissionHierarchyRepository.cs
🧠 Learnings (3)
📚 Learning: 2025-10-06T08:24:02.136Z
Learnt from: MagnusSandgren
Repo: Altinn/dialogporten PR: 2818
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/FusionCache/FeatureMetricServiceResourceCache.cs:12-23
Timestamp: 2025-10-06T08:24:02.136Z
Learning: Before raising concerns about captive dependencies or DI lifetime mismatches in the Altinn/dialogporten repository, verify the actual service registration lifetime by checking the DI configuration files (e.g., InfrastructureExtensions.cs).

Applied to files:

  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
📚 Learning: 2025-10-09T08:50:14.740Z
Learnt from: oskogstad
Repo: Altinn/dialogporten PR: 2841
File: src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20251009083408_AddDialogSearch.cs:9-10
Timestamp: 2025-10-09T08:50:14.740Z
Learning: Files in `src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/*/**` are auto-generated Entity Framework Core migrations and should not be reviewed for formatting or coding style issues.

Applied to files:

  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
📚 Learning: 2025-12-15T13:34:26.796Z
Learnt from: CR
Repo: Altinn/dialogporten PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-15T13:34:26.796Z
Learning: Run tests with `dotnet test Digdir.Domain.Dialogporten.sln` after making changes; skip integration tests in non-Docker environments with `--filter 'FullyQualifiedName!~Integration'`

Applied to files:

  • src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs
🧬 Code graph analysis (1)
src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs (1)
src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Repositories/TransmissionHierarchyRepository.cs (1)
  • TransmissionHierarchyRepository (9-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Dry run deploy apps / Deploy job sync-resource-policy-information-job to test
  • GitHub Check: Dry run deploy apps / Deploy job aggregate-cost-metrics-job to test
  • GitHub Check: Dry run deploy apps / Deploy job reindex-dialogsearch-job to test
  • GitHub Check: Dry run deploy apps / Deploy job sync-subject-resource-mappings-job to test
  • GitHub Check: build / build-and-test
🔇 Additional comments (5)
src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureExtensions.cs (1)

114-114: LGTM!

The transient registration for ITransmissionHierarchyRepository is correctly placed alongside other repository registrations and uses the appropriate lifetime for a stateless repository.

src/Digdir.Domain.Dialogporten.Application/Externals/ITransmissionHierarchyRepository.cs (1)

5-13: LGTM!

The interface and record are well-designed:

  • IReadOnlyCollection promotes immutability for both input and output
  • The TransmissionHierarchyNode record uses modern positional parameters
  • Nullable ParentId appropriately models root nodes in the hierarchy
  • Async signature with CancellationToken follows best practices
src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Common/TransmissionHierarchyValidator.cs (1)

45-77: LGTM!

The validation logic is well-structured:

  • Early return for empty collections (line 50)
  • Efficient filtering to identify only existing parent IDs that need to be fetched (lines 54-60)
  • Appropriate use of collection expression [] for empty collections (line 64, C# 13)
  • Correct combination of new and existing nodes before validation (lines 66-69)
  • The Distinct().Except() chain properly eliminates duplicate fetches

The use of x!.Value on line 57 is justified by the preceding Where(x => x.HasValue) filter.

src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Commands/CreateTransmission/CreateDialogTransmissionEndpoint.cs (2)

38-65: Clean refactoring to command-based flow.

The endpoint now follows a straightforward CQRS pattern: create command, dispatch, handle result. The collection expression on line 45 is idiomatic, error handling is comprehensive via Match, and the routing logic correctly passes both the query object and transmissionId to the GET endpoint.


68-77: Request structure looks good.

The DTO reuses the Update command's TransmissionDto structure (line 10 alias), which is reasonable for this refactoring phase. The additional properties (DialogId, IfMatchDialogRevision, IsSilentUpdate) are correctly annotated with FastEndpoints attributes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants