From 38baafcdd7bea628af563db2f7e34e93bed81751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 23 Oct 2026 14:00:05 +0200 Subject: [PATCH] fix(openapi): admit the bare-instant route samples older tracks carry A track stored before the object form holds one ISO instant per point, and the detail answers it as stored. The route-sample declaration accepts both forms. --- docs/api/openapi.yaml | 34 +++++++++++++++++------------- src/lib/openapi/routes/workouts.ts | 15 +++++++------ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index b06996210..f53659552 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -50492,24 +50492,28 @@ components: anyOf: - type: array items: - type: object - properties: - t: - type: string + anyOf: + - 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: 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)." + description: "One entry per geometry point, as the track was stored: a bare instant, or (from the workout batch and the + Apple Health export) an object with 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 dce40db64..9483839fc 100644 --- a/src/lib/openapi/routes/workouts.ts +++ b/src/lib/openapi/routes/workouts.ts @@ -187,15 +187,18 @@ const workoutRouteGeometry = z geometry: z.unknown(), sampleTimestamps: z .array( - z.object({ - t: isoDateTime(), - speedMs: z.number().optional(), - hr: z.number().int().optional(), - }), + z.union([ + isoDateTime(), + 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).", + "One entry per geometry point, as the track was stored: a bare instant, or (from the workout batch and the Apple Health export) an object with its instant and, where recorded, speed (m/s) and heart rate (bpm).", ), }) .meta({ id: "WorkoutRouteGeometry" });