diff --git a/src/commands/databases.rs b/src/commands/databases.rs index 0e0b617..e46a780 100644 --- a/src/commands/databases.rs +++ b/src/commands/databases.rs @@ -957,6 +957,33 @@ fn partition_keys(values: &[String]) -> Result, String> { .collect() } +/// Build the request body for `POST /v1/databases/{id}/schemas/{schema}/tables`, +/// leaving out every empty list so the server applies its defaults. +fn table_declaration_body( + table: &str, + key: &[String], + key_determines: &[String], + sorted_by: &[serde_json::Value], + partition_by: &[serde_json::Value], +) -> serde_json::Value { + let mut body = serde_json::json!({ "name": table }); + if !key.is_empty() { + body["key"] = serde_json::json!(key); + } + if !key_determines.is_empty() { + // The API calls this `constant_per_key`; the flag keeps its original + // name. The server rejects unknown fields, so the old wire name fails. + body["constant_per_key"] = serde_json::json!(key_determines); + } + if !sorted_by.is_empty() { + body["sorted_by"] = serde_json::json!(sorted_by); + } + if !partition_by.is_empty() { + body["partition_by"] = serde_json::json!(partition_by); + } + body +} + /// `databases tables add` — declare a table on an existing instant database. #[allow(clippy::too_many_arguments)] pub fn add_table( @@ -1001,19 +1028,7 @@ pub fn add_table( let api = Api::new(Some(workspace_id)); let db = resolve_database(&api, &database); - let mut body = serde_json::json!({ "name": table }); - if !key.is_empty() { - body["key"] = serde_json::json!(key); - } - if !key_determines.is_empty() { - body["key_determines"] = serde_json::json!(key_determines); - } - if !sorted_by.is_empty() { - body["sorted_by"] = serde_json::json!(sorted_by); - } - if !partition_by.is_empty() { - body["partition_by"] = serde_json::json!(partition_by); - } + let body = table_declaration_body(table, key, key_determines, &sorted_by, &partition_by); let (status, resp) = declare_table(&api, &db.id, schema, &body); @@ -4091,6 +4106,29 @@ mod tests { mock.assert(); } + #[test] + fn table_declaration_body_sends_key_determines_as_constant_per_key() { + let cols = |v: &[&str]| v.iter().map(|s| s.to_string()).collect::>(); + assert_eq!( + table_declaration_body( + "orders", + &cols(&["order_id"]), + &cols(&["event_date"]), + &[], + &[] + ), + serde_json::json!({ + "name": "orders", + "key": ["order_id"], + "constant_per_key": ["event_date"], + }) + ); + assert_eq!( + table_declaration_body("orders", &[], &[], &[], &[]), + serde_json::json!({"name": "orders"}) + ); + } + #[test] fn fork_database_request_defaults_name_to_source_label_fork() { let to_json = |r| serde_json::to_value(&r).unwrap();