redis_cloud/handlers/
database.rs

1//! Database operations handler
2//!
3//! This module provides comprehensive database management capabilities for Redis Cloud,
4//! including CRUD operations, backups, imports, metrics, and scaling operations.
5//!
6//! # Examples
7//!
8//! ```rust,no_run
9//! use redis_cloud::{CloudClient, CloudDatabaseHandler};
10//! use serde_json::json;
11//!
12//! # #[tokio::main]
13//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
14//! let client = CloudClient::builder()
15//!     .api_key("your-api-key")
16//!     .api_secret("your-api-secret")
17//!     .build()?;
18//!
19//! let db_handler = CloudDatabaseHandler::new(client);
20//!
21//! // Get database information  
22//! let db_info = db_handler.get(123, 456).await?;
23//! println!("Database: {}", db_info.name);
24//!
25//! // Create database using raw client API
26//! let database_config = json!({
27//!     "name": "my-redis-db",
28//!     "memory_limit_in_gb": 2.5,
29//!     "support_oss_cluster_api": false,
30//!     "replication": true,
31//!     "data_persistence": "aof-every-1-sec"
32//! });
33//! # Ok(())
34//! # }
35//! ```
36
37use crate::{
38    Result,
39    client::CloudClient,
40    models::{CloudDatabase, CreateDatabaseRequest, UpdateDatabaseRequest},
41};
42use serde_json::Value;
43
44/// Handler for Cloud database operations
45///
46/// Provides methods for managing Redis Cloud databases including creation, updates,
47/// backups, imports, metrics collection, and scaling operations.
48///
49/// All database operations require both a subscription ID and database ID, as databases
50/// are scoped within subscriptions in Redis Cloud.
51pub struct CloudDatabaseHandler {
52    client: CloudClient,
53}
54
55impl CloudDatabaseHandler {
56    /// Create a new database handler instance
57    ///
58    /// # Arguments
59    /// * `client` - The configured CloudClient instance
60    pub fn new(client: CloudClient) -> Self {
61        CloudDatabaseHandler { client }
62    }
63
64    /// Retrieve a specific database by ID
65    ///
66    /// Returns detailed information about a database including its configuration,
67    /// status, endpoints, and current metrics.
68    ///
69    /// # Arguments
70    /// * `subscription_id` - The ID of the subscription containing the database
71    /// * `database_id` - The unique database identifier
72    ///
73    /// # Examples
74    /// ```rust,no_run
75    /// # use redis_cloud::{CloudClient, CloudDatabaseHandler};
76    /// # #[tokio::main]
77    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
78    /// # let client = CloudClient::builder().api_key("key").api_secret("secret").build()?;
79    /// let db_handler = CloudDatabaseHandler::new(client);
80    /// let database = db_handler.get(123, 456).await?;
81    /// println!("Database name: {}", database.name);
82    /// println!("Memory limit: {} GB", database.memory_limit_in_gb);
83    /// # Ok(())
84    /// # }
85    /// ```
86    pub async fn get(&self, subscription_id: u32, database_id: u32) -> Result<CloudDatabase> {
87        self.client
88            .get(&format!(
89                "/subscriptions/{}/databases/{}",
90                subscription_id, database_id
91            ))
92            .await
93    }
94
95    /// Create a new database in a subscription
96    ///
97    /// Creates a new Redis database with the specified configuration. The database
98    /// will be deployed across the subscription's defined regions and cloud providers.
99    ///
100    /// # Arguments
101    /// * `subscription_id` - The ID of the subscription to create the database in
102    /// * `request` - Database configuration including name, memory, replication settings
103    ///
104    /// # Examples
105    /// ```rust,no_run
106    /// # use redis_cloud::{CloudClient, CloudDatabaseHandler};
107    /// # use serde_json::json;
108    /// # #[tokio::main]
109    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
110    /// # let client = CloudClient::builder().api_key("key").api_secret("secret").build()?;
111    /// let db_handler = CloudDatabaseHandler::new(client);
112    /// let config = json!({
113    ///     "name": "production-cache",
114    ///     "memory_limit_in_gb": 5.0,
115    ///     "replication": true,
116    ///     "data_persistence": "aof-every-1-sec",
117    ///     "password": "secure-password"
118    /// });
119    /// // Note: create() takes a typed request struct, not JSON
120    /// // let result = db_handler.create(123, create_request).await?;
121    /// # Ok(())
122    /// # }
123    /// ```
124    pub async fn create(
125        &self,
126        subscription_id: u32,
127        request: CreateDatabaseRequest,
128    ) -> Result<Value> {
129        self.client
130            .post(
131                &format!("/subscriptions/{}/databases", subscription_id),
132                &request,
133            )
134            .await
135    }
136
137    /// Update an existing database configuration
138    ///
139    /// Modifies database settings such as memory limits, replication, persistence,
140    /// and other configuration options. Some changes may require a database restart.
141    ///
142    /// # Arguments
143    /// * `subscription_id` - The ID of the subscription containing the database
144    /// * `database_id` - The unique database identifier
145    /// * `request` - Updated configuration settings
146    ///
147    /// # Examples
148    /// ```rust,no_run
149    /// # use redis_cloud::{CloudClient, CloudDatabaseHandler};
150    /// # use serde_json::json;
151    /// # #[tokio::main]
152    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
153    /// # let client = CloudClient::builder().api_key("key").api_secret("secret").build()?;
154    /// let db_handler = CloudDatabaseHandler::new(client);
155    /// let updates = json!({
156    ///     "memory_limit_in_gb": 10.0,
157    ///     "replication": false
158    /// });
159    /// // Note: update() takes a typed request struct, not JSON
160    /// // let updated_db = db_handler.update(123, 456, update_request).await?;
161    /// # Ok(())
162    /// # }
163    /// ```
164    pub async fn update(
165        &self,
166        subscription_id: u32,
167        database_id: u32,
168        request: UpdateDatabaseRequest,
169    ) -> Result<CloudDatabase> {
170        self.client
171            .put(
172                &format!(
173                    "/subscriptions/{}/databases/{}",
174                    subscription_id, database_id
175                ),
176                &request,
177            )
178            .await
179    }
180
181    /// Delete a database permanently
182    ///
183    /// **Warning**: This operation is irreversible and will permanently delete
184    /// all data in the database. Consider creating a backup before deletion.
185    ///
186    /// # Arguments
187    /// * `subscription_id` - The ID of the subscription containing the database
188    /// * `database_id` - The unique database identifier
189    ///
190    /// # Examples
191    /// ```rust,no_run
192    /// # use redis_cloud::{CloudClient, CloudDatabaseHandler};
193    /// # #[tokio::main]
194    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
195    /// # let client = CloudClient::builder().api_key("key").api_secret("secret").build()?;
196    /// let db_handler = CloudDatabaseHandler::new(client);
197    /// let result = db_handler.delete(123, 456).await?;
198    /// println!("Database deleted successfully");
199    /// # Ok(())
200    /// # }
201    /// ```
202    pub async fn delete(&self, subscription_id: u32, database_id: u32) -> Result<Value> {
203        self.client
204            .delete(&format!(
205                "/subscriptions/{}/databases/{}",
206                subscription_id, database_id
207            ))
208            .await?;
209        Ok(serde_json::json!({"message": format!("Database {} deleted", database_id)}))
210    }
211
212    /// Resize database
213    pub async fn resize(
214        &self,
215        subscription_id: u32,
216        database_id: u32,
217        memory_limit_in_gb: f64,
218    ) -> Result<CloudDatabase> {
219        let request = UpdateDatabaseRequest {
220            memory_limit_in_gb: Some(memory_limit_in_gb),
221            name: None,
222            data_persistence: None,
223            replication: None,
224            data_eviction: None,
225            password: None,
226        };
227        self.update(subscription_id, database_id, request).await
228    }
229
230    /// List all databases across all subscriptions
231    pub async fn list_all(&self) -> Result<Vec<CloudDatabase>> {
232        let response: Value = self.client.get("/databases").await?;
233        if let Some(dbs) = response.get("databases") {
234            serde_json::from_value(dbs.clone()).map_err(Into::into)
235        } else {
236            Ok(vec![])
237        }
238    }
239
240    /// List databases for subscription as Value
241    pub async fn list(&self, subscription_id: u32) -> Result<Value> {
242        self.client
243            .get(&format!("/subscriptions/{}/databases", subscription_id))
244            .await
245    }
246
247    /// Get database as Value
248    pub async fn get_raw(&self, subscription_id: u32, database_id: u32) -> Result<Value> {
249        self.client
250            .get(&format!(
251                "/subscriptions/{}/databases/{}",
252                subscription_id, database_id
253            ))
254            .await
255    }
256
257    /// Create database with Value
258    pub async fn create_raw(&self, subscription_id: u32, request: Value) -> Result<Value> {
259        self.client
260            .post(
261                &format!("/subscriptions/{}/databases", subscription_id),
262                &request,
263            )
264            .await
265    }
266
267    /// Update database with Value
268    pub async fn update_raw(
269        &self,
270        subscription_id: u32,
271        database_id: u32,
272        request: Value,
273    ) -> Result<Value> {
274        self.client
275            .put(
276                &format!(
277                    "/subscriptions/{}/databases/{}",
278                    subscription_id, database_id
279                ),
280                &request,
281            )
282            .await
283    }
284
285    /// Backup database
286    pub async fn backup(&self, subscription_id: u32, database_id: u32) -> Result<Value> {
287        self.client
288            .post(
289                &format!(
290                    "/subscriptions/{}/databases/{}/backup",
291                    subscription_id, database_id
292                ),
293                &Value::Null,
294            )
295            .await
296    }
297
298    /// Import data
299    pub async fn import(
300        &self,
301        subscription_id: u32,
302        database_id: u32,
303        request: Value,
304    ) -> Result<Value> {
305        self.client
306            .post(
307                &format!(
308                    "/subscriptions/{}/databases/{}/import",
309                    subscription_id, database_id
310                ),
311                &request,
312            )
313            .await
314    }
315
316    /// Get metrics
317    pub async fn get_metrics(
318        &self,
319        subscription_id: u32,
320        database_id: u32,
321        metrics: &str,
322        period: &str,
323    ) -> Result<Value> {
324        self.client
325            .get(&format!(
326                "/subscriptions/{}/databases/{}/metrics?metrics={}&period={}",
327                subscription_id, database_id, metrics, period
328            ))
329            .await
330    }
331
332    /// Get database backup status
333    pub async fn backup_status(&self, subscription_id: u32, database_id: u32) -> Result<Value> {
334        self.client
335            .get(&format!(
336                "/subscriptions/{}/databases/{}/backup",
337                subscription_id, database_id
338            ))
339            .await
340    }
341}