fix(cli-generator): page-all extraction, fail-fast auth, help placeholders, CI clippy; AnyType Default - #17620
Conversation
…lders, CI clippy; fix(rust): AnyType Default Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
AI Review Summary
Solid fix set: page-all merging, fail-fast auth, help placeholders, CI clippy, and unknown Default. A couple of correctness concerns: the fail-fast check lives in build_http_request where it may also trip for dry-run/other paths that previously succeeded, and merge_captured_pages uses per-page heuristics that can silently pick different keys or fall back to raw envelopes; also the new --page-all merge changes the -o/--output / raw formats' payload shape. Docs/tests look consistent.
- 🟡 3 warning(s)
- 🔵 2 suggestion(s)
AI Review
🔵 suggestion — generators/cli/sdk/src/openapi/executor.rs (line 12401)
These new tests sit outside the mod tests { ... } block (the diff shows } closing the module right above at 12399). They compile as top-level items in the non-test build too unless the surrounding file/section is #[cfg(test)]. Confirm they're inside a #[cfg(test)] region; otherwise #[tokio::test]/json! helpers will leak into release builds.
🔵 suggestion — generators/cli/sdk/src/openapi/commands.rs (line 115)
env_var_prefix only strips -. Binary names with ., spaces, or leading digits produce invalid env var names (my.cli → MY.CLI). Consider mapping all non-alphanumerics to _:
binary_name
.to_uppercase()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect()
To request another review, comment /ai-review on this pull request.
| let items_path: Option<String> = match results_path { | ||
| Some(path) if !path.trim().is_empty() => Some(path.to_string()), | ||
| _ => pages | ||
| .iter() | ||
| .find_map(|p| crate::formatter::extract_items(p).map(|(key, _)| key.to_string())), |
There was a problem hiding this comment.
🟡 warning
The heuristic branch picks the key from the first page that yields any array, then requires every page to have an array at that key. If page 1 is empty-ish (e.g. {"items": [], "warnings": ["..."]}) extract_items may select warnings, and subsequent pages then mismatch → you return the raw envelopes. Consider deriving the key per the first page that has a non-empty array and verifying it exists across all pages before committing, or preferring a key present in every page.
| if pagination.page_all && pages_fetched > 0 { | ||
| let results_path = endpoint_pag.map(|p| p.results_path()); | ||
| return Ok(Some(merge_captured_pages(captured_values, results_path))); |
There was a problem hiding this comment.
🟡 warning
This merge now applies to every captured --page-all invocation, including --format raw/http and -o <PATH> binary captures where callers previously received the per-page values verbatim. Worth gating on the output pipeline (only for structured formats) so raw/binary paging output isn't reshaped into a JSON array.
| if auth_metadata.requires_credentials() && !auth_provider.has_credentials_for(auth_metadata) | ||
| { | ||
| return Err(crate::auth::missing_credentials_error( | ||
| auth_provider.as_ref(), | ||
| )); | ||
| } |
There was a problem hiding this comment.
🟡 warning
Placing the fail-fast check inside build_http_request means it also fires for --dry-run (and any other non-sending caller of this function). Previously --dry-run without credentials would print the request; now it errors. If that's intentional, note it in the changelog; otherwise move the check to the send path or skip it when dry-run is set.
SDK Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on Full benchmark table (click to expand)
main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via |
Description
Linear ticket: Refs https://github.com/fern-demo/benchling-cli/pull/1
Fixes the seven generator/runtime defects reported against the generated Benchling CLI (fern-demo/benchling-cli#1) at the source, so they disappear on regeneration rather than requiring hand patches in the generated repo.
Changes Made
Rust model generator (
generators/rust/model)hasDefaultImplnow returnstrueforunknown, matchingStructGenerator.typeSupportsDefault. Aliases ofunknown(AnyType(pub serde_json::Value)) therefore deriveDefault, so containing structs that deriveDefaultcompile. Requiredunknownfields also pick up#[serde(default)]like every otherDefault-able field type.CLI runtime (
generators/cli/sdk)--page-allwith captured output: pages are now merged into one flat item array instead of[envelope, envelope].merge_captured_pagesusesx-fern-pagination.results(PaginationConfig::results_path(), new) and falls back to the formatter'sextract_itemsheuristic; unrecognized shapes are left untouched rather than dropping data.--format csv|table|json|jsonl --page-allnow match the plain-list shape.executor.rschecksAuthProvider::requires_credentials()(new: non-empty security requirements with no anonymous{}alternative) +has_credentials_for()before dispatching and returnsmissing_credentials_error(new shared helper, also used for the 401/403 path) listing the env vars/flags to set. Public and optional-auth ops are untouched.<NAME>_OUTPUT/<NAME>_USER_AGENT_SUFFIXin--format/--user-agent-suffixhelp (rootapp.rs,openapi/commands.rs,graphql/commands.rs,--schemaglobalFlags) are replaced withenv_var_prefix(binary_name)(benchling→BENCHLING_).3.14literal in a formatter test that failedcargo clippy --all-targets(clippy::approx_constant).CLI generator emitters (
generators/cli/src)README/reference: full
--formatlist, correct default (tableon TTY,jsonpiped),--page-allmarked paginated-only,-o, --output <PATH>documented as binary-response-only.Generated
ci.yml: dropRUSTFLAGS: "-A warnings", add aclippyjob (cargo clippy --all-targets),publishnowneeds: [check, clippy, compile, test, version].Unreleased changelogs:
generators/cli/changes/unreleased/fix-page-all-auth-help-ci.yml,generators/rust/sdk/changes/unreleased/fix-any-type-default.yml.Regenerated seed:
rust-sdk/{exhaustive,unknown}(showsDocumentedUnknownType/MyAliasgainingDefault),cli/cli-basic-auth.Updated README.md generator (if applicable)
Testing
test_execute_method_fails_fast_when_required_credentials_missing,merge_captured_pagestests (heuristic, declared dotted path, pre-extracted arrays, unrecognized shape),requires_credentialssemantics. TS:emitPublishWorkflow.test.tsasserts clippy job /needs/ no-A warnings.fern-demo/benchling-cliand ran against a 2-page mock ofGET /dna-sequence/items:Before:
After:
generators/cli/sdk:cargo test --lib1971 passed;cargo clippy --all-targetsreports only pre-existing warnings, none in touched code.pnpm turbo run compile/test --filter @fern-api/cli-generator --filter @fern-api/rust-modelgreen (440 + 18 tests);pnpm formatclean.pnpm seed test --generator rust-sdk --fixture exhaustive --fixture unknownand--generator cli --fixture cli-basic-authpass;seed/rust-sdk/exhaustivecargo buildOK.Link to Devin session: https://app.devin.ai/sessions/d3e6c0a2eab24903ae7765c8a4c54032
Open in Devin Desktop: https://app.devin.ai/desktop/session/d3e6c0a2eab24903ae7765c8a4c54032?variant=devin