mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(measurements): accept deviceType on single-entry POST (mirrors batch route)
`Measurement.deviceType` exists on the row, was added by the W8c batch ingest path, but the single-entry `createMeasurementSchema` omitted it — every single-row POST silently dropped the device tag even when the client supplied one. Manual rows tagged with watch / scale / ring metadata lost their attribution at the schema boundary and showed up as unsourced. Declare `deviceType` on the schema (optional, nullable, 1–32 chars), thread it through both single-entry and array-body POST handlers, and add three RTL cases (present, null, omitted) so the parsed shape stays observable.
This commit is contained in:
1 parent
72fcdc656f
commit
c49fe731e4
3 files changed
+60
-1
No files matched your search
@@ -96,6 +96,10 @@ async function postMeasurement(request: NextRequest) {
|
||||
notes: m.notes ?? null,
|
||||
glucoseContext:
|
||||
(m.glucoseContext as GlucoseContext | undefined) ?? null,
|
||||
// v1.4.25 W10 reconcile (code-review M4): mirror the
|
||||
// single-entry path so the multi-entry batch persists
|
||||
// `deviceType` instead of silently dropping it.
|
||||
deviceType: m.deviceType ?? null,
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -127,7 +131,7 @@ async function postMeasurement(request: NextRequest) {
|
||||
return apiError(parsed.error.issues[0].message, 422);
|
||||
}
|
||||
|
||||
const { type, value, measuredAt, notes, source, glucoseContext } =
|
||||
const { type, value, measuredAt, notes, source, glucoseContext, deviceType } =
|
||||
parsed.data;
|
||||
|
||||
// Handle unique constraint violation
|
||||
@@ -143,6 +147,11 @@ async function postMeasurement(request: NextRequest) {
|
||||
measuredAt,
|
||||
notes: notes ?? null,
|
||||
glucoseContext: (glucoseContext as GlucoseContext | undefined) ?? null,
|
||||
// v1.4.25 W10 reconcile (code-review M4): the deviceType column
|
||||
// already accepts client metadata from the batch route. Mirror
|
||||
// the behaviour here so single-entry POST persists the tag
|
||||
// instead of silently dropping it.
|
||||
deviceType: deviceType ?? null,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -130,5 +130,47 @@ describe("measurement validation", () => {
|
||||
});
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
|
||||
// v1.4.25 W10 reconcile (code-review M4): `deviceType` was a
|
||||
// batch-only field; the single-entry schema silently dropped it.
|
||||
// Now it parses through so the column is populated whether the
|
||||
// client posts one row or many.
|
||||
it("accepts an optional deviceType tag and surfaces it on the parsed value", () => {
|
||||
const parsed = createMeasurementSchema.safeParse({
|
||||
...validBase,
|
||||
type: "WEIGHT",
|
||||
value: 82,
|
||||
deviceType: "scale",
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.deviceType).toBe("scale");
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts a null deviceType so a client can explicitly clear the column", () => {
|
||||
const parsed = createMeasurementSchema.safeParse({
|
||||
...validBase,
|
||||
type: "WEIGHT",
|
||||
value: 82,
|
||||
deviceType: null,
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.deviceType).toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it("treats a missing deviceType as undefined (back-compat with pre-W10 clients)", () => {
|
||||
const parsed = createMeasurementSchema.safeParse({
|
||||
...validBase,
|
||||
type: "WEIGHT",
|
||||
value: 82,
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
if (parsed.success) {
|
||||
expect(parsed.data.deviceType).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -205,6 +205,14 @@ export const createMeasurementSchema = z
|
||||
// Only applies when type === BLOOD_GLUCOSE. Mirrored by a CHECK
|
||||
// constraint in Postgres (see migration 0021).
|
||||
glucoseContext: glucoseContextEnum.optional(),
|
||||
// v1.4.25 W10 reconcile (code-review M4): the single-entry POST
|
||||
// dropped `deviceType` silently because the column existed on
|
||||
// `Measurement` and was accepted by the batch route, but never
|
||||
// declared in this schema. iOS clients that POST one row at a
|
||||
// time (and dashboards that backfill manual rows with device
|
||||
// metadata) now persist the tag instead of seeing it disappear.
|
||||
// Accepts `null` so a client can explicitly clear the column.
|
||||
deviceType: z.string().min(1).max(32).nullable().optional(),
|
||||
})
|
||||
.refine((data) => validateMeasurementRange(data.type, data.value) === null, {
|
||||
message: "Value out of plausible range",
|
||||
|
||||
Reference in new issue
Block a user