Skip to content

Commit 9b0c33c

Browse files
feat(enterprise): implement database group (bdb-group) commands (#160) (#290)
- Add List, Get, Create, Update, Delete operations for database groups - Add group membership management (add/remove databases) - Add list-databases command to view databases in a group - Support for stdin input with '-' flag for JSON data - Add comprehensive mdBook documentation - Successfully tested list command against Docker environment
1 parent 2ba4ce6 commit 9b0c33c

File tree

6 files changed

+590
-0
lines changed

6 files changed

+590
-0
lines changed

crates/redisctl/src/cli.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,9 @@ pub enum EnterpriseCommands {
962962
/// Action (task) operations
963963
#[command(subcommand)]
964964
Action(crate::commands::enterprise::actions::ActionCommands),
965+
/// Database group operations
966+
#[command(subcommand, name = "bdb-group")]
967+
BdbGroup(crate::commands::enterprise::bdb_group::BdbGroupCommands),
965968
/// Cluster operations
966969
#[command(subcommand)]
967970
Cluster(EnterpriseClusterCommands),
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
use anyhow::Context;
2+
use clap::Subcommand;
3+
4+
use crate::cli::OutputFormat;
5+
use crate::connection::ConnectionManager;
6+
use crate::error::Result as CliResult;
7+
8+
#[derive(Debug, Clone, Subcommand)]
9+
pub enum BdbGroupCommands {
10+
/// List all database groups
11+
List,
12+
13+
/// Get database group details
14+
Get {
15+
/// Database group UID
16+
uid: u32,
17+
},
18+
19+
/// Create a new database group
20+
Create {
21+
/// JSON data for database group creation (use @filename or - for stdin)
22+
#[arg(short, long)]
23+
data: String,
24+
},
25+
26+
/// Update a database group
27+
Update {
28+
/// Database group UID
29+
uid: u32,
30+
/// JSON data for update (use @filename or - for stdin)
31+
#[arg(short, long)]
32+
data: String,
33+
},
34+
35+
/// Delete a database group
36+
Delete {
37+
/// Database group UID
38+
uid: u32,
39+
/// Force deletion without confirmation
40+
#[arg(short, long)]
41+
force: bool,
42+
},
43+
44+
/// Add database to group
45+
AddDatabase {
46+
/// Database group UID
47+
group_uid: u32,
48+
/// Database UID to add
49+
#[arg(long)]
50+
database: u32,
51+
},
52+
53+
/// Remove database from group
54+
RemoveDatabase {
55+
/// Database group UID
56+
group_uid: u32,
57+
/// Database UID to remove
58+
#[arg(long)]
59+
database: u32,
60+
},
61+
62+
/// List databases in group
63+
ListDatabases {
64+
/// Database group UID
65+
group_uid: u32,
66+
},
67+
}
68+
69+
impl BdbGroupCommands {
70+
#[allow(dead_code)]
71+
pub async fn execute(
72+
&self,
73+
conn_mgr: &ConnectionManager,
74+
profile_name: Option<&str>,
75+
output_format: OutputFormat,
76+
query: Option<&str>,
77+
) -> CliResult<()> {
78+
let client = conn_mgr.create_enterprise_client(profile_name).await?;
79+
80+
match self {
81+
BdbGroupCommands::List => {
82+
let response: serde_json::Value = client
83+
.get("/v1/bdb_groups")
84+
.await
85+
.context("Failed to list database groups")?;
86+
87+
let output_data = if let Some(q) = query {
88+
super::utils::apply_jmespath(&response, q)?
89+
} else {
90+
response
91+
};
92+
super::utils::print_formatted_output(output_data, output_format)?;
93+
}
94+
95+
BdbGroupCommands::Get { uid } => {
96+
let response: serde_json::Value = client
97+
.get(&format!("/v1/bdb_groups/{}", uid))
98+
.await
99+
.context("Failed to get database group")?;
100+
101+
let output_data = if let Some(q) = query {
102+
super::utils::apply_jmespath(&response, q)?
103+
} else {
104+
response
105+
};
106+
super::utils::print_formatted_output(output_data, output_format)?;
107+
}
108+
109+
BdbGroupCommands::Create { data } => {
110+
let json_data = super::utils::read_json_data(data)?;
111+
112+
let response: serde_json::Value = client
113+
.post("/v1/bdb_groups", &json_data)
114+
.await
115+
.context("Failed to create database group")?;
116+
117+
let output_data = if let Some(q) = query {
118+
super::utils::apply_jmespath(&response, q)?
119+
} else {
120+
response
121+
};
122+
super::utils::print_formatted_output(output_data, output_format)?;
123+
}
124+
125+
BdbGroupCommands::Update { uid, data } => {
126+
let json_data = super::utils::read_json_data(data)?;
127+
128+
let response: serde_json::Value = client
129+
.put(&format!("/v1/bdb_groups/{}", uid), &json_data)
130+
.await
131+
.context("Failed to update database group")?;
132+
133+
let output_data = if let Some(q) = query {
134+
super::utils::apply_jmespath(&response, q)?
135+
} else {
136+
response
137+
};
138+
super::utils::print_formatted_output(output_data, output_format)?;
139+
}
140+
141+
BdbGroupCommands::Delete { uid, force } => {
142+
if !force
143+
&& !super::utils::confirm_action(&format!("Delete database group {}?", uid))?
144+
{
145+
return Ok(());
146+
}
147+
148+
client
149+
.delete(&format!("/v1/bdb_groups/{}", uid))
150+
.await
151+
.context("Failed to delete database group")?;
152+
153+
println!("Database group {} deleted successfully", uid);
154+
}
155+
156+
BdbGroupCommands::AddDatabase {
157+
group_uid,
158+
database,
159+
} => {
160+
// Get current group data
161+
let mut group_data: serde_json::Value = client
162+
.get(&format!("/v1/bdb_groups/{}", group_uid))
163+
.await
164+
.context("Failed to get database group")?;
165+
166+
// Add database to the group
167+
if let Some(bdbs) = group_data["bdbs"].as_array_mut() {
168+
if !bdbs.contains(&serde_json::json!(database)) {
169+
bdbs.push(serde_json::json!(database));
170+
} else {
171+
println!("Database {} is already in group {}", database, group_uid);
172+
return Ok(());
173+
}
174+
} else {
175+
group_data["bdbs"] = serde_json::json!([database]);
176+
}
177+
178+
// Update the group
179+
let response: serde_json::Value = client
180+
.put(&format!("/v1/bdb_groups/{}", group_uid), &group_data)
181+
.await
182+
.context("Failed to add database to group")?;
183+
184+
println!("Database {} added to group {}", database, group_uid);
185+
186+
let output_data = if let Some(q) = query {
187+
super::utils::apply_jmespath(&response, q)?
188+
} else {
189+
response
190+
};
191+
super::utils::print_formatted_output(output_data, output_format)?;
192+
}
193+
194+
BdbGroupCommands::RemoveDatabase {
195+
group_uid,
196+
database,
197+
} => {
198+
// Get current group data
199+
let mut group_data: serde_json::Value = client
200+
.get(&format!("/v1/bdb_groups/{}", group_uid))
201+
.await
202+
.context("Failed to get database group")?;
203+
204+
// Remove database from the group
205+
if let Some(bdbs) = group_data["bdbs"].as_array_mut() {
206+
if let Some(pos) = bdbs.iter().position(|x| x == &serde_json::json!(database)) {
207+
bdbs.remove(pos);
208+
} else {
209+
println!("Database {} is not in group {}", database, group_uid);
210+
return Ok(());
211+
}
212+
} else {
213+
println!("Group {} has no databases", group_uid);
214+
return Ok(());
215+
}
216+
217+
// Update the group
218+
let response: serde_json::Value = client
219+
.put(&format!("/v1/bdb_groups/{}", group_uid), &group_data)
220+
.await
221+
.context("Failed to remove database from group")?;
222+
223+
println!("Database {} removed from group {}", database, group_uid);
224+
225+
let output_data = if let Some(q) = query {
226+
super::utils::apply_jmespath(&response, q)?
227+
} else {
228+
response
229+
};
230+
super::utils::print_formatted_output(output_data, output_format)?;
231+
}
232+
233+
BdbGroupCommands::ListDatabases { group_uid } => {
234+
let response: serde_json::Value = client
235+
.get(&format!("/v1/bdb_groups/{}", group_uid))
236+
.await
237+
.context("Failed to get database group")?;
238+
239+
// Extract just the databases list
240+
let databases = &response["bdbs"];
241+
242+
let output_data = if let Some(q) = query {
243+
super::utils::apply_jmespath(databases, q)?
244+
} else {
245+
databases.clone()
246+
};
247+
super::utils::print_formatted_output(output_data, output_format)?;
248+
}
249+
}
250+
251+
Ok(())
252+
}
253+
}
254+
255+
#[allow(dead_code)]
256+
pub async fn handle_bdb_group_command(
257+
conn_mgr: &ConnectionManager,
258+
profile_name: Option<&str>,
259+
bdb_group_cmd: BdbGroupCommands,
260+
output_format: OutputFormat,
261+
query: Option<&str>,
262+
) -> CliResult<()> {
263+
bdb_group_cmd
264+
.execute(conn_mgr, profile_name, output_format, query)
265+
.await
266+
}

crates/redisctl/src/commands/enterprise/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Enterprise command implementations
22
33
pub mod actions;
4+
pub mod bdb_group;
45
pub mod cluster;
56
pub mod cluster_impl;
67
pub mod crdb;

crates/redisctl/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ async fn execute_enterprise_command(
196196
)
197197
.await
198198
}
199+
BdbGroup(bdb_group_cmd) => {
200+
commands::enterprise::bdb_group::handle_bdb_group_command(
201+
conn_mgr,
202+
profile,
203+
bdb_group_cmd.clone(),
204+
output,
205+
query,
206+
)
207+
.await
208+
}
199209
Cluster(cluster_cmd) => {
200210
commands::enterprise::cluster::handle_cluster_command(
201211
conn_mgr,

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- [Overview](./enterprise/overview.md)
3030
- [Cluster](./enterprise/cluster.md)
3131
- [Databases](./enterprise/databases.md)
32+
- [Database Groups](./enterprise/bdb-groups.md)
3233
- [Nodes](./enterprise/nodes.md)
3334
- [Users & RBAC](./enterprise/users.md)
3435
- [Statistics](./enterprise/stats.md)

0 commit comments

Comments
 (0)