mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
Merge branch 'v143/dawarich' into release/v1.43.0
# Conflicts: # src/__tests__/contract-stub-inventory.test.ts
This commit is contained in:
commit
da860aeb3e
101 files changed
+5354
-216
No files matched your search
@@ -127,7 +127,14 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
|
||||
],
|
||||
"cycle-prefs": ["cycle-prefs.body.invalid_json", "cycle-prefs.invalid"],
|
||||
"disable-coach": ["disable-coach.body.invalid_json"],
|
||||
dawarich: ["dawarich.invalid", "dawarich.rate_limited"],
|
||||
dawarich: [
|
||||
"dawarich.credentials_rejected",
|
||||
"dawarich.home_place_unknown",
|
||||
"dawarich.invalid",
|
||||
"dawarich.not_connected",
|
||||
"dawarich.rate_limited",
|
||||
"dawarich.unreachable",
|
||||
],
|
||||
day: ["day.invalid"],
|
||||
demo: ["demo.readOnly"],
|
||||
documents: [
|
||||
@@ -193,6 +200,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
|
||||
"environment.no_home",
|
||||
"environment.range_too_large",
|
||||
"environment.travel.not_found",
|
||||
"environment.travel_suggestion.not_found",
|
||||
],
|
||||
export: ["export.selection.empty", "export.selection.unknown_leaf"],
|
||||
familyHistory: ["familyHistory.invalid"],
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
dataEnvelope,
|
||||
errorEnvelope,
|
||||
moduleDisabledResponse,
|
||||
notImplementedResponse,
|
||||
stdResponses,
|
||||
} from "./shared";
|
||||
|
||||
@@ -76,7 +75,6 @@ const responses = (
|
||||
...moduleDisabledResponse,
|
||||
...extra,
|
||||
...stdResponses,
|
||||
...notImplementedResponse,
|
||||
});
|
||||
|
||||
const errorOnly = (description: string) => ({
|
||||
@@ -165,27 +163,33 @@ export const dawarichPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Integrations"],
|
||||
summary: "Dawarich home place and country (v1.43)",
|
||||
description:
|
||||
"422 `dawarich.invalid`; 422 `dawarich.home_place_unknown` for a place id the instance does not have.",
|
||||
"422 `dawarich.invalid`; 422 `dawarich.home_place_unknown` for a place id the instance does not have; 409 `dawarich.not_connected`; 502 `dawarich.unreachable` when the place cannot be checked. A new home country re-judges the stored travel days at once; a new home place fetches the stored days again in the background (up to a year).",
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: { "application/json": { schema: dawarichSettings } },
|
||||
},
|
||||
responses: responses({
|
||||
"200": {
|
||||
description: "The settings after the change.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: dataEnvelope(
|
||||
z.object({
|
||||
homePlaceId: z.number().int().nullable(),
|
||||
homeCountry: z.string().nullable(),
|
||||
}),
|
||||
"DawarichSettingsEnvelope",
|
||||
),
|
||||
responses: responses(
|
||||
{
|
||||
"200": {
|
||||
description: "The settings after the change.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: dataEnvelope(
|
||||
z.object({
|
||||
homePlaceId: z.number().int().nullable(),
|
||||
homeCountry: z.string().nullable(),
|
||||
}),
|
||||
"DawarichSettingsEnvelope",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{
|
||||
"409": errorOnly("`dawarich.not_connected`."),
|
||||
"502": errorOnly("`dawarich.unreachable`."),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
"/api/dawarich/test": {
|
||||
@@ -193,24 +197,29 @@ export const dawarichPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Integrations"],
|
||||
summary: "Probe the Dawarich connection (v1.43)",
|
||||
description:
|
||||
"Answers the shared integration probe shape; a failed probe is an error status. The probe classes are those of the other `/test` routes.",
|
||||
responses: responses({
|
||||
"200": {
|
||||
description: "The probe reached the instance.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: dataEnvelope(
|
||||
z.object({
|
||||
ok: z.literal(true),
|
||||
lastSyncedAt: z.iso.datetime({ offset: true }).nullable(),
|
||||
latencyMs: z.number().int(),
|
||||
}),
|
||||
"DawarichProbeEnvelope",
|
||||
),
|
||||
"Answers the shared integration probe shape. A failed probe is a 502 whose `errorCode` is one of the shared probe classes (`credentials_rejected`, `rate_limited`, `upstream_error`, `timeout`, `url_not_public`, `connection_failed`); 422 `not_configured` without a connection; 429 `rate_limited_self` past five probes a minute.",
|
||||
responses: responses(
|
||||
{
|
||||
"200": {
|
||||
description: "The probe reached the instance.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: dataEnvelope(
|
||||
z.object({
|
||||
ok: z.literal(true),
|
||||
lastSyncedAt: z.iso.datetime({ offset: true }).nullable(),
|
||||
latencyMs: z.number().int(),
|
||||
}),
|
||||
"DawarichProbeEnvelope",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{
|
||||
"502": errorOnly("A probe class; see the description."),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
"/api/dawarich/sync": {
|
||||
@@ -218,7 +227,7 @@ export const dawarichPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Integrations"],
|
||||
summary: "Fetch Dawarich daily values again (v1.43)",
|
||||
description:
|
||||
"From `fromDate` (at most 365 days back). 409 `dawarich.not_connected`; 422 `dawarich.invalid`.",
|
||||
"From `fromDate` (inside the 365 days ending yesterday; without it the nightly window of seven days). `enqueued` is false when a run for this account is already queued or no worker is bound. 409 `dawarich.not_connected`; 422 `dawarich.invalid`.",
|
||||
requestBody: {
|
||||
required: false,
|
||||
content: { "application/json": { schema: dawarichSyncSchema } },
|
||||
@@ -246,7 +255,7 @@ export const dawarichPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Integrations"],
|
||||
summary: "Disconnect Dawarich (v1.43)",
|
||||
description:
|
||||
"Forgets the connection; with `deleteData: true` the stored days go too.",
|
||||
"Forgets the connection; with `deleteData: true` the stored days go too. No body is `{ deleteData: false }`. Disconnecting twice is harmless.",
|
||||
requestBody: {
|
||||
required: false,
|
||||
content: { "application/json": { schema: dawarichDisconnectSchema } },
|
||||
@@ -273,6 +282,8 @@ export const dawarichPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
get: {
|
||||
tags: ["Environment"],
|
||||
summary: "Suggested trips from Dawarich travel days (v1.43)",
|
||||
description:
|
||||
"Runs of at least two consecutive travel days in one country, inside the last year, that no stored trip overlaps and the person has not dismissed; newest first. Empty without a Dawarich connection.",
|
||||
responses: responses({
|
||||
"200": {
|
||||
description: "The suggestions.",
|
||||
|
||||
@@ -1046,6 +1046,7 @@ const integrationEnvelopeEntry = z
|
||||
"oura",
|
||||
"strava",
|
||||
"nightscout",
|
||||
"dawarich",
|
||||
])
|
||||
.describe("Which provider this entry describes."),
|
||||
state: integrationLedgerState,
|
||||
@@ -1121,18 +1122,18 @@ const integrationStatusEnvelope = z
|
||||
metricFreshnessDegraded: z
|
||||
.boolean()
|
||||
.describe(
|
||||
"The per-metric freshness query failed, so EVERY entry's `metricFreshness` is an empty array regardless of what the record actually holds. One flag rather than one per entry, because it is a single query for all eight providers: it fails for all of them or for none. When true, render the freshness section as unavailable rather than as 'no metric has gone quiet' — the two used to be indistinguishable on the wire, which turned the honesty signal into the thing it was built to prevent. The rest of the envelope is unaffected and still complete.",
|
||||
"The per-metric freshness query failed, so EVERY entry's `metricFreshness` is an empty array regardless of what the record actually holds. One flag rather than one per entry, because it is a single query for every provider: it fails for all of them or for none. When true, render the freshness section as unavailable rather than as 'no metric has gone quiet' — the two used to be indistinguishable on the wire, which turned the honesty signal into the thing it was built to prevent. The rest of the envelope is unaffected and still complete.",
|
||||
),
|
||||
integrations: z
|
||||
.array(integrationEnvelopeEntry)
|
||||
.describe(
|
||||
"All eight providers, always, in the fixed order withings, whoop, fitbit, google-health, polar, oura, strava, nightscout. A provider the user has never touched still appears, with `connected: false` and a synthesised `connected` ledger state — absence from this list never means anything, because nothing is ever absent from it.",
|
||||
"All nine providers, always, in the fixed order withings, whoop, fitbit, google-health, polar, oura, strava, nightscout, dawarich. A provider the user has never touched still appears, with `connected: false` and a synthesised `connected` ledger state — absence from this list never means anything, because nothing is ever absent from it.",
|
||||
),
|
||||
})
|
||||
.meta({
|
||||
id: "IntegrationStatusEnvelope",
|
||||
description:
|
||||
"The one read behind the integrations settings surface. It carries every field the per-provider status routes carry, so a client has the choice of eight round-trips or this one; the per-provider routes remain for callers that want a single provider.",
|
||||
"The one read behind the integrations settings surface. It carries every field the per-provider status routes carry, so a client has the choice of nine round-trips or this one; the per-provider routes remain for callers that want a single provider.",
|
||||
});
|
||||
|
||||
export const integrationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
@@ -2551,10 +2552,10 @@ export const integrationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Integrations"],
|
||||
summary: "Read every integration's status in one call",
|
||||
description:
|
||||
"The consolidated envelope behind the integrations settings surface: all eight providers, always, in a fixed order, each with the ledger core, the server-resolved liveness verdict and its own extras. Prefer this over eight per-provider round-trips. Pure read. Auth via cookie or Bearer.",
|
||||
"The consolidated envelope behind the integrations settings surface: all nine providers, always, in a fixed order, each with the ledger core, the server-resolved liveness verdict and its own extras. Prefer this over nine per-provider round-trips. Pure read. Auth via cookie or Bearer.",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "All eight provider entries plus the alert threshold.",
|
||||
description: "All nine provider entries plus the alert threshold.",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: dataEnvelope(
|
||||
|
||||
Reference in new issue
Block a user