From ab15096a6a616333a06ebd47dc3d79e331150b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 23 Oct 2026 13:57:17 +0200 Subject: [PATCH] fix(openapi): declare the route samples a workout detail answers WorkoutRouteGeometry.sampleTimestamps was declared as an array of strings. Both track ingests (the workout batch and the Apple Health export) store one object per point, { t, speedMs?, hr? }, and the detail answers them as stored. The response is unchanged; the contract now says what it holds. --- docs/api/openapi.yaml | 18 +++++++++++++++++- src/lib/openapi/routes/workouts.ts | 13 ++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index a059cffe2..b06996210 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -50492,8 +50492,24 @@ components: anyOf: - type: array items: - type: string + type: object + properties: + t: + type: string + format: date-time + pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ + speedMs: + type: number + hr: + type: integer + minimum: -9007199254740991 + maximum: 9007199254740991 + required: + - t + additionalProperties: false - type: "null" + description: "One entry per geometry point, as the track was ingested: its instant and, where recorded, speed (m/s) and + heart rate (bpm)." required: - geometry - sampleTimestamps diff --git a/src/lib/openapi/routes/workouts.ts b/src/lib/openapi/routes/workouts.ts index 1fec9ab70..dce40db64 100644 --- a/src/lib/openapi/routes/workouts.ts +++ b/src/lib/openapi/routes/workouts.ts @@ -185,7 +185,18 @@ const workoutListResponse = z const workoutRouteGeometry = z .object({ geometry: z.unknown(), - sampleTimestamps: z.array(z.string()).nullable(), + sampleTimestamps: z + .array( + z.object({ + t: isoDateTime(), + speedMs: z.number().optional(), + hr: z.number().int().optional(), + }), + ) + .nullable() + .describe( + "One entry per geometry point, as the track was ingested: its instant and, where recorded, speed (m/s) and heart rate (bpm).", + ), }) .meta({ id: "WorkoutRouteGeometry" });