mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
refactor(settings): remove the preview-feature opt-ins
The per-account preview switch was kept for the topic overview pages, which were dropped before they shipped. Its one flag was stored, served and backed up, and read by nothing; the column-reader audit listed it as a pending consumer that will not come. Removed completely: - `/api/settings/preview-features` (GET, PATCH) is retired: it answers 410 Gone with `meta.errorCode: route.retired` and `removedIn: 1.46.0`, and the contract publishes it under Retired. The route, its validation schema, the `PreviewFeatures` OpenAPI schemas and the settings module are deleted. - Migration 0485 drops `users.preview_features_json` under a bounded lock wait, with the retry marker. - The backup no longer writes the field. A file from v1.44 or v1.45 that carries it still restores: the schema accepts it and no codec reads it, so nothing is refused or written for it. The backup-parity goldens lose the field and apply one setting fewer. - The data-wipe plan entry and the sharing-surface actor entry go with the route (frozen count 273 -> 272). No client build reads the route: neither iOS repository nor any local checkout references it.
This commit is contained in:
1 parent
f3b9329fb2
commit
2ae23f4ca8
26 files changed
+125
-437
No files matched your search
@@ -2660,7 +2660,6 @@
|
||||
"src/lib/search/__tests__/match.test.ts": 0,
|
||||
"src/lib/search/__tests__/search-records.test.ts": 0,
|
||||
"src/lib/settings/__tests__/module-list-layout.test.ts": 0,
|
||||
"src/lib/settings/__tests__/preview-features.test.ts": 0,
|
||||
"src/lib/sharing/__tests__/account-access-parse.test.ts": 0,
|
||||
"src/lib/sharing/__tests__/account-access.test.ts": 0,
|
||||
"src/lib/sharing/__tests__/account-label.test.ts": 0,
|
||||
|
||||
@@ -124,9 +124,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
@@ -2566,9 +2563,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
|
||||
@@ -63,9 +63,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
@@ -1725,9 +1722,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
"symptomDefinitions": 2,
|
||||
"lifeEvents": 3
|
||||
},
|
||||
"accountSettingsApplied": 62,
|
||||
"accountSettingsApplied": 61,
|
||||
"skipped": {
|
||||
"catalogueKeys": [],
|
||||
"links": 0
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
"symptomDefinitions": 0,
|
||||
"lifeEvents": 0
|
||||
},
|
||||
"accountSettingsApplied": 62,
|
||||
"accountSettingsApplied": 61,
|
||||
"skipped": {
|
||||
"catalogueKeys": [],
|
||||
"links": 0
|
||||
@@ -2759,9 +2759,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
@@ -3029,9 +3026,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
|
||||
@@ -378,7 +378,7 @@
|
||||
"symptomDefinitions": 0,
|
||||
"lifeEvents": 0
|
||||
},
|
||||
"accountSettingsApplied": 62,
|
||||
"accountSettingsApplied": 61,
|
||||
"skipped": {
|
||||
"catalogueKeys": [
|
||||
{
|
||||
@@ -2254,9 +2254,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
@@ -2463,9 +2460,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
|
||||
@@ -894,7 +894,6 @@
|
||||
"documentsLayoutJson": null,
|
||||
"moodTagLayoutJson": null,
|
||||
"moduleListLayoutJson": null,
|
||||
"previewFeaturesJson": null,
|
||||
"activityGoalsJson": null,
|
||||
"glucoseUnit": null,
|
||||
"hasDiabetes": false,
|
||||
|
||||
@@ -1027,7 +1027,6 @@
|
||||
"documentsLayoutJson": null,
|
||||
"moodTagLayoutJson": null,
|
||||
"moduleListLayoutJson": null,
|
||||
"previewFeaturesJson": null,
|
||||
"activityGoalsJson": null,
|
||||
"glucoseUnit": null,
|
||||
"hasDiabetes": false,
|
||||
@@ -1247,7 +1246,6 @@
|
||||
"moodTagLayoutJson": null,
|
||||
"documentsLayoutJson": null,
|
||||
"moduleListLayoutJson": null,
|
||||
"previewFeaturesJson": null,
|
||||
"activityGoalsJson": null,
|
||||
"reportSelectionJson": null,
|
||||
"globalExcludedInjectionSites": [],
|
||||
|
||||
@@ -2374,9 +2374,6 @@
|
||||
"moduleListLayoutJson": {
|
||||
"roundTrip": "rt-User-moduleListLayoutJson"
|
||||
},
|
||||
"previewFeaturesJson": {
|
||||
"roundTrip": "rt-User-previewFeaturesJson"
|
||||
},
|
||||
"activityGoalsJson": {
|
||||
"stepsPerMonth": 250000,
|
||||
"workoutsPerWeek": 4
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/**
|
||||
* The two per-user Settings preferences that live outside the account
|
||||
* payload, end to end against Postgres:
|
||||
*
|
||||
* - `/api/settings/module-list-layout`: defaults without a write, one list
|
||||
* changed per PUT with preserve-when-absent fields and untouched neighbours,
|
||||
* the multi-issue 422 for unknown values, and the column carrying only what
|
||||
* was chosen.
|
||||
* - `/api/settings/preview-features`: all off by default, PATCH keeps the
|
||||
* flags it does not name, an unknown flag or an empty body is refused.
|
||||
* The per-user list layout, a Settings preference that lives outside the
|
||||
* account payload, end to end against Postgres:
|
||||
* `/api/settings/module-list-layout` answers defaults without a write,
|
||||
* changes one list per PUT with preserve-when-absent fields and untouched
|
||||
* neighbours, refuses unknown values with the multi-issue 422, and stores
|
||||
* only what was chosen.
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
@@ -173,51 +170,8 @@ describe("/api/settings/module-list-layout — integration", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("/api/settings/preview-features — integration", () => {
|
||||
const URL = "http://localhost/api/settings/preview-features";
|
||||
|
||||
it("is all off by default and keeps a flag once switched on", async () => {
|
||||
const { userId } = await seedUser();
|
||||
const { GET, PATCH } =
|
||||
await import("@/app/api/settings/preview-features/route");
|
||||
|
||||
const before = await (GET as Handler)(new Request(URL));
|
||||
expect(((await before.json()) as { data: unknown }).data).toEqual({
|
||||
topics: false,
|
||||
});
|
||||
|
||||
const on = await (PATCH as Handler)(json("PATCH", URL, { topics: true }));
|
||||
expect(on.status).toBe(200);
|
||||
expect(((await on.json()) as { data: unknown }).data).toEqual({
|
||||
topics: true,
|
||||
});
|
||||
|
||||
const after = await (GET as Handler)(new Request(URL));
|
||||
expect(((await after.json()) as { data: unknown }).data).toEqual({
|
||||
topics: true,
|
||||
});
|
||||
const row = await getPrismaClient().user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { previewFeaturesJson: true },
|
||||
});
|
||||
expect(row?.previewFeaturesJson).toEqual({ topics: true });
|
||||
});
|
||||
|
||||
it("refuses an unknown flag and an empty body", async () => {
|
||||
await seedUser();
|
||||
const { PATCH } = await import("@/app/api/settings/preview-features/route");
|
||||
const unknown = await (PATCH as Handler)(
|
||||
json("PATCH", URL, { topics: true, darkMagic: true }),
|
||||
);
|
||||
expect(unknown.status).toBe(422);
|
||||
const empty = await (PATCH as Handler)(json("PATCH", URL, {}));
|
||||
expect(empty.status).toBe(422);
|
||||
});
|
||||
});
|
||||
|
||||
describe("settings presentation follows the person, also while acting for a record", () => {
|
||||
const LAYOUT = "http://localhost/api/settings/module-list-layout";
|
||||
const PREVIEW = "http://localhost/api/settings/preview-features";
|
||||
|
||||
async function actingForAnotherRecord() {
|
||||
const prisma = getPrismaClient();
|
||||
@@ -251,10 +205,9 @@ describe("settings presentation follows the person, also while acting for a reco
|
||||
return { ownerId: owner.id, actorId: actor.id };
|
||||
}
|
||||
|
||||
it("reads and saves the actor's own layout and preview switch", async () => {
|
||||
it("reads and saves the actor's own layout", async () => {
|
||||
const { ownerId, actorId } = await actingForAnotherRecord();
|
||||
const layout = await import("@/app/api/settings/module-list-layout/route");
|
||||
const preview = await import("@/app/api/settings/preview-features/route");
|
||||
|
||||
expect((await (layout.GET as Handler)(new Request(LAYOUT))).status).toBe(
|
||||
200,
|
||||
@@ -263,20 +216,12 @@ describe("settings presentation follows the person, also while acting for a reco
|
||||
json("PUT", LAYOUT, { version: 1, list: "labs", view: "cards" }),
|
||||
);
|
||||
expect(put.status).toBe(200);
|
||||
expect((await (preview.GET as Handler)(new Request(PREVIEW))).status).toBe(
|
||||
200,
|
||||
);
|
||||
const patch = await (preview.PATCH as Handler)(
|
||||
json("PATCH", PREVIEW, { topics: true }),
|
||||
);
|
||||
expect(patch.status).toBe(200);
|
||||
|
||||
const rows = await getPrismaClient().user.findMany({
|
||||
where: { id: { in: [ownerId, actorId] } },
|
||||
select: {
|
||||
id: true,
|
||||
moduleListLayoutJson: true,
|
||||
previewFeaturesJson: true,
|
||||
},
|
||||
});
|
||||
const actor = rows.find((r) => r.id === actorId)!;
|
||||
@@ -284,9 +229,7 @@ describe("settings presentation follows the person, also while acting for a reco
|
||||
expect(actor.moduleListLayoutJson).toMatchObject({
|
||||
labs: { view: "cards" },
|
||||
});
|
||||
expect(actor.previewFeaturesJson).toEqual({ topics: true });
|
||||
expect(owner.moduleListLayoutJson).toBeNull();
|
||||
expect(owner.previewFeaturesJson).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ import * as medicationLayout from "@/app/api/medications/layout/route";
|
||||
import * as moodTagLayout from "@/app/api/mood/tags/layout/route";
|
||||
import * as documentsLayout from "@/app/api/documents/inbound/layout/route";
|
||||
import * as moduleListLayout from "@/app/api/settings/module-list-layout/route";
|
||||
import * as previewFeatures from "@/app/api/settings/preview-features/route";
|
||||
import * as activityGoals from "@/app/api/activity/goals/route";
|
||||
import * as healthKit from "@/app/api/integrations/healthkit/route";
|
||||
import { POST as exportHealthRecord } from "@/app/api/export/health-record/route";
|
||||
@@ -888,21 +887,6 @@ function entries(user: Pick<User, "id">): Entry[] {
|
||||
},
|
||||
expected: ["list", "alphaAsc"],
|
||||
},
|
||||
{
|
||||
columns: ["previewFeaturesJson"],
|
||||
write: {
|
||||
handler: previewFeatures.PATCH,
|
||||
method: "PATCH",
|
||||
route: "/api/settings/preview-features",
|
||||
body: { topics: true },
|
||||
},
|
||||
read: get(
|
||||
previewFeatures.GET,
|
||||
"/api/settings/preview-features",
|
||||
"topics",
|
||||
),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
columns: ["activityGoalsJson"],
|
||||
write: {
|
||||
|
||||
Reference in new issue
Block a user