Skip to content

Add bunny.net Database (libSQL) support (iter-20)#22

Merged
ractive merged 3 commits into
mainfrom
iter-20/database
May 7, 2026
Merged

Add bunny.net Database (libSQL) support (iter-20)#22
ractive merged 3 commits into
mainfrom
iter-20/database

Conversation

@ractive

@ractive ractive commented May 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • New bunny-api-database crate: types + client covering all 26 paths in specs/database.json (Database v1, Database v2, DatabaseGroup, Live Metrics, Config) plus a libSQL data-plane ping that returns a typed PingResult { ok, latency_ms, error }.
  • New hoppy db CLI subtree: list/get/create/delete, fork, restore, versions, ping, statistics, usage, active-usage, live, plus db v2, db group, db token, db config groups.
  • Token redaction by default: db token mint prints <set, length=N> placeholder unless --reveal is passed; same logic for JSON / table / text.
  • Local slug validation (^[a-z][a-z0-9-]{0,23}$) catches the upstream "Internal error" 500 footgun before any HTTP call.
  • Forward-compat enums applied from day one: regions as String, LiveStatus with #[serde(other)] fallback.
  • Docs: README features + env-vars table, full bunny-database-research.md note, quirks updated with v2 create 500, slug-length footgun, libSQL URL preservation, live-metrics custom headers, v1 invalidate vs v2 revoke wart.

Test plan

  • cargo fmt — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace — all pass (21 new crate tests, 10 new CLI tests)
  • hoppy db --help renders correctly with all subcommands
  • Token redaction verified in default and --reveal JSON output
  • Slug validator rejects 25-char and uppercase locally before HTTP
  • Ping test exercises both control-plane (get_database + mint_database_token) and data-plane (/v2/pipeline)
  • Live API smoke test (manual, blocked on bunny credentials)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New top-level db command: database and group CRUD, fork/restore, v2 ops, token mint/revoke, ping health checks, live metrics, usage/stats, and config/optimization suggestions
    • Default token redaction with global --reveal/--reveal-env to show raw tokens
    • Local slug validation to prevent invalid names
  • Documentation

    • README and knowledgebase updated with Database (libSQL) usage and BUNNY_DATABASE_URL override
  • Tests

    • Comprehensive CLI and client tests and fixtures added

Adds a new bunny-api-database crate covering the full Database control
plane (26 paths across config, database v1/v2, group, live metrics) plus
a libSQL data-plane ping convenience. Wires it into a new `hoppy db`
subcommand tree with token redaction by default and local slug validation.

- bunny-api-database: types + client for v1, v2, group, live, config
- LiveStatus tagged enum with #[serde(other)] forward-compat fallback
- Region enums modelled as String for forward-compat (bunny adds silently)
- DatabaseClient::ping rewrites libsql:// → https:// + /v2/pipeline
  and returns a typed PingResult { ok, latency_ms, error }
- CLI: db list/get/create/delete, fork, restore, versions, ping,
  statistics, usage, active-usage, live, v2, group, token, config
- Slug validator rejects upper, leading digit, underscore, len > 24
- Token mint defaults to redacted output; --reveal opts in to raw JWT
- Wiremock + insta tests for crate (21) and CLI (10)
- Docs: README features+envvars, bunny-database-research note,
  bunny-api-quirks updated with v2 500, slug footgun, libSQL URL
  preservation, live-metrics custom headers, invalidate vs revoke

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 93e4b6c3-55b7-42bc-b0ad-2a2dfe8d75c6

📥 Commits

Reviewing files that changed from the base of the PR and between f834656 and bc11512.

📒 Files selected for processing (7)
  • README.md
  • crates/bunny-api-database/src/client.rs
  • crates/bunny-api-database/src/types.rs
  • crates/bunny-api-database/tests/database_api.rs
  • fixtures/database/token_mint.json
  • hoppy-knowledgebase/iterations/iteration-22-test-binary-consolidation.md
  • src/commands/database.rs

📝 Walkthrough

Walkthrough

This PR introduces a new crate bunny-api-database with a typed async DatabaseClient (control-plane v1/v2 and libSQL data-plane ping), integrates it into the CLI under a new db subcommand (CRUD, groups, tokens, ping, stats, live), adds unit/integration tests and JSON fixtures, and updates README and knowledge-base documentation.

Changes

Database API Client and CLI Integration

Layer / File(s) Summary
Workspace / Root manifest
Cargo.toml
Adds crates/bunny-api-database as a workspace member and root path dependency.
Crate manifest
crates/bunny-api-database/Cargo.toml
New crate manifest with runtime deps (reqwest, serde, anyhow, bytes, urlencoding) and dev-deps (tokio, wiremock).
Crate root / exports
crates/bunny-api-database/src/lib.rs
Publishes client and types modules, re-exports DatabaseClient and common types, and includes crate-level documentation/example.
API Data Types
crates/bunny-api-database/src/types.rs
Defines v1/v2 database and group models, Authorization, metrics/usage types, LiveStatus with Unknown fallback, config/region shapes, AppError, and PingResult; includes serde/unit tests.
API Client Core
crates/bunny-api-database/src/client.rs
Implements DatabaseClient with fluent configuration (.with_base_url, .with_debug, .with_record), AccessKey auth injection, request/response helpers, full v1/v2 database and group endpoints, live metrics (adds db-ids/group-ids headers), and libSQL pipeline ping with URL rewrite to /v2/pipeline.
Client helpers & unit tests
crates/bunny-api-database/src/client.rs (tests)
Internal helpers for body capture/recording, typed JSON parsing, check_status flow, libsql_pipeline_url rewrite logic, and unit tests for rewrite/scheme rejection and constructor setters.
CLI enums & mapping
src/cli.rs
Adds Db top-level subcommand and nested enums: DbAction, DbV2Action, DbGroupAction, DbTokenAction, DbConfigAction, and TokenAuthorization → API Authorization mapping.
CLI handlers
src/commands/database.rs, src/commands/mod.rs, src/main.rs
Implements handle(...) dispatch supporting v1/v2/groups/tokens/config/ping, slug validation (^[a-z][a-z0-9-]{0,23}$), table/JSON output, token redaction/reveal via RedactConfig, destructive confirmations, and unit tests for slug validation.
Auth integration
src/auth.rs
Adds get_database_url() env override (BUNNY_DATABASE_URL) and database_client(debug, record) factory applying API key, optional base URL, debug and recording configuration.
Tests & fixtures
crates/bunny-api-database/tests/*, tests/cli_database.rs, fixtures/database/*.json
Extensive client unit tests and CLI integration tests using wiremock and fixtures covering CRUD, token mint/invalidate, config/limits, usage/stats, live metrics header behavior, ping success/failure, local slug validation, POST body forwarding, and JWT redaction/--reveal.
Documentation
README.md, hoppy-knowledgebase/api/*, hoppy-knowledgebase/iterations/*
README documents hoppy db usage, JWT redaction and --reveal, and BUNNY_DATABASE_URL; knowledge-base adds database research and quirks (v2 create 500, slug-length failures, URL preservation, live metrics header requirements, v1/v2 verb differences).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐰 A new database client hops into view,
With libSQL pipelines and tokens that ring true,
Slug checks and redactions, pinging far and near,
CLI speaks JSON and tables crystal clear,
Tests and docs in tow — the bunny's ready to cheer!


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class bunny.net Database (libSQL) support to Hoppy by introducing a new bunny-api-database client crate, wiring it into the CLI as a new hoppy db command tree, and documenting/testing the new functionality.

Changes:

  • Added crates/bunny-api-database (control-plane REST client + data-plane ping, with wiremock tests and fixtures).
  • Added hoppy db CLI subtree (v1/v2 DB ops, groups, tokens, config, live metrics, ping) and integrated it into command dispatch/auth wiring.
  • Added documentation and snapshot/CLI tests for the new database commands.

Reviewed changes

Copilot reviewed 35 out of 36 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/main.rs Routes Commands::Db to the new database command handler.
src/commands/mod.rs Registers the new database command module.
src/commands/database.rs Implements hoppy db handlers, output row mapping, slug validation, ping, and token redaction behavior.
src/cli.rs Adds the db command subtree and associated subcommands/flags.
src/auth.rs Adds BUNNY_DATABASE_URL support and constructs DatabaseClient.
crates/bunny-api-database/Cargo.toml Defines the new database client crate and dependencies.
crates/bunny-api-database/src/lib.rs Public crate entrypoint + re-exports.
crates/bunny-api-database/src/types.rs Adds request/response types for Database v1/v2, live metrics, config, and ping result.
crates/bunny-api-database/src/client.rs Implements REST endpoints + libSQL pipeline URL rewrite and ping.
crates/bunny-api-database/tests/database_api.rs Wiremock tests validating endpoints, headers, query params, and ping behavior.
tests/cli_database.rs End-to-end CLI tests for list/get/create validation, token redaction, config, and ping.
tests/snapshots/*.snap Snapshot baselines for new database CLI outputs.
fixtures/database/*.json Mock API fixtures used by unit/integration tests.
README.md Documents the new Database feature set and BUNNY_DATABASE_URL.
hoppy-knowledgebase/api/bunny-database-research.md Adds a research note describing control-plane vs data-plane and quirks.
hoppy-knowledgebase/api/bunny-api-quirks.md Documents known database API quirks (v2 create 500, slug footgun, etc.).
hoppy-knowledgebase/api/bunny-api-overview.md Updates overview to include database control-plane and libSQL data-plane.
hoppy-knowledgebase/iterations/iteration-20-database.md Updates iteration metadata/status.
Cargo.toml / Cargo.lock Adds the new crate to the workspace and dependency graph.

Comment thread src/commands/database.rs
Comment on lines +243 to +249
DbAction::Delete { id } => {
let resp = client.delete_database(id).await?;
let row = StatusRow {
status: format!("deleted: {}", resp.database),
};
output::print_single(&row, format);
}
Comment thread src/commands/database.rs
Comment on lines +271 to +284
DbAction::Restore { id, version } => {
let resp = client
.restore_database(
id,
&RestoreVersionDatabasePayload {
generation: version.clone(),
},
)
.await?;
let row = StatusRow {
status: format!("restored to generation {}", resp.generation),
};
output::print_single(&row, format);
}
Comment thread src/commands/database.rs
Comment on lines +447 to +451
DbGroupAction::Delete { id } => {
let resp = client.delete_group(id).await?;
let row: GroupRow = (&resp.group).into();
output::print_single(&row, format);
}
Comment thread src/commands/database.rs
Comment on lines +303 to +309
let db = client.get_database(id).await?.database;
let token = if let Some(path) = token_file {
std::fs::read_to_string(path)
.with_context(|| format!("reading token file {path}"))?
.trim()
.to_owned()
} else {
Comment thread src/commands/database.rs
Comment on lines +371 to +374
let json = serde_json::to_string_pretty(&resp)?;
println!("{json}");
// Suppress unused for `format` in non-table cases:
let _ = format;
Comment thread src/commands/database.rs Outdated
Comment on lines +475 to +489
let mut body = GenerateTokenDatabaseGroupPayload::new((*authorization).into());
if let Some(ts) = expires_at {
body.expires_at = Some(ts.clone());
}
let resp = client.generate_group_keys(id, &body).await?;
// Group key minting also surfaces a JWT — same redaction policy
// as `db token mint` would be ideal, but groups are batch-only,
// so for now we reveal it as it's typically used in scripts that
// immediately distribute the token.
let row = StatusRow {
status: format!(
"minted group token (length={}, expires_at={})",
resp.token.chars().count(),
resp.expires_at.as_deref().unwrap_or("never")
),
Comment thread README.md Outdated
hoppy db live --id db_01HX... --id db_02HX...
```

By default, `hoppy db token mint` prints `{ "Token": "<set, length=N>", ... }`
Comment thread crates/bunny-api-database/src/types.rs Outdated
Comment on lines +4 to +6
//! v1 and v2 are kept as parallel modules — the spec exposes parallel schemas
//! (`Database` vs `Database2`, etc.) and unifying them would silently lose
//! fields one side or the other doesn't model.
Comment on lines +245 to +253
#[derive(Debug, Clone, Default, Serialize)]
pub struct ListVersionsDatabaseGroupPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub older_than: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub newer_than: Option<String>,
}
ractive and others added 2 commits May 7, 2026 15:33
- Add confirmation prompts (bypassable with --yes) to destructive commands:
  db delete, db restore, db v2 delete, db group delete (Copilot)
- db group generate-keys now prints the actual minted token using the
  same redaction policy as db token mint, instead of just a length+expiry
  status row that left the token unrecoverable (Copilot, CodeRabbit)
- Use tokio::fs::read_to_string in the async ping handler (Copilot)
- Rename ListVersionsDatabaseGroupPayload -> ListVersionsDatabasePayload;
  the payload is for listing database versions, not groups (Copilot)
- README: document --reveal / --reveal-env in the global options table
  and fix the JSON example to use lowercase serde field names (Copilot,
  CodeRabbit)
- types.rs: clarify "parallel types" wording in module docs (Copilot)
- Fixture: bump token_mint.json expires_at to 2099 to avoid future test
  brittleness (CodeRabbit)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iter-20 added bunny-api-database (1 test file) and tests/cli_database.rs
to the root, taking the workspace test-binary count from 22 to 24 and
the target from 7 to 8. Replaces the TBD row with concrete numbers and
the placeholder iter-20-fork-in-the-road section with the actual
move-files tasks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ractive
ractive merged commit e77e8d6 into main May 7, 2026
5 of 7 checks passed
@ractive
ractive deleted the iter-20/database branch May 7, 2026 13:35
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.

2 participants