Skip to main content

systemprompt_cloud/api_client/
tenant_api.rs

1//! Tenant-scoped endpoints for [`super::CloudApiClient`].
2
3use serde::Serialize;
4use systemprompt_identifiers::TenantId;
5use systemprompt_models::modules::ApiPaths;
6
7use super::CloudApiClient;
8use super::types::{
9    ApiResponse, CustomDomainResponse, DeployResponse, ExternalDbAccessResponse,
10    ListSecretsResponse, RegistryToken, RotateCredentialsResponse, RotateSyncTokenResponse,
11    SetCustomDomainRequest, SetExternalDbAccessRequest, SetSecretsRequest, StatusResponse,
12    TenantSecrets, TenantStatus,
13};
14use crate::error::CloudResult;
15
16#[derive(Serialize)]
17struct DeployRequest {
18    image: String,
19}
20
21impl CloudApiClient {
22    pub async fn get_tenant_status(&self, tenant_id: &TenantId) -> CloudResult<TenantStatus> {
23        let response: ApiResponse<TenantStatus> = self
24            .get(&ApiPaths::tenant_status(tenant_id.as_str()))
25            .await?;
26        Ok(response.data)
27    }
28
29    pub async fn get_registry_token(&self, tenant_id: &TenantId) -> CloudResult<RegistryToken> {
30        let response: ApiResponse<RegistryToken> = self
31            .get(&ApiPaths::tenant_registry_token(tenant_id.as_str()))
32            .await?;
33        Ok(response.data)
34    }
35
36    pub async fn deploy(&self, tenant_id: &TenantId, image: &str) -> CloudResult<DeployResponse> {
37        let request = DeployRequest {
38            image: image.to_string(),
39        };
40        let response: ApiResponse<DeployResponse> = self
41            .post(&ApiPaths::tenant_deploy(tenant_id.as_str()), &request)
42            .await?;
43        Ok(response.data)
44    }
45
46    pub async fn fetch_secrets(&self, secrets_url: &str) -> CloudResult<TenantSecrets> {
47        let path = secrets_url
48            .strip_prefix(&self.api_url)
49            .unwrap_or(secrets_url);
50        self.get(path).await
51    }
52
53    pub async fn delete_tenant(&self, tenant_id: &TenantId) -> CloudResult<()> {
54        self.delete(&ApiPaths::tenant(tenant_id.as_str())).await
55    }
56
57    pub async fn restart_tenant(&self, tenant_id: &TenantId) -> CloudResult<StatusResponse> {
58        self.post_empty(&ApiPaths::tenant_restart(tenant_id.as_str()))
59            .await
60    }
61
62    pub async fn retry_provision(&self, tenant_id: &TenantId) -> CloudResult<StatusResponse> {
63        self.post_empty(&ApiPaths::tenant_retry_provision(tenant_id.as_str()))
64            .await
65    }
66
67    pub async fn set_secrets(
68        &self,
69        tenant_id: &TenantId,
70        secrets: std::collections::HashMap<String, String>,
71    ) -> CloudResult<Vec<String>> {
72        let keys: Vec<String> = secrets.keys().cloned().collect();
73        let request = SetSecretsRequest { secrets };
74        self.put_no_content(&ApiPaths::tenant_secrets(tenant_id.as_str()), &request)
75            .await?;
76        Ok(keys)
77    }
78
79    pub async fn unset_secret(&self, tenant_id: &TenantId, key: &str) -> CloudResult<()> {
80        let path = format!("{}/{}", ApiPaths::tenant_secrets(tenant_id.as_str()), key);
81        self.delete(&path).await
82    }
83
84    pub async fn set_external_db_access(
85        &self,
86        tenant_id: &TenantId,
87        enabled: bool,
88    ) -> CloudResult<ExternalDbAccessResponse> {
89        let request = SetExternalDbAccessRequest { enabled };
90        let response: ApiResponse<ExternalDbAccessResponse> = self
91            .put(
92                &ApiPaths::tenant_external_db_access(tenant_id.as_str()),
93                &request,
94            )
95            .await?;
96        Ok(response.data)
97    }
98
99    pub async fn rotate_credentials(
100        &self,
101        tenant_id: &TenantId,
102    ) -> CloudResult<RotateCredentialsResponse> {
103        self.post_empty(&ApiPaths::tenant_rotate_credentials(tenant_id.as_str()))
104            .await
105    }
106
107    pub async fn rotate_sync_token(
108        &self,
109        tenant_id: &TenantId,
110    ) -> CloudResult<RotateSyncTokenResponse> {
111        let response: ApiResponse<RotateSyncTokenResponse> = self
112            .post_empty(&ApiPaths::tenant_rotate_sync_token(tenant_id.as_str()))
113            .await?;
114        Ok(response.data)
115    }
116
117    pub async fn list_secrets(&self, tenant_id: &TenantId) -> CloudResult<ListSecretsResponse> {
118        self.get(&ApiPaths::tenant_secrets(tenant_id.as_str()))
119            .await
120    }
121
122    pub async fn set_custom_domain(
123        &self,
124        tenant_id: &TenantId,
125        domain: &str,
126    ) -> CloudResult<CustomDomainResponse> {
127        let request = SetCustomDomainRequest {
128            domain: domain.to_string(),
129        };
130        let response: ApiResponse<CustomDomainResponse> = self
131            .post(
132                &ApiPaths::tenant_custom_domain(tenant_id.as_str()),
133                &request,
134            )
135            .await?;
136        Ok(response.data)
137    }
138
139    pub async fn get_custom_domain(
140        &self,
141        tenant_id: &TenantId,
142    ) -> CloudResult<CustomDomainResponse> {
143        let response: ApiResponse<CustomDomainResponse> = self
144            .get(&ApiPaths::tenant_custom_domain(tenant_id.as_str()))
145            .await?;
146        Ok(response.data)
147    }
148
149    pub async fn delete_custom_domain(&self, tenant_id: &TenantId) -> CloudResult<()> {
150        self.delete(&ApiPaths::tenant_custom_domain(tenant_id.as_str()))
151            .await
152    }
153
154    pub async fn cancel_subscription(&self, tenant_id: &TenantId) -> CloudResult<()> {
155        self.post_empty(&ApiPaths::tenant_subscription_cancel(tenant_id.as_str()))
156            .await
157    }
158}