feat(api): manage localized push template definitions

Add revision-protected reads, atomic upsert and deletion with structured actions. Preserve omitted translations and fix insertion of new push languages.

Ref: IT-1627
This commit is contained in:
2026-10-31 10:03:47 +02:00
parent 1351279b36
commit 517b38791b
19 changed files with 519 additions and 14 deletions
+78
View File
@@ -264,3 +264,81 @@ allowlist. Rider reported no errors in inspected changed production files; warni
remain for existing Firebase Token deprecation, defensive JSON null checks and style.
Cognitive complexity was reviewed structurally against the limit of 10; numeric
scores were not measured. All changes remain uncommitted for owner review/testing.
## Push template management REST API (IT-1627, 2026-10-31)
Push definitions now have the same service-scoped management operations as Email:
| Method | Route | Purpose |
| --- | --- | --- |
| GET | `/api/v1/services/{serviceId}/push-template-definitions?key={key}` | Read one exact key |
| GET | `/api/v1/services/{serviceId}/push-template-definitions/all` | List Push definitions |
| POST | `/api/v1/services/{serviceId}/push-template-definitions/upsert?key={key}` | Create or atomically patch a definition |
| DELETE | `/api/v1/services/{serviceId}/push-template-definitions?key={key}&revision={revision}` | Delete only Push, preserving Email |
Requests and responses use camelCase and the existing `success/data/error` envelope.
Create with omitted/null `revision`; an existing key returns 409. Update with the
last read nonempty revision; stale/missing definitions return 409. Successful
creation returns 201 with Location; update/read/delete return 200. Unknown services
or absent reads/deletes return 404; invalid requests return 400.
Example creation body:
```json
{
"revision": null,
"variables": [{ "name": "ActorName", "required": true }],
"translations": [{
"languageCode": "uk",
"title": "Довірені контакти",
"body": "{{ActorName}} додав вас до довірених контактів.",
"actions": {
"defaultAction": "contacts",
"buttons": [{ "action": "contacts", "label": "Перейти в розділ" }]
}
}],
"removeLanguageCodes": []
}
```
The action key above illustrates a producer/client agreement; this endpoint neither
registers actions nor activates any Invemory producer. Object IDs are per-message
metadata, not template content. `actions` is a structured object, not an escaped JSON
string. Omission means no actions for a supplied translation; explicit null is invalid.
`variables` and `translations` arrays are required (empty is valid). Variables replace
the channel-wide contract. Supplied translations replace their full content, including
actions. Omitted translations remain; only `removeLanguageCodes` removes versions.
Removing every language retains the definition and variables. Codes exactly match the
service catalog; duplicate/unknown codes and simultaneous replace/remove are rejected.
The final merged definition is validated, including retained translations' placeholders,
so a variable change cannot invalidate an omitted translation. Invalid/stale requests
leave content and revision unchanged. The existing transaction/advisory lock covers
catalog checks, merge and revision-guarded persistence. API and administration share
validation and storage, including explicit EF insertion when adding a new language.
Existing Email endpoints remain unchanged. Both channels can independently use the
same service/key. No migration, new registry, access boundary or Android change is
needed for these management endpoints. Keep them within the existing private
Notification Service administration network boundary.
Validation of the management API increment: 84 service/unit and 187 PostgreSQL
integration tests passed (271 total). After review cleanup, all 29 focused Push API,
editor and model tests passed again. Coverage includes parallel create/update conflicts,
atomic invalid writes, retained translations, structured localized actions, explicit
language removal and Email preservation. Rider reported no errors in inspected files;
manual structural complexity review was performed, with no numeric score recorded.
Reuse review compared Email definition controllers/handlers, API validation/envelopes,
TemplateMutationRunner and the existing Push editor/storage path.
A separate local Web launch for additional browser validation was rejected by the
automatic execution policy without further reason. No standalone-browser or installed
Android validation is claimed for this increment; API requests were exercised through
the ASP.NET integration host with real PostgreSQL. Changes await owner testing and
remain uncommitted; the previously delivered service does not yet include these endpoints.
Owner follow-up: authorized code delivery and another Kubernetes deployment after
verification, and authorized stopping the Rider Web process. The Rider process was
stopped. A second background Web launch was rejected by the execution tool with only
`blocked by policy`; this is an execution-tool restriction, not a request for more
owner permission. The automated API/PostgreSQL checks remain available.