feat: domain availability + suggest via spec-generated client#59
Conversation
Adds a `gddy domain` command group with `available` and `suggest`, built on two patterns: - Spec-generated HTTP client: new `domains-client` workspace crate generates a typed reqwest client from a vendored, trimmed OpenAPI 3.0 spec via progenitor at build time (scripts/regenerate-spec.sh converts the upstream Swagger 2.0 Domains spec). Pinned to progenitor 0.11 to keep the workspace on one reqwest 0.12 + ring rustls stack (clean Windows cross-build). - Composite auth: `CompositeAuthProvider` wraps the OAuth provider and, for `domain:*` commands with a configured sso-key, authenticates with `Authorization: sso-key KEY:SECRET`; otherwise it falls back to OAuth (the domain endpoints are OAuth-enabled). Commands declare the `domains.domain:read` scope (per the api-domain-data scope whitelist) so OAuth scope step-up mints a token that carries it. Environment config gains optional per-env `domains_api_url`, `api_key`, `api_secret` (sso-key), resolved with the existing env-var/flag precedence; adds `--api-key`/`--api-secret` global flags and a `rust/examples/environments.toml` template for internal dev/test. Built-in ote/prod OAuth client ids updated; adds RUST_LOG=debug visibility into the resolved OAuth client/endpoints in auth.rs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds domain-availability functionality to the Rust gddy CLI by introducing a new domain command group backed by a spec-generated Domains API client, along with environment + auth plumbing to support either OAuth or an sso-key bypass.
Changes:
- Add
gddy domain availableandgddy domain suggestcommands using a typed client generated from a vendored OpenAPI 3.0 spec. - Introduce
CompositeAuthProviderto choose sso-key auth fordomain:*when configured, otherwise delegate to OAuth/PKCE (including scope step-up). - Extend environment resolution to support
domains_api_urlplus optionalapi_key/api_secret, with config/env-var precedence and global CLI overrides.
Reviewed changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/src/main.rs | Registers the domain module, switches to composite auth, and adds global --api-key/--api-secret flags. |
| rust/src/environments/mod.rs | Adds domains_api_url + optional sso-key creds to resolved environments, including env-var + config precedence and tests. |
| rust/src/domain/mod.rs | Implements domain available and domain suggest commands and response formatting. |
| rust/src/auth.rs | Adds debug logging for resolved OAuth params and implements CompositeAuthProvider with sso-key bypass for domain:*. |
| rust/examples/environments.toml | Provides an internal template showing env config + override options for domains/OAuth. |
| rust/domains-client/src/lib.rs | Exposes the generated Domains client and a helper to build an authenticated reqwest client. |
| rust/domains-client/build.rs | Generates the typed client at build time from the vendored OpenAPI 3.0 spec. |
| rust/domains-client/Cargo.toml | Declares the domains-client crate and pins generator/client dependencies. |
| rust/domains-client/scripts/regenerate-spec.sh | Documents + automates re-vendoring/trimming/converting the upstream spec. |
| rust/domains-client/openapi/domains.oas3.json | Vendored, trimmed OpenAPI 3.0 spec for the two Domains GET endpoints. |
| rust/Cargo.toml | Adds domains-client to the workspace and main crate dependencies. |
| rust/Cargo.lock | Locks new dependencies introduced by the generated client pipeline. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…g fixes Addresses Copilot review on PR #59: - --api-key/--api-secret now require each other (clap `requires`), so a lone flag errors at parse time instead of being silently ignored. - ResolvedEnv/ResolvedDomains/EnvEntry no longer derive Debug; hand-written Debug impls redact the sso-key api_key/api_secret fields to avoid credential leakage via {:?}. - auth debug log adds auth_url_from_env_var for consistency with client_id/token_url. - domain module docs corrected: the endpoints accept sso-key OR OAuth bearer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Copilot round-2 review on PR #59: adds domain_commands_require_auth, mirroring application_list_requires_auth — builds the CLI with no auth provider and asserts `domain available`/`suggest` are rejected at credential resolution (exit 2, provider error) before the handler runs, guarding against accidentally marking them no_auth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Copilot round-3 review on PR #59: - domains-client/src/lib.rs: move the #![allow(...)] onto a `generated` module wrapping the progenitor include! (re-exported via `pub use generated::*`), so the hand-written client_with_auth/BuildError are linted by the workspace again. - regenerate-spec.sh: pin swagger2openapi@7.0.8 so spec regeneration is deterministic (verified the committed domains.oas3.json is unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Copilot round-4 review on PR #59: reword the ResolvedEnv api_key field doc, the CompositeAuthProvider doc, and the environments.toml template so none imply OAuth is unavailable for domain commands or that api_key/api_secret affect non-domain commands. Docs only; no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Copilot round-5 review on PR #59: <ENV>_API_KEY/<ENV>_API_SECRET and the file's api_key/api_secret were applied independently, so an env-var key could combine with a file secret (or vice versa) into a bogus sso-key pair and confusing 401s. Resolve the pair atomically from a single layer (env-var pair wins over the file pair); a partial pair yields no sso-key (domain commands fall back to OAuth), matching the --api-key/--api-secret flag pairing. Adds tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Copilot round-6 review on PR #59: make `env_prefix` public and call it from `log_resolved_oauth` instead of re-deriving the uppercase/`-`→`_` prefix, so the convention has a single authority and can't drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull in the latest cli-engine patch release. No source changes required; build, clippy (-D warnings), and the test suite (149) all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
runruh-godaddy
left a comment
There was a problem hiding this comment.
Just a question about the listed examples in the open source repo
Switch the progenitor build to InterfaceStyle::Builder so operations expose fluent, named setters (e.g. client.suggest().query(..).limit(..).send()) instead of positional args. Call sites in domain/mod.rs now read clearly without an IDE and only set the parameters they use — no more strings of `None` whose meaning is opaque in a diff. No behavior change (verified: request omits unset optional query params). fmt/clippy/test all pass (149). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses deep-review findings on PR #59: - domains-client: add offline httpmock tests for the generated client — available/suggest request shape (query-param names, Authorization/ x-request-id/api-version headers) and response deserialization. The suggest test asserts each scalar lands in the correctly named query param, guarding the 10-positional-argument call against silent drift if the spec is regenerated (most params share Option types). - domain: extract the sso-key-vs-Bearer scheme choice into a pure authorization_header() and unit-test it; the prior inline logic had no coverage. - domain: emit `suggest` results as objects with a `domain` field and add .with_default_fields to both commands, so list output supports --fields/default-field projection (matches the application module). - domain: pin the suggest() positional-argument order in a comment and document format_price's intentional whole-cent truncation (+ test). - main: warn in --api-secret help that a value on the command line is visible in the process list / shell history.
Addresses Copilot round-7 review on PR #59: - format_price formats the sign explicitly and uses unsigned_abs, so sub-unit negatives render correctly (-500_000 -> "-0.50", not "0.50") and i64::MIN can't overflow. Adds negative-value tests. - The api-key override Mutex now recovers the inner value on poison (with a tracing::warn) instead of silently dropping the override, which would have confusingly switched a domain command from sso-key to OAuth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Minor: the PR description is now slightly stale. It still lists "New `rust/examples/environments.toml` template for internal dev/test," but commit |
Passing the sso-key as command-line arguments exposes it via the process list and shell history (CWE-214). Remove the global --api-key/--api-secret flags and the flag->override bridge before they ship in this PR. The sso-key now comes only from the existing, non-CWE-214 sources: the <ENV>_API_KEY/<ENV>_API_SECRET env vars or the gitignored environments.toml entry. - main.rs: remove the two flag definitions and the apply_flags bridge. - auth.rs: remove the API_KEY_OVERRIDE static + set_api_key_override/api_key_override and the lock helper; sso_key_credential now reads only environments::resolve_domains. Env-var/config resolution, the sso-key vs Bearer scheme selection, and the pure credential-factory tests are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The atomic-pair comment in environments::resolve_with referenced the --api-key/--api-secret flags removed earlier in this PR. Reworded to describe only the env-var/config layers (the actual sources). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Merging; if the domains team wants to chime in on this, we can circle back with updates. |
Summary
Adds a
gddy domaincommand group (available,suggest) and establishes two reusable patterns: a spec-generated typed HTTP client and per-environment auth that composes OAuth with an sso-key bypass.domains-clientcrate — generates a typed reqwest client from a vendored, trimmed OpenAPI 3.0 spec viaprogenitorinbuild.rs(scripts/regenerate-spec.shconverts the upstream Swagger 2.0 Domains spec and trims it to the two GET ops). Pinned toprogenitor 0.11so the whole workspace stays on onereqwest 0.12+ ring-based rustls stack — avoidsaws-lc-rsand keeps the Windows-msvc cross-build clean. The crate exposesclient_with_auth(...)so the CLI owns theAuthorizationheader and never crosses the reqwest version boundary.CompositeAuthProvider(auth.rs) — wraps the OAuth provider; fordomain:*commands with a configured sso-key it returnsAuthorization: sso-key KEY:SECRET, otherwise it delegates to OAuth (the domain endpoints are OAuth-enabled). Scoped byreq.commandso other commands are unaffected.domain available/suggestdeclaredomains.domain:read(pergdcorp-domains/api-domain-dataoauthscopewhitelist.json) viaCommandSpec::with_scopes, so OAuth scope step-up mints a token carrying it.EnvEntry/ResolvedEnvgain optionaldomains_api_url(defaults toapi_url) and an sso-key (api_key/api_secret), resolved from the gitignoredenvironments.tomland<ENV>_API_URL/<ENV>_API_KEY/<ENV>_API_SECRETenv vars (env-var pair wins; resolved atomically per layer). Secrets are never accepted as CLI args (CWE-214). Built-in ote/prod OAuth client ids updated.RUST_LOG=gddy=debugnow logs the resolved OAuth client id / auth+token URLs (env-override aware, no secrets).Verification
cargo fmt,cargo clippy --all-targets --workspace -- -D warnings,cargo test --workspace(147 pass),cargo build— all green.regenerate-spec.shreproducesdomains.oas3.jsondeterministically (hermetic build; no network at build time).domain available/suggesthit…/v1/domains/{available,suggest}with correct query params and the rightAuthorizationscheme (sso-keywhen a key is configured, captured on a local server; OAuth bearer otherwise). Honors--env.🤖 Generated with Claude Code