mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
Extend the Fitbit/Google Health provider with the read path: the dataPoints.list client half, the metrics-bundle sync, the poll + backfill jobs, and the connection-management routes. Builds on the existing OAuth + credentials + token-refresh layer. - client: paginated `fetchDataPoints` walker over `safeFetch` keyed on `nextPageToken`, encoding each data type kebab-case in the path and snake_case in the filter predicate; defensive per-type mappers (finite + positive guards) for weight, body fat, daily SpO2, daily HRV, daily resting heart rate, daily respiratory rate, intraday heart rate, sleep-temperature derivations, and a height profile-seed extractor. `FITBIT_FIELD_MAP` + `mapping.md` are the source of truth. - mapping (no new MeasurementType): weight→WEIGHT, body-fat→BODY_FAT, SpO2→OXYGEN_SATURATION, HRV→HEART_RATE_VARIABILITY (SDNN slot, not HRV_RMSSD), resting-HR→RESTING_HEART_RATE, respiratory-rate→RESPIRATORY_RATE, heart-rate→PULSE, sleep-temp→WRIST_TEMPERATURE, height→User.heightCm when null. Steps/distance/energy/floors/VO2 + sleep + workouts stay for a later wave. - sync: `syncUserFitbit` orchestrator with the reauth short-circuit, the per-data-class 403 soft-skip (the six Restricted bundles grant independently), the all-soft-skipped success guard, and incremental-vs-full windows; `upsertFitbitMeasurements` keyed (userId,type,FITBIT,externalId) with a 24 h overlap for after-the-fact daily re-rolls, recomputing rollups and invalidating status caches. Google refresh tokens do not rotate, so the stored refresh token is kept unless a new one is returned. - jobs: hourly `fitbit-sync` poll (poll-only; Pub/Sub deferred), the self-converging boot backfill, and the daily OAuth-state sweep — every queue registered in both `allQueues` and a `boss.work` binding, staggered off the WHOOP/Withings crons. - routes: status / sync / disconnect mirroring the WHOOP shape. Tests cover the mappers, the casing invariant, `fetchDataPoints`, the orchestrator soft-skip guard, the idempotent upsert, the backfill discovery, and the queue registration; an integration test pins the upsert idempotency, the DAY rollup fold, and the Fitbit-vs-Withings per-source no-collapse against the Postgres testcontainer.