mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
fix(medications): keep a custom category when a native client sends OTHER back
An iPhone build that predates custom categories decodes an unknown category as OTHER and sends it back on every edit. A Bearer update sending OTHER over a custom category now keeps the custom one; the web can still move it back.
This commit is contained in:
1 parent
4d7abdd12b
commit
50bc0dab5a
2 files changed
+65
-2
No files matched your search
@@ -285,7 +285,10 @@ export const PUT = apiHandler(
|
||||
// below excludes every actioned row — and the previous cadence is the one
|
||||
// genuinely unrecoverable part, which is what C7 puts in the audit row.
|
||||
// The DELETE beside it stays refused.
|
||||
const { user } = await requireRecordAuth("manage", "medications");
|
||||
const { user, authMethod } = await requireRecordAuth(
|
||||
"manage",
|
||||
"medications",
|
||||
);
|
||||
|
||||
const { id } = await params;
|
||||
// v1.5.5 C-E3-3 — route ownership check through the shared helper
|
||||
@@ -1061,7 +1064,24 @@ export const PUT = apiHandler(
|
||||
if (projected) medication = { ...medication, ...projected };
|
||||
}
|
||||
|
||||
if (category !== undefined) await setMedicationCategory(id, category);
|
||||
// v1.40 (#1041) — an iPhone app build that predates custom categories
|
||||
// decodes an unknown category as OTHER and sends it back on every edit,
|
||||
// which would overwrite the person's own category with OTHER. A native
|
||||
// client (Bearer) sending OTHER over a custom category keeps the custom
|
||||
// one; moving a custom category back to OTHER stays possible on the web.
|
||||
let categoryToWrite = category;
|
||||
if (category === "OTHER" && authMethod === "bearer") {
|
||||
const current = (await resolveMedicationCategories([id]))[id];
|
||||
if (current?.category.startsWith("custom:")) {
|
||||
categoryToWrite = undefined;
|
||||
annotate({
|
||||
action: { name: "medication.update.custom_category_kept" },
|
||||
meta: { entity_id: id },
|
||||
});
|
||||
}
|
||||
}
|
||||
if (categoryToWrite !== undefined)
|
||||
await setMedicationCategory(id, categoryToWrite);
|
||||
const resolvedCategory = (await resolveMedicationCategories([id]))[id];
|
||||
const normalizedCategory = resolvedCategory?.category ?? "OTHER";
|
||||
|
||||
|
||||
@@ -192,6 +192,49 @@ describe("custom medication categories", () => {
|
||||
expect(body.data).toMatchObject({ category: key, categoryLabel: "Heart" });
|
||||
});
|
||||
|
||||
it("keeps a custom category when an older iPhone build sends OTHER back", async () => {
|
||||
const key = await createCategory("Heart");
|
||||
const created = await (await createMedication(key)).json();
|
||||
const { hashToken } = await import("@/lib/auth/hmac");
|
||||
const raw = "hlk_custom-category-native-0000";
|
||||
await getPrismaClient().apiToken.create({
|
||||
data: {
|
||||
userId: OWNER,
|
||||
name: "native",
|
||||
tokenHash: hashToken(raw),
|
||||
permissions: ["*"],
|
||||
},
|
||||
});
|
||||
cookieJar.clear();
|
||||
headerJar.set("authorization", `Bearer ${raw}`);
|
||||
const { PUT } = await import("@/app/api/medications/[id]/route");
|
||||
const res = await PUT(
|
||||
req("PUT", `/api/medications/${created.data.id}`, {
|
||||
name: "Renamed",
|
||||
category: "OTHER",
|
||||
}),
|
||||
{ params: Promise.resolve({ id: created.data.id }) },
|
||||
);
|
||||
const body = await res.json();
|
||||
expect(res.status, JSON.stringify(body)).toBe(200);
|
||||
expect(body.data).toMatchObject({ category: key, categoryLabel: "Heart" });
|
||||
headerJar.clear();
|
||||
await signIn(OWNER);
|
||||
});
|
||||
|
||||
it("moves a custom category back to OTHER when the web asks", async () => {
|
||||
const key = await createCategory("Heart");
|
||||
const created = await (await createMedication(key)).json();
|
||||
const { PUT } = await import("@/app/api/medications/[id]/route");
|
||||
const res = await PUT(
|
||||
req("PUT", `/api/medications/${created.data.id}`, { category: "OTHER" }),
|
||||
{ params: Promise.resolve({ id: created.data.id }) },
|
||||
);
|
||||
const body = await res.json();
|
||||
expect(res.status, JSON.stringify(body)).toBe(200);
|
||||
expect(body.data.category).toBe("OTHER");
|
||||
});
|
||||
|
||||
it("renames, hides and caps", async () => {
|
||||
const key = await createCategory("Heart");
|
||||
const { PATCH } =
|
||||
|
||||
Reference in new issue
Block a user