Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ path = "src/main.rs"
# behind a shared multi-thread tokio runtime. The `arrow` feature backs result
# decode; `upload_file` runs the presigned direct-to-storage upload flow
# (see src/sdk.rs::Api::upload).
hotdata = { version = "0.8.0", features = ["arrow"] }
hotdata = { version = "0.8.1", features = ["arrow"] }
# Shared multi-thread runtime for the sync wrapper; block_on is called
# concurrently from rayon worker threads (see src/indexes.rs). The presigned
# upload (src/sdk.rs::Api::upload) drives the SDK's async `upload_file` on this
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Pull data from SQL databases, APIs, S3/GCS/Azure buckets, or Iceberg catalogs
into a managed database:

```sh
hotdata ingest connectors # browse: SQL dialects, ~150 API services,
hotdata ingest datasources # browse: SQL dialects, ~150 API services,
# buckets, iceberg, api (bring-your-own)

# Add a datasource — validates credentials and discovers the schema, loads no data.
Expand Down
6 changes: 3 additions & 3 deletions skills/hotdata/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@ Pull data from external sources (SQL databases, APIs, S3/GCS/Azure buckets, Iceb
Commands that create or change things (`new-datasource`, `new-import`, `trigger-import`, `delete-datasource`) **require a workspace API key** (`HOTDATA_API_KEY` / `--api-key`, `hd_...`); a login session is not accepted.

```bash
hotdata ingest connectors [filter] # browse the catalog; "added" = you have a
# datasource for it. SQL dialects + ~150 named
hotdata ingest datasources [filter] # browse available datasource types; "added" =
# you already have one. SQL dialects + ~150 named
# API services + generic buckets / iceberg / api.
# -o json includes each entry's config_schema —
# the exact fields --config takes.
Expand All @@ -323,7 +323,7 @@ hotdata ingest new-datasource --service buckets --bucket-url s3://bucket/prefix
hotdata ingest new-datasource --service iceberg --config @catalog.json --table ns.orders
# Iceberg via a REST catalog. --table is REQUIRED (repeatable, namespace.table).
# catalog.json fields: {"uri": …, "warehouse": …, "token": …} or "credential" +
# "scope" for an OAuth exchange — see the iceberg config_schema in `connectors -o json`.
# "scope" for an OAuth exchange — see the iceberg config_schema in `datasources -o json`.

hotdata ingest list-datasources # names, ids, statuses; --all includes replaced ones
hotdata ingest show-datasource <id> # status + discovered tables/columns
Expand Down
28 changes: 24 additions & 4 deletions src/commands/databases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ struct DatabaseSummary {
name: Option<String>,
#[serde(default)]
default_catalog: Option<String>,
#[serde(default)]
created_at: Option<String>,
}

/// CLI output shape for `databases get`. A curated, stably-ordered view mapped
Expand All @@ -275,6 +277,8 @@ pub struct Database {
#[serde(default)]
pub expires_at: Option<String>,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
attachments: Vec<DatabaseAttachment>,
}

Expand Down Expand Up @@ -347,6 +351,7 @@ impl From<hotdata::models::DatabaseDetailResponse> for Database {
default_catalog: Some(d.default_catalog),
default_connection_id: d.default_connection_id,
expires_at: d.expires_at.flatten(),
created_at: d.created_at.flatten(),
attachments: d
.attachments
.into_iter()
Expand All @@ -360,12 +365,12 @@ impl From<hotdata::models::DatabaseDetailResponse> for Database {
}

impl From<hotdata::models::DatabaseSummary> for DatabaseSummary {
/// Map the SDK's typed summary into the CLI's list-row output shape.
fn from(s: hotdata::models::DatabaseSummary) -> Self {
DatabaseSummary {
id: s.id,
name: s.name.flatten(),
default_catalog: Some(s.default_catalog),
created_at: s.created_at.flatten(),
}
}
}
Expand All @@ -386,7 +391,7 @@ pub(crate) fn get_database(api: &Api, id: &str) -> Result<Database, ApiError> {
/// up worker" hint instead of an unexplained pause — `databases list` is a
/// common first command against an idle workspace.
fn list_database_summaries(api: &Api) -> Result<Vec<DatabaseSummary>, ApiError> {
block_with_wakeup(api, "Loading databases…", api.client().databases().list())
block_with_wakeup(api, "Loading databases...", api.client().databases().list())
.map(|r| r.databases.into_iter().map(DatabaseSummary::from).collect())
}

Expand All @@ -398,8 +403,12 @@ fn list_database_summaries(api: &Api) -> Result<Vec<DatabaseSummary>, ApiError>
/// rediscovers managed databases here and resolves each one's
/// `default_connection_id` via [`get_database`]. The list summary omits the
/// connection id, hence ids only.
///
/// Deliberately spinner-less (plain [`block`], unlike
/// [`list_database_summaries`]): the caller owns its own "Loading indexes…"
/// spinner, and two indicatif bars would fight over the same line.
pub(crate) fn list_database_ids(api: &Api) -> Result<Vec<String>, ApiError> {
list_database_summaries(api).map(|dbs| dbs.into_iter().map(|d| d.id).collect())
block(api.client().databases().list()).map(|r| r.databases.into_iter().map(|d| d.id).collect())
}

fn fetch_database(api: &Api, id: &str) -> Database {
Expand Down Expand Up @@ -818,10 +827,14 @@ pub fn list(workspace_id: &str, format: &str) {
marker.to_string(),
d.id.clone(),
d.name.as_deref().unwrap_or("-").to_string(),
d.created_at
.as_deref()
.map(crate::util::format_date)
.unwrap_or_else(|| "-".to_string()),
]
})
.collect();
crate::output::table::print(&["", "ID", "NAME"], &rows);
crate::output::table::print(&["", "ID", "NAME", "CREATED"], &rows);
}
}
_ => unreachable!(),
Expand All @@ -845,6 +858,13 @@ pub fn get(workspace_id: &str, id_or_name: &str, format: &str) {
if let Some(c) = &db.default_catalog {
println!("{}{}", label("catalog:"), c.clone().cyan());
}
if let Some(ts) = &db.created_at {
println!(
"{}{}",
label("created_at:"),
crate::util::format_date(ts).dark_grey()
);
}
println!(
"{}{}",
label("default_connection_id:"),
Expand Down
Loading
Loading