feat: Replace openapi-generator with openapi-to-rust #22

Merged
martin merged 13 commits from openapi-to-rust into master 2026-08-08 07:08:34 +00:00
Owner

Replace openapi-generator with openapi-to-rust

Replace openapi-generator with openapi-to-rust
martin self-assigned this 2026-08-06 19:55:36 +00:00
feat: Migrate to openapi-to-rust
Some checks failed
ci/woodpecker/push/verify Pipeline failed
7b90c27109
fix: Nix build using openapi-to-rust
All checks were successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
574dfe0e83
Collaborator

Review Summary

This revision resolves nearly all prior review findings: the unwrap()/swallowed-end_date paths are now mapped to 400/422 with a ProblemDetail, the override_title shadowing is gone, the $ref indentation is fixed, the devshell ships openapi-to-rust, serve_openapi_yaml serves application/yaml again, CI installs the generator only in the Generate step (artifacts persist to Build/Test/Clippy via the shared workspace), and the aws-lc/cmake concern is moot with jsonschema built with default-features = false. The contract-first layout, generated-code wiring, and docs are coherent.

Still open from prior reviews: (1) .nix/openapi-to-rust.nix uses rustPlatform.buildPackage without cargoHash/cargoLock/cargoVendorDir, so a sandboxed nix build/nix develop will attempt to fetch crates over the network and fail; (2) {{CARGO_PKG_VERSION}} is substituted only at serve-time in serve_openapi_yaml, while codegen runs against the raw placeholder (latent today, but input/served-spec divergence).

New findings on this revision:

  • EventRequest::try_new computes the default end as (start + ONE_YEAR).date() (src/server.rs:62). start_date_time is only validated against format: date-time, which does not bound the year, so a value like 9999-12-31T00:00:00Z without end_date overflows chrono's range and panics → 500 instead of a 4xx (the same panic-on-overflow pattern remains in date_service.rs:17's checked_add_months(...).unwrap()).
  • Both handlers now share parse_date_or_date_time for end_date, but the spec still types /'s end_date as format: date and /ics's as format: date-time. Since router-level format validation demonstrably runs (the new 422 tests), one acceptance branch is dead per endpoint, and /ics?end_date=2025-03-15 is rejected with 422 even though the handler's parser would accept it — the endpoints behave stricter than the shared parser suggests.
  • Test names are stale: get_calendar_missing_required_params_returns_400 and get_calendar_invalid_recurring_returns_400 assert UNPROCESSABLE_ENTITY, and the new ICS tests are named ..._returns_bad_request while asserting 422.

Verdict: changes — mostly cosmetic, but the overflow panic deserves a checked_add_days + 400 fix given this PR's input-hardening intent.


*🤖 Review by opencode (opencode/deepseek-v4-flash-free) — verdict: changes

# Review Summary This revision resolves nearly all prior review findings: the `unwrap()`/swallowed-`end_date` paths are now mapped to 400/422 with a `ProblemDetail`, the `override_title` shadowing is gone, the `$ref` indentation is fixed, the devshell ships `openapi-to-rust`, `serve_openapi_yaml` serves `application/yaml` again, CI installs the generator only in the Generate step (artifacts persist to Build/Test/Clippy via the shared workspace), and the aws-lc/cmake concern is moot with `jsonschema` built with `default-features = false`. The contract-first layout, generated-code wiring, and docs are coherent. Still open from prior reviews: (1) `.nix/openapi-to-rust.nix` uses `rustPlatform.buildPackage` without `cargoHash`/`cargoLock`/`cargoVendorDir`, so a sandboxed `nix build`/`nix develop` will attempt to fetch crates over the network and fail; (2) `{{CARGO_PKG_VERSION}}` is substituted only at serve-time in `serve_openapi_yaml`, while codegen runs against the raw placeholder (latent today, but input/served-spec divergence). New findings on this revision: - `EventRequest::try_new` computes the default end as `(start + ONE_YEAR).date()` (`src/server.rs:62`). `start_date_time` is only validated against `format: date-time`, which does not bound the year, so a value like `9999-12-31T00:00:00Z` without `end_date` overflows chrono's range and panics → 500 instead of a 4xx (the same panic-on-overflow pattern remains in `date_service.rs:17`'s `checked_add_months(...).unwrap()`). - Both handlers now share `parse_date_or_date_time` for `end_date`, but the spec still types `/`'s `end_date` as `format: date` and `/ics`'s as `format: date-time`. Since router-level format validation demonstrably runs (the new 422 tests), one acceptance branch is dead per endpoint, and `/ics?end_date=2025-03-15` is rejected with 422 even though the handler's parser would accept it — the endpoints behave stricter than the shared parser suggests. - Test names are stale: `get_calendar_missing_required_params_returns_400` and `get_calendar_invalid_recurring_returns_400` assert `UNPROCESSABLE_ENTITY`, and the new ICS tests are named `..._returns_bad_request` while asserting 422. Verdict: changes — mostly cosmetic, but the overflow panic deserves a `checked_add_days` + 400 fix given this PR's input-hardening intent. --- *🤖 Review by opencode (opencode/deepseek-v4-flash-free) — verdict: **changes** <!-- forgejo-opencode-review -->
clank-bot left a comment

Code review findings from opencode.

Code review findings from opencode. <!-- forgejo-opencode-review -->
.nix/pkgs.nix Outdated
@ -7,0 +10,4 @@
pname = "openapi-to-rust";
version = "0.12.0";
src = openapi-to-rust-src;
cargoLock = "${openapi-to-rust-src}/Cargo.lock";
Collaborator
[bug] `cargoLock` is not a recognized argument for a Crane-backed `rustPlatform.buildPackage` (AGENTS.md states mkRustPlatform is Crane-backed). It will likely be silently ignored, meaning openapi-to-rust's dependencies are not vendored — `nix build` will attempt to fetch crates from the network (failing in the sandbox) or produce a non-hermetic result. If the builder is actually nixpkgs' legacy `buildRustPackage`, `cargoLock` expects `{ lockFile = ...; outputHashes = ...; }`. Please verify which builder is in use and vendor dependencies explicitly (e.g. `vendorCargoDeps`/`cargoVendorDir`).
<!-- forgejo-opencode-review --> [bug] `cargoLock` is not a recognized argument for a Crane-backed `rustPlatform.buildPackage` (AGENTS.md states mkRustPlatform is Crane-backed). It will likely be silently ignored, meaning openapi-to-rust's dependencies are not vendored — `nix build` will attempt to fetch crates from the network (failing in the sandbox) or produce a non-hermetic result. If the builder is actually nixpkgs' legacy `buildRustPackage`, `cargoLock` expects `{ lockFile = ...; outputHashes = ...; }`. Please verify which builder is in use and vendor dependencies explicitly (e.g. `vendorCargoDeps`/`cargoVendorDir`).
@ -20,3 +17,1 @@
- test -s /usr/local/bin/openapi-generator-cli.jar
- printf '#!/bin/sh\nexec java -jar /usr/local/bin/openapi-generator-cli.jar "$@"\n' > /usr/local/bin/openapi-generator-cli
- chmod +x /usr/local/bin/openapi-generator-cli
- cargo install --locked openapi-to-rust --version 0.12.0
Collaborator
[performance] Every step runs `cargo install --locked openapi-to-rust --version 0.12.0`, compiling the generator from source in fresh containers with no shared cargo cache — several minutes per step, three times over. Install once (e.g. a dedicated codegen step that also runs `openapi-to-rust generate`) and let the workspace volume carry the artifacts through Build/Test/Clippy.
<!-- forgejo-opencode-review --> [performance] Every step runs `cargo install --locked openapi-to-rust --version 0.12.0`, compiling the generator from source in fresh containers with no shared cargo cache — several minutes per step, three times over. Install once (e.g. a dedicated codegen step that also runs `openapi-to-rust generate`) and let the workspace volume carry the artifacts through Build/Test/Clippy.
martin marked this conversation as resolved
src/server.rs Outdated
@ -48,0 +39,4 @@
override_title: Option<String>,
) -> GetIcsResponse {
let start = chrono::DateTime::parse_from_rfc3339(&start_date_time)
.unwrap()
Collaborator
[bug] `.unwrap()` on user-controlled `start_date_time`. If the generated router's jsonschema validation is more lenient than `chrono::parse_from_rfc3339` (e.g. accepts a space separator, missing seconds, or no timezone) — or if format validation isn't applied to query params — a malformed value will panic and produce a 500/connection reset instead of a 4xx response. The previous openapi-generator code deserialized `NaiveDateTime` and returned 400 on invalid input. Prefer mapping the parse error to a 400/422 response, and apply the same fix to `get_calendar` (`let start = start.unwrap()...` at line 85).
<!-- forgejo-opencode-review --> [bug] `.unwrap()` on user-controlled `start_date_time`. If the generated router's jsonschema validation is more lenient than `chrono::parse_from_rfc3339` (e.g. accepts a space separator, missing seconds, or no timezone) — or if format validation isn't applied to query params — a malformed value will panic and produce a 500/connection reset instead of a 4xx response. The previous openapi-generator code deserialized `NaiveDateTime` and returned 400 on invalid input. Prefer mapping the parse error to a 400/422 response, and apply the same fix to `get_calendar` (`let start = start.unwrap()...` at line 85).
src/server.rs Outdated
@ -48,0 +41,4 @@
let start = chrono::DateTime::parse_from_rfc3339(&start_date_time)
.unwrap()
.naive_utc();
let end = end_date
Collaborator
[style] `get_ics` parses `end_date` as either `YYYY-MM-DD` or full RFC3339, while `get_calendar` only accepts `YYYY-MM-DD`. The spec types `/ics` `end_date` as `NaiveDateTime` and `/` as `NaiveDate`. If the generated validation enforces those formats, the `%Y-%m-%d` fallback branch in `get_ics` is dead code and `/ics?end_date=2025-03-15` would be rejected with 422; otherwise the two endpoints behave inconsistently. Align the parsing with the spec (or loosen `/` to match).
<!-- forgejo-opencode-review --> [style] `get_ics` parses `end_date` as either `YYYY-MM-DD` or full RFC3339, while `get_calendar` only accepts `YYYY-MM-DD`. The spec types `/ics` `end_date` as `NaiveDateTime` and `/` as `NaiveDate`. If the generated validation enforces those formats, the `%Y-%m-%d` fallback branch in `get_ics` is dead code and `/ics?end_date=2025-03-15` would be rejected with 422; otherwise the two endpoints behave inconsistently. Align the parsing with the spec (or loosen `/` to match).
src/server.rs Outdated
@ -118,3 +129,2 @@
};
let title = if let Some(override_str) = &query.override_condition
&& let Some(title) = &query.override_title
let title = if let Some(override_str) = override_condition
Collaborator
[style] `let Some(title) = override_title` shadows the `title` function parameter, so both arms of the if/else return a value named `title` from different sources. It is correct today, but a reader/refactor could easily invert the behavior (the original code had distinct names: `query.override_title` vs `query.title`). Rename to avoid the shadowing, e.g. bind `override_title` and use an `event_title` result.
<!-- forgejo-opencode-review --> [style] `let Some(title) = override_title` shadows the `title` function parameter, so both arms of the if/else return a value named `title` from different sources. It is correct today, but a reader/refactor could easily invert the behavior (the original code had distinct names: `query.override_title` vs `query.title`). Rename to avoid the shadowing, e.g. bind `override_title` and use an `event_title` result.
Move generate into first step
All checks were successful
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
1ea11fd6b5
clank-bot left a comment

Code review findings from opencode.

Code review findings from opencode. <!-- forgejo-opencode-review -->
@ -8,3 +29,3 @@
src = lib.cleanSource ../.;
src = source;
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
nativeBuildInputs = with pkgs; [
Collaborator
[bug] This PR adds `jsonschema` (0.49) as a direct dependency, which pulls in `reqwest` → `rustls` → `aws-lc-rs`/`aws-lc-sys`. `aws-lc-sys` compiles C sources and requires `cmake` (and `ninja`) at build time. The Nix `nativeBuildInputs` here only provide `autoPatchelfHook` and `pkg-config`, so `nix build` of the app will likely fail in the sandbox when compiling `aws-lc-sys` (cmake not found). Verify and add `cmake`/`ninja` to `nativeBuildInputs` (and ensure the `rust:1.97-bookworm` CI image also has them, or the Build/Test steps will fail too), or configure rustls to use the `ring` provider to drop the aws-lc toolchain. Note this also links a full TLS stack into the server binary for what is only jsonschema's remote-reference support.
<!-- forgejo-opencode-review --> [bug] This PR adds `jsonschema` (0.49) as a direct dependency, which pulls in `reqwest` → `rustls` → `aws-lc-rs`/`aws-lc-sys`. `aws-lc-sys` compiles C sources and requires `cmake` (and `ninja`) at build time. The Nix `nativeBuildInputs` here only provide `autoPatchelfHook` and `pkg-config`, so `nix build` of the app will likely fail in the sandbox when compiling `aws-lc-sys` (cmake not found). Verify and add `cmake`/`ninja` to `nativeBuildInputs` (and ensure the `rust:1.97-bookworm` CI image also has them, or the Build/Test steps will fail too), or configure rustls to use the `ring` provider to drop the aws-lc toolchain. Note this also links a full TLS stack into the server binary for what is only jsonschema's remote-reference support.
martin marked this conversation as resolved
openapi.yaml Outdated
@ -155,3 +155,3 @@
application/json:
schema:
$ref: "#/components/schemas/HealthResponse"
$ref: "#/components/schemas/HealthStatusResponse"
Collaborator
[style] The renamed `$ref` has one extra leading space compared to every other `schema:`/`$ref:` pair in the file (17 vs 16). It still parses (it becomes a nested property of `schema`), but the indentation is inconsistent — collapse it to align with the other schema refs.
<!-- forgejo-opencode-review --> [style] The renamed `$ref` has one extra leading space compared to every other `schema:`/`$ref:` pair in the file (17 vs 16). It still parses (it becomes a nested property of `schema`), but the indentation is inconsistent — collapse it to align with the other schema refs.
src/server.rs Outdated
@ -48,1 +51,4 @@
.map(|dt| dt.date_naive())
})
})
.unwrap_or_else(|| (start + TimeDelta::days(365)).date());
Collaborator
[bug] Both handlers silently swallow an unparseable `end_date`. In `get_ics` (lines 44-54) and `get_calendar` (lines 86-88), if the value fails both `%Y-%m-%d` and RFC3339 parsing, `.and_then(...).unwrap_or_else(...)` silently falls back to `start + 365 days`. A client passing e.g. `end_date=2025-13-45` or `end_date=garbage` gets a full year of events instead of a 4xx — the previous serde-based path rejected unparseable input with 400. This is especially harmful if the generated router's jsonschema validation does not enforce `format: date`/`date-time` on query params (the same uncertainty the existing `unwrap()` concern hinges on). Return a 400/422 when `end_date` is present but unparseable, and add a test — the `%Y-%m-%d` branch of `get_ics` is currently exercised by no test.
<!-- forgejo-opencode-review --> [bug] Both handlers silently swallow an unparseable `end_date`. In `get_ics` (lines 44-54) and `get_calendar` (lines 86-88), if the value fails both `%Y-%m-%d` and RFC3339 parsing, `.and_then(...).unwrap_or_else(...)` silently falls back to `start + 365 days`. A client passing e.g. `end_date=2025-13-45` or `end_date=garbage` gets a full year of events instead of a 4xx — the previous serde-based path rejected unparseable input with 400. This is especially harmful if the generated router's jsonschema validation does not enforce `format: date`/`date-time` on query params (the same uncertainty the existing `unwrap()` concern hinges on). Return a 400/422 when `end_date` is present but unparseable, and add a test — the `%Y-%m-%d` branch of `get_ics` is currently exercised by no test.
martin marked this conversation as resolved
clank-bot left a comment

Code review findings from opencode.

Code review findings from opencode. <!-- forgejo-opencode-review -->
@ -25,0 +25,4 @@
- name: Test
image: docker.io/rust:1.97-bookworm
commands:
- cargo install cargo-nextest --locked
Collaborator
[performance] `cargo install cargo-nextest --locked` is unpinned and recompiles nextest from source in a fresh container on every run (with no shared cargo cache), adding minutes to every pipeline. Pin a specific version and prefer a prebuilt image (or restore the shared cargo cache between steps) so the Test step only builds the crate under test.
<!-- forgejo-opencode-review --> [performance] `cargo install cargo-nextest --locked` is unpinned and recompiles nextest from source in a fresh container on every run (with no shared cargo cache), adding minutes to every pipeline. Pin a specific version and prefer a prebuilt image (or restore the shared cargo cache between steps) so the Test step only builds the crate under test.
@ -61,7 +67,6 @@
devShells.default = mkDevShell {
tooltip = "defaultToolchain";
packages = with pkgs; [
Collaborator
[bug] The devshell previously provided `openapi-generator-cli`, but the replacement `openapi-to-rust` was not added to `devShells.default` packages. Since `just generate` (Justfile:37) invokes `openapi-to-rust` directly and neither the Justfile nor AGENTS.md tells the developer to `cargo install` it, `just generate` fails in the documented `nix develop` environment. Add `openapi-to-rust` to the devshell packages (or have `just generate` install the pinned version on demand).
<!-- forgejo-opencode-review --> [bug] The devshell previously provided `openapi-generator-cli`, but the replacement `openapi-to-rust` was not added to `devShells.default` packages. Since `just generate` (Justfile:37) invokes `openapi-to-rust` directly and neither the Justfile nor AGENTS.md tells the developer to `cargo install` it, `just generate` fails in the documented `nix develop` environment. Add `openapi-to-rust` to the devshell packages (or have `just generate` install the pinned version on demand).
martin marked this conversation as resolved
@ -39,3 +46,3 @@
(
[(http::header::CONTENT_TYPE, "application/yaml")],
recurring_events_core::OPENAPI_YAML,
include_str!("../openapi.yaml").replace("{{CARGO_PKG_VERSION}}", env!("CARGO_PKG_VERSION")),
Collaborator
[style] `{{CARGO_PKG_VERSION}}` is now resolved only at serve-time here, while `openapi-to-rust generate` (in `just generate`, CI, and the Nix `source` derivation) runs against the raw spec containing the literal placeholder. Codegen inputs and the served spec can diverge (e.g. a future generated endpoint embedding `info.version` would emit `{{CARGO_PKG_VERSION}}` verbatim). Consider rendering the version before generation (as `core/build.rs` did) and/or restoring the AGENTS.md note that documents this substitution.
<!-- forgejo-opencode-review --> [style] `{{CARGO_PKG_VERSION}}` is now resolved only at serve-time here, while `openapi-to-rust generate` (in `just generate`, CI, and the Nix `source` derivation) runs against the raw spec containing the literal placeholder. Codegen inputs and the served spec can diverge (e.g. a future generated endpoint embedding `info.version` would emit `{{CARGO_PKG_VERSION}}` verbatim). Consider rendering the version before generation (as `core/build.rs` did) and/or restoring the AGENTS.md note that documents this substitution.
refactor: serve openapi yaml
All checks were successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
928f43833e
clank-bot left a comment

Code review findings from opencode.

Code review findings from opencode. <!-- forgejo-opencode-review -->
@ -18,4 +17,2 @@
recurring_events_core = { package = "recurring-events-core", path = "core" }
axum = { version = "0.8" }
axum-extra = { version = "0.12", features = ["cookie"] }
Collaborator
[style] axum-extra only enables the `cookie` feature, but AGENTS.md documents that the generated router extracts `TypedHeader` (the `typed-header` feature in axum-extra 0.12). Verify the generator's REQUIRED_DEPS were fully merged into Cargo.toml, or enable the feature explicitly — the old `core/Cargo.toml` had `typed-header` and it is easy to lose during this consolidation.
<!-- forgejo-opencode-review --> [style] axum-extra only enables the `cookie` feature, but AGENTS.md documents that the generated router extracts `TypedHeader<Host>` (the `typed-header` feature in axum-extra 0.12). Verify the generator's REQUIRED_DEPS were fully merged into Cargo.toml, or enable the feature explicitly — the old `core/Cargo.toml` had `typed-header` and it is easy to lose during this consolidation.
@ -3,3 +7,3 @@
mod service;
use crate::server::RecurringEventsServer;
use crate::{generated::build_router, server::RecurringEventsServer};
Collaborator
[style] `use crate::{generated::build_router, ...}` imports the router at the top level of the generated module, while the tests (src/server.rs:187) and AGENTS.md both use `crate::generated::server::build_router`. Both can only resolve if the generator re-exports `build_router` at the top level of `mod.rs`; otherwise `main.rs` fails to compile. Pick one canonical path and make the docs and code agree.
<!-- forgejo-opencode-review --> [style] `use crate::{generated::build_router, ...}` imports the router at the top level of the generated module, while the tests (src/server.rs:187) and AGENTS.md both use `crate::generated::server::build_router`. Both can only resolve if the generator re-exports `build_router` at the top level of `mod.rs`; otherwise `main.rs` fails to compile. Pick one canonical path and make the docs and code agree.
src/main.rs Outdated
@ -39,3 +48,2 @@
(
[(http::header::CONTENT_TYPE, "application/yaml")],
recurring_events_core::OPENAPI_YAML,
[(CONTENT_TYPE, APPLICATION_JSON.essence_str())],
Collaborator
[bug] Regression from the old `application/yaml`: the YAML spec is now served with `Content-Type: application/json` (`APPLICATION_JSON.essence_str()`). Swagger UI and strict clients treat an `application/json` response as JSON and fail to parse YAML (see swagger-api/swagger-ui#5325), risking a "Failed to load API definition" on `/docs` and breaking any consumer that dispatches on the content type. Serve `application/yaml`.
<!-- forgejo-opencode-review --> [bug] Regression from the old `application/yaml`: the YAML spec is now served with `Content-Type: application/json` (`APPLICATION_JSON.essence_str()`). Swagger UI and strict clients treat an `application/json` response as JSON and fail to parse YAML (see swagger-api/swagger-ui#5325), risking a "Failed to load API definition" on `/docs` and breaking any consumer that dispatches on the content type. Serve `application/yaml`.
Remove unused deps and fix
All checks were successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
b6c74a181a
clank-bot left a comment

Code review findings from opencode.

Code review findings from opencode. <!-- forgejo-opencode-review -->
@ -29,0 +57,4 @@
start,
end: match end_date {
Some(date) => {
parse_date_or_date_time(&date).ok_or(String::from("Invalid end date"))?
Collaborator
[style] Both endpoints now share `parse_date_or_date_time` for `end_date`, but the spec types `/`'s `end_date` as `format: date` and `/ics`'s as `format: date-time`. Given router-level format enforcement (the 422 tests prove it runs), one branch of this parser is always dead per endpoint, and `/ics?end_date=2025-03-15` gets rejected with 422 even though the handler's own parser accepts it. Align the two `end_date` schemas (or the parser) so the spec, the generated validation, and the handler agree.
<!-- forgejo-opencode-review --> [style] Both endpoints now share `parse_date_or_date_time` for `end_date`, but the spec types `/`'s `end_date` as `format: date` and `/ics`'s as `format: date-time`. Given router-level format enforcement (the 422 tests prove it runs), one branch of this parser is always dead per endpoint, and `/ics?end_date=2025-03-15` gets rejected with 422 even though the handler's own parser accepts it. Align the two `end_date` schemas (or the parser) so the spec, the generated validation, and the handler agree.
@ -29,0 +59,4 @@
Some(date) => {
parse_date_or_date_time(&date).ok_or(String::from("Invalid end date"))?
}
None => (start + ONE_YEAR).date(),
Collaborator
[bug] `(start + ONE_YEAR).date()` panics on overflow. `start_date_time` is validated only against `format: date-time` (which bounds neither the year nor the range), so a request like `start_date_time=9999-12-31T00:00:00Z` with no `end_date` makes chrono's `Add` overflow and panic → 500 instead of a 4xx. This contradicts the PR's intent of hardening user input into 400s. Use `start.checked_add_days(365)` (or `checked_add_signed`) and return a 400 on `None`; the same `unwrap()` on `checked_add_months` in `src/service/date_service.rs:17` (reachable via extreme `start_date_time` with `recurring=Monthly`) is worth the same treatment.
<!-- forgejo-opencode-review --> [bug] `(start + ONE_YEAR).date()` panics on overflow. `start_date_time` is validated only against `format: date-time` (which bounds neither the year nor the range), so a request like `start_date_time=9999-12-31T00:00:00Z` with no `end_date` makes chrono's `Add<TimeDelta>` overflow and panic → 500 instead of a 4xx. This contradicts the PR's intent of hardening user input into 400s. Use `start.checked_add_days(365)` (or `checked_add_signed`) and return a 400 on `None`; the same `unwrap()` on `checked_add_months` in `src/service/date_service.rs:17` (reachable via extreme `start_date_time` with `recurring=Monthly`) is worth the same treatment.
@ -221,7 +263,7 @@ mod tests {
#[tokio::test]
async fn get_calendar_missing_required_params_returns_400() {
Collaborator
[style] Stale test names: `get_calendar_missing_required_params_returns_400` and `get_calendar_invalid_recurring_returns_400` now assert `UNPROCESSABLE_ENTITY`, and the new `get_ics_invalid_start_date_returns_bad_request` / `get_ics_invalid_end_date_returns_bad_request` (lines 312/326) also assert 422 while named "returns_bad_request". Rename to reflect 422 (e.g. `..._returns_422` or `_returns_unprocessable_entity`) so the suite doesn't mislead.
<!-- forgejo-opencode-review --> [style] Stale test names: `get_calendar_missing_required_params_returns_400` and `get_calendar_invalid_recurring_returns_400` now assert `UNPROCESSABLE_ENTITY`, and the new `get_ics_invalid_start_date_returns_bad_request` / `get_ics_invalid_end_date_returns_bad_request` (lines 312/326) also assert 422 while named "returns_bad_request". Rename to reflect 422 (e.g. `..._returns_422` or `_returns_unprocessable_entity`) so the suite doesn't mislead.
martin merged commit 5c6e8df1af into master 2026-08-08 07:08:34 +00:00
martin deleted branch openapi-to-rust 2026-08-08 07:08:34 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
martin/recurring-events!22
No description provided.