mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
feat(garmin): sync Garmin Connect through the Personal API
Garmin Connect now offers a Personal API: a person creates a key for their own account and any client can buy short-lived tokens with it. HealthLog takes that key in a new Garmin card, checks it with Garmin before storing it sealed on the user row, and polls every hour. - Transport: key exchange with a per-process token cache keyed on the key, one re-exchange on a refused token and then reauth_required, the 429 Retry-After carried to the caller, the China host tried when the global one does not know the key, answers read with a byte cap, all egress through safeFetch to the two fixed hosts. - Sync: daily summaries, sleep summaries, HRV, weigh-ins and the activity search, chunked to each resource's documented range, read one after another and stopped at the first rate limit; today's menstrual cycle summary adds logged period days the person has not logged. - Backfill: a resumable walk through the shared admission lane that learns where the history starts from the daily summaries and the activities, waits out Garmin's rate limit and stamps its marker. - Types: GARMIN source; BODY_BATTERY and STRESS_LEVEL for Garmin's own day scores, kept apart from the computed STRESS_SCORE; TRAINING_READINESS and ACUTE_TRAINING_LOAD for figures a bridge pushes through the ingest token. All four chart on the recovery page and sit on the ladders, units, ranges, labels and report groups like the device-native scores. - Key expiry: learned from a token capped short of a day, else estimated from the validity picked at connect; the card warns a week ahead and asks for a new key once Garmin refuses the old one. - Ledger, health, freshness, watchdog, backup classes, wipe plan, encrypted-column registry, rotation script, OpenAPI and all ten locales carry the new integration. The old "no Garmin connector" note is gone.
This commit is contained in:
1 parent
67909f2d9d
commit
e2ba86b12f
119 files changed
+5798
-400
No files matched your search
@@ -102,6 +102,7 @@ const PROVIDERS = [
|
||||
"oura",
|
||||
"strava",
|
||||
"nightscout",
|
||||
"garmin",
|
||||
] as const;
|
||||
|
||||
function health(overrides: Record<string, unknown> = {}) {
|
||||
@@ -176,7 +177,7 @@ describe("IntegrationsSection — one status display per card", () => {
|
||||
expect(html).not.toContain("Last attempt");
|
||||
// The seven other providers and Apple Health are not connected and say so
|
||||
// once each.
|
||||
expect(count(html, ">Not connected.<")).toBe(8);
|
||||
expect(count(html, ">Not connected.<")).toBe(9);
|
||||
});
|
||||
|
||||
it("never shows the stored provider error or a failure counter", () => {
|
||||
@@ -247,17 +248,12 @@ describe("IntegrationsSection — one status display per card", () => {
|
||||
});
|
||||
|
||||
const html = render();
|
||||
expect(count(html, 'data-slot="integration-setup-guide"')).toBe(8);
|
||||
expect(count(html, 'data-slot="integration-setup-guide"')).toBe(9);
|
||||
for (const provider of PROVIDERS) {
|
||||
expect(html).toContain(`data-testid="${provider}-setup-guide"`);
|
||||
expect(html).toContain(
|
||||
`href="https://docs.healthlog.dev/guides/connect-${provider}/"`,
|
||||
);
|
||||
}
|
||||
// Garmin has no connector; its note goes straight to the section of the
|
||||
// Google Health guide that explains the route, not through a redirect.
|
||||
expect(html).toContain(
|
||||
'href="https://docs.healthlog.dev/guides/connect-google-health/#garmin-owners"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -194,9 +194,9 @@ describe("<HealthRecordExportPanel> — the first run", () => {
|
||||
expect(groupChip(html, "medications")).toBe("0/5");
|
||||
// ALLERGIES, ILLNESS_EPISODES, VISITS, SURGICAL_HISTORY, IMMUNIZATIONS.
|
||||
expect(groupChip(html, "history")).toBe("0/5");
|
||||
expect(groupChip(html, "cardio")).toBe("0/15");
|
||||
expect(groupChip(html, "cardio")).toBe("0/16");
|
||||
expect(groupChip(html, "activity")).toBe("0/12");
|
||||
expect(groupChip(html, "sleepRecovery")).toBe("0/16");
|
||||
expect(groupChip(html, "sleepRecovery")).toBe("0/19");
|
||||
expect(groupChip(html, "mobility")).toBe("0/11");
|
||||
expect(groupChip(html, "environment")).toBe("0/7");
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user