use crate::client::ObjectstoreClient;
use crate::response::get_content_text;
use anyhow::{bail, Context as _, Result};
use derive_builder::Builder;
use reqwest::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use serde_aux::field_attributes::deserialize_default_from_null;
#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
#[builder(setter(skip))]
#[serde(
rename_all(serialize = "snake_case", deserialize = "camelCase"),
rename(serialize = "tenant_create")
)]
pub struct Tenant {
#[serde(deserialize_with = "deserialize_default_from_null")]
pub name: String,
#[builder(setter(into))]
#[serde(rename(serialize = "account_id"))]
pub id: String,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub link: String,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub creation_time: String,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub inactive: bool,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub global: bool,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub remote: bool,
#[serde(deserialize_with = "deserialize_default_from_null")]
pub internal: bool,
pub tenant_default_vpool: String,
#[builder(setter(skip = false), default = "false")]
pub is_encryption_enabled: bool,
#[builder(setter(skip = false), default)]
pub default_bucket_block_size: i64,
#[builder(setter(skip = false), default = "false")]
pub is_compliance_enabled: bool,
pub hard_quota_in_g_b: i64,
pub soft_quota_in_g_b: i64,
pub hard_quota_in_count: i64,
pub soft_quota_in_count: i64,
#[builder(setter(into))]
pub alias: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(
rename_all(serialize = "snake_case", deserialize = "camelCase"),
rename(serialize = "tenant_update")
)]
struct TenantUpdate {
pub alias: String,
pub default_bucket_block_size: i64,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ListTenantsResponse {
pub tenant: Vec<Tenant>,
#[serde(rename = "Filter")]
pub filter: String,
#[serde(rename = "NextMarker")]
pub next_marker: Option<String>,
#[serde(rename = "MaxTenants")]
pub max_tenants: Option<u32>,
pub next_page_link: Option<String>,
}
impl Tenant {
pub(crate) fn create(client: &mut ObjectstoreClient, tenant: Tenant) -> Result<Tenant> {
let request_url = format!("{}object/tenants/tenant", client.endpoint,);
let body = quick_xml::se::to_string(&tenant)?;
let resp = client
.management_client
.http_client
.post(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
)
.header(CONTENT_TYPE, "application/xml")
.header("X-EMC-Override", "true")
.body(body)
.send()?;
let text = get_content_text(resp)?;
let resp: Tenant = serde_json::from_str(&text).with_context(|| {
format!("Unable to deserialise CreateTenant. Body was: \"{}\"", text)
})?;
Ok(resp)
}
pub(crate) fn update(client: &mut ObjectstoreClient, tenant: Tenant) -> Result<()> {
let request_url = format!("{}object/tenants/tenant/{}", client.endpoint, tenant.id);
let tenant_update = TenantUpdate {
alias: tenant.alias,
default_bucket_block_size: tenant.default_bucket_block_size,
};
let body = quick_xml::se::to_string(&tenant_update)?;
let resp = client
.management_client
.http_client
.put(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
)
.header(CONTENT_TYPE, "application/xml")
.header("X-EMC-Override", "true")
.body(body)
.send()?;
if !resp.status().is_success() {
bail!("Request failed: {}", resp.text()?);
}
Ok(())
}
pub(crate) fn get(client: &mut ObjectstoreClient, name: &str) -> Result<Tenant> {
let request_url = format!("{}object/tenants/tenant/{}", client.endpoint, name,);
let resp = client
.management_client
.http_client
.get(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
)
.send()?;
let text = get_content_text(resp)?;
let resp: Tenant = serde_json::from_str(&text)
.with_context(|| format!("Unable to deserialise GetTenant. Body was: \"{}\"", text))?;
Ok(resp)
}
pub(crate) fn delete(client: &mut ObjectstoreClient, name: &str) -> Result<()> {
let request_url = format!("{}object/tenants/tenant/{}/delete", client.endpoint, name,);
let resp = client
.management_client
.http_client
.post(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
)
.header("X-EMC-Override", "true")
.send()?;
if !resp.status().is_success() {
bail!("Request failed: {}", resp.text()?);
}
Ok(())
}
pub(crate) fn list(client: &mut ObjectstoreClient, name_prefix: &str) -> Result<Vec<Tenant>> {
let request_url = format!("{}object/tenants", client.endpoint,);
let mut req = client
.management_client
.http_client
.get(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
);
if !name_prefix.is_empty() {
req = req.query(&[("name", name_prefix)]);
}
let response = req.send()?;
let text = get_content_text(response)?;
let mut resp: ListTenantsResponse = serde_json::from_str(&text).with_context(|| {
format!(
"Unable to deserialise ListTenantsResponse. Body was: \"{}\"",
text
)
})?;
let mut tenants: Vec<Tenant> = vec![];
tenants.extend(resp.tenant);
while let Some(marker) = resp.next_marker {
let request_url = format!("{}object/tenants?marker={}", client.endpoint, marker,);
let mut req = client
.management_client
.http_client
.get(request_url)
.header(ACCEPT, "application/json")
.header(
AUTHORIZATION,
client.management_client.access_token.as_ref().unwrap(),
);
if !name_prefix.is_empty() {
req = req.query(&[("name", name_prefix)]);
}
let response = req.send()?;
let text = get_content_text(response)?;
resp = serde_json::from_str(&text).with_context(|| {
format!(
"Unable to deserialise ListTenantsResponse. Body was: \"{}\"",
text
)
})?;
tenants.extend(resp.tenant);
}
Ok(tenants)
}
}