mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
refactor(withings): route sleep sync catch-block through typed classifier
Mirror the activity-sync migration on the sleep path. The sleep catch-block now consumes the shared `recordWithingsSyncFailure` helper instead of re-parsing the error message via the legacy regex chain. Drops the `extractWithingsStatus` + `isWithingsRefreshReauthFailure` imports from sync-sleep.ts; the BL-P3-2 403 short-circuit stays inline at the call site for the same reason it does on the activity path — 403 isn't a documented Withings body-status code. The two sync paths now share one classification surface and behave identically for every documented Withings status taxonomy entry.
This commit is contained in:
1 parent
f469749efc
commit
9432fec242
1 file changed
+24
-18
@@ -46,11 +46,7 @@ import {
|
||||
WithingsApiError,
|
||||
classifyWithingsResponse,
|
||||
} from "./response-classifier";
|
||||
import {
|
||||
extractWithingsStatus,
|
||||
isWithingsRefreshReauthFailure,
|
||||
} from "./sync";
|
||||
import { getValidToken } from "./sync";
|
||||
import { getValidToken, recordWithingsSyncFailure } from "./sync";
|
||||
import {
|
||||
isReauthRequired,
|
||||
parkIntegrationAtReauth,
|
||||
@@ -220,19 +216,29 @@ export async function syncUserSleep(
|
||||
endUnix,
|
||||
);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const status = extractWithingsStatus(message);
|
||||
// v1.4.26 defence-in-depth — see sync-activity.ts. A 403 on a
|
||||
// scope-gated endpoint always means scope-missing or token-revoked.
|
||||
const isReauth =
|
||||
isWithingsRefreshReauthFailure(message) || status === "403";
|
||||
await recordSyncFailure({
|
||||
userId,
|
||||
integration: "withings",
|
||||
kind: isReauth ? "reauth_required" : "transient",
|
||||
message,
|
||||
errorCode: status,
|
||||
});
|
||||
// v1.4.43 W7-B3 — typed-classification path (see sync-activity.ts
|
||||
// for the full rationale). The thrown `WithingsApiError` carries
|
||||
// the classification verdict; `recordWithingsSyncFailure` routes
|
||||
// it through `classifyError`, which falls back to the legacy regex
|
||||
// for un-prototyped errors (pg-boss JSON round-trip).
|
||||
//
|
||||
// BL-P3-2 defence-in-depth — symmetric to sync-activity. A 403 on
|
||||
// this scope-gated endpoint always means scope-missing or
|
||||
// token-revoked; force `reauth_required` so the 3-strike alert
|
||||
// still pages on the unexpected case.
|
||||
const withingsStatus =
|
||||
err instanceof WithingsApiError ? err.withingsStatus : undefined;
|
||||
if (withingsStatus === 403) {
|
||||
await recordSyncFailure({
|
||||
userId,
|
||||
integration: "withings",
|
||||
kind: "reauth_required",
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
errorCode: "403",
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
await recordWithingsSyncFailure(userId, err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user