Skip to content

Updated Correspondence models - #1820

Merged
danielskovli merged 17 commits into
mainfrom
fix/correspondence-notification-details-v2-17405
Jun 19, 2026
Merged

danielskovli merged 17 commits into
mainfrom
fix/correspondence-notification-details-v2-17405

Conversation

@danielskovli

@danielskovli danielskovli commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

Addresses the remaining gaps from altinn-studio#17405 — the Correspondence Notification V2 integration made non-breaking changes to the request/response contract, and our client needed to catch up.

Status of the three change areas in #17405

Area State
Initialize request — customNotificationRecipients → customRecipients + overrideRegisteredContactInformation ✅ Already handled by earlier work (now on main)
Initialize response — per-correspondence notifications may be null ✅ Already nullable, deserializes cleanly
Correspondence details — notificationStatusDetails plural lists + nullable id 🛠️ Fixed here

Changes

All on the correspondence details/status (GetStatus) response path:

  1. CorrespondenceNotificationStatusDetailsResponse.Id: Guid → Guid?
    V2 can return "id": null for a notification channel status. The non-nullable Guid caused System.Text.Json to throw when deserializing the details response, so any GetStatus call against a V2 notification with a null id failed.

  2. Added Emails / Smses to CorrespondenceNotificationSummaryResponse
    The API now exposes notificationStatusDetails.emails / .smses as lists (for notifications sent to multiple recipients), alongside the legacy singular email / sms. These were previously dropped on deserialization.

  3. Deprecated the singular Email / Sms
    In the API's V2→V1 mapper these are a back-compat projection (the latest recipient) of the plural lists. Marked [Obsolete] pointing at Emails / Smses; the properties are retained (still deserialized), so this is not a breaking change.

Additional drift found & fixed

A sanity check of the full client⇄API contract surfaced two response status enums missing values the API can return. Since these enums deserialize by string name and JsonStringEnumConverter throws on an unknown name, each was a latent crash:

  1. CorrespondenceStatus — added AttachmentsDownloaded (server value 12). Set once a recipient downloads attachments; it appears in the issue's own example payload and broke GetStatus for any such correspondence (including via statusHistory).

  2. CorrespondenceAttachmentStatusResponse — added Expired (server value 5).

All changes are additive / widening — no binary-compatibility impact. The public-API snapshot has been regenerated accordingly.

Related

Tests

  • Status_NotificationV2Response_DeserializesNullIdAndPluralRecipientLists — null id + populated emails/smses.
  • Status_AttachmentsDownloadedStatus_DeserializesCorrectly — AttachmentsDownloaded as both top-level and statusHistory status.

🤖 Generated with Claude Code

danielskovli and others added 11 commits June 16, 2026 16:41
A signee with more than one notification identifier (e.g. SSN + email +
mobile) caused the Correspondence API to reject the call with "Custom
recipient with multiple identifiers is not allowed", so no notification
was sent (#19153).

The signing call-to-action packed email, mobile and the org/person
identifier into a single custom recipient. The Correspondence API
requires exactly one identifier per custom recipient, with multiple
recipients forming a list.

Correspondence client:
- Add CustomRecipients (list) and OverrideRegisteredContactInformation
  to CorrespondenceNotification and the request DTO, mapping to the
  customRecipients / overrideRegisteredContactInformation wire fields.
- Add builder methods WithCustomRecipients(...) (additive: KRR + custom)
  and WithRecipientOverrides(...) (exclusive: custom only, overriding
  registered KRR contact info), plus *IfConfigured variants. Each sets
  both the recipient list and the override flag, so the last call wins.
- Deprecate the singular CustomRecipient, CustomNotificationRecipients
  and the WithRecipientOverride family in favour of the new surface,
  formalizing the CustomRecipients term (#17407). All additive and
  binary compatible.

Signing:
- Emit one single-identifier recipient per channel and override the
  registered contact information, fixing the multiple-identifier error.

Co-Authored-By: Claude Opus 4.8 (1M context) 
Incorporates improvements identified while comparing against PR #1809:

- Add EmailAndSms to CorrespondenceNotificationChannel and map the
  signing SmsAndEmail choice to it. Previously SmsAndEmail mapped to
  EmailPreferred ("prefer email, fall back to SMS"), which only sent on
  one channel instead of both.
- CorrespondenceClient now explodes every custom recipient (the new
  plural CustomRecipients and the legacy singular CustomRecipient) into
  one wire entry per populated identifier, so a recipient carrying more
  than one identifier can no longer trigger the "Custom recipient with
  multiple identifiers is not allowed" error from any caller - not just
  the signing path. The deprecated singular customRecipient wire field
  is no longer emitted.

Registry contact-info enrichment in SigneeContextsManager is
intentionally retained: it is established, published behaviour and lets
apps declare an Email/Sms block without an address and have the contact
resolved from the registry. Combined with overrideRegisteredContact
Information, the signee is still notified exactly once.

Co-Authored-By: Claude Opus 4.8 (1M context) 
…ation builder

Co-Authored-By: Claude Opus 4.8 (1M context) 
Co-Authored-By: Claude Opus 4.8 (1M context) 
The Correspondence notification V2 integration changed the details/status
response (GET correspondence status):

- notificationStatusDetails.id may now be null. CorrespondenceNotification-
  StatusDetailsResponse.Id was a non-nullable Guid, so System.Text.Json
  threw when deserializing "id": null. Widen it to Guid?.
- notificationStatusDetails now also exposes "emails"/"smses" lists (for
  notifications sent to multiple recipients) alongside the singular
  "email"/"sms". Add CorrespondenceNotificationSummaryResponse.Emails/Smses.

Both changes are additive/widening (no binary-compat impact). Refs
Altinn/altinn-studio#17405.

Co-Authored-By: Claude Opus 4.8 (1M context) 
@coderabbitai

coderabbitai Bot commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Three correspondence response models are updated: CorrespondenceStatus adds AttachmentsDownloaded, CorrespondenceAttachmentStatusResponse adds Expired, CorrespondenceNotificationStatusDetailsResponse.Id becomes Guid?, and CorrespondenceNotificationSummaryResponse adds plural Emails/Smses list properties while marking Email/Sms obsolete. Two regression tests and a public API snapshot are updated accordingly.

Changes

Correspondence Model Contract Updates

Layer / File(s) Summary
Enum additions, nullable Id, and plural notification list properties
src/.../CorrespondenceStatus.cs, src/.../Response/CorrespondenceAttachmentStatusResponse.cs, src/.../Response/CorrespondenceNotificationStatusDetailsResponse.cs, src/.../Response/CorrespondenceNotificationSummaryResponse.cs
CorrespondenceStatus gains AttachmentsDownloaded = 12; CorrespondenceAttachmentStatusResponse gains Expired = 5; CorrespondenceNotificationStatusDetailsResponse.Id changes from Guid to Guid?; CorrespondenceNotificationSummaryResponse adds Emails and Smses as IReadOnlyList? and marks the existing Email and Sms properties [Obsolete].
Regression tests and public API snapshot
test/.../Features/Correspondence/Models/CorrespondenceResponseTests.cs, test/.../PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
Two new [Fact] tests assert deserialization of V2-shaped notification payloads (null id, plural emails/smses) and of AttachmentsDownloaded in both top-level status and status history. Public API snapshot updated to record all contract changes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

other

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The pull request successfully addresses the primary coding requirements from issue #17405: adding nullable Id support and Emails/Smses list properties to handle V2 multi-recipient notifications, plus supporting new enum values AttachmentsDownloaded and Expired for proper deserialization.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the linked issue objectives: model property updates, enum value additions, test additions for V2 notification deserialization, and public API snapshot updates to reflect the model changes.
Title check ✅ Passed The title accurately summarizes the main changes: supporting V2 notification details and adding missing enum status values.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 fix/correspondence-notification-details-v2-17405

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.

danielskovli and others added 2 commits June 18, 2026 15:19
The Correspondence notification V2 integration introduced the plural
notificationStatusDetails.emails/smses lists as the forward-looking shape
for multi-recipient notifications, leaving the singular email/sms as a
back-compat projection of the latest recipient.

Mark CorrespondenceNotificationSummaryResponse.Email/Sms [Obsolete],
pointing consumers at Emails/Smses. The properties remain (still
deserialized) to avoid a breaking change. Refs Altinn/altinn-studio#17405.

Co-Authored-By: Claude Opus 4.8 (1M context) 
Our response status enums had drifted from the Correspondence API:

- CorrespondenceStatus was missing AttachmentsDownloaded (server value 12),
  set once a recipient downloads attachments. Since statuses deserialize by
  string name and JsonStringEnumConverter throws on unknown names, GetStatus
  threw a JsonException for any correspondence whose status — or any
  statusHistory entry — was "AttachmentsDownloaded".
- CorrespondenceAttachmentStatusResponse was missing Expired (server value 5),
  which would likewise throw when deserializing an expired attachment overview.

Both are additive enum members (non-breaking). Refs Altinn/altinn-studio#17405.

Co-Authored-By: Claude Opus 4.8 (1M context) 
@danielskovli
danielskovli marked this pull request as ready for review June 19, 2026 06:25
Base automatically changed from fix/signing-notification-multiple-identifiers-19153 to main June 19, 2026 07:30
@danielskovli danielskovli added the feature Label Pull requests with new features. Used when generation releasenotes label Jun 19, 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.

🧹 Nitpick comments (1)
test/Altinn.App.Core.Tests/Features/Correspondence/Models/CorrespondenceResponseTests.cs (1)

509-523: ⚡ Quick win

Use xUnit asserts in the newly added tests instead of FluentAssertions.

Please replace the new .Should() assertions in these added blocks with xUnit Assert.* equivalents to stay consistent with repo test guidelines.

As per coding guidelines, "**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests".

Also applies to: 564-565

🤖 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/Features/Correspondence/Models/CorrespondenceResponseTests.cs`
around lines 509 - 523, Replace all FluentAssertions with xUnit Assert
equivalents in the test assertions for statusDetails.Email, statusDetails.Sms,
statusDetails.Emails, and statusDetails.Smses properties. Convert the
.Should().BeNull() calls to Assert.Null(), .Should().Be() calls to
Assert.Equal(), .Should().ContainSingle() calls to Assert.Single(), and
.Should() property access calls to appropriate Assert.NotNull() assertions.
Ensure all assertions in the newly added test blocks (lines 509-523 and also at
lines 564-565 as noted) are converted to use the xUnit Assert.* API for
consistency with repository test guidelines.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In
`@test/Altinn.App.Core.Tests/Features/Correspondence/Models/CorrespondenceResponseTests.cs`:
- Around line 509-523: Replace all FluentAssertions with xUnit Assert
equivalents in the test assertions for statusDetails.Email, statusDetails.Sms,
statusDetails.Emails, and statusDetails.Smses properties. Convert the
.Should().BeNull() calls to Assert.Null(), .Should().Be() calls to
Assert.Equal(), .Should().ContainSingle() calls to Assert.Single(), and
.Should() property access calls to appropriate Assert.NotNull() assertions.
Ensure all assertions in the newly added test blocks (lines 509-523 and also at
lines 564-565 as noted) are converted to use the xUnit Assert.* API for
consistency with repository test guidelines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7fbc8434-439e-43cd-a3b8-37fca46ba606

📥 Commits

Reviewing files that changed from the base of the PR and between 79ad1a0 and 7bfcbed.

📒 Files selected for processing (6)
  • src/Altinn.App.Core/Features/Correspondence/Models/CorrespondenceStatus.cs
  • src/Altinn.App.Core/Features/Correspondence/Models/Response/CorrespondenceAttachmentStatusResponse.cs
  • src/Altinn.App.Core/Features/Correspondence/Models/Response/CorrespondenceNotificationStatusDetailsResponse.cs
  • src/Altinn.App.Core/Features/Correspondence/Models/Response/CorrespondenceNotificationSummaryResponse.cs
  • test/Altinn.App.Core.Tests/Features/Correspondence/Models/CorrespondenceResponseTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt

@danielskovli danielskovli changed the title Support notification V2 fields in correspondence details response Support Notification V2 correspondence details and add missing status enum values Jun 19, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) 
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
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

@danielskovli
danielskovli merged commit c36a8c2 into main Jun 19, 2026
11 of 13 checks passed
@danielskovli
danielskovli deleted the fix/correspondence-notification-details-v2-17405 branch June 19, 2026 08:38
@danielskovli danielskovli changed the title Support Notification V2 correspondence details and add missing status enum values Updated Correspondence models Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Label Pull requests with new features. Used when generation releasenotes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Determine which CorrespondenceClient changes are required Update correspondence client following API changes

3 participants