feat: planned workouts TypeSpec and OpenAPI spec#5
Conversation
Add TypeSpec definition for the Planned Workouts API including: - Workout template CRUD (create, plan/schedule) - Planned workout management (list, get, update, delete) - Full enum definitions for sport types, intensity types, targets - Coercion warning model for provider compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a TypeSpec-based Terra Workouts API specification and OpenAPI emitter config, updates TypeSpec dependencies, and extends CI/CD to generate, validate, and publish Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Dev as Developer
participant GH as GitHub Actions
participant TS as TypeSpec Emitter
participant Validator as OpenAPI Validator
participant Storage as Release Artifacts
Dev->>GH: push tag / push to main
GH->>TS: run TypeScript + TypeSpec emit -> produce workouts.yaml
TS-->>GH: workouts.yaml
GH->>Validator: validate workouts.yaml in bundle
Validator-->>GH: validation result
GH->>Storage: upload workouts.yaml + other artifacts
GH->>Storage: create release (create_release) with assets
Storage-->>GH: release published
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a foundational shift in how the Planned Workouts API is defined and documented, moving to a TypeSpec-driven approach. This change establishes a single source of truth for the API contract, improving consistency, maintainability, and the developer experience for consumers. The automated generation of the OpenAPI specification from TypeSpec ensures that API documentation remains accurate with code changes. Highlights
Changelog
Ignored Files
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces TypeSpec definitions and a generated OpenAPI specification for the Planned Workouts API. While the changes are well-structured, a significant security concern exists due to an inconsistency in the authorization scoping of the planned workout endpoints. Specifically, the getPlannedWorkout endpoint is missing the user_id parameter, which poses a high risk of Insecure Direct Object Reference (IDOR). Additionally, some read-only fields in the WorkoutTemplate model are not properly marked, potentially leading to mass assignment issues. Further improvements are needed regarding pinning NPM dependencies, refactoring the WorkoutTemplate model for clearer request/response separation, refining date/date-time type definitions, and renaming an enum member to avoid conflicts. Addressing these points will enhance both the security and overall quality of the API specification.
| op getPlannedWorkout( | ||
| ...TerraHeaders, | ||
| @path planned_workout_id: string | ||
| ): PlannedWorkout | { |
There was a problem hiding this comment.
The getPlannedWorkout endpoint (GET /plannedWorkouts/{planned_workout_id}) is missing the user_id query parameter, which is required by all other endpoints that operate on a specific planned workout (e.g., updatePlannedWorkout, deletePlannedWorkout) or list them (listPlannedWorkouts). This inconsistency in the API design creates a high risk of an Insecure Direct Object Reference (IDOR) vulnerability. Without the user_id parameter to scope the request, the backend might not properly verify that the authenticated developer is authorized to access the specific planned workout, potentially allowing unauthorized access to user data if a planned_workout_id is guessed or obtained.
op getPlannedWorkout(
...TerraHeaders,
@path planned_workout_id: string,
@query user_id: string
): PlannedWorkout | {
| "@typespec/json-schema": "latest", | ||
| "@typespec/http": "latest", | ||
| "@typespec/rest": "latest", | ||
| "@typespec/openapi3": "latest" |
There was a problem hiding this comment.
Using "latest" for dependencies can lead to unexpected breaking changes and makes builds non-reproducible. It's a best practice to pin dependencies to specific versions. You can use npm install <package-name>@latest to find the latest version and then pin it in package.json. For example, you could use ^ to allow non-breaking updates.
| "@typespec/json-schema": "latest", | |
| "@typespec/http": "latest", | |
| "@typespec/rest": "latest", | |
| "@typespec/openapi3": "latest" | |
| "@typespec/json-schema": "^1.8.0", | |
| "@typespec/http": "^1.8.0", | |
| "@typespec/rest": "^0.78.0", | |
| "@typespec/openapi3": "^1.8.0" |
| /** Estimated total duration in seconds (read-only, populated on retrieval) */ | ||
| estimated_duration_seconds?: float32; | ||
|
|
||
| /** Estimated total distance in meters (read-only, populated on retrieval) */ | ||
| estimated_distance_meters?: float32; | ||
|
|
||
| /** Estimated total calories (read-only, populated on retrieval) */ | ||
| estimated_calories?: float32; |
There was a problem hiding this comment.
The fields estimated_duration_seconds, estimated_distance_meters, and estimated_calories are described as "read-only" but are not marked with the @visibility("read") decorator. This omission can lead to mass assignment vulnerabilities or data inconsistency if these fields are included in CreateWorkoutTemplateRequest. To prevent this, these read-only fields should be explicitly marked with @visibility("read"). Additionally, consider refactoring the WorkoutTemplate model to separate request and response concerns more clearly, potentially using a base model, a request model, and a response model to avoid including read-only fields in creation requests.
/** Estimated total duration in seconds (read-only, populated on retrieval) */
@visibility("read")
estimated_duration_seconds?: float32;
/** Estimated total distance in meters (read-only, populated on retrieval) */
@visibility("read")
estimated_distance_meters?: float32;
/** Estimated total calories (read-only, populated on retrieval) */
@visibility("read")
estimated_calories?: float32;
| /** Training Stress Score (exact: value) */ | ||
| tss: "tss", | ||
| /** Intensity Factor (exact: value) */ | ||
| `if`: "if", |
There was a problem hiding this comment.
The enum member if is a keyword in many programming languages. While TypeSpec handles this with backticks, it can lead to awkwardly named properties or enums in generated code (e.g., _if, if_). To improve compatibility and clarity, consider renaming it to something more descriptive like intensity_factor.
intensity_factor: "if",
| /** Response from updating a planned workout */ | ||
| model UpdatePlannedWorkoutResponse { | ||
| status: "success"; | ||
| planned_workout_id?: string; |
There was a problem hiding this comment.
In the UpdatePlannedWorkoutResponse model, planned_workout_id is marked as optional. For a successful update operation, it's good practice to return the ID of the resource that was updated to provide clear confirmation to the client. I suggest making this field required in the success response.
planned_workout_id: string;
| created_at: string; | ||
| last_updated_at: string; |
There was a problem hiding this comment.
The created_at and last_updated_at fields are defined as string. It's better to use a more specific format for date-time values. TypeSpec provides the utcDateTime type, which represents an RFC 3339 date-time. This improves type safety and API documentation.
created_at: utcDateTime;
last_updated_at: utcDateTime;
| @query start_date?: string, | ||
| @query end_date?: string |
There was a problem hiding this comment.
The start_date and end_date query parameters in listPlannedWorkouts are defined as strings. To ensure they are in the correct YYYY-MM-DD format as indicated in the endpoint description, you should add a @pattern or @format("date") decorator. This makes the API contract explicit and enables validation.
@query @pattern("^\\d{4}-\\d{2}-\\d{2}$") start_date?: string,
@query @pattern("^\\d{4}-\\d{2}-\\d{2}$") end_date?: string
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
data_models/package.json (1)
40-43: Pin explicit semver ranges for TypeSpec dependencies instead oflatest.All four
@typespecpackages (@typespec/json-schema,@typespec/http,@typespec/rest,@typespec/openapi3) currently uselatest, which creates floating dependencies. While the lockfile pins concrete versions (1.8.0, 1.8.0, 1.8.0, 0.78.0 respectively), usinglatestin the manifest makes future regeneration and lockfile refresh unpredictable—upgrades could pull breaking changes unexpectedly. Pin explicit semver ranges (e.g.,^1.8.0) for stability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@data_models/package.json` around lines 40 - 43, Replace the floating "latest" versions in package.json for the TypeSpec deps with explicit semver ranges matching the current locked versions; update "@typespec/json-schema", "@typespec/http", and "@typespec/rest" from "latest" to a range like "^1.8.0" and "@typespec/openapi3" from "latest" to a range like "^0.78.0" so future installs remain predictable while reflecting the current lockfile versions.data_models/specs/data_models/PlannedWorkouts.tsp (1)
395-396: UseutcDateTimefor timestamp fields to strengthen schema typing.Replace plain
stringwith theutcDateTimescalar forcreated_atandlast_updated_at. This provides stronger type safety and semantic clarity while encoding as RFC 3339 strings in the OpenAPI output by default.Suggested change
- created_at: string; - last_updated_at: string; + created_at: utcDateTime; + last_updated_at: utcDateTime;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 395 - 396, The Created/Updated timestamp fields in the PlannedWorkouts model use plain string types; change the types of created_at and last_updated_at from string to the utcDateTime scalar to strengthen typing and ensure RFC3339-encoded OpenAPI output—update the PlannedWorkouts type definition to reference utcDateTime for those two fields and run any schema/type checks to confirm imports and scalar declarations (ensure the utcDateTime scalar is available in the schema).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@data_models/specs/data_models/PlannedWorkouts.tsp`:
- Around line 238-239: The PlannedWorkouts request model exposes unbounded
arrays (e.g., the intensity_targets?: IntensityTarget[] property) which need
explicit `@maxItems`(...) bounds; update the PlannedWorkouts model to add a
`@maxItems`(limit) decorator on intensity_targets and the other user-supplied
array properties mentioned in the review (the two other array fields around the
same area) using product-aligned limits, ensure the appropriate validation
decorator/import is present, and run/adjust tests or schema generation to
confirm the maxItems constraint is applied.
- Around line 473-474: The start_date and end_date query parameters in
PlannedWorkouts.tsp currently accept any string; add a `@pattern` decorator
enforcing the YYYY-MM-DD format (e.g. regex ^\d{4}-\d{2}-\d{2}$) to both `@query`
start_date and `@query` end_date declarations so the specs validate dates against
the documented format; update the `@query` lines for start_date and end_date in
PlannedWorkouts.tsp to include the `@pattern` constraint.
---
Nitpick comments:
In `@data_models/package.json`:
- Around line 40-43: Replace the floating "latest" versions in package.json for
the TypeSpec deps with explicit semver ranges matching the current locked
versions; update "@typespec/json-schema", "@typespec/http", and "@typespec/rest"
from "latest" to a range like "^1.8.0" and "@typespec/openapi3" from "latest" to
a range like "^0.78.0" so future installs remain predictable while reflecting
the current lockfile versions.
In `@data_models/specs/data_models/PlannedWorkouts.tsp`:
- Around line 395-396: The Created/Updated timestamp fields in the
PlannedWorkouts model use plain string types; change the types of created_at and
last_updated_at from string to the utcDateTime scalar to strengthen typing and
ensure RFC3339-encoded OpenAPI output—update the PlannedWorkouts type definition
to reference utcDateTime for those two fields and run any schema/type checks to
confirm imports and scalar declarations (ensure the utcDateTime scalar is
available in the schema).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 99d55913-6297-4e47-a600-0dc98fd9fa3f
⛔ Files ignored due to path filters (1)
data_models/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
.github/workflows/release.ymldata_models/package.jsondata_models/specs/data_models/PlannedWorkouts.tspdata_models/specs/data_models/tspconfig.yamlworkouts.yaml
| intensity_targets?: IntensityTarget[]; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and examine the relevant sections
find . -name "PlannedWorkouts.tsp" -type fRepository: tryterra/openapi
Length of output: 111
🏁 Script executed:
# Get file content around the mentioned lines to verify the arrays
if [ -f "data_models/specs/data_models/PlannedWorkouts.tsp" ]; then
wc -l "data_models/specs/data_models/PlannedWorkouts.tsp"
echo "=== Lines 235-260 ==="
sed -n '235,260p' "data_models/specs/data_models/PlannedWorkouts.tsp"
echo "=== Lines 275-285 ==="
sed -n '275,285p' "data_models/specs/data_models/PlannedWorkouts.tsp"
fiRepository: tryterra/openapi
Length of output: 1125
🌐 Web query:
TypeSpec @Maxitemsdecorator support and@typespec/openapi3 maxItems emission
💡 Result:
- TypeSpec support:
@maxItems(value)is a built-in TypeSpec decorator. It targets array types (unknown[]) orModelProperty. Example from the docs:@maxItems(5) model Endpoints is string[];and you can also apply it to a property like@maxItems(10) tags: string[];. [2] - @typespec/openapi3 emission: The OpenAPI v3 emitter maps TypeSpec’s built-in
@maxItems(value)to the OpenAPI/JSON Schema keywordmaxItems: valueon the emitted array schema. [1]
Add explicit array bounds on request models.
These user-supplied arrays are unbounded, which can create expensive payloads and validation pressure. Add @maxItems(...) limits aligned with product constraints.
Suggested change
model WorkoutStep {
/** Target intensities for this step (heart rate, power, pace, etc.) */
+ `@maxItems`(20)
intensity_targets?: IntensityTarget[];
}
model StepBlock {
/** Steps within this block */
+ `@maxItems`(200)
steps: WorkoutStep[];
}
model WorkoutTemplate {
/** Blocks of steps that make up the workout */
+ `@maxItems`(50)
step_blocks: StepBlock[];
}Also applies to: 253-254, 278-279
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 238 - 239,
The PlannedWorkouts request model exposes unbounded arrays (e.g., the
intensity_targets?: IntensityTarget[] property) which need explicit
`@maxItems`(...) bounds; update the PlannedWorkouts model to add a
`@maxItems`(limit) decorator on intensity_targets and the other user-supplied
array properties mentioned in the review (the two other array fields around the
same area) using product-aligned limits, ensure the appropriate validation
decorator/import is present, and run/adjust tests or schema generation to
confirm the maxItems constraint is applied.
| @query start_date?: string, | ||
| @query end_date?: string |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the actual file and context
cd /tmp && find . -name "PlannedWorkouts.tsp" -type f 2>/dev/null | head -5Repository: tryterra/openapi
Length of output: 42
🏁 Script executed:
# Search for files in the repository
git ls-files | grep -i plannedworkoutRepository: tryterra/openapi
Length of output: 109
🏁 Script executed:
# Look for TypeSpec files to understand the structure
git ls-files | grep "\.tsp$" | head -20Repository: tryterra/openapi
Length of output: 678
🌐 Web query:
TypeSpec @patterndecorator with@query parameters OpenAPI constraint emission
💡 Result:
In TypeSpec, @pattern(...) is a data validation decorator for string | bytes | ModelProperty (so it can be applied directly to an operation parameter, including one marked with @query). When you emit OpenAPI with @typespec/openapi3, @pattern(regex) is emitted as the OpenAPI/JSON Schema keyword pattern: <regex> in the parameter’s schema. [1], [2]
Example:
import "@typespec/http";
import "@typespec/openapi3";
using Http;
`@pattern`("^[A-Z]{2}$")
scalar StateCode extends string;
op list(`@query` state: StateCode): void;
// (equivalently) op list(`@query` `@pattern`("^[A-Z]{2}$") state: string): void;Emits (conceptually):
parameters:
- in: query
name: state
schema:
type: string
pattern: "^[A-Z]{2}$"Notes:
- The optional
validationMessageon@pattern(pattern, validationMessage?)is emitter-defined (“may choose to use”); OpenAPI emission typically only needs thepatternkeyword. [1]
Sources:
[1] TypeSpec “Built-in Decorators” — @pattern definition/targets and optional message behavior: https://typespec.io/docs/standard-library/built-in-decorators/
[2] TypeSpec “OpenAPI v3 emitter” — validation mapping @pattern(regex) → pattern: regex and @query as query parameter location: https://typespec.io/docs/emitters/openapi3/openapi/
🏁 Script executed:
# Search for existing `@pattern` usage in the codebase
rg "@pattern" --type tsp -B 2 -A 2Repository: tryterra/openapi
Length of output: 87
🏁 Script executed:
# Read the PlannedWorkouts.tsp file to see the context
wc -l data_models/specs/data_models/PlannedWorkouts.tspRepository: tryterra/openapi
Length of output: 113
🏁 Script executed:
# Read the specific section around lines 473-474
sed -n '450,500p' data_models/specs/data_models/PlannedWorkouts.tspRepository: tryterra/openapi
Length of output: 1212
🏁 Script executed:
# Search for date format documentation in the file
grep -n "YYYY-MM-DD\|yyyy-mm-dd\|date format" data_models/specs/data_models/PlannedWorkouts.tspRepository: tryterra/openapi
Length of output: 255
🏁 Script executed:
# Look for similar `@pattern` or date validation in other TypeSpec files
grep -r "YYYY-MM-DD\|yyyy-mm-dd\|pattern.*date" --include="*.tsp" data_models/Repository: tryterra/openapi
Length of output: 393
Apply @pattern constraint to date query parameters.
The endpoint documentation specifies YYYY-MM-DD format (line 466), but the start_date and end_date parameters currently accept arbitrary strings. Apply the @pattern decorator to enforce the documented date format:
op listPlannedWorkouts(
...TerraHeaders,
`@query` user_id: string,
- `@query` start_date?: string,
- `@query` end_date?: string
+ `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") start_date?: string,
+ `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") end_date?: string
): ListPlannedWorkoutsResponse | {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @query start_date?: string, | |
| @query end_date?: string | |
| op listPlannedWorkouts( | |
| ...TerraHeaders, | |
| `@query` user_id: string, | |
| `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") start_date?: string, | |
| `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") end_date?: string | |
| ): ListPlannedWorkoutsResponse | { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 473 - 474,
The start_date and end_date query parameters in PlannedWorkouts.tsp currently
accept any string; add a `@pattern` decorator enforcing the YYYY-MM-DD format
(e.g. regex ^\d{4}-\d{2}-\d{2}$) to both `@query` start_date and `@query` end_date
declarations so the specs validate dates against the documented format; update
the `@query` lines for start_date and end_date in PlannedWorkouts.tsp to include
the `@pattern` constraint.
- Add GET /workouts (list all templates)
- Add GET /workouts/{workout_id} (get template by ID)
- Add DELETE /workouts/{workout_id} (delete template)
- Add WorkoutTemplateWithId model with workout_id for GET responses
- Regenerate workouts.yaml OpenAPI spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
data_models/specs/data_models/PlannedWorkouts.tsp (2)
537-538:⚠️ Potential issue | 🟡 MinorEnforce documented date format on list filters.
start_dateandend_dateare documented asYYYY-MM-DDbut currently accept arbitrary strings (Line [537]-Line [538]).Proposed fix
op listPlannedWorkouts( ...TerraHeaders, `@query` user_id: string, - `@query` start_date?: string, - `@query` end_date?: string + `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") start_date?: string, + `@query` `@pattern`("^\\d{4}-\\d{2}-\\d{2}$") end_date?: string ): ListPlannedWorkoutsResponse | {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 537 - 538, The list filter query parameters start_date and end_date in PlannedWorkouts.tsp currently accept arbitrary strings; update the `@query` parameter validation for start_date and end_date to enforce the documented YYYY-MM-DD format (e.g., validate with a strict regex like /^\d{4}-\d{2}-\d{2}$/ or parse-and-validate using a date utility), return a 400/validation error when the format is invalid, and include the validation on the same symbols (`@query` start_date?: string and `@query` end_date?: string) so only properly formatted dates are accepted.
237-239:⚠️ Potential issue | 🟠 MajorBound user-controlled arrays with explicit
@maxItems.These request-path arrays are currently unbounded (Line [238], Line [253], Line [278]), which allows oversized payloads and weakens validation limits.
Proposed fix
model WorkoutStep { /** Target intensities for this step (heart rate, power, pace, etc.) */ + `@maxItems`(20) intensity_targets?: IntensityTarget[]; } model StepBlock { /** Steps within this block */ + `@maxItems`(200) steps: WorkoutStep[]; } model WorkoutTemplate { /** Blocks of steps that make up the workout */ + `@maxItems`(50) step_blocks: StepBlock[]; }Also applies to: 252-254, 277-279
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 237 - 239, Several request-path array properties in the PlannedWorkouts model (notably intensity_targets: IntensityTarget[] and the other two request arrays flagged in the review) are unbounded; add explicit TypeSpec `@maxItems` annotations to each array declaration (e.g., `@maxItems`(<reasonable_limit>)) to enforce an upper bound, update any related schema/validators to honor the new limits, and run/adjust tests to reflect the new maximums. Ensure you modify the declarations for intensity_targets and the other two array properties in PlannedWorkouts.tsp rather than changing call sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@data_models/specs/data_models/PlannedWorkouts.tsp`:
- Around line 290-297: The CreateWorkoutTemplateRequest currently inherits
WorkoutTemplate which includes computed read-only fields
(estimated_duration_seconds, estimated_distance_meters, estimated_calories)
defined in PlannedWorkouts.tsp; to fix this, introduce a separate input model
(e.g., WorkoutTemplateCreate or WorkoutTemplateInput) that contains only
writable properties and have CreateWorkoutTemplateRequest use that instead of
inheriting WorkoutTemplate, then keep the computed fields only on the output
model (e.g., WorkoutTemplate or WorkoutTemplateResponse) and mark them readOnly
in the schema so they are not accepted in create requests.
---
Duplicate comments:
In `@data_models/specs/data_models/PlannedWorkouts.tsp`:
- Around line 537-538: The list filter query parameters start_date and end_date
in PlannedWorkouts.tsp currently accept arbitrary strings; update the `@query`
parameter validation for start_date and end_date to enforce the documented
YYYY-MM-DD format (e.g., validate with a strict regex like /^\d{4}-\d{2}-\d{2}$/
or parse-and-validate using a date utility), return a 400/validation error when
the format is invalid, and include the validation on the same symbols (`@query`
start_date?: string and `@query` end_date?: string) so only properly formatted
dates are accepted.
- Around line 237-239: Several request-path array properties in the
PlannedWorkouts model (notably intensity_targets: IntensityTarget[] and the
other two request arrays flagged in the review) are unbounded; add explicit
TypeSpec `@maxItems` annotations to each array declaration (e.g.,
`@maxItems`(<reasonable_limit>)) to enforce an upper bound, update any related
schema/validators to honor the new limits, and run/adjust tests to reflect the
new maximums. Ensure you modify the declarations for intensity_targets and the
other two array properties in PlannedWorkouts.tsp rather than changing call
sites.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eba37e53-b49d-431b-a5ed-011bd049cdc6
📒 Files selected for processing (2)
data_models/specs/data_models/PlannedWorkouts.tspworkouts.yaml
| /** Estimated total duration in seconds (read-only, populated on retrieval) */ | ||
| estimated_duration_seconds?: float32; | ||
|
|
||
| /** Estimated total distance in meters (read-only, populated on retrieval) */ | ||
| estimated_distance_meters?: float32; | ||
|
|
||
| /** Estimated total calories (read-only, populated on retrieval) */ | ||
| estimated_calories?: float32; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "PlannedWorkouts.tsp" -type fRepository: tryterra/openapi
Length of output: 111
🏁 Script executed:
cat -n ./data_models/specs/data_models/PlannedWorkouts.tsp | sed -n '280,320p'Repository: tryterra/openapi
Length of output: 1621
🏁 Script executed:
cat -n ./data_models/specs/data_models/PlannedWorkouts.tsp | sed -n '240,300p'Repository: tryterra/openapi
Length of output: 2246
🏁 Script executed:
# Check if there are other request models that might have similar issues
rg "model.*Request.*is.*Workout" ./data_models/specs/data_models/PlannedWorkouts.tspRepository: tryterra/openapi
Length of output: 116
🏁 Script executed:
# Check if there's any OpenAPI schema generation or output that shows the issue
fd -e "json" -e "yaml" -e "yml" | xargs grep -l "CreateWorkoutTemplateRequest" 2>/dev/null | head -5Repository: tryterra/openapi
Length of output: 73
🏁 Script executed:
rg -A 30 "CreateWorkoutTemplateRequest:" ./workouts.yaml | head -50Repository: tryterra/openapi
Length of output: 1029
🏁 Script executed:
rg -A 50 "CreateWorkoutTemplateRequest:" ./workouts.yaml | head -70Repository: tryterra/openapi
Length of output: 1740
Create request exposes read-only computed fields in OpenAPI schema.
CreateWorkoutTemplateRequest inherits from WorkoutTemplate, which includes fields documented as read-only (lines 290–297). The generated OpenAPI schema incorrectly exposes estimated_duration_seconds, estimated_distance_meters, and estimated_calories in the create request, allowing clients to attempt setting computed fields. Separate input and output models so the create request only accepts writable fields.
Proposed refactor
+model WorkoutTemplateInput {
+ name: string;
+ sport: SportType;
+ step_blocks: StepBlock[];
+ description?: string;
+ environment?: EnvironmentType;
+ `@minValue`(0)
+ pool_length_meters?: float32;
+}
+
-model WorkoutTemplate {
+model WorkoutTemplate is WorkoutTemplateInput {
- /** Workout name/title */
- name: string;
- /** Sport/activity type */
- sport: SportType;
- /** Blocks of steps that make up the workout */
- step_blocks: StepBlock[];
- /** Optional workout description */
- description?: string;
- /** Indoor/outdoor/pool environment */
- environment?: EnvironmentType;
- /** For lap_swimming: pool length in meters (can be overridden at scheduling time) */
- `@minValue`(0)
- pool_length_meters?: float32;
/** Estimated total duration in seconds (read-only, populated on retrieval) */
estimated_duration_seconds?: float32;
/** Estimated total distance in meters (read-only, populated on retrieval) */
estimated_distance_meters?: float32;
/** Estimated total calories (read-only, populated on retrieval) */
estimated_calories?: float32;
}
-model CreateWorkoutTemplateRequest is WorkoutTemplate {}
+model CreateWorkoutTemplateRequest is WorkoutTemplateInput {}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@data_models/specs/data_models/PlannedWorkouts.tsp` around lines 290 - 297,
The CreateWorkoutTemplateRequest currently inherits WorkoutTemplate which
includes computed read-only fields (estimated_duration_seconds,
estimated_distance_meters, estimated_calories) defined in PlannedWorkouts.tsp;
to fix this, introduce a separate input model (e.g., WorkoutTemplateCreate or
WorkoutTemplateInput) that contains only writable properties and have
CreateWorkoutTemplateRequest use that instead of inheriting WorkoutTemplate,
then keep the computed fields only on the output model (e.g., WorkoutTemplate or
WorkoutTemplateResponse) and mark them readOnly in the schema so they are not
accepted in create requests.
Summary
PlannedWorkouts.tsp)workouts.yaml)Endpoints covered
POST /workouts— Create workout templatePOST /workouts/{id}/plan?user_id=X— Schedule workout to userGET /plannedWorkouts?user_id=X— List planned workoutsGET /plannedWorkouts/{id}— Get planned workoutPATCH /plannedWorkouts/{id}?user_id=X— Update planned dateDELETE /plannedWorkouts/{id}?user_id=X— Remove planned workoutTODO (follow-up)
GET /workouts,GET /workouts/{id},DELETE /workouts/{id}workout_idto template GET response model🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores