Skip to content

feat(wizard): render schema description, examples, defaults#75

Merged
anoop-narang merged 3 commits into
mainfrom
feat/connection-wizard-schema-metadata
May 4, 2026
Merged

feat(wizard): render schema description, examples, defaults#75
anoop-narang merged 3 commits into
mainfrom
feat/connection-wizard-schema-metadata

Conversation

@anoop-narang
Copy link
Copy Markdown
Contributor

Summary

Two small CLI changes that go alongside runtimedb #408:

  1. feat(wizard): render schema description, examples, and defaults (src/connections_new.rs). The schema-driven connections new wizard previously ignored every JSON Schema annotation except default (and only on plain string fields). Now prompt_field also reads description (rendered as inquire help text on every supported field type — string, password, integer, boolean, array) and examples[0] (rendered as the inquire placeholder for plain strings, integers, and arrays when no default is set; password fields skip the placeholder by design and rely on the description). When a field is optional, the description and "optional — press Enter to skip" hint are concatenated rather than one displacing the other.

  2. fix(api): add request timeout and tcp keepalive (src/api.rs). The blocking HTTP client was built via reqwest::blocking::Client::new() with no explicit configuration. On macOS this surfaces as error sending request when a request is in flight long enough for the OS to drop the quiet TCP connection (e.g. while runtimedb is doing slow synchronous work like ducklake schema discovery against a remote catalog). Add an explicit 5-min request timeout (bounds the worst case if the server genuinely hangs) and a 30s TCP keepalive (keeps the socket warm across long synchronous server work).

Why

The schema-metadata change is what makes runtimedb #408 actually useful in the CLI. Without it, the new descriptions on credentials_json for ducklake / bigquery / snowflake are sent over the wire but never reach the user — the wizard would still show bare credentials_json: prompts. Both changes are generic and schema-driven; no per-connector code anywhere in the CLI.

The timeout fix was hit live while testing the ducklake wizard against a local runtimedb: the wizard's POST landed correctly server-side and the connection was created, but the CLI gave up waiting before discovery returned and printed error sending request. Adding tcp_keepalive is what actually fixes the symptom (prevents the OS from dropping the quiet socket); the explicit timeout is belt-and-suspenders for "what if the server actually hangs."

Wire compatibility

Pure additive client behaviour. Nothing in the wire contract changes:

  • The wizard still POSTs the same shape it did before.
  • The HTTP client still talks plain HTTP/1.1 + JSON. Just keeps the TCP socket alive and bounds the worst case.

What this branch does NOT contain

  • Multi-field auth bundling (which an earlier iteration had). The runtimedb PR keeps multi-credential connectors on a single credentials_json field, so the wizard never has multi-field auth to bundle.
  • Changes to the 9 other Client::new() sites in jwt.rs / auth.rs / skill.rs. We can consolidate them in a follow-up if you want; this PR only touches the construction sites in ApiClient.

Test plan

  • cargo test — 121 tests pass locally.
  • cargo build clean.
  • Manual end-to-end against a locally-running runtimedb on the matching feat/credentials-json-schema-metadata branch:
    • connections create list ducklake --output json — new descriptions/examples/defaults flow through.
    • connections new for ducklake — every prompt now shows description help text; s3_endpoint / s3_region show defaults; credentials_json prompt shows the JSON shape from the description.
    • Connection POST + discovery still completes despite the long-running synchronous discovery (no more error sending request).

Schema-driven prompts in `connections new` previously ignored every
JSON Schema annotation except `default` (and only on plain string
fields). Even when the API attached descriptive metadata to a field,
the wizard still showed bare `field_name:` with the only hint being
"optional — press Enter to skip" when the field was non-required.

Surface that metadata at the prompt:

- `description` is rendered as inquire help text on every supported
  field type (string, password, integer, boolean, array). When the
  field is also optional, the description and the existing
  "optional — press Enter to skip" hint are concatenated rather than
  one displacing the other.
- `examples[0]` is rendered as the inquire placeholder for plain
  strings, integers, and arrays when no explicit `default` is set.
  Password fields skip the placeholder (placeholders alongside
  hidden input read awkwardly in terminals); their description still
  carries any example shape.

This is a generic improvement keyed on schema content, not on
connector type — every connector that adds description / examples to
its connection-types schema picks it up automatically.
The blocking HTTP client was built via reqwest::blocking::Client::new()
with no explicit configuration, which on macOS surfaces as
"error sending request" when a request is in flight long enough for
the OS to drop the quiet TCP connection (e.g. while the server is
doing slow synchronous work like ducklake schema discovery against
a remote catalog).

Add an explicit overall request timeout (5 min) to bound the worst
case if the server genuinely hangs, and a 30s TCP keepalive so the
socket stays warm across long synchronous server work. Both values
live as constants near the helper for clarity.
@sentry
Copy link
Copy Markdown

sentry Bot commented May 4, 2026

Codecov Report

❌ Patch coverage is 16.00000% with 42 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/connections_new.rs 0.00% 41 Missing ⚠️
src/api.rs 88.88% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

claude[bot]
claude Bot previously approved these changes May 4, 2026
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed both changes. api.rs fix is correct and well-motivated. connections_new.rs schema-annotation wiring is clean. One nit on integer example extraction inline.

Comment thread src/connections_new.rs Outdated
Comment on lines +117 to +120
let example = field["examples"]
.as_array()
.and_then(|a| a.first())
.and_then(|v| v.as_str());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: as_str() silently returns None for non-string JSON values, so this works for string fields but not for integer fields whose schemas naturally carry numeric examples (e.g. "examples": [8080]). The PR description says integer placeholders are supported, but a schema like that will silently produce no placeholder.

Fix: widen the extraction to also accept numbers and coerce them to String. Because with_placeholder takes a &str, the owned string needs to live long enough — easiest approach is to compute example as Option<String> from the start:

Suggested change
let example = field["examples"]
.as_array()
.and_then(|a| a.first())
.and_then(|v| v.as_str());
let example: Option<String> = field["examples"]
.as_array()
.and_then(|a| a.first())
.and_then(|v| v.as_str().map(str::to_owned).or_else(|| v.as_i64().map(|n| n.to_string())));

Then use example.as_deref() at every call site. (not blocking)

The previous extraction used `as_str()` only, which silently returned
None for numeric JSON examples. So a schema like
`"examples": [8080]` on an integer field — exactly the kind of example
that would benefit from a placeholder — produced no placeholder
despite the integer branch wiring one up. Widen the extraction to
also accept i64 and coerce to String, then pass through call sites
via `as_deref()`.

No schema in runtimedb today carries numeric examples, so this is a
pure consistency fix — but it aligns the code with what the original
commit's description claimed to support.
@anoop-narang anoop-narang merged commit 12da62f into main May 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant