Skip to content

Conversation

@bjorntore
Copy link
Contributor

@bjorntore bjorntore commented Oct 10, 2025

Is it OK to add this to the PdfService, or should I rather create a separate service for only SubformPdf?

Description

    <bpmn:serviceTask id="PdfSubform" name="PDF - subform">
      <bpmn:extensionElements>
        <altinn:taskExtension>
          <altinn:taskType>subform-pdf</altinn:taskType>
          <altinn:subformPdfConfig>
            <altinn:filenameTextResourceKey>dispensasjonssoeknadFileName</altinn:filenameTextResourceKey>
            <altinn:subformComponentId>subform</altinn:subformComponentId>
            <altinn:subformDatatTypeId>Dispensasjonssoeknad</altinn:subformDatatTypeId>
          </altinn:subformPdfConfig>
        </altinn:taskExtension>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1ypajov</bpmn:incoming>
      <bpmn:outgoing>Flow_0qgb61t</bpmn:outgoing>
    </bpmn:serviceTask>

Related Issue(s)

  • #{issue number}

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)
  • All tests run green

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

Summary by CodeRabbit

  • New Features

    • Generate and store PDFs for subforms as tracked data elements with configurable filenames.
    • Signing endpoints accept an optional taskId query parameter to load signing data from a different task.
  • Improvements

    • PDF generation now returns a data-element reference for easier tracking and linking.
    • Process flow updated to recognize subform PDF tasks; OpenAPI and error messages updated to document taskId usage.
  • Tests

    • Added tests for taskId overrides and subform PDF generation.

✏️ Tip: You can customize this high-level summary in your review settings.

…ult enum and use result classes instead, for expandability.
…elds from result class and instead return generic failed ProcessChangeResult.
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

♻️ Duplicate comments (4)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (2)

39-62: Add an upper bound on parallel execution.

The parallel execution path creates concurrent tasks for all subform data elements without limiting concurrency. Consider using SemaphoreSlim or Parallel.ForEachAsync with a MaxDegreeOfParallelism to prevent overwhelming the PDF microservice.

As per coding guidelines: "Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)."


10-17: Add sealed keyword to the class declaration.

Per coding guidelines, classes should be sealed unless inheritance is a valid use case. This service task class has no need for inheritance.

-internal class SubformPdfServiceTask(
+internal sealed class SubformPdfServiceTask(
     IProcessReader processReader,
     IPdfService pdfService,
     IDataClient dataClient,
     IProcessTaskCleaner processTaskCleaner,
     ILogger<SubformPdfServiceTask> logger
 ) : IServiceTask
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

484-509: Dispose CancellationTokenSource to avoid resource leak.

CancellationTokenSource implements IDisposable and should be disposed after use.

     [Fact]
     public async Task Execute_WithCancellationToken_Should_PropagateCancellation()
     {
         // Arrange
         SetupProcessReader(parallelExecution: false);
         var instance = CreateInstanceWithSubformData();
-        var cts = new CancellationTokenSource();
-        cts.Cancel(); // Already cancelled
-        var context = CreateServiceTaskContext(instance, cts.Token);
+        using var cts = new CancellationTokenSource();
+        cts.Cancel(); // Already cancelled
+        var context = CreateServiceTaskContext(instance, cts.Token);

656-692: Dispose CancellationTokenSource to avoid resource leak.

Same issue as the other test - CancellationTokenSource should be disposed.

     [Fact]
     public async Task Execute_Should_PassCancellationTokenToAllDependencies()
     {
         // Arrange
         SetupProcessReader(parallelExecution: false);
         var instance = CreateInstanceWithSubformData();
-        var cts = new CancellationTokenSource();
+        using var cts = new CancellationTokenSource();
         var context = CreateServiceTaskContext(instance, cts.Token);
🧹 Nitpick comments (4)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1)

98-104: Remove or update the misleading comment.

The comment on line 100 says "return a default valid configuration. No required config as of now" but the code immediately throws an ApplicationConfigException. The comment contradicts the actual behavior.

         if (subformPdfConfiguration == null)
         {
-            // If no PDF configuration is specified, return a default valid configuration. No required config as of now.
             throw new ApplicationConfigException(
                 "The subformPdfConfig node is missing in the subform pdf process task configuration."
             );
         }
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (3)

189-202: Update test name to reflect actual behavior.

The test name Execute_WithNoPdfConfiguration_Should_Use_DefaultConfiguration suggests a default configuration is used, but the test actually verifies that an ApplicationConfigException is thrown. Consider renaming for clarity.

 [Fact]
-public async Task Execute_WithNoPdfConfiguration_Should_Use_DefaultConfiguration()
+public async Task Execute_WithNoPdfConfiguration_Should_ThrowApplicationConfigException()
 {

513-543: Test doesn't verify execution order.

The test name and comment claim to verify that cleanup happens before PDF generation, but the assertions only verify both methods are called—not their order. Consider using MockSequence or callback counters to enforce ordering if this is an important invariant.

// Example with callback counters:
var callOrder = new List<string>();
_processTaskCleanerMock
    .Setup(x => x.RemoveAllDataElementsGeneratedFromTask(It.IsAny<Instance>(), It.IsAny<string>()))
    .Callback(() => callOrder.Add("cleanup"))
    .Returns(Task.CompletedTask);

_pdfServiceMock
    .Setup(x => x.GenerateAndStoreSubformPdf(...))
    .Callback(() => callOrder.Add("pdf"))
    .ReturnsAsync(...);

// Assert
Assert.Equal(new[] { "cleanup", "pdf", "pdf" }, callOrder);

76-161: Good test coverage for execution paths.

Tests appropriately cover parallel execution, sequential execution, and no matching data elements scenarios. The verification of call counts and parameter matching is thorough.

Note: Per coding guidelines, xUnit asserts are preferred over FluentAssertions in tests. The result.Should().BeOfType<ServiceTaskSuccessResult>() calls could be replaced with Assert.IsType<ServiceTaskSuccessResult>(result), but this is a minor stylistic preference.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5017242 and a5ba199.

📒 Files selected for processing (2)
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
🧠 Learnings (13)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Use sealed for classes unless inheritance is considered a valid use-case

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-08-27T06:58:28.126Z
Learnt from: cammiida
Repo: Altinn/app-lib-dotnet PR: 1451
File: src/Altinn.App.Api/Controllers/DataTagsController.cs:0-0
Timestamp: 2025-08-27T06:58:28.126Z
Learning: In the Altinn app-lib-dotnet codebase, validation endpoints follow a pattern where validation only runs when the ignoredValidators query parameter is provided (not null). This is a conscious design choice for consistency across validation endpoints.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-09-07T20:03:48.030Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1463
File: test/Altinn.App.SourceGenerator.Tests/DiagnosticTests.RunJsonError.verified.txt:1-21
Timestamp: 2025-09-07T20:03:48.030Z
Learning: In Altinn.App.SourceGenerator.Tests, the path C:\temp\config\applicationmetadata.json is a hardcoded test fixture path required by Roslyn and is used consistently across all operating systems, not an actual filesystem path that varies by OS.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
🧬 Code graph analysis (1)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (3)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (5)
  • Task (17-17)
  • Task (29-35)
  • Task (48-55)
  • Task (63-63)
  • Task (76-76)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (2)
  • ValidAltinnSubformPdfConfiguration (38-58)
  • AltinnSubformPdfConfiguration (10-67)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/IServiceTask.cs (1)
  • ServiceTaskSuccessResult (42-42)
⏰ 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: Static code analysis
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (3)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1)

109-124: LGTM!

The metadata helper correctly sets the subform component ID and data element ID, then persists via dataClient.Update with proper cancellation token propagation.

test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

16-74: LGTM!

Test setup is well-structured with clearly defined mocks and a reusable _serviceTask instance. The mock configurations for GenerateAndStoreSubformPdf and dataClient.Update properly capture and return relevant data for verification.


694-792: LGTM!

Helper methods are well-organized, reducing duplication across tests. The parameterized CreateInstanceWithManySubformData helper is a good pattern for testing edge cases with varying data sizes.

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

♻️ Duplicate comments (4)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

513-538: Dispose the CancellationTokenSource to prevent resource leaks.

The CancellationTokenSource at line 519 is not disposed. While this may not cause issues in short-lived test runs, it's good practice to dispose it.

Apply this diff:

     [Fact]
     public async Task Execute_WithCancellationToken_Should_PropagateCancellation()
     {
         // Arrange
         SetupProcessReader(degreeOfParallelism: 1);
         var instance = CreateInstanceWithSubformData();
-        var cts = new CancellationTokenSource();
-        cts.Cancel(); // Already cancelled
-        var context = CreateServiceTaskContext(instance, cts.Token);
+        using var cts = new CancellationTokenSource();
+        cts.Cancel(); // Already cancelled
+        var context = CreateServiceTaskContext(instance, cts.Token);

685-721: Dispose the CancellationTokenSource to prevent resource leaks.

The CancellationTokenSource at line 691 is not disposed.

Apply this diff:

     [Fact]
     public async Task Execute_Should_PassCancellationTokenToAllDependencies()
     {
         // Arrange
         SetupProcessReader(degreeOfParallelism: 1);
         var instance = CreateInstanceWithSubformData();
-        var cts = new CancellationTokenSource();
+        using var cts = new CancellationTokenSource();
         var context = CreateServiceTaskContext(instance, cts.Token);
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (2)

7-10: Fix the XML documentation comment.

The documentation incorrectly states "Configuration properties for payments" but this class is for subform PDF configuration.

Apply this diff:

 /// <summary>
-/// Configuration properties for payments in a process task
+/// Configuration properties for subform PDF generation in a process task
 /// </summary>

39-66: Accumulate all validation errors before throwing.

The current logic throws immediately after the first validation failure. If both SubformComponentId and SubformDataTypeId are invalid, only the first error is reported. This forces developers to fix errors one at a time.

Apply this diff to accumulate all errors before throwing:

     internal ValidAltinnSubformPdfConfiguration Validate()
     {
         List<string>? errorMessages = null;

         string? normalizedFilename = string.IsNullOrWhiteSpace(FilenameTextResourceKey)
             ? null
             : FilenameTextResourceKey.Trim();

-        if (SubformComponentId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformComponentId)} is missing."))
-            ThrowApplicationConfigException(errorMessages);
-
-        if (SubformDataTypeId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformDataTypeId)} is missing."))
-            ThrowApplicationConfigException(errorMessages);
+        SubformComponentId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformComponentId)} is missing.");
+        SubformDataTypeId.IsNullOrWhitespace(ref errorMessages, $"{nameof(SubformDataTypeId)} is missing.");

         if (DegreeOfParallelism < 1)
         {
             errorMessages ??= [];
             errorMessages.Add($"{nameof(DegreeOfParallelism)} must be at least 1.");
-            ThrowApplicationConfigException(errorMessages);
         }

+        if (errorMessages is not null)
+            ThrowApplicationConfigException(errorMessages);
+
         return new ValidAltinnSubformPdfConfiguration(
             normalizedFilename,
             SubformComponentId,
             SubformDataTypeId,
             DegreeOfParallelism
         );
     }
🧹 Nitpick comments (2)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1)

87-88: Consider using xUnit asserts for consistency.

The tests use a mix of FluentAssertions (.Should().BeOfType<>()) and xUnit asserts (Assert.ThrowsAsync). Per coding guidelines, prefer xUnit asserts in tests.

Example replacement:

-        result.Should().BeOfType<ServiceTaskSuccessResult>();
+        Assert.IsType<ServiceTaskSuccessResult>(result);

Also applies to: 117-117, 146-146, 643-643, 670-670

src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1)

116-120: Simplify collection initialization.

Per static analysis hint, the collection initialization can be simplified using a collection expression.

Apply this diff:

-        pdfDataElement.Metadata = new List<KeyValueEntry>
-        {
-            new() { Key = "subformComponentId", Value = subformComponentId },
-            new() { Key = "subformDataElementId", Value = subformDataElementId },
-        };
+        pdfDataElement.Metadata =
+        [
+            new() { Key = "subformComponentId", Value = subformComponentId },
+            new() { Key = "subformDataElementId", Value = subformDataElementId },
+        ];
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5ba199 and 137a688.

📒 Files selected for processing (4)
  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1 hunks)
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1 hunks)
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
🧠 Learnings (11)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-08-27T06:58:28.126Z
Learnt from: cammiida
Repo: Altinn/app-lib-dotnet PR: 1451
File: src/Altinn.App.Api/Controllers/DataTagsController.cs:0-0
Timestamp: 2025-08-27T06:58:28.126Z
Learning: In the Altinn app-lib-dotnet codebase, validation endpoints follow a pattern where validation only runs when the ignoredValidators query parameter is provided (not null). This is a conscious design choice for consistency across validation endpoints.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Use sealed for classes unless inheritance is considered a valid use-case

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
🧬 Code graph analysis (2)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (3)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (4)
  • Task (17-17)
  • Task (29-35)
  • Task (48-55)
  • Task (63-63)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnTaskExtension.cs (1)
  • AltinnTaskExtension (9-82)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)
  • AltinnSubformPdfConfiguration (10-75)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnExtensionConfigValidationExtensions.cs (1)
  • IsNullOrWhitespace (7-21)
🪛 GitHub Check: SonarCloud Code Analysis
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs

[warning] 116-116: Collection initialization can be simplified

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZrKEkZcgOZucZGNH4ZR&open=AZrKEkZcgOZucZGNH4ZR&pullRequest=1512

🔇 Additional comments (9)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (3)

16-74: Well-structured test setup with comprehensive mocking.

The test class setup properly mocks all dependencies and configures sensible default behaviors. The constructor-based initialization pattern keeps tests clean and maintainable.


76-103: LGTM - Parallel execution test.

This test correctly verifies that PDF generation is called for all subform data elements when parallel execution is configured.


723-821: Well-designed helper methods for test data creation.

The helper methods provide clean, reusable test data creation with clear naming conventions (CreateInstanceWithSubformData, CreateInstanceWithoutSubformData, etc.). The parameterized CreateInstanceWithManySubformData(int count) is particularly useful for edge case testing.

src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (3)

10-18: LGTM - Class declaration follows coding guidelines.

The class is properly declared as internal sealed and uses primary constructor syntax. The Type property clearly identifies this service task.


39-85: Well-implemented bounded parallelism pattern.

The semaphore-based approach correctly limits concurrent PDF generation to degreeOfParallelism. The using statement ensures proper disposal, and the try/finally ensures semaphore release even on failure.


92-106: Configuration validation approach is correct.

The method appropriately throws ApplicationConfigException when configuration is missing, providing clear guidance to developers. The delegation to Validate() keeps validation logic centralized in the configuration class.

src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)

77-82: LGTM - Clean validated configuration record.

The ValidAltinnSubformPdfConfiguration readonly record struct provides a clean, immutable representation of validated configuration with non-nullable required fields.

test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (2)

3265-3271: Public API change acknowledged: confirm migration and DI stability

  • GenerateAndStorePdf now returns DataElement; new overloads added; PdfService ctor extended/reordered. Please:
    • Confirm all call sites and implementations updated.
    • Add release notes/upgrade guide highlighting the return-type change and new overloads.
    • Verify DI registrations still resolve unambiguously (constructor injection by type should be unaffected).

As per project learnings about accepting source-only compatibility with documented changes.

Also applies to: 3287-3294


3433-3444: New subform PDF configuration: validate defaults and docs

  • Ensure DegreeOfParallelism has sane defaults and input validation.
  • Confirm XML schema/docs for subformPdfConfig are updated and examples provided.
  • Verify end-to-end tests cover SubformPdfServiceTask happy-path and error cases.

Based on learnings, proposing follow-up docs/tests rather than code changes in snapshot.

Also applies to: 3459-3461

@bjorntore bjorntore changed the title WIP: Feat/subform PDF service task Subform PDF service task Dec 9, 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: 0

🧹 Nitpick comments (3)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (3)

7-14: DI of InstanceDataUnitOfWorkInitializer via IServiceProvider

Resolving InstanceDataUnitOfWorkInitializer lazily through IServiceProvider is a reasonable way to keep the dependency optional and maintain backwards compatibility. Two things to double‑check:

  • Ensure the lifetime of PdfService is not Singleton if InstanceDataUnitOfWorkInitializer is registered as Scoped/Transient, to avoid capturing a scoped service in a singleton.
  • Confirm InstanceDataUnitOfWorkInitializer (and any cached state it returns) is safe to use from multiple concurrent calls to GetFileName (e.g., when SubformPdfServiceTask runs in parallel).

If both are satisfied, the pattern is fine; otherwise it would be cleaner to inject InstanceDataUnitOfWorkInitializer? directly instead of going via IServiceProvider.

Also applies to: 33-63


177-205: Subform URL construction in BuildUri

The extended GeneratePdfContent/BuildUri pipeline for subform PDFs is generally sound: you only inject the /currentTaskId/subform/{subformComponentId}/{subformDataElementId} segment when both subform identifiers are present, and you preserve existing query handling and the lang parameter.

Two small points:

  1. The branch that inserts before ?pdf=1 currently builds:
    url = $"{beforePdf}/{subformComponentId}/{subformDataElementId}/{afterPdf}";
    Since afterPdf starts with ?pdf=1..., this produces a URL with an extra / immediately before the query (.../{subformDataElementId}/?pdf=1...). That’s usually tolerated by routing, but slightly odd. You can tighten this up by dropping the extra slash:
  •        string beforePdf = $"{url[..pdfIndex]}/{currentTaskId}/subform";
    
  •        string afterPdf = url[pdfIndex..];
    
  •        url = $"{beforePdf}/{subformComponentId}/{subformDataElementId}/{afterPdf}";
    
  •        string beforePdf = $"{url[..pdfIndex]}/{currentTaskId}/subform";
    
  •        string afterPdf = url[pdfIndex..]; // starts with '?pdf=1...'
    
  •        url = $"{beforePdf}/{subformComponentId}/{subformDataElementId}{afterPdf}";
    
  1. Because currentTaskId is supplied separately, behavior relies on instance.Process?.CurrentTask?.ElementId matching the logical task you want to encode in the URL. If you ever call this while CurrentTask has advanced or is null, you’ll get an empty or stale segment. If that’s a concern, consider threading the intended task id down into GeneratePdfContent and into BuildUri instead of re‑reading it from the instance.

Also applies to: 224-252


293-351: Filename generation with variable substitution and escaping

The new GetFileName logic looks good:

  • When _instanceDataUnitOfWorkInitializer and a custom text key are available, you use LayoutEvaluatorState + ComponentContext + DataElementIdentifier to enable variable substitution.
  • You fall back to simple translation (with "backend.pdf_default_file_name" as default key) and finally a hardcoded "Altinn PDF.pdf" if the translation is missing.
  • Returning Uri.EscapeDataString(fileName.AsFileName(false)) and then enforcing a .pdf suffix fixes the previously reported bug and ensures a safe, escaped filename.

Two minor suggestions:

  • The InvalidOperationException("LayoutEvaluatorState should not be null.") is a bit opaque in production. Including at least the instance.Id and perhaps customFileNameTextResourceKey (and/or current task id) would make misconfiguration much easier to diagnose.
  • If InstanceDataUnitOfWorkInitializer.Init ever becomes more expensive or cancellable, consider threading a CancellationToken into GetFileName so the service task can be cancelled cleanly during filename evaluation.

Functionally, though, this implementation is solid.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48b9cdf and 51e9c89.

📒 Files selected for processing (1)
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs (13 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
🧠 Learnings (7)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
🧬 Code graph analysis (1)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (1)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (4)
  • DataElement (227-238)
  • InstanceDataUnitOfWork (25-729)
  • InstanceDataUnitOfWork (64-97)
  • LayoutEvaluatorState (179-206)
⏰ 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: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Static code analysis
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (2)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (2)

67-95: Public API shape and internal refactor around GenerateAndStorePdf

The split into:

  • Task GenerateAndStorePdf(Instance, string taskId, CancellationToken) (fire‑and‑forget wrapper), and
  • Task<DataElement> GenerateAndStorePdf(Instance, string taskId, string? customFileNameTextResourceKey, List<string>? autoGeneratePdfForTaskIds = null, CancellationToken ct = default)

with a shared GenerateAndStorePdfInternal returning DataElement looks consistent and improves reuse.

However, changing the return type of the overload that now returns Task<DataElement> is a source‑level breaking change for any consumers that previously called it and ignored the result only implicitly. Given this sits on a public abstraction, please:

  • Verify all usages in this repo and any known downstreams compile cleanly after the change.
  • Mention the behavioral/API change in release notes/changelog, in line with the project’s stance on source‑only compatibility for NuGet consumers. Based on learnings, this is expected for public API shifts.

The internal flow (language resolution, GeneratePdfContent, InsertBinaryData, and returning the DataElement) looks correct.

Also applies to: 128-135, 137-175


96-115: New GenerateAndStoreSubformPdf overload

The dedicated GenerateAndStoreSubformPdf that threads subformComponentId and subformDataElementId into GenerateAndStorePdfInternal is straightforward and aligns with how SubformPdfServiceTask uses it (per the provided snippet). Passing null for autoGeneratePdfForTaskIds here is also appropriate.

No issues from a correctness standpoint.

Copy link
Contributor

@danielskovli danielskovli left a comment

Choose a reason for hiding this comment

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

💪

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

♻️ Duplicate comments (2)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

460-462: Dispose the CancellationTokenSource to prevent resource leaks.

The CancellationTokenSource created here is never disposed. Wrap it in a using statement.

-        var cts = new CancellationTokenSource();
-        cts.Cancel(); // Already cancelled
-        var context = CreateServiceTaskContext(instance, cts.Token);
+        using var cts = new CancellationTokenSource();
+        cts.Cancel(); // Already cancelled
+        var context = CreateServiceTaskContext(instance, cts.Token);

As per coding guidelines, remember to dispose IDisposable/IAsyncDisposable instances.


632-633: Dispose the CancellationTokenSource to prevent resource leaks.

Same issue as above - the CancellationTokenSource is not disposed.

-        var cts = new CancellationTokenSource();
-        var context = CreateServiceTaskContext(instance, cts.Token);
+        using var cts = new CancellationTokenSource();
+        var context = CreateServiceTaskContext(instance, cts.Token);
🧹 Nitpick comments (2)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)

10-10: Seal the class per coding guidelines.

Per the coding guidelines, classes should be sealed unless inheritance is a valid use case. Since this is a configuration class for XML deserialization with no apparent inheritance scenario, it should be sealed.

Apply this diff:

-public class AltinnSubformPdfConfiguration
+public sealed class AltinnSubformPdfConfiguration
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1)

10-10: Consider using xUnit asserts instead of FluentAssertions.

The coding guidelines prefer xUnit asserts over FluentAssertions in tests. The file already uses Assert.ThrowsAsync in several places but uses FluentAssertions for type assertions (e.g., lines 88, 117, 584, 611).

Example replacement:

-        result.Should().BeOfType<ServiceTaskSuccessResult>();
+        Assert.IsType<ServiceTaskSuccessResult>(result);

As per coding guidelines, prefer xUnit asserts over FluentAssertions in tests.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51e9c89 and 3741d33.

📒 Files selected for processing (5)
  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (1 hunks)
  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1 hunks)
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1 hunks)
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-08-27T06:58:28.126Z
Learnt from: cammiida
Repo: Altinn/app-lib-dotnet PR: 1451
File: src/Altinn.App.Api/Controllers/DataTagsController.cs:0-0
Timestamp: 2025-08-27T06:58:28.126Z
Learning: In the Altinn app-lib-dotnet codebase, validation endpoints follow a pattern where validation only runs when the ignoredValidators query parameter is provided (not null). This is a conscious design choice for consistency across validation endpoints.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Use sealed for classes unless inheritance is considered a valid use-case

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
🧬 Code graph analysis (4)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (2)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (1)
  • ValidAltinnSubformPdfConfiguration (76-90)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnExtensionConfigValidationExtensions.cs (1)
  • IsNullOrWhitespace (7-21)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (2)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (2)
  • Task (20-74)
  • Task (92-107)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/PdfServiceTask.cs (1)
  • Task (34-56)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (3)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (3)
  • SubformPdfServiceTask (10-108)
  • Task (20-74)
  • Task (92-107)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (5)
  • Task (17-17)
  • Task (29-35)
  • Task (48-55)
  • Task (63-63)
  • Task (76-76)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)
  • AltinnSubformPdfConfiguration (10-54)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (4)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (5)
  • Task (17-17)
  • Task (29-35)
  • Task (48-55)
  • Task (63-63)
  • Task (76-76)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (11)
  • Task (67-72)
  • Task (75-94)
  • Task (97-115)
  • Task (118-129)
  • Task (132-135)
  • Task (137-175)
  • Task (177-222)
  • Task (293-351)
  • Task (353-361)
  • Task (363-402)
  • List (404-419)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (2)
  • ValidAltinnSubformPdfConfiguration (30-45)
  • AltinnSubformPdfConfiguration (10-54)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnTaskExtension.cs (1)
  • AltinnTaskExtension (9-82)
⏰ 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: Analyze (csharp)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
🔇 Additional comments (7)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (1)

30-60: LGTM!

The validation logic correctly validates required fields and returns a strongly-typed record struct with non-nullable validated values. The optional FilenameTextResourceKey is appropriately normalized and remains nullable in the result.

test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (1)

16-74: LGTM! Well-structured test class with comprehensive setup.

The test class is well-organized with:

  • Clear mock setup in the constructor
  • Appropriate constants for test data
  • Good separation of concerns between test setup and assertions
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (2)

28-35: LGTM! Return type enhancement with backward-compatible default.

The change from Task to Task<DataElement> enables callers to use the returned data element (as demonstrated in SubformPdfServiceTask). The default implementation throwing NotImplementedException ensures existing implementations that don't override this method will fail fast rather than silently.


37-55: LGTM! Internal method addition is well-documented.

The new GenerateAndStoreSubformPdf method:

  • Uses appropriate internal visibility for internal-only consumption
  • Has complete XML documentation for all parameters
  • Follows the established pattern with a NotImplementedException default
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (3)

10-18: LGTM! Class declaration follows all coding guidelines.

The class correctly uses:

  • internal accessibility
  • sealed modifier
  • Primary constructor for dependency injection
  • Descriptive type identifier

38-69: Sequential PDF generation is acceptable for this use case.

While the coding guidelines generally recommend avoiding await in loops, the sequential approach here is reasonable because:

  1. Each PDF generation may modify shared state (metadata updates)
  2. Simpler error handling and debugging
  3. Previous parallel implementation was removed (per past reviews)

If performance becomes a concern with many subform elements, consider adding bounded parallelism in a follow-up.


92-107: LGTM! Metadata update method is clean and correct.

The method properly:

  • Sets the required metadata keys
  • Passes the cancellation token to the async operation
  • Uses the injected dataClient dependency

@bjorntore
Copy link
Contributor Author

/publish

@github-actions
Copy link

github-actions bot commented Dec 15, 2025

PR release:

⚙️ Building...
✅ Done!

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

Caution

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

⚠️ Outside diff range comments (1)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (1)

224-273: Consider adding input validation for URL construction edge cases.

The string manipulation logic to inject subform segments (lines 238-252) has potential fragility concerns:

  1. URL construction assumes specific structure: The code searches for "?pdf=1" as a query string marker, which is brittle if URL format changes.
  2. Empty currentTaskId with subform parameters: While theoretically possible (line 197 returns string.Empty), in practice the calling code structure mitigates this—SubformPdfServiceTask requires a valid CurrentTask, and subform parameters are only passed from that context.
  3. No explicit validation: The code checks that subform parameters are non-empty but doesn't validate currentTaskId when subform insertion is needed.

Recommended improvements:

  • Add comments documenting the expected URL format and why "?pdf=1" is the insertion marker
  • Consider adding a validation check that currentTaskId is not empty if subform parameters are provided, or document why this invariant is guaranteed
  • Add unit tests directly for BuildUri with various parameter combinations, including edge cases
🤖 Fix all issues with AI Agents
In @src/Altinn.App.Core/Internal/Pdf/PdfService.cs:
- Around line 196-204: GenerateAndStorePdfInternal currently accepts
subformComponentId/subformDataElementId without ensuring instance.Process (and
its CurrentTask) is present, which can lead to empty currentTaskId being passed
into BuildUri; add a guard clause at the start of GenerateAndStorePdfInternal
that if either subformComponentId or subformDataElementId is non-empty, validate
instance.Process and instance.Process.CurrentTask are non-null and throw/return
a clear error (or ArgumentException/InvalidOperationException) if they are
missing; update the method to perform this check before calling BuildUri so
BuildUri always receives a valid currentTaskId when subform params are provided.
🧹 Nitpick comments (2)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (2)

33-33: Verify: Service Locator pattern for optional dependency.

Resolving InstanceDataUnitOfWorkInitializer via IServiceProvider?.GetService<>() couples this service to the DI container and makes the dependency graph less explicit. While this appears intentional to make the feature optional (for variable substitution in filenames), consider whether this dependency could be injected directly as an optional parameter with a default null value instead.

If this pattern is necessary for backward compatibility or gradual rollout, please confirm this is the intended approach.

Alternative: Direct optional injection
 public PdfService(
     IDataClient dataClient,
     IHttpContextAccessor httpContextAccessor,
     IPdfGeneratorClient pdfGeneratorClient,
     IOptions<PdfGeneratorSettings> pdfGeneratorSettings,
     IOptions<GeneralSettings> generalSettings,
     ILogger<PdfService> logger,
     IAuthenticationContext authenticationContext,
     ITranslationService translationService,
-    IServiceProvider? serviceProvider = null,
+    InstanceDataUnitOfWorkInitializer? instanceDataUnitOfWorkInitializer = null,
     Telemetry? telemetry = null
 )
 {
     // ...
-    _instanceDataUnitOfWorkInitializer = serviceProvider?.GetService<InstanceDataUnitOfWorkInitializer>();
+    _instanceDataUnitOfWorkInitializer = instanceDataUnitOfWorkInitializer;
     _telemetry = telemetry;
 }

Also applies to: 50-50, 62-62


293-351: LGTM! Variable substitution added with proper fallback.

The enhanced GetFileName method:

  • ✅ Supports variable substitution when InstanceDataUnitOfWorkInitializer is available
  • ✅ Correctly creates subform-specific DataElementIdentifier when needed
  • ✅ Falls back to simple translation if substitution is unavailable
  • ✅ Bug fix correctly applied: returns escapedFileName instead of unescaped fileName

Minor suggestion: The fallback behavior (lines 331-338) when _instanceDataUnitOfWorkInitializer is null but customFileNameTextResourceKey is provided means variable expressions in the custom filename won't be substituted. This is reasonable but could surprise users. Consider adding a log message or documentation noting when substitution is skipped.

Optional: Log when substitution is unavailable
 else
 {
+    if (customFileNameTextResourceKey != null && _instanceDataUnitOfWorkInitializer == null)
+    {
+        _logger.LogDebug(
+            "Variable substitution not available for custom filename '{Key}'. Using simple translation.",
+            customFileNameTextResourceKey
+        );
+    }
     // Fall back to simple translation without variable substitution
     fileName = await _translationService.TranslateTextKey(
         customFileNameTextResourceKey ?? "backend.pdf_default_file_name",
         language
     );
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5e6a5 and 7e98ca1.

📒 Files selected for processing (2)
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
🧠 Learnings (7)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
⏰ 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: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Static code analysis
🔇 Additional comments (4)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (4)

7-7: LGTM! Imports support new functionality.

The new imports are necessary for variable substitution (Expressions, Models.Expressions) and optional dependency resolution (DependencyInjection).

Also applies to: 10-10, 13-13


67-94: LGTM! Signature extended to return DataElement.

The existing void-returning overload now discards the result appropriately, and the new overload returns Task<DataElement> for callers that need the created data element. The null values for subform parameters correctly indicate this is not a subform PDF generation.


96-115: LGTM! New subform PDF generation method.

The new GenerateAndStoreSubformPdf method properly accepts subform-specific parameters and delegates to the internal implementation. The signature is clear and follows established patterns.


137-175: LGTM! Internal method updated for subform support.

The method signature correctly propagates subform context to child methods and returns the created DataElement for upstream callers.

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

🤖 Fix all issues with AI Agents
In @src/Altinn.App.Api/Controllers/SigningController.cs:
- Around line 91-95: When computing finalTaskId you currently access
instance.Process.CurrentTask.ElementId directly which throws if CurrentTask is
null; change usages to use the null-conditional operator and null-coalescing so
you read taskId ?? instance.Process.CurrentTask?.ElementId (and similarly where
you construct other taskId variables at the other occurrences), then ensure
VerifyIsSigningTask and the NotSigningTask call paths handle a null finalTaskId
(i.e., allow VerifyIsSigningTask to return false for null). Update the three
places that follow this pattern in SigningController (the initial finalTaskId
and the two other similar computations around the later checks) to use
?.ElementId and guarded null checks instead of dereferencing CurrentTask
directly.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e98ca1 and 84b8c65.

📒 Files selected for processing (1)
  • src/Altinn.App.Api/Controllers/SigningController.cs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
🧠 Learnings (10)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
🧬 Code graph analysis (1)
src/Altinn.App.Api/Controllers/SigningController.cs (6)
src/Altinn.App.Core/Internal/Linq/Extensions.cs (1)
  • IsNullOrEmpty (21-24)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • IInstanceDataAccessor (144-154)
  • IInstanceDataAccessor (159-175)
src/Altinn.App.Core/Features/IInstanceDataAccessor.cs (2)
  • IInstanceDataAccessor (52-52)
  • IInstanceDataAccessor (58-58)
src/Altinn.App.Core/Internal/Process/ProcessTasks/SigningProcessTask.cs (1)
  • AltinnSignatureConfiguration (140-154)
src/Altinn.App.Core/Internal/Process/Elements/ProcessTask.cs (1)
  • ProcessTask (9-25)
src/Altinn.App.Core/Internal/Process/Elements/ExtensionElements.cs (1)
  • ExtensionElements (9-22)
⏰ 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: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Static code analysis
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (6)
src/Altinn.App.Api/Controllers/SigningController.cs (6)

3-3: LGTM! Using statements support new functionality.

The added namespaces are necessary for IInstanceDataAccessor and ProcessTask usage in the new code.

Also applies to: 12-12


97-101: LGTM! Proper initialization of IInstanceDataAccessor.

The use of InstanceDataUnitOfWorkInitializer to initialize the data accessor with the finalTaskId is correct and follows the established pattern.


173-225: Implementation follows consistent pattern.

The changes to GetAuthorizedOrganizations mirror the approach in GetSigneesState, properly using finalTaskId and IInstanceDataAccessor. The same NullReferenceException concern at line 191 was already flagged in the previous comment.


251-292: LGTM! Efficient implementation without unnecessary data accessor initialization.

This endpoint correctly uses finalTaskId to fetch signing configuration and filter data elements. Unlike the other endpoints, it doesn't need to initialize IInstanceDataAccessor since it operates directly on the instance's data elements, which is more efficient.

The NullReferenceException concern at line 267 was already flagged in the previous comment.


295-301: LGTM! Clean implementation of task type validation.

The helper method correctly validates whether a given taskId corresponds to a signing task by inspecting the process task's extension elements.


303-314: LGTM! Error message clearly explains both usage scenarios.

The updated message properly informs users that the endpoint works either when the current task is a signing task OR when the taskId query parameter points to one.

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 (2)
test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

10-10: Consider using xUnit asserts instead of FluentAssertions.

Per coding guidelines, xUnit asserts are preferred over FluentAssertions in tests. Multiple test methods use .Should().BeOfType<>() which could be replaced with Assert.IsType<>().

🔎 Example replacement
-        result.Should().BeOfType<ServiceTaskSuccessResult>();
+        Assert.IsType<ServiceTaskSuccessResult>(result);

Also applies to: 87-87


477-506: Test verifies ordering but doesn't enforce strict sequence.

The test name Execute_Should_CleanupBeforePdfGeneration implies ordering verification, but Moq's Verify calls don't actually enforce that cleanup happens before PDF generation - they only verify both were called. Consider using MockSequence or Callback with counters to verify the actual order if ordering is a critical requirement.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84b8c65 and 3a2ba9e.

📒 Files selected for processing (5)
  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
🧠 Learnings (14)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/IPdfService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Use sealed for classes unless inheritance is considered a valid use-case

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-08-27T06:58:28.126Z
Learnt from: cammiida
Repo: Altinn/app-lib-dotnet PR: 1451
File: src/Altinn.App.Api/Controllers/DataTagsController.cs:0-0
Timestamp: 2025-08-27T06:58:28.126Z
Learning: In the Altinn app-lib-dotnet codebase, validation endpoints follow a pattern where validation only runs when the ignoredValidators query parameter is provided (not null). This is a conscious design choice for consistency across validation endpoints.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
🧬 Code graph analysis (3)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (2)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (2)
  • Task (20-73)
  • Task (90-105)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/PdfServiceTask.cs (1)
  • Task (34-56)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (2)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (5)
  • Task (17-17)
  • Task (29-35)
  • Task (47-53)
  • Task (61-61)
  • Task (74-74)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (2)
  • Task (20-73)
  • Task (90-105)
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (4)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (5)
  • Task (17-17)
  • Task (29-35)
  • Task (47-53)
  • Task (61-61)
  • Task (74-74)
src/Altinn.App.Core/Internal/Pdf/PdfService.cs (11)
  • Task (67-72)
  • Task (75-93)
  • Task (96-112)
  • Task (115-126)
  • Task (129-132)
  • Task (134-176)
  • Task (178-215)
  • Task (285-343)
  • Task (345-353)
  • Task (355-394)
  • List (396-411)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnSubformPdfConfiguration.cs (2)
  • ValidAltinnSubformPdfConfiguration (30-45)
  • AltinnSubformPdfConfiguration (10-54)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnTaskExtension.cs (1)
  • AltinnTaskExtension (9-82)
⏰ 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: Analyze (csharp)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Static code analysis
🔇 Additional comments (16)
src/Altinn.App.Core/Internal/Pdf/IPdfService.cs (2)

28-35: LGTM - Return type change and new parameters well documented.

The signature change from Task to Task<DataElement> enables callers to access the created PDF data element. The default implementation throwing NotImplementedException ensures existing implementations continue to work via the simpler overload on line 17.


47-53: Internal interface method correctly scoped.

The internal access modifier on this default interface method appropriately restricts visibility to within the assembly, preventing external consumers from calling or needing to implement it. This aligns with the previous review discussion to keep this method internal.

test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/SubformPdfServiceTaskTests.cs (2)

75-101: Good test coverage for core functionality.

The test properly verifies that GenerateAndStoreSubformPdf is called for each matching data element with the correct parameters. The verification of component ID and call count is thorough.


173-211: Good coverage of cleanup and error propagation scenarios.

Tests properly verify that:

  1. Cleanup is called with correct task ID
  2. Cleanup exceptions propagate correctly
  3. Configuration validation throws appropriate exceptions
src/Altinn.App.Core/Internal/Process/ProcessTasks/ServiceTasks/SubformPdfServiceTask.cs (4)

10-18: LGTM - Class properly marked as internal sealed.

The class follows coding guidelines with internal sealed modifier and uses primary constructor for dependency injection. The Type property correctly returns "subformPdf" matching the renamed identifier from past review discussions.


38-68: Sequential PDF generation is acceptable here.

While coding guidelines advise against awaiting async operations in a loop, the sequential approach was intentionally chosen (per past review discussion) to avoid overwhelming the PDF microservice. The code properly propagates cancellation tokens and logs progress for each data element.


75-88: Configuration validation is thorough.

The method correctly validates that SubformPdfConfiguration exists and delegates to Validate() which checks required fields (SubformComponentId, SubformDataTypeId). The error message clearly indicates the missing configuration node.


90-105: Metadata update happens after each PDF is stored.

The metadata linking the PDF to its source subform data element is correctly added. However, consider whether the metadata update failure should be handled differently than PDF generation failure, since the PDF was already stored successfully at this point.

If metadata update fails after a PDF is stored, the cleanup at the start of the next retry attempt will remove the orphaned PDF. Verify this is the intended behavior for partial failures.

src/Altinn.App.Core/Internal/Pdf/PdfService.cs (7)

50-63: Service locator pattern used for optional dependency.

Using IServiceProvider to optionally resolve InstanceDataUnitOfWorkInitializer is acceptable for features that may not always be available. The null-conditional ensures graceful fallback when the service isn't registered.


95-112: LGTM - Subform PDF generation method correctly delegates to internal implementation.

The method follows the existing pattern, passing through the SubformPdfContext to the internal implementation. No telemetry activity is started here - consider whether subform PDF generation should have its own telemetry span.

Based on learnings, additional OTel spans for service tasks were deferred. Confirm this is intentional for subform PDF generation as well.


229-244: URL construction for subform paths looks correct.

The logic properly inserts the subform path segments (/{taskId}/subform/{componentId}/{dataElementId}/) before the ?pdf=1 query parameter when present, or appends them to the end otherwise. This maintains URL structure integrity.


294-321: Variable substitution for custom filenames is well-implemented.

The code properly initializes the unit of work, creates a component context with the subform data element identifier, and uses the translation service for variable substitution. The fallback to simple translation when _instanceDataUnitOfWorkInitializer is null ensures backward compatibility.


339-342: Filename escaping and suffix logic is correct.

The code properly escapes the filename using Uri.EscapeDataString and ensures the .pdf suffix is present. The previous bug (returning unescaped filename) has been fixed.


414-419: LGTM - SubformPdfContext record is well-designed.

The public sealed record with clear XML documentation appropriately exposes the subform context for PDF generation. Using a record provides value semantics and immutability.


297-301: No action needed. The Init method is explicitly designed to accept a nullable taskId parameter (signature: string? taskId), so passing instance.Process?.CurrentTask?.ElementId is correct and safe. The contrast with SubformPdfServiceTask.Execute is not relevant—that's a different context where Process.CurrentTask is guaranteed to exist, whereas PdfService appropriately uses null-coalescing to handle cases where Process or CurrentTask may be null.

Likely an incorrect or invalid review comment.

test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (1)

3265-3299: No review on .verified Public API snapshot

This file is an auto-generated PublicApi snapshot (*.verified.txt). Per project conventions, it should reflect the C# public surface but is not manually maintained. I’m intentionally not reviewing or suggesting changes to the contents here; any updates should come from regenerating the snapshot after adjusting the real source code.

Also applies to: 3438-3447, 3462-3463

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/Altinn.App.Api/Controllers/SigningController.cs (1)

91-101: Consider extracting repeated task verification and initialization pattern.

The pattern of resolving finalTaskId, verifying it's a signing task, and initializing IInstanceDataAccessor is repeated in both GetSigneesState and GetAuthorizedOrganizations. Consider extracting this into a private helper method to reduce duplication.

🔎 Example refactoring approach
+private async Task<(string taskId, IInstanceDataAccessor dataAccessor)> ValidateAndInitializeSigningContext(
+    Instance instance,
+    string? requestedTaskId,
+    string? language)
+{
+    string? finalTaskId = requestedTaskId ?? instance.Process?.CurrentTask?.ElementId;
+    if (string.IsNullOrEmpty(finalTaskId) || !VerifyIsSigningTask(finalTaskId))
+    {
+        throw new InvalidOperationException("Not a signing task");
+    }
+    
+    IInstanceDataAccessor dataAccessor = await _instanceDataUnitOfWorkInitializer.Init(
+        instance,
+        finalTaskId,
+        language
+    );
+    
+    return (finalTaskId, dataAccessor);
+}

Then use it in the endpoint methods with appropriate error handling.

Also applies to: 191-201

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2ba9e and e7dcc88.

📒 Files selected for processing (2)
  • src/Altinn.App.Api/Controllers/SigningController.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
🧠 Learnings (10)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • src/Altinn.App.Api/Controllers/SigningController.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
🧬 Code graph analysis (1)
src/Altinn.App.Api/Controllers/SigningController.cs (3)
src/Altinn.App.Core/Internal/Process/ProcessTasks/SigningProcessTask.cs (1)
  • AltinnSignatureConfiguration (140-154)
src/Altinn.App.Core/Internal/Process/Elements/ProcessTask.cs (1)
  • ProcessTask (9-25)
src/Altinn.App.Core/Internal/Process/Elements/ExtensionElements.cs (1)
  • ExtensionElements (9-22)
⏰ 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: Run dotnet build and test (windows-latest)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Run dotnet build and test (macos-latest)
🔇 Additional comments (8)
test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (4)

3265-3270: Public API snapshot entry for new IPdfService overload

This file is a generated PublicApi snapshot. The added GenerateAndStorePdf overload returning DataElement is just being recorded here; no changes should be made directly in this file. As long as the implementation and tests reflect this signature and intended behavior, this snapshot update looks fine.

Based on learnings, this file is not used for semantic review of the API.


3287-3299: Snapshot update for PdfService constructor/methods and SubformPdfContext

These lines capture the new PdfService constructor parameter (serviceProvider), the overloads returning DataElement, and the new GenerateAndStoreSubformPdf plus SubformPdfContext type. Since this is a PublicApi snapshot, no direct code changes are expected here; just ensure the underlying implementation, tests, and release notes for public API changes are aligned with this surface.

Based on learnings, semantic review should be done on the actual source, not this snapshot.


3438-3447: Snapshot entry for AltinnSubformPdfConfiguration

The new AltinnSubformPdfConfiguration type and its XML-mapped properties are correctly reflected in the public API snapshot. No modifications should be made in this file; just verify the corresponding implementation and BPMN/XML handling behave as intended.

Based on learnings, this snapshot is only guarding the public surface.


3462-3463: Snapshot entry for AltinnTaskExtension.SubformPdfConfiguration

This property addition records the new <subformPdfConfig> extension on tasks. The snapshot change itself is expected and requires no edit; ensure the process parsing and service-task wiring for subform-pdf are covered in source and tests.

Based on learnings, review of behavior belongs in the implementation files, not here.

src/Altinn.App.Api/Controllers/SigningController.cs (4)

256-293: Verify that GetDataElements intentionally doesn't use IInstanceDataAccessor.

Unlike GetSigneesState and GetAuthorizedOrganizations, this method doesn't initialize an IInstanceDataAccessor. It only filters instance.Data based on the signing configuration. Confirm this is intentional and that the method doesn't need task-specific data access.


295-301: LGTM!

The helper method correctly validates whether a task is a signing task by checking the TaskType property. The null-conditional operators ensure safe navigation when the task or its extension elements are not found.


309-310: LGTM!

The updated error message clearly communicates that the endpoint can be called either when the current task is a signing task OR when the taskId query parameter specifies a signing task. This improves clarity for API consumers.


110-110: Pass finalTaskId to GetSigneeContexts instead of taskId.

Line 91 resolves finalTaskId from taskId ?? instance.Process?.CurrentTask?.ElementId, and line 92 validates it's non-empty and a signing task. Use finalTaskId in the call to GetSigneeContexts (line 110) to maintain consistency with how it's used elsewhere in the method and to ensure the service receives the resolved, validated task identifier rather than the potentially null user input.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

@bjorntore bjorntore merged commit 104ed75 into main Jan 6, 2026
14 checks passed
@bjorntore bjorntore deleted the feat/subform-service-task branch January 6, 2026 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches feature Label Pull requests with new features. Used when generation releasenotes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants