From 19cfd69ff13291d129ff233c23e769de783dcac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 28 May 2026 22:23:34 +0200 Subject: [PATCH] feat(measurements): accept restingHeartRate, hrv, vo2max in the series enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iOS app surfaces these as series-capable, but the kind enum on /api/measurements/series did not accept them — opening the detail or trend view returned 422. The underlying MeasurementType enum already carries the values; this commit extends the route-side enum so the wire shape matches the data shape. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../measurements/series/__tests__/route.test.ts | 15 +++++++++++++++ src/app/api/measurements/series/route.ts | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/app/api/measurements/series/__tests__/route.test.ts b/src/app/api/measurements/series/__tests__/route.test.ts index 421908782..982c849de 100644 --- a/src/app/api/measurements/series/__tests__/route.test.ts +++ b/src/app/api/measurements/series/__tests__/route.test.ts @@ -133,6 +133,21 @@ describe("GET /api/measurements/series", () => { const res = await GET(req("kind=weight&days=3651")); expect(res.status).toBe(422); }); + + it.each(["restingHeartRate", "heartRateVariability", "vo2Max"])( + "accepts kind=%s (v1.5.5)", + async (kind) => { + // v1.5.5 — the iOS app surfaces these as series-capable; the + // previous enum rejected them with 422 even though the + // underlying MeasurementType already carried the values. + vi.mocked(getSession).mockResolvedValue(SESSION_OK as never); + vi.mocked(prisma.measurement.findMany).mockResolvedValue([] as never); + const res = await GET(req(`kind=${kind}&days=30`)); + expect(res.status).toBe(200); + const body = (await res.json()) as { data: { kind: string } }; + expect(body.data.kind).toBe(kind); + }, + ); }); describe("GET /api/measurements/series — 422 multi-issue (v1.4.43 W6)", () => { diff --git a/src/app/api/measurements/series/route.ts b/src/app/api/measurements/series/route.ts index 95e7f2fba..02536ac2b 100644 --- a/src/app/api/measurements/series/route.ts +++ b/src/app/api/measurements/series/route.ts @@ -34,6 +34,14 @@ const kindEnum = z.enum([ "totalBodyWater", "boneMass", "oxygenSaturation", + // v1.5.5 — the iOS app surfaces these as series-capable, but + // the route used to reject the trend/detail view request with a + // 422. The underlying MeasurementType enum already carries each + // value; this list extends the camelCase wire shape so the route + // matches the data it can serve. + "restingHeartRate", + "heartRateVariability", + "vo2Max", ]); const querySchema = z.object({ @@ -58,6 +66,9 @@ const KIND_TO_TYPE: Record, MeasurementType> = { totalBodyWater: "TOTAL_BODY_WATER", boneMass: "BONE_MASS", oxygenSaturation: "OXYGEN_SATURATION", + restingHeartRate: "RESTING_HEART_RATE", + heartRateVariability: "HEART_RATE_VARIABILITY", + vo2Max: "VO2_MAX", }; interface SeriesPoint {