mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
From the campaign's dead-code + code-health audits (grep-verified; the custom-metrics query-key finding was verified-false and excluded): - sleep-overview empty-state CTA linked to /settings/devices, not a valid settings slug (dynamicParams=false → 404). Repointed to /settings/integrations (where every device OAuth callback already lands). - six live, iOS-consumed routes were absent from docs/api/openapi.yaml — auth/check-user, insights/cards, integrations/healthkit (GET+PATCH), measurement-categories, medications/intake/bulk, mood-entries/bulk — registered in the Zod OpenAPI registry mirroring each handler's real shape. - mood/labs/medications module pages lacked the redirect-when-off self-guard their siblings have; added, mirroring cycle/achievements exactly. No migration. Verifier REJECTED the custom-metrics bare-key finding (they are invalidateQueries prefix filters, not colliding useQuery keys).
116 lines
3.9 KiB
TypeScript
116 lines
3.9 KiB
TypeScript
/**
|
|
* OpenAPI route table — third-party integration config (HealthKit).
|
|
*
|
|
* Part of the OpenAPI route table; aggregated in `./index.ts`.
|
|
* Response DTOs are declared here mirroring the route handler under
|
|
* `src/app/api/integrations/healthkit/route.ts`; the request schema
|
|
* mirrors the handler's `patchSchema`.
|
|
*/
|
|
import { z } from "zod/v4";
|
|
import type { ZodOpenApiObject } from "zod-openapi";
|
|
import { dataEnvelope, stdResponses } from "./shared";
|
|
|
|
// Mirror the route's `directionEnum` — per-metric sync direction.
|
|
const healthKitDirectionEnum = z
|
|
.enum(["bidirectional", "readOnly", "writeOnly", "disabled"])
|
|
.describe("Per-metric sync direction.");
|
|
|
|
// Resolved entry (defaults merged): `kind` + `enabled` are always present.
|
|
const healthKitEntry = z
|
|
.object({
|
|
id: z.string().describe("Stable metric key (e.g. `bodyMass`)."),
|
|
kind: z.string().describe("HealthKit sample kind (e.g. `bloodPressure`)."),
|
|
direction: healthKitDirectionEnum,
|
|
enabled: z.boolean(),
|
|
})
|
|
.meta({
|
|
id: "HealthKitEntry",
|
|
description:
|
|
"One resolved HealthKit metric mapping (defaults merged with the user's stored overrides).",
|
|
});
|
|
|
|
const healthKitConfigResponse = z
|
|
.object({
|
|
entries: z.array(healthKitEntry),
|
|
lastSyncedAt: z.iso
|
|
.datetime({ offset: true })
|
|
.nullable()
|
|
.describe(
|
|
"When HealthKit last synced for this user; null when never (and always null on the PATCH echo).",
|
|
),
|
|
})
|
|
.meta({
|
|
id: "HealthKitConfigResponse",
|
|
description:
|
|
"The resolved HealthKit integration config: the default metric set merged with the user's stored per-metric overrides.",
|
|
});
|
|
|
|
// Mirror the route's `patchSchema` — merge-by-id; unknown ids are ignored.
|
|
const healthKitPatchEntry = z.object({
|
|
id: z.string().min(1).max(64),
|
|
kind: z.string().min(1).max(64).optional(),
|
|
direction: healthKitDirectionEnum,
|
|
enabled: z.boolean().optional(),
|
|
});
|
|
|
|
const healthKitPatchRequest = z
|
|
.object({
|
|
entries: z.array(healthKitPatchEntry).max(50),
|
|
})
|
|
.meta({
|
|
id: "HealthKitConfigPatchRequest",
|
|
description:
|
|
"Merge-by-`id` update of the HealthKit metric config. Unknown ids are silently ignored; omitted fields fall back to the stored (or default) value for that entry. Up to 50 entries per call.",
|
|
});
|
|
|
|
export const integrationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
|
"/api/integrations/healthkit": {
|
|
get: {
|
|
tags: ["Integrations"],
|
|
summary: "Read the HealthKit integration config",
|
|
description:
|
|
"Returns the resolved per-metric HealthKit config — the default metric set merged with the user's stored overrides — plus the last HealthKit sync instant. Auth via cookie or Bearer.",
|
|
responses: {
|
|
"200": {
|
|
description: "Resolved HealthKit config.",
|
|
content: {
|
|
"application/json": {
|
|
schema: dataEnvelope(
|
|
healthKitConfigResponse,
|
|
"HealthKitConfigEnvelope",
|
|
),
|
|
},
|
|
},
|
|
},
|
|
...stdResponses,
|
|
},
|
|
},
|
|
patch: {
|
|
tags: ["Integrations"],
|
|
summary: "Update the HealthKit integration config",
|
|
description:
|
|
"Merges the supplied entries into the stored config by `id` (unknown ids are ignored) and returns the resolved config (defaults merged) so the client always sees a complete metric list. `lastSyncedAt` is null on the echo. Auth via cookie or Bearer.",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": { schema: healthKitPatchRequest },
|
|
},
|
|
},
|
|
responses: {
|
|
"200": {
|
|
description: "Updated + resolved HealthKit config.",
|
|
content: {
|
|
"application/json": {
|
|
schema: dataEnvelope(
|
|
healthKitConfigResponse,
|
|
"HealthKitConfigPatchEnvelope",
|
|
),
|
|
},
|
|
},
|
|
},
|
|
...stdResponses,
|
|
},
|
|
},
|
|
},
|
|
};
|