Unblock postgres_instance_config PATCH (#163)#215
Conversation
Blocked: response shape returns string-encoded numbersMarking this PR as draft and blocked. The request-shape fix in this branch works (PATCH/POST now succeed against the live API per the captured matrix), but the response shape can't be deserialised into the spec-declared types. What we seeFor {"result":{"pgConfig":{"max_connections":"200"},"pgBouncerConfig":{},"message":"…"}}
This affects every strictly-numeric The same asymmetry applies in both directions: requests must send JSON numbers (string-encoded values are rejected), but responses come back string-encoded. So we can't just flip the field types — the model would then serialise wrong on the request side. Why this PR is pausedA client-side workaround (custom Next steps
|
d7b6491 to
f1e661b
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f1e661b. Configure here.
| }, | ||
| ) | ||
| .await?; | ||
| } |
There was a problem hiding this comment.
Probe mutates before stale baseline
Medium Severity
When the CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE is enabled, run_pg_config_probe mutates the Postgres instance configuration before the PATCH round-trip test. This means the round-trip test operates with a stale baseline from an earlier GET, which can lead to incorrect verification and leaves the instance in an altered state for settings other than max_connections.
Reviewed by Cursor Bugbot for commit f1e661b. Configure here.
| .max_connections | ||
| .as_ref() | ||
| .and_then(pg_config_value_as_i64) | ||
| .unwrap_or(100); |
There was a problem hiding this comment.
Wrong default max_connections baseline
High Severity
The test's baseline_max_connections calculation defaults to 100 if the field is missing or unparseable. This can cause the test to PATCH and then reset the live instance's max_connections to an unintended value, altering its actual configuration.
Reviewed by Cursor Bugbot for commit f1e661b. Configure here.
| if env::var("CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE") | ||
| .ok() | ||
| .filter(|s| !s.is_empty()) | ||
| .is_some() |
There was a problem hiding this comment.
Probe env accepts any value
Medium Severity
The CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE environment variable check enables the destructive behavior-matrix probe for any non-empty value, including "0" or "false." This deviates from standard boolean environment variable parsing and can unintentionally trigger twelve live POST/PATCH calls.
Reviewed by Cursor Bugbot for commit f1e661b. Configure here.
Follow-up:
|
The `pgConfig` schema has no `required` array and no field descriptions
start with "Optional", so the resolver marks every property required and
the generated model emits bare `T` fields. Serializing
`PgConfig { max_connections: 200, ..Default::default() }` then sends
zero/null/first-variant for every other property, and the live API
rejects all of them with
`Validation failed for following fields: pg_config.*`. The wrapping
`postgresInstanceConfig` also lists both nested objects as required,
making a partial PATCH unrepresentable.
Flip all 23 `PgConfig` fields and both `PostgresInstanceConfig` nested
fields to `Option<T>` with `skip_serializing_if = "Option::is_none"`,
add matching `OPTIONALITY_EXEMPTIONS` entries (caught automatically as
stale when the spec is fixed upstream), and re-enable the deferred
PATCH round-trip in the postgres integration test. The integration
test also gains a gated `CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE=1`
phase that captures the 6×2 behaviour matrix the upstream report
needs to cite real responses rather than assumptions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The behaviour matrix captured in #163 shows the live API rejects any postgresInstanceConfig PATCH or POST body that omits either nested object: omitting pgBouncerConfig yields `BAD_REQUEST: request body.pgBouncerConfig: 'undefined'`, omitting pgConfig yields the symmetric error. Sending `{}` for either is accepted (matrix body 2 → 200). The previous commit had flipped both to Option<T> with skip_serializing_if so partial PATCHes could send only the changed half, but that shape doesn't match the API. Flip pg_config and pg_bouncer_config on PostgresInstanceConfig back to required (bare T with #[serde(default)] so the default envelope still serialises to `{ "pgConfig": {}, "pgBouncerConfig": {} }`), drop the two corresponding OPTIONALITY_EXEMPTIONS entries, and update the fixture/integration tests to construct the new shape. Inner pgConfig fields stay Option<T> with skip_serializing_if — those remain opt-in per the spec's all-optional partial-update semantics. Response-side deserialisation is still broken (the API returns strictly-numeric pgConfig fields wrapped in JSON strings), which is tracked separately and blocks the integration round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The refreshed OpenAPI snapshot (#224) now types most pgConfig properties as string-or-number (["string","integer"], random_page_cost as ["string","number"]), matching the live API's behaviour of returning numeric values wrapped in JSON strings. Follow the existing PgConfig convention of serde_json::Value for multi-type fields and flip the remaining typed numerics — max_connections, effective_io_concurrency, max_parallel_maintenance_workers, max_parallel_workers, max_parallel_workers_per_gather, max_worker_processes (i64) and random_page_cost (f64) — to Option<serde_json::Value>. This unblocks the GET path: Option<i64> failed to deserialize the string-wrapped values the live endpoint returns. The enum-backed plain string fields (default_transaction_isolation, ssl_min_protocol_version, wal_compression) keep their enum types. Also re-add the "pgBouncerConfig": {} assertion to the POST/PATCH body_partial_json matchers in client_test.rs — always sending both nested objects is load-bearing (#163) — and make the integration round-trip tolerant of string-wrapped numbers via a shared extractor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f1e661b to
519c306
Compare


Summary
PgConfigfield (23) plus bothPostgresInstanceConfignested fields (pgConfig,pgBouncerConfig) toOption<T>withskip_serializing_if = "Option::is_none", soClient::postgres_instance_config_patchcan express a real partial update. Pair the change with 25OPTIONALITY_EXEMPTIONSentries inspec_coverage_test.rs; the staleness detector will tell us automatically when upstream fixes the spec.integration_postgres_test.rs(GET baseline → PATCHmax_connections→ poll-until-applied → PATCH back).CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE=1probe phase that fires the 6 × 2 behaviour-matrix scenarios from the issue's follow-up comment via rawreqwestand prints a markdown table on stderr. Default off so it doesn't run on every integration test.Closes #163.
Follow-ups
OPTIONALITY_EXEMPTIONSentries become stale andfield_optionality_matches_specwill flag them — remove the block.Test plan
cargo build --tests(whole workspace)cargo clippy --testscargo test(all non-ignored tests pass)cargo test -p clickhouse-cloud-api --test spec_coverage_test field_optionality_matches_live_spec -- --ignored— 25 new exemptions all register against the live spec, no stale entriescargo test -p clickhouse-cloud-api --test integration_postgres_test -- --ignored --nocapture(live cloud creds required)CLICKHOUSE_CLOUD_POSTGRES_CONFIG_PROBE=1 cargo test -p clickhouse-cloud-api --test integration_postgres_test -- --ignored --nocapture— capture behaviour matrix for upstream🤖 Generated with Claude Code