cellstate/entity/
tenant.rs

1//! `cellstate tenant` — Manage tenants.
2//!
3//! Subcommands: list, get, update.
4
5use anyhow::{anyhow, Result};
6use clap::{Arg, ArgMatches, Command};
7
8use cellstate_core::api_types::{ListTenantsResponse, TenantInfo, UpdateTenantRequest};
9
10use super::client::ApiClient;
11use super::output::OutputConfig;
12
13const BASE: &str = "/api/v1/tenants";
14
15pub fn build_command() -> Command {
16    Command::new("tenant")
17        .about("Manage tenants")
18        .subcommand_required(true)
19        .subcommand(Command::new("list").about("List tenants"))
20        .subcommand(
21            Command::new("get")
22                .about("Get tenant details")
23                .arg(Arg::new("id").required(true).help("Tenant ID")),
24        )
25        .subcommand(
26            Command::new("update")
27                .about("Update a tenant")
28                .arg(Arg::new("id").required(true).help("Tenant ID"))
29                .arg(Arg::new("name").long("name").help("New name"))
30                .arg(Arg::new("domain").long("domain").help("New email domain"))
31                .arg(
32                    Arg::new("status")
33                        .long("status")
34                        .help("New status (active, suspended, archived)"),
35                )
36                .arg(
37                    Arg::new("settings")
38                        .long("settings")
39                        .help("Tenant settings as JSON string"),
40                ),
41        )
42}
43
44pub async fn dispatch(
45    matches: &ArgMatches,
46    client: &ApiClient,
47    output: &OutputConfig,
48    _session: &crate::session::CliSession,
49) -> Result<()> {
50    match matches.subcommand() {
51        Some(("list", _sub)) => {
52            let resp: ListTenantsResponse = client.get(BASE).await?;
53            output.print_list(&resp.tenants, resp.tenants.len() as i64);
54        }
55        Some(("get", sub)) => {
56            let id = sub.get_one::<String>("id").unwrap();
57            let resp: TenantInfo = client.get(&format!("{BASE}/{id}")).await?;
58            output.print(&resp);
59        }
60        Some(("update", sub)) => {
61            let id = sub.get_one::<String>("id").unwrap();
62            let req = UpdateTenantRequest {
63                name: sub.get_one::<String>("name").cloned(),
64                domain: sub.get_one::<String>("domain").cloned(),
65                status: sub
66                    .get_one::<String>("status")
67                    .map(|s| s.parse().map_err(|e: String| anyhow!(e)))
68                    .transpose()?,
69                settings: sub
70                    .get_one::<String>("settings")
71                    .map(|s| serde_json::from_str(s))
72                    .transpose()?,
73            };
74            let resp: TenantInfo = client.patch(&format!("{BASE}/{id}"), &req).await?;
75            output.print(&resp);
76        }
77        _ => unreachable!("subcommand_required(true) prevents this"),
78    }
79    Ok(())
80}