feat(apns): forward reminderId to iOS on measurement reminders

The measurement-reminder push already carries the reminder id in its
metadata, but the iOS allowlist stripped it before it reached the device.
Forward the key so a foreground or handled notification can post the
completion directly instead of routing through the capture deep link.
This commit is contained in:
Marc-André Bombeck committed 2026-06-19 21:03:59 +02:00
1 parent 572ff3502e
commit a4b7ae5966
2 files changed
+23

No files matched your search

@@ -590,6 +590,25 @@ describe("sendViaApns — dispatcher fan-out", () => {
expect(note.payload.eventType).toBe("MEDICATION_REMINDER");
});
it("forwards reminderId on MEASUREMENT_REMINDER and strips unlisted keys", async () => {
// The iOS "Erledigt" action POSTs the reminder completion directly
// from a foreground / handled notification, so the reminder's id must
// ride the APNs payload; channel-specific extras stay stripped.
vi.mocked(prisma.device.findMany).mockResolvedValueOnce([
{ id: "d1", apnsToken: "tok-a", apnsEnvironment: "sandbox" },
] as never);
sendMock.mockResolvedValueOnce({ sent: [{ device: "tok-a" }], failed: [] });
await sendViaApns("u-1", {
title: "t",
message: "m",
eventType: "MEASUREMENT_REMINDER",
metadata: { reminderId: "rem-1", replyMarkup: { inline: true } },
});
const note = sendMock.mock.calls[0][0];
expect(note.payload.reminderId).toBe("rem-1");
expect(note.payload.replyMarkup).toBeUndefined();
});
it("forwards MOOD_REMINDER eventType as aps.category too", async () => {
// SR-2 contract: the mood-reminder daily job emits this event-type;
// the iOS app registers a `MOOD_REMINDER` category whose only
+4
View File
@@ -904,6 +904,10 @@ const IOS_METADATA_ALLOWLIST = new Set([
"url",
"runwayDays",
"thresholdDays",
// v1.18.7 — MEASUREMENT_REMINDER carries the reminder's id so a
// foreground / handled notification can POST the completion directly
// instead of routing through the capture deep link.
"reminderId",
]);
function pickIosMetadata(