Skip to content

Messagebox feature + letter transfer - #56

Merged
pgaetani merged 74 commits into
developfrom
feat/messagebox-transfer
Aug 21, 2026
Merged

Messagebox feature + letter transfer#56
pgaetani merged 74 commits into
developfrom
feat/messagebox-transfer

Conversation

@pgaetani

Copy link
Copy Markdown
Member

No description provided.

pgaetani and others added 30 commits August 5, 2026 12:22
- Rename task name from `process_messagebox_client_response` to `process-messagebox-client-response`
- Remove unused `get_base_folder_name` function and simplify file location structure
- Update antivirus scanning to use new task name and pass notification_id instead of filename
- Make organisation_id optional in response and fix response structure
- Remove redundant filename processing loop in favor of direct task call
- Add new debug configurations for non-local API, worker, and beat
- Change log level from INFO to DEBUG for celery worker and beat
- Add celery flags --without-gossip, --without-mingle, --without-heartbeat
- Update VSCode settings to use env file for Python terminal
- Add new pre-launch tasks for non-local debugging
- Add compound configuration for non-local Notify API debugging
- Add tasks for functional test fixtures and platform admin user creation
messagebox_deliver had no guard against re-dispatch, unlike
send_sms_to_provider/send_email_to_provider's `if notification.status ==
"created":` idempotency check. A duplicate or stale trigger would blindly
resend to ebms-core, which rejects it as a duplicate BerichtID
(BerichtBestaatAl) and can race the real async result.
…real status detail

check_if_messagebox_still_pending treated a notification stuck in `sending`
the same as one stuck in `created`, blindly resending both. `sending` means
ebms-core already accepted the message and we're only waiting on its async
result, which has no fixed SLA -- a real pre-prod capture showed this bug
in action: the same BerichtID rejected 7 times as a duplicate
(BerichtBestaatAl). Split the DAO query so `sending` is never touched for
resend, and add a separate 24h Zendesk alert (never resend) so a silently
stuck unprocessed-message poller gets noticed early instead of piling up
for months.

Separately, messagebox_response_map had two real bugs found against
captured production responses: three Stadium values were listed as if they
were VerwerkingsCode values (they're a different field entirely, so those
entries were dead code), and the delivered-status entry expected
detailed_status_code "0" when real success responses always carry
"Verwerkt". Thread Stadium through as its own parameter instead of folding
it into detailed_status_code, and persist both detailed_status_code and the
new messagebox_stadium on the notification (new NL migration) plus into
notification_history, so this detail survives retention archival instead of
being discarded.
Messagebox was entirely absent from delete_notifications_older_than_retention
(only email/sms/letter were wired up), even though ServiceDataRetention and
notification_type already supported it at the DB layer. Add the missing
per-service purge task, reusing _delete_notifications_older_than_retention_by_type
unmodified, and widen the data-retention API schema so a messagebox row can
actually be created.

The compliance-critical part: messagebox purging must never run while a
notification is still in flight, since we need a definitive success or
failure before removing the citizen's BSN, respecting messagebox_deliver's
retry logic. Reuses the exact status-exclusion pattern letters already use
(NOT IN ('pending-virus-check', 'created', 'sending')) rather than the
age-only query email/sms use, so this is gated the same way, not something
bespoke.
Adds the -e ../ebms-adapter-client editable dependency the messagebox
channel is built on, plus devcontainer tooling to support it: VS Code tasks
for creating/compiling NL migrations and requirements, a run-on-save
compile step for requirements_nl*.in, and the recommended extension it
needs. Also drops the "Deactivate Spryng provider" task (superseded now
that the SMS provider stub round-trips Spryng correctly) and tidies the
celerybeat-schedule gitignore pattern to match its -wal/-shm variants.
The Service.oin model field and its use in the messagebox send path
(BerichtLeverancierID) were already committed, but the migration that
actually creates the column, and schema-level validation (must be exactly
20 digits), were never committed alongside it.
…ring

Adds app.clients.messagebox.EbmsAdapterClient (wraps ebms-adapter-client
for the send path, decoding attachments and BSN before dispatch) and the
supporting config: QueueNamesNL/TaskNamesNL entries, the messagebox Celery
beat schedule (poll-unprocessed every 5 min, check-still-pending hourly),
EBMS_ADAPTER_URL and the GLOBE-R-BV contract constants, and a dedicated
api-worker-messagebox entrypoint. This is the foundation the messagebox
Celery tasks (messagebox_tasks.py, messagebox_scheduled_tasks.py,
process_messagebox_client_response_tasks.py) already committed on this
branch depend on.
Wires MESSAGEBOX_TYPE into the v2 notification type schema and completes
the /v2/notifications/messagebox request handling and notification
persistence path.
Extends fact_billing_dao's per-service usage queries with a messagebox
variant (free/cost-0, matching email's treatment) so messagebox
notifications flow into ft_billing instead of being silently excluded from
annual/monthly usage reporting.
…essagebox delivery

- Allow up to 3 precompiled letter PDFs to be uploaded and merged into a
  single canonical letter downstream (app/letters/utils.py,
  app/celery/letters_pdf_tasks.py).
- Track detailed Logius failure reasons/codes and stuck-notification
  retries for messagebox sends (messagebox_tasks, messagebox_scheduled_tasks,
  process_messagebox_client_response_tasks, ebms_adapter).
- Make notifications.to nullable so a messagebox notification's BSN can be
  wiped once it reaches a terminal state (migrations_nl/0020).
- Bump notifynl-utils to 101.2.0.
Bump sentry-sdk to 2.x to pick up enable_logs (Sentry Logs ingestion) and
max_request_body_size, and add Boto3Integration for visibility into S3/SQS
calls (including kombu's own SQS broker polling). Tag notification_id on
Sentry events and attach it as structured `extra` log context across the
messagebox send/callback path (messagebox_tasks, client record_outcome,
process_messagebox_client_response_tasks, post_notifications_messagebox), so
individual notifications become traceable end-to-end.

Also re-pins notifications-utils to 101.1.15 and regenerates the NL
lockfiles, which pick up defusedxml (transitive via ebms-adapter-client).
Allow up to 2 ad-hoc PDF attachments (base64, 2MB each pre-encoding) on the
templated-letter send API, merged into the generated letter after the
template's own fixed letter_attachment (if any):

- notification_schemas/validators: accept and size-check the new
  `attachments` field on POST /v2/notifications/letter.
- letters/utils: upload attachments to the scan bucket keyed by
  notification id (upload_letter_attachments/get_letter_attachment_keys),
  since a templated letter has no canonical filename yet at send time.
- post_notifications: decode/upload attachments inside the notification
  create transaction, then route real-key sends through antivirus
  (SCAN_LETTER_ATTACHMENTS) while test-key/no-attachment sends go straight
  to PDF generation as before.
- letters_pdf_tasks: three-way virus-scan callback (success/failed/error),
  mirroring the existing messagebox virus-scan task shape, and include the
  attachment keys in the payload sent to notifynl-template-preview.
- scheduled_tasks: recognise letters stuck pending virus-check by ad-hoc
  attachments (no single scan-bucket file to check) and auto-retry the scan
  instead of only falling through to the Zendesk alert.
New `flask command create-letter-template` CLI command for spinning up a
basic letter template locally, matching the existing create-sms-template/
create-email-template commands. Wire all three into .vscode/tasks.json
(the latter two were previously only reachable via the CLI, not tasks.json).
The rebase onto develop surfaced dead references to transaction(), which
develop dropped as part of its SQLAlchemy 2.0 migration. Ports the same
call sites over to the _autocommit=False + explicit commit/rollback
pattern already used elsewhere in these files.
…st-dotenv

pytest-env>=1.2 registers its own --envfile CLI option, which collides with
pytest-dotenv's option of the same name and breaks pytest collection
entirely when both are installed.
- _messagebox_query referenced start_date/end_date, but _query_for_billing_data's
  params are named start_dt/end_dt -- NameError on any billing query touching
  the messagebox notification type.
- test_fetch_usage_for_service_annual_includes_messagebox accessed
  sample_service.id inside the QueryRecorder block, triggering a stray
  default-session query; hoisted to match the sibling test's pattern.
Marshmallow's @validates callback signature now always passes data_key;
every other validator in this file already accepts it, this one was
missed when the OIN field was added.
_dispatch_templated_letter_pdf (added for ad-hoc attachment support) omitted
MessageGroupId on its celery calls, breaking SQS FIFO ordering per service --
every other dispatch call in this file passes it. Updates the attachment
tests that had asserted the missing-MessageGroupId behavior to match.

Also suppresses a pre-existing ruff B017 (blind exception assert) that
started failing the pre-commit hook after this rebase's ruff version bump,
in an unrelated test in the same file -- the blind catch there is
intentional (verifying rollback-and-reraise), not a bug.
@pytest.mark.parametrize("reference_paceholder,", ...) was parsed as a
2-name spec because of the trailing comma, which broke collection of the
entire module under the newer, stricter pytest bundled by this rebase.
…nder

Org invitation emails hardcoded 'The GOV.UK Notify team' as the inviter
name, leaking upstream branding into an otherwise fully NL-translated
email.
This error is flashed directly to the end user in notifynl-admin
(InviteTokenError -> flash(str(error))), so it should match the rest
of the localized admin UI instead of staying in English.
…r message

Sibling of the same fix already applied to organisation invitations.
Replaces the UK GOV.UK Notify docs URL with docs.notifynl.nl.
Points to NotifyNL's own trial-mode guidance page (built off
ADMIN_BASE_URL) instead of the UK site.
@github-actions

This comment was marked as outdated.

…branding

11 upstream-seeded system templates (MOU receipts, go-live requests/
approvals/rejections, service-join requests, team-member-edit
notifications) were never touched by any NL translation migration and
were sending verbatim GOV.UK-branded English content in production —
same bug class as the already-fixed org/service invitation emails.

Also fixes the 'you're now live' email: ConfigNL already points at a
dedicated NL template (ec92ba79) but it only had a bare stub, while a
fully detailed Dutch translation already existed unused on the old
upstream template row (618185c6, migrations_nl/0008). Reuses that
content on the correct, live template id instead of re-authoring it.

Excludes the DVLA letter-volume template pending a separate decision
on its postage-class labels and underlying counting logic.
@github-actions

This comment was marked as outdated.

_get_callback_url built the URL dvla-interceptor POSTs letter-status
callbacks back to using API_HOST_NAME, which the notifynl-full chart is
about to flip from the internal Service DNS to the public ingress
hostname for its genuinely-public consumers (e.g. the unsubscribe
link). This callback is pod-to-pod within the same namespace and needs
to stay off the public ingress, so it now reads the new
API_HOST_NAME_INTERNAL instead -- added to ConfigNL (falls back to
API_HOST_NAME) so upstream Config/Test/Development are untouched.
…te to Dutch

Personalisation keys were built from item.postage (netherlands/europe/
rest-of-world) but the template still used UK-era placeholders
(first_class_volume etc.), so the real report email always showed
all-zero counts. Fixes the key mismatch and translates the seeded
template to Dutch/NotifyNL branding with the correct NL postage zones.
Stop sending the daily volume-report email to DVLA -- letter delivery
via send_dvla_letters_via_api is unaffected. Local dev's
LETTER_COLLATION_FREQUENCY schedule (every 5 min) also currently
applies to Test/Acc/Prod since no environment overrides it, so this
was firing far more often than the intended twice-daily cadence.
Deletes -pr tagged images beyond the newest 10 via
snok/container-retention-policy, and publishes a daily tag inventory
to the job summary.
@pgaetani
pgaetani merged commit 2d3b038 into develop Aug 21, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request letters Pertaining to LETTER type notification processing messagebox Pertaining to MESSAGEOBX type notification processing

Projects

None yet

3 participants