Skip to content

add hideAppNameInPdf functionality to footer in the pdf#1753

Merged
walldenfilippa merged 13 commits into
mainfrom
feat/18402-hide-app-name-footer-pdf
Jun 8, 2026
Merged

add hideAppNameInPdf functionality to footer in the pdf#1753
walldenfilippa merged 13 commits into
mainfrom
feat/18402-hide-app-name-footer-pdf

Conversation

@walldenfilippa

@walldenfilippa walldenfilippa commented May 15, 2026

Copy link
Copy Markdown
Contributor

Description

The hideAppNameInPdf property determines whether the app name is displayed in the PDF footer.
Frontend implementation: Altinn/app-frontend-react#4173

Needs to be checked for consistency and alignment with next.

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • 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

    • PDF footer now adapts to layout/UI settings and can optionally hide the app name per task and language.
  • Public API

    • Constructor/API updated to accept an additional resource dependency to enable layout-driven footer behavior.
    • Internal API tightened: layout evaluator state access now has a non-nullable contract.
  • Tests

    • Expanded tests covering footer rendering and hide-app-name behavior for expressions, true/false, missing, and malformed settings.

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

PdfService now reads layoutSets.uiSettings.hideAppNameInPdf (literal or expression) to decide whether to include the app name in PDF footers; it accepts IAppResources and uses JSON deserialization and expression evaluation via the instance/unit-of-work initializer. Several instance data accessor APIs were tightened to return non-null LayoutEvaluatorState.

Changes

PDF Footer Customization with App Name Hiding

Layer / File(s) Summary
LayoutEvaluatorState non-nullability updates
src/Altinn.App.Core/Features/IInstanceDataAccessor.cs, src/Altinn.App.Core/Internal/Data/*, src/Altinn.App.Core/Internal/Expressions/*, test/.../InstanceDataAccessorFake.cs
Tighten multiple GetLayoutEvaluatorState() signatures and related fields/initializers to return non-nullable LayoutEvaluatorState, update lazy/cache initialization and the WithDataAccessor call sites.
PdfService dependencies and JSON configuration
src/Altinn.App.Core/Internal/Pdf/PdfService.cs
Add _resources (IAppResources) constructor parameter and a static JsonSerializerOptions (camelCase, allow trailing commas, skip comments) for expression deserialization.
Footer generation and hide-app-name evaluation
src/Altinn.App.Core/Internal/Pdf/PdfService.cs
Pass taskId into GetFooterContent; conditionally omit the translated appName when GetHideAppNameInPdf(...) returns true. GetHideAppNameInPdf reads layoutSets from IAppResources, treats literal booleans directly or deserializes an Expression and evaluates it using an initialized layout evaluator state; logs and falls back to showing the app name on errors.
Tests and controller wiring updates
test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs, test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
Add required usings, pass _appResources.Object into PdfService in test helpers, and add five tests that mock IAppResources.GetLayoutSets() for expression/true/false/missing/malformed hideAppNameInPdf scenarios asserting footer app-name presence/absence.
Public API snapshot
test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
Update snapshot to include added IAppResources resources parameter on PdfService constructor.

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Possibly related PRs:

    • Altinn/app-lib-dotnet#1747: Related changes to layout evaluator state/accessor behavior used by expression evaluation for footer hiding.
  • Suggested reviewers:

    • ivarne
    • martinothamar
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'add hideAppNameInPdf functionality to footer in the pdf' accurately describes the main change: adding a new hideAppNameInPdf feature that controls whether the app name appears in PDF footers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/18402-hide-app-name-footer-pdf

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs (1)

37-37: ⚡ Quick win

Add focused tests for the new footer branches.

The fixture now wires ILayoutEvaluatorStateInitializer, but there are still no assertions around uiSettings.hideAppNameInPdf as a literal boolean, as an expression, or when the config is malformed. Those are the new branches most likely to regress here.

Also applies to: 538-539

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Altinn.App.Core/Internal/Pdf/PdfService.cs`:
- Around line 409-447: Wrap the whole hideAppNameInPdf evaluation in a try/catch
that returns false on any exception so PDF generation fails closed; when parsing
layoutSetsString call JsonDocument.Parse with an explicit JsonDocumentOptions
(set AllowTrailingCommas = true and CommentHandling = JsonCommentHandling.Skip)
instead of relying on _jsonSerializerOptions; after obtaining hideAppNameElement
ensure ValueKind is True/False or safely call
hideAppNameElement.Deserialize<Expression>(_jsonSerializerOptions) and if that
returns null return false; guard against null instance.Data before using
instance.Data.Find; and catch exceptions from
_instanceDataUnitOfWorkInitializer.Init, _layoutStateInit.Init and
ExpressionEvaluator.EvaluateExpression and return false on error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ddfdd5b6-cadf-40d7-9e23-9d1f72286dec

📥 Commits

Reviewing files that changed from the base of the PR and between 829f12b and 72328cc.

📒 Files selected for processing (4)
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt

Comment thread src/Altinn.App.Core/Internal/Pdf/PdfService.cs Outdated
Comment thread src/Altinn.App.Core/Internal/Pdf/PdfService.cs Fixed
@walldenfilippa walldenfilippa added feature Label Pull requests with new features. Used when generation releasenotes backport-ignore This PR is a new feature and should not be cherry-picked onto release branches labels May 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs (1)

37-37: ⚡ Quick win

Consider adding setup for the layout state initializer mock or verifying it's not needed.

The _layoutStateInit mock is instantiated but never configured with any behavior. While Moq allows this, it's worth confirming whether:

  1. The mock should be set up to support expression-based hideAppNameInPdf tests (see comment on lines 480-629), or
  2. The current test scenarios don't require the initializer to be invoked
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs` at line 37, The
_layoutStateInit mock (Mock<ILayoutEvaluatorStateInitializer>) is created but
never configured; either add explicit setups for the calls used in
PdfServiceTests — e.g., configure _layoutStateInit.Setup(s =>
s.InitializeState(It.IsAny<...>())).Returns(...) or Setup for any
expression-based behavior used by the hideAppNameInPdf-related tests — or add
assertions that the initializer is never invoked; update tests referencing
hideAppNameInPdf to call the initializer mock or remove the unused field if it
is truly unnecessary so the test intent is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs`:
- Around line 480-629: Add a unit test that covers expression-based
hideAppNameInPdf by supplying layoutSets JSON with an expression (e.g.
{"sets":[],"uiSettings":{"hideAppNameInPdf":["equals",["dataModel","someField"],"someValue"]}})
when constructing the PdfService via SetupPdfService, and mock the
ILayoutEvaluatorStateInitializer / its evaluator to evaluate that expression to
true and false respectively so you can Verify GeneratePdf called with footer
that omits the app name when the expression evaluates true and includes it when
false; target the GenerateAndStorePdf method and reuse the existing Instance
setup and _pdfGeneratorClient.Verify pattern to assert behavior.

---

Nitpick comments:
In `@test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs`:
- Line 37: The _layoutStateInit mock (Mock<ILayoutEvaluatorStateInitializer>) is
created but never configured; either add explicit setups for the calls used in
PdfServiceTests — e.g., configure _layoutStateInit.Setup(s =>
s.InitializeState(It.IsAny<...>())).Returns(...) or Setup for any
expression-based behavior used by the hideAppNameInPdf-related tests — or add
assertions that the initializer is never invoked; update tests referencing
hideAppNameInPdf to call the initializer mock or remove the unused field if it
is truly unnecessary so the test intent is explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c681f9d8-14df-4a3f-ab85-bfe2243b5910

📥 Commits

Reviewing files that changed from the base of the PR and between 72328cc and 8b7ee7d.

📒 Files selected for processing (2)
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs

Comment thread test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
@sonarqubecloud

sonarqubecloud Bot commented Jun 8, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
61.5% Condition Coverage on New Code (required ≥ 65%)
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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)

400-461: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard against null expression after deserialization.

Line 433 deserializes hideAppName into an Expression, but Deserialize<T> can return null if the JSON value is literally null or in certain deserialization failure modes. Line 448 then evaluates the expression without checking, which will throw an unhandled exception (likely ArgumentNullException or NullReferenceException) that is not caught by the JsonException/InvalidOperationException handlers below.

🛡️ Proposed fix
             var expression = hideAppName.Deserialize<Expression>(_jsonSerializerOptions);
+            if (expression is null)
+            {
+                _logger.LogWarning("hideAppNameInPdf expression deserialized to null, defaulting to showing app name");
+                return false;
+            }
             var dataAccessor = await _instanceDataUnitOfWorkInitializer.Init(instance, taskId, language);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Altinn.App.Core/Internal/Pdf/PdfService.cs` around lines 400 - 461, In
GetHideAppNameInPdf, guard against null after deserializing hideAppName into an
Expression: after calling
hideAppName.Deserialize<Expression>(_jsonSerializerOptions) (variable
expression), check if expression is null and if so log a warning (including
context like "hideAppName expression was null") and return false (default)
before calling ExpressionEvaluator.EvaluateExpression; keep existing exception
handlers unchanged.
🧹 Nitpick comments (1)
test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs (1)

479-518: ⚡ Quick win

Add test coverage for expression evaluating to false.

The test at line 479-518 verifies an expression that evaluates to true (["equals","a","a"]), and line 558-594 tests a boolean literal false. Consider adding a test for an expression that evaluates to false (e.g., ["equals","a","b"]) to ensure the expression evaluation path correctly handles both outcomes, not just boolean literals.

✅ Suggested test case
[Fact]
public async Task GenerateAndStorePdf_WithDisplayFooter_HideAppNameInPdfExpression_EvaluatesToFalse_FooterShouldContainAppName()
{
    // Arrange
    _appResources
        .Setup(s => s.GetLayoutSets())
        .Returns("""{"sets":[],"uiSettings":{"hideAppNameInPdf":["equals","a","b"]}}""");

    _pdfGeneratorClient.Setup(s =>
        s.GeneratePdf(It.IsAny<Uri>(), It.IsAny<string?>(), It.IsAny<CancellationToken>())
    );
    _generalSettingsOptions.Value.ExternalAppBaseUrl = "https://{org}.apps.{hostName}/{org}/{app}";

    var target = SetupPdfService(
        pdfGeneratorClient: _pdfGeneratorClient,
        generalSettingsOptions: _generalSettingsOptions,
        pdfGeneratorSettingsOptions: Options.Create(new PdfGeneratorSettings { DisplayFooter = true })
    );

    Instance instance = new()
    {
        Id = $"509378/{Guid.NewGuid()}",
        AppId = "digdir/not-really-an-app",
        Org = "digdir",
        Data = [],
    };

    // Act
    await target.GenerateAndStorePdf(instance, "Task_1", CancellationToken.None);

    // Assert
    _pdfGeneratorClient.Verify(
        s =>
            s.GeneratePdf(
                It.IsAny<Uri>(),
                It.Is<string?>(footer => footer != null && footer.Contains("not-really-an-app")),
                It.IsAny<CancellationToken>()
            ),
        Times.Once
    );
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs` around lines 479
- 518, The test suite lacks coverage for a layout expression that evaluates to
false; add a new unit test (e.g.,
GenerateAndStorePdf_WithDisplayFooter_HideAppNameInPdfExpression_EvaluatesToFalse_FooterShouldContainAppName)
that sets _appResources.GetLayoutSets() to return
{"sets":[],"uiSettings":{"hideAppNameInPdf":["equals","a","b"]}}, create the
PdfService via SetupPdfService with DisplayFooter = true, call
target.GenerateAndStorePdf(instance, "Task_1", CancellationToken.None) and
verify _pdfGeneratorClient.GeneratePdf(...) was called with a footer string that
contains the app id ("not-really-an-app") to ensure the false-expression path is
exercised; mirror the structure/assertions used in the existing true-case test
to keep consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Altinn.App.Core/Internal/Pdf/PdfService.cs`:
- Around line 400-461: In GetHideAppNameInPdf, guard against null after
deserializing hideAppName into an Expression: after calling
hideAppName.Deserialize<Expression>(_jsonSerializerOptions) (variable
expression), check if expression is null and if so log a warning (including
context like "hideAppName expression was null") and return false (default)
before calling ExpressionEvaluator.EvaluateExpression; keep existing exception
handlers unchanged.

---

Nitpick comments:
In `@test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs`:
- Around line 479-518: The test suite lacks coverage for a layout expression
that evaluates to false; add a new unit test (e.g.,
GenerateAndStorePdf_WithDisplayFooter_HideAppNameInPdfExpression_EvaluatesToFalse_FooterShouldContainAppName)
that sets _appResources.GetLayoutSets() to return
{"sets":[],"uiSettings":{"hideAppNameInPdf":["equals","a","b"]}}, create the
PdfService via SetupPdfService with DisplayFooter = true, call
target.GenerateAndStorePdf(instance, "Task_1", CancellationToken.None) and
verify _pdfGeneratorClient.GeneratePdf(...) was called with a footer string that
contains the app id ("not-really-an-app") to ensure the false-expression path is
exercised; mirror the structure/assertions used in the existing true-case test
to keep consistency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88c79b58-1255-48b3-93de-8090a731fd7c

📥 Commits

Reviewing files that changed from the base of the PR and between 3dfeedc and 050b8ed.

📒 Files selected for processing (11)
  • src/Altinn.App.Core/Features/IInstanceDataAccessor.cs
  • src/Altinn.App.Core/Internal/Data/CleanInstanceDataAccessor.cs
  • src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs
  • src/Altinn.App.Core/Internal/Data/PreviousDataAccessor.cs
  • src/Altinn.App.Core/Internal/Expressions/LayoutEvaluatorState.cs
  • src/Altinn.App.Core/Internal/Expressions/LayoutEvaluatorStateInitializer.cs
  • src/Altinn.App.Core/Internal/Pdf/PdfService.cs
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/TestUtilities/InstanceDataAccessorFake.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt

@walldenfilippa walldenfilippa merged commit b86fb54 into main Jun 8, 2026
13 of 14 checks passed
@walldenfilippa walldenfilippa deleted the feat/18402-hide-app-name-footer-pdf branch June 8, 2026 11:36
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.

Skjule appName på underskjemaene i pdf-visning

3 participants