mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 23:12:04 +00:00
fix(oura): ask for the spo2 and heart_health scopes, explain a missing scope calmly
Oura's current authorization server enforces scopes per collection. daily_spo2 needs spo2; vO2_max, daily_cardiovascular_age and daily_resilience need heart_health. The authorize request now asks for both. A grant issued before keeps answering 401 on those four collections while every other one succeeds. Such a 401 next to a success is now recorded as a scope gap rather than a failure: it no longer counts toward the reconnect threshold, it no longer forces a token refresh once known, and the Oura card asks for one reconnect instead. The Withings card names the readings the account's API plan withholds.
This commit is contained in:
1 parent
39b0cabb43
commit
044f9008ba
29 files changed
+824
-101
No files matched your search
@@ -34,6 +34,8 @@ import { z } from "zod/v4";
|
||||
import type { ZodOpenApiObject } from "zod-openapi";
|
||||
import { fitbitCredentialsSchema } from "@/lib/validations/fitbit";
|
||||
import { googleHealthCredentialsSchema } from "@/lib/validations/google-health";
|
||||
import { WITHINGS_PLAN_FEATURES } from "@/lib/withings/plan-features";
|
||||
import { OURA_SCOPE_GATED_COLLECTIONS } from "@/lib/oura/scope";
|
||||
import { ouraCredentialsSchema } from "@/lib/validations/oura";
|
||||
import { polarCredentialsSchema } from "@/lib/validations/polar";
|
||||
import { stravaCredentialsSchema } from "@/lib/validations/strava";
|
||||
@@ -925,12 +927,24 @@ const integrationEnvelopeEntry = z
|
||||
backfillCompleted: z.boolean().nullable().optional(),
|
||||
scope: z.string().nullable().optional().describe("Withings only."),
|
||||
hasActivityScope: z.boolean().optional().describe("Withings only."),
|
||||
planUnavailable: z
|
||||
.array(z.enum(WITHINGS_PLAN_FEATURES))
|
||||
.optional()
|
||||
.describe(
|
||||
"Withings only. The readings this connection's Withings API plan withholds (status 277 since the 2026-10-12 plan split), deduplicated and in display order. Empty when the plan refuses nothing. The sync skips these and keeps importing everything else; the list refreshes when the weekly re-probe asks again.",
|
||||
),
|
||||
needsReauth: z.boolean().optional().describe("Google Health only."),
|
||||
available: z.boolean().optional().describe("Oura / Polar / Strava only."),
|
||||
hasOwnCredentials: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Oura / Polar / Strava only."),
|
||||
scopeReconnect: z
|
||||
.array(z.enum(OURA_SCOPE_GATED_COLLECTIONS))
|
||||
.optional()
|
||||
.describe(
|
||||
"Oura only. Collections the stored grant does not cover: they answered 401 while sibling collections succeeded with the same token. Not a failure and not counted toward the reconnect threshold; one reconnect asks for the missing scopes. Empty when the grant covers everything HealthLog reads.",
|
||||
),
|
||||
hasToken: z.boolean().optional().describe("Nightscout only."),
|
||||
allowPrivateHost: z.boolean().optional().describe("Nightscout only."),
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user