use super::VectorizerClient;
use crate::error::{Result, VectorizerError};
use crate::models::{
CreateUserBackupRequest, HubApiKeyValidation, QuotaInfo, RestoreUserBackupRequest,
UploadUserBackupRequest, UsageStatistics, UserBackup,
};
impl VectorizerClient {
pub async fn list_user_backups(&self, user_id: &str) -> Result<Vec<UserBackup>> {
let endpoint = format!("/hub/backups?user_id={user_id}");
let response = self.make_request("GET", &endpoint, None).await?;
let val: serde_json::Value = serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!("Failed to parse list_user_backups response: {e}"))
})?;
let arr = val
.get("backups")
.and_then(|b| b.as_array())
.cloned()
.unwrap_or_default();
arr.into_iter()
.map(|v| {
serde_json::from_value(v).map_err(|e| {
VectorizerError::server(format!("Failed to parse user backup entry: {e}"))
})
})
.collect()
}
pub async fn create_user_backup(&self, request: CreateUserBackupRequest) -> Result<UserBackup> {
let payload = serde_json::to_value(&request).map_err(|e| {
VectorizerError::server(format!(
"Failed to serialize create_user_backup request: {e}"
))
})?;
let response = self
.make_request("POST", "/hub/backups", Some(payload))
.await?;
let val: serde_json::Value = serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!("Failed to parse create_user_backup response: {e}"))
})?;
serde_json::from_value(val.get("backup").cloned().unwrap_or(val)).map_err(|e| {
VectorizerError::server(format!("Failed to parse user backup from response: {e}"))
})
}
pub async fn restore_user_backup(&self, request: RestoreUserBackupRequest) -> Result<()> {
let payload = serde_json::to_value(&request).map_err(|e| {
VectorizerError::server(format!(
"Failed to serialize restore_user_backup request: {e}"
))
})?;
self.make_request("POST", "/hub/backups/restore", Some(payload))
.await?;
Ok(())
}
pub async fn upload_user_backup(&self, request: UploadUserBackupRequest) -> Result<UserBackup> {
let mut qs = format!("user_id={}", request.user_id);
if let Some(name) = &request.name {
qs.push_str(&format!("&name={name}"));
}
let endpoint = format!("/hub/backups/upload?{qs}");
let payload = serde_json::json!({ "data": request.data });
let response = self.make_request("POST", &endpoint, Some(payload)).await?;
let val: serde_json::Value = serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!("Failed to parse upload_user_backup response: {e}"))
})?;
serde_json::from_value(val.get("backup").cloned().unwrap_or(val)).map_err(|e| {
VectorizerError::server(format!(
"Failed to parse user backup from upload response: {e}"
))
})
}
pub async fn get_user_backup(&self, user_id: &str, backup_id: &str) -> Result<UserBackup> {
let endpoint = format!("/hub/backups/{backup_id}?user_id={user_id}");
let response = self.make_request("GET", &endpoint, None).await?;
let val: serde_json::Value = serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!("Failed to parse get_user_backup response: {e}"))
})?;
serde_json::from_value(val.get("backup").cloned().unwrap_or(val)).map_err(|e| {
VectorizerError::server(format!(
"Failed to parse user backup from get response: {e}"
))
})
}
pub async fn delete_user_backup(&self, user_id: &str, backup_id: &str) -> Result<()> {
let endpoint = format!("/hub/backups/{backup_id}?user_id={user_id}");
self.make_request("DELETE", &endpoint, None).await?;
Ok(())
}
pub async fn download_user_backup(&self, user_id: &str, backup_id: &str) -> Result<Vec<u8>> {
let endpoint = format!("/hub/backups/{backup_id}/download?user_id={user_id}");
let response = self.make_request("GET", &endpoint, None).await?;
Ok(response.into_bytes())
}
pub async fn get_usage_statistics(&self, user_id: &str) -> Result<UsageStatistics> {
let endpoint = format!("/hub/usage/statistics?user_id={user_id}");
let response = self.make_request("GET", &endpoint, None).await?;
serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!(
"Failed to parse get_usage_statistics response: {e}"
))
})
}
pub async fn get_quota_info(&self, user_id: &str) -> Result<QuotaInfo> {
let endpoint = format!("/hub/usage/quota?user_id={user_id}");
let response = self.make_request("GET", &endpoint, None).await?;
serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!("Failed to parse get_quota_info response: {e}"))
})
}
pub async fn validate_hub_api_key(&self, key: &str) -> Result<HubApiKeyValidation> {
let payload = serde_json::json!({ "key": key });
let response = self
.make_request("POST", "/hub/validate-key", Some(payload))
.await?;
serde_json::from_str(&response).map_err(|e| {
VectorizerError::server(format!(
"Failed to parse validate_hub_api_key response: {e}"
))
})
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use serde_json::json;
use crate::models::{
CreateUserBackupRequest, HubApiKeyValidation, QuotaInfo, RestoreUserBackupRequest,
UsageStatistics, UserBackup,
};
#[test]
fn user_backup_deserializes() {
let raw = json!({
"id": "b-1",
"user_id": "u-1",
"name": "my-backup",
"collections": ["docs"],
"created_at": "2026-05-02T00:00:00Z",
"size": 8192u64,
"status": "active"
});
let b: UserBackup = serde_json::from_value(raw).unwrap();
assert_eq!(b.id, "b-1");
assert_eq!(b.status, "active");
assert_eq!(b.size, 8192);
}
#[test]
fn user_backup_round_trip() {
let b = UserBackup {
id: "b-2".into(),
user_id: "u-2".into(),
name: "weekly".into(),
description: Some("desc".into()),
collections: vec!["col1".into()],
created_at: "2026-05-02T00:00:00Z".into(),
size: 1024,
status: "active".into(),
};
let serialized = serde_json::to_value(&b).unwrap();
let parsed: UserBackup = serde_json::from_value(serialized).unwrap();
assert_eq!(parsed.id, "b-2");
assert_eq!(parsed.description.as_deref(), Some("desc"));
}
#[test]
fn create_user_backup_request_serializes() {
let req = CreateUserBackupRequest {
user_id: "u-1".into(),
name: "nightly".into(),
description: None,
collections: Some(vec!["code".into()]),
};
let v = serde_json::to_value(&req).unwrap();
assert_eq!(v["user_id"], "u-1");
assert_eq!(v["name"], "nightly");
assert_eq!(v["collections"][0], "code");
assert!(v.get("description").is_none());
}
#[test]
fn restore_user_backup_request_serializes() {
let req = RestoreUserBackupRequest {
user_id: "u-1".into(),
backup_id: "b-99".into(),
overwrite: true,
};
let v = serde_json::to_value(&req).unwrap();
assert_eq!(v["backup_id"], "b-99");
assert!(v["overwrite"].as_bool().unwrap());
}
#[test]
fn usage_statistics_deserializes() {
let raw = json!({
"success": true,
"message": "ok",
"stats": {
"user_id": "u-1",
"total_collections": 3,
"total_vectors": 500u64,
"total_storage": 512000u64
}
});
let us: UsageStatistics = serde_json::from_value(raw).unwrap();
assert!(us.success);
assert!(us.stats.is_some());
}
#[test]
fn quota_info_deserializes() {
let raw = json!({
"success": true,
"message": "ok",
"quota": {
"tenant_id": "t-1",
"storage": { "limit": 1_000_000u64, "used": 50_000u64 }
}
});
let qi: QuotaInfo = serde_json::from_value(raw).unwrap();
assert!(qi.success);
assert!(qi.quota.is_some());
}
#[test]
fn hub_api_key_validation_deserializes_valid() {
let raw = json!({
"valid": true,
"tenant_id": "t-abc",
"tenant_name": "Acme",
"permissions": ["Read", "Write"],
"validated_at": "2026-05-02T00:00:00Z"
});
let v: HubApiKeyValidation = serde_json::from_value(raw).unwrap();
assert!(v.valid);
assert_eq!(v.tenant_id, "t-abc");
assert_eq!(v.permissions.len(), 2);
}
#[test]
fn hub_api_key_validation_deserializes_invalid() {
let raw = json!({
"valid": false,
"tenant_id": "",
"tenant_name": "",
"permissions": [],
"validated_at": ""
});
let v: HubApiKeyValidation = serde_json::from_value(raw).unwrap();
assert!(!v.valid);
}
}