redis_cloud/flexible/databases.rs
1//! Database management operations for Pro subscriptions
2//!
3//! This module provides comprehensive database management functionality for Redis Cloud
4//! Pro subscriptions, including creation, configuration, backup, import/export, and
5//! monitoring capabilities.
6//!
7//! # Overview
8//!
9//! Pro databases offer the full range of Redis Cloud features including high availability,
10//! auto-scaling, clustering, modules, and advanced data persistence options. They can be
11//! deployed across multiple cloud providers and regions.
12//!
13//! # Key Features
14//!
15//! - **Database Lifecycle**: Create, update, delete, and manage databases
16//! - **Backup & Restore**: Automated and on-demand backup operations
17//! - **Import/Export**: Import data from RDB files or other Redis instances
18//! - **Modules**: Support for `RedisJSON`, `RediSearch`, `RedisGraph`, `RedisTimeSeries`, `RedisBloom`
19//! - **High Availability**: Replication, auto-failover, and clustering support
20//! - **Monitoring**: Metrics, alerts, and performance insights
21//! - **Security**: TLS, password protection, and ACL support
22//!
23//! # Database Configuration Options
24//!
25//! - Memory limits from 250MB to 500GB+
26//! - Support for Redis OSS Cluster API
27//! - Data persistence: AOF, snapshot, or both
28//! - Data eviction policies
29//! - Replication and clustering
30//! - Custom Redis versions
31//!
32//! # Example Usage
33//!
34//! ```no_run
35//! use redis_cloud::{CloudClient, DatabaseHandler};
36//!
37//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
38//! let client = CloudClient::builder()
39//! .api_key("your-api-key")
40//! .api_secret("your-api-secret")
41//! .build()?;
42//!
43//! let handler = DatabaseHandler::new(client);
44//!
45//! // List all databases in a subscription (subscription ID 123)
46//! let databases = handler.get_subscription_databases(123, None, None).await?;
47//!
48//! // Get specific database details
49//! let database = handler.get_subscription_database_by_id(123, 456).await?;
50//! # Ok(())
51//! # }
52//! ```
53
54use crate::types::{Link, ProcessorResponse};
55use crate::{CloudClient, Result};
56use async_stream::try_stream;
57use futures_core::Stream;
58use serde::{Deserialize, Deserializer, Serialize};
59use serde_json::Value;
60use std::collections::HashMap;
61use typed_builder::TypedBuilder;
62
63// ============================================================================
64// Models
65// ============================================================================
66
67/// `RedisLabs` Account Subscription Databases information
68///
69/// Response from GET /subscriptions/{subscriptionId}/databases
70#[derive(Debug, Clone, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct AccountSubscriptionDatabases {
73 /// Account ID
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub account_id: Option<i32>,
76
77 /// Subscription information with nested databases array.
78 /// The API returns this as an array, each element containing subscriptionId, databases, and links.
79 #[serde(default, deserialize_with = "deserialize_subscription_info")]
80 pub subscription: Vec<SubscriptionDatabasesInfo>,
81
82 /// HATEOAS links for API navigation
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub links: Option<Vec<Link>>,
85}
86
87/// Subscription databases info returned within `AccountSubscriptionDatabases`
88#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct SubscriptionDatabasesInfo {
91 /// Subscription ID
92 pub subscription_id: i32,
93
94 /// Number of databases (may not always be present)
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub number_of_databases: Option<i32>,
97
98 /// List of databases in this subscription
99 #[serde(default)]
100 pub databases: Vec<Database>,
101
102 /// HATEOAS links
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub links: Option<Vec<Link>>,
105}
106
107/// Custom deserializer that handles both object and array formats for subscription field.
108/// The API returns an array, but some test mocks use an object format.
109fn deserialize_subscription_info<'de, D>(
110 deserializer: D,
111) -> std::result::Result<Vec<SubscriptionDatabasesInfo>, D::Error>
112where
113 D: Deserializer<'de>,
114{
115 let value: Option<Value> = Option::deserialize(deserializer)?;
116
117 match value {
118 None => Ok(Vec::new()),
119 Some(Value::Array(arr)) => {
120 serde_json::from_value(Value::Array(arr)).map_err(serde::de::Error::custom)
121 }
122 Some(Value::Object(obj)) => {
123 // Single object - wrap in array
124 let item: SubscriptionDatabasesInfo =
125 serde_json::from_value(Value::Object(obj)).map_err(serde::de::Error::custom)?;
126 Ok(vec![item])
127 }
128 Some(other) => Err(serde::de::Error::custom(format!(
129 "expected array or object for subscription, got {other:?}"
130 ))),
131 }
132}
133
134/// Optional. Expected read and write throughput for this region.
135#[derive(Debug, Clone, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct LocalThroughput {
138 /// Specify one of the selected cloud provider regions for the subscription.
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub region: Option<String>,
141
142 /// Write operations for this region per second. Default: 1000 ops/sec
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub write_operations_per_second: Option<i64>,
145
146 /// Read operations for this region per second. Default: 1000 ops/sec
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub read_operations_per_second: Option<i64>,
149}
150
151/// Database tag update request message
152#[derive(Debug, Clone, Serialize, Deserialize)]
153#[serde(rename_all = "camelCase")]
154pub struct DatabaseTagUpdateRequest {
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub subscription_id: Option<i32>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub database_id: Option<i32>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub key: Option<String>,
163
164 /// Database tag value
165 pub value: String,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub command_type: Option<String>,
169}
170
171/// Database tag
172#[derive(Debug, Clone, Serialize, Deserialize)]
173#[serde(rename_all = "camelCase")]
174pub struct Tag {
175 /// Database tag key.
176 pub key: String,
177
178 /// Database tag value.
179 pub value: String,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub command_type: Option<String>,
183}
184
185/// Active-Active database flush request message
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct CrdbFlushRequest {
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub subscription_id: Option<i32>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub database_id: Option<i32>,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub command_type: Option<String>,
197}
198
199/// Database certificate
200#[derive(Debug, Clone, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct DatabaseCertificate {
203 /// An X.509 PEM (base64) encoded server certificate with new line characters replaced by '\n'.
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub public_certificate_pem_string: Option<String>,
206}
207
208/// Database tags update request message
209#[derive(Debug, Clone, Serialize, Deserialize)]
210#[serde(rename_all = "camelCase")]
211pub struct DatabaseTagsUpdateRequest {
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub subscription_id: Option<i32>,
214
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub database_id: Option<i32>,
217
218 /// List of database tags.
219 pub tags: Vec<Tag>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub command_type: Option<String>,
223}
224
225/// Optional. This database will be a replica of the specified Redis databases, provided as a list of objects with endpoint and certificate details.
226#[derive(Debug, Clone, Serialize, Deserialize)]
227#[serde(rename_all = "camelCase")]
228pub struct DatabaseSyncSourceSpec {
229 /// Redis URI of a source database. Example format: 'redis://user:password@host:port'. If the URI provided is a Redis Cloud database, only host and port should be provided. Example: '<redis://endpoint1:6379>'.
230 pub endpoint: String,
231
232 /// Defines if encryption should be used to connect to the sync source. If not set the source is a Redis Cloud database, it will automatically detect if the source uses encryption.
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub encryption: Option<bool>,
235
236 /// TLS/SSL certificate chain of the sync source. If not set and the source is a Redis Cloud database, it will automatically detect the certificate to use.
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub server_cert: Option<String>,
239}
240
241/// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections. If set to an empty list, TLS client certificates will be removed and mTLS will not be required. TLS connection may still apply, depending on the value of 'enableTls'.
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct DatabaseCertificateSpec {
245 /// Client certificate public key in PEM format, with new line characters replaced with '\n'.
246 pub public_certificate_pem_string: String,
247}
248
249/// Database tag
250#[derive(Debug, Clone, Serialize, Deserialize)]
251#[serde(rename_all = "camelCase")]
252pub struct CloudTag {
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub key: Option<String>,
255
256 #[serde(skip_serializing_if = "Option::is_none")]
257 pub value: Option<String>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub created_at: Option<String>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub updated_at: Option<String>,
264
265 /// HATEOAS links
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub links: Option<Vec<Link>>,
268}
269
270/// `BdbVersionUpgradeStatus`
271#[derive(Debug, Clone, Serialize, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct BdbVersionUpgradeStatus {
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub database_id: Option<i32>,
276
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub target_redis_version: Option<String>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub progress: Option<f64>,
282
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub upgrade_status: Option<String>,
285}
286
287/// Active-Active database update local properties request message
288#[derive(Debug, Clone, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct CrdbUpdatePropertiesRequest {
291 #[serde(skip_serializing_if = "Option::is_none")]
292 pub subscription_id: Option<i32>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
295 pub database_id: Option<i32>,
296
297 /// Optional. Updated database name. Database name is limited to 40 characters or less and must include only letters, digits, and hyphens ('-'). It must start with a letter and end with a letter or digit.
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub name: Option<String>,
300
301 /// Optional. When 'false': Creates a deployment plan and deploys it, updating any resources required by the plan. When 'true': creates a read-only deployment plan and does not update any resources. Default: 'false'
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub dry_run: Option<bool>,
304
305 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub memory_limit_in_gb: Option<f64>,
308
309 /// Optional. The maximum amount of data in the dataset for this database in GB. You cannot set both datasetSizeInGb and totalMemoryInGb. If ‘replication’ is 'true', the database’s total memory will be twice as large as the datasetSizeInGb.If ‘replication’ is false, the database’s total memory will be the datasetSizeInGb value.
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub dataset_size_in_gb: Option<f64>,
312
313 /// Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub support_oss_cluster_api: Option<bool>,
316
317 /// Optional. If set to 'true', the database will use the external endpoint for OSS Cluster API. This setting blocks the database's private endpoint. Can only be set if 'supportOSSClusterAPI' is 'true'.
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
320
321 /// Optional. A public key client TLS/SSL certificate with new line characters replaced with '\n'. If specified, mTLS authentication will be required to authenticate user connections if it is not already required. If set to an empty string, TLS client certificates will be removed and mTLS will not be required. TLS connection may still apply, depending on the value of 'enableTls'.
322 #[serde(skip_serializing_if = "Option::is_none")]
323 pub client_ssl_certificate: Option<String>,
324
325 /// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections. If set to an empty list, TLS client certificates will be removed and mTLS will not be required. TLS connection may still apply, depending on the value of 'enableTls'.
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
328
329 /// Optional. When 'true', requires TLS authentication for all connections - mTLS with valid clientTlsCertificates, regular TLS when clientTlsCertificates is not provided. If enableTls is set to 'false' while mTLS is required, it will remove the mTLS requirement and erase previously provided clientTlsCertificates.
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub enable_tls: Option<bool>,
332
333 /// Optional. Type and rate of data persistence in all regions that don't set local 'dataPersistence'.
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub global_data_persistence: Option<String>,
336
337 /// Optional. Changes the password used to access the database in all regions that don't set a local 'password'.
338 #[serde(skip_serializing_if = "Option::is_none")]
339 pub global_password: Option<String>,
340
341 /// Optional. List of source IP addresses or subnet masks to allow in all regions that don't set local 'sourceIp' settings. If set, Redis clients will be able to connect to this database only from within the specified source IP addresses ranges. Example: ['192.168.10.0/32', '192.168.12.0/24']
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub global_source_ip: Option<Vec<String>>,
344
345 /// Optional. Redis database alert settings in all regions that don't set local 'alerts'.
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub global_alerts: Option<Vec<DatabaseAlertSpec>>,
348
349 /// Optional. A list of regions and local settings to update.
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub regions: Option<Vec<LocalRegionProperties>>,
352
353 /// Optional. Data eviction policy.
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub data_eviction_policy: Option<String>,
356
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub command_type: Option<String>,
359}
360
361/// Database slowlog entry
362#[derive(Debug, Clone, Serialize, Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct DatabaseSlowLogEntry {
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub id: Option<i32>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub start_time: Option<String>,
370
371 #[serde(skip_serializing_if = "Option::is_none")]
372 pub duration: Option<i32>,
373
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub arguments: Option<String>,
376}
377
378/// Database tag
379#[derive(Debug, Clone, Serialize, Deserialize)]
380#[serde(rename_all = "camelCase")]
381pub struct DatabaseTagCreateRequest {
382 /// Database tag key.
383 pub key: String,
384
385 /// Database tag value.
386 pub value: String,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub subscription_id: Option<i32>,
390
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub database_id: Option<i32>,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub command_type: Option<String>,
396}
397
398/// Optional. Throughput measurement method.
399#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct DatabaseThroughputSpec {
401 /// Throughput measurement method. Use 'operations-per-second' for all new databases.
402 pub by: String,
403
404 /// Throughput value in the selected measurement method.
405 pub value: i64,
406}
407
408/// Optional. Changes Remote backup configuration details.
409#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(rename_all = "camelCase")]
411pub struct DatabaseBackupConfig {
412 /// Optional. Determine if backup should be active. Default: null
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub active: Option<bool>,
415
416 /// Required when active is 'true'. Defines the interval between backups. Format: 'every-x-hours', where x is one of 24, 12, 6, 4, 2, or 1. Example: "every-4-hours"
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub interval: Option<String>,
419
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub backup_interval: Option<String>,
422
423 /// Optional. Hour when the backup starts. Available only for "every-12-hours" and "every-24-hours" backup intervals. Specified as an hour in 24-hour UTC time. Example: "14:00" is 2 PM UTC.
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub time_utc: Option<String>,
426
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub database_backup_time_utc: Option<String>,
429
430 /// Required when active is 'true'. Type of storage to host backup files. Can be "aws-s3", "google-blob-storage", "azure-blob-storage", or "ftp". See [Set up backup storage locations](https://redis.io/docs/latest/operate/rc/databases/back-up-data/#set-up-backup-storage-locations) to learn how to set up backup storage locations.
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub storage_type: Option<String>,
433
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub backup_storage_type: Option<String>,
436
437 /// Required when active is 'true'. Path to the backup storage location.
438 #[serde(skip_serializing_if = "Option::is_none")]
439 pub storage_path: Option<String>,
440}
441
442/// Optional. Redis advanced capabilities (also known as modules) to be provisioned in the database. Use GET /database-modules to get a list of available advanced capabilities.
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct DatabaseModuleSpec {
445 /// Redis advanced capability name. Use GET /database-modules for a list of available capabilities.
446 pub name: String,
447
448 /// Optional. Redis advanced capability parameters. Use GET /database-modules to get the available capabilities and their parameters.
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub parameters: Option<HashMap<String, Value>>,
451}
452
453/// Optional. Changes Replica Of (also known as Active-Passive) configuration details.
454#[derive(Debug, Clone, Serialize, Deserialize)]
455#[serde(rename_all = "camelCase")]
456pub struct ReplicaOfSpec {
457 /// Optional. This database will be a replica of the specified Redis databases, provided as a list of objects with endpoint and certificate details.
458 pub sync_sources: Vec<DatabaseSyncSourceSpec>,
459}
460
461/// Regex rule for custom hashing policy
462#[derive(Debug, Clone, Serialize, Deserialize)]
463#[serde(rename_all = "camelCase")]
464pub struct RegexRule {
465 /// The ordinal/order of this rule
466 pub ordinal: i32,
467
468 /// The regex pattern for this rule
469 pub pattern: String,
470}
471
472/// Backup configuration status (response)
473#[derive(Debug, Clone, Serialize, Deserialize)]
474#[serde(rename_all = "camelCase")]
475pub struct Backup {
476 /// Whether remote backup is enabled
477 #[serde(skip_serializing_if = "Option::is_none")]
478 pub enable_remote_backup: Option<bool>,
479
480 /// Backup time in UTC
481 #[serde(skip_serializing_if = "Option::is_none")]
482 pub time_utc: Option<String>,
483
484 /// Backup interval (e.g., "every-24-hours", "every-12-hours")
485 #[serde(skip_serializing_if = "Option::is_none")]
486 pub interval: Option<String>,
487
488 /// Backup destination path
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub destination: Option<String>,
491}
492
493/// Security configuration (response)
494#[derive(Debug, Clone, Serialize, Deserialize)]
495#[serde(rename_all = "camelCase")]
496pub struct Security {
497 /// Whether default Redis user is enabled
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub enable_default_user: Option<bool>,
500
501 /// Whether SSL client authentication is enabled
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub ssl_client_authentication: Option<bool>,
504
505 /// Whether TLS client authentication is enabled
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub tls_client_authentication: Option<bool>,
508
509 /// List of source IP addresses allowed to connect
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub source_ips: Option<Vec<String>>,
512
513 /// Database password (masked in responses)
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub password: Option<String>,
516
517 /// Whether TLS is enabled
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub enable_tls: Option<bool>,
520}
521
522/// Clustering configuration (response)
523#[derive(Debug, Clone, Serialize, Deserialize)]
524#[serde(rename_all = "camelCase")]
525pub struct Clustering {
526 /// Number of shards
527 #[serde(skip_serializing_if = "Option::is_none")]
528 pub number_of_shards: Option<i32>,
529
530 /// Regex rules for custom hashing
531 #[serde(skip_serializing_if = "Option::is_none")]
532 pub regex_rules: Option<Vec<RegexRule>>,
533
534 /// Hashing policy
535 #[serde(skip_serializing_if = "Option::is_none")]
536 pub hashing_policy: Option<String>,
537}
538
539/// Active-Active (CRDB) database information
540///
541/// Represents an Active-Active database with global settings and per-region configurations.
542#[derive(Debug, Clone, Serialize, Deserialize)]
543#[serde(rename_all = "camelCase")]
544pub struct ActiveActiveDatabase {
545 /// Database ID
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub database_id: Option<i32>,
548
549 /// Database name
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub name: Option<String>,
552
553 /// Database protocol
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub protocol: Option<String>,
556
557 /// Database status
558 #[serde(skip_serializing_if = "Option::is_none")]
559 pub status: Option<String>,
560
561 /// Redis version
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub redis_version: Option<String>,
564
565 /// Memory storage type
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub memory_storage: Option<String>,
568
569 /// Whether this is an Active-Active database
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub active_active_redis: Option<bool>,
572
573 /// Timestamp when database was activated
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub activated_on: Option<String>,
576
577 /// Timestamp of last modification
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub last_modified: Option<String>,
580
581 /// Support for OSS Cluster API
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub support_oss_cluster_api: Option<bool>,
584
585 /// Use external endpoint for OSS Cluster API
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
588
589 /// Whether replication is enabled
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub replication: Option<bool>,
592
593 /// Data eviction policy
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub data_eviction_policy: Option<String>,
596
597 /// Security configuration
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub security: Option<Security>,
600
601 /// Redis modules enabled
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub modules: Option<Vec<DatabaseModuleSpec>>,
604
605 /// Global data persistence setting
606 #[serde(skip_serializing_if = "Option::is_none")]
607 pub global_data_persistence: Option<String>,
608
609 /// Global source IP allowlist
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub global_source_ip: Option<Vec<String>>,
612
613 /// Global password
614 #[serde(skip_serializing_if = "Option::is_none")]
615 pub global_password: Option<String>,
616
617 /// Global alert configurations
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub global_alerts: Option<Vec<DatabaseAlertSpec>>,
620
621 /// Global enable default user setting
622 #[serde(skip_serializing_if = "Option::is_none")]
623 pub global_enable_default_user: Option<bool>,
624
625 /// Per-region CRDB database configurations
626 #[serde(skip_serializing_if = "Option::is_none")]
627 pub crdb_databases: Option<Vec<CrdbDatabase>>,
628
629 /// Whether automatic minor version upgrades are enabled
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub auto_minor_version_upgrade: Option<bool>,
632}
633
634/// Per-region configuration for an Active-Active (CRDB) database
635#[derive(Debug, Clone, Serialize, Deserialize)]
636#[serde(rename_all = "camelCase")]
637pub struct CrdbDatabase {
638 /// Cloud provider
639 #[serde(skip_serializing_if = "Option::is_none")]
640 pub provider: Option<String>,
641
642 /// Cloud region
643 #[serde(skip_serializing_if = "Option::is_none")]
644 pub region: Option<String>,
645
646 /// Redis version compliance
647 #[serde(skip_serializing_if = "Option::is_none")]
648 pub redis_version_compliance: Option<String>,
649
650 /// Public endpoint
651 #[serde(skip_serializing_if = "Option::is_none")]
652 pub public_endpoint: Option<String>,
653
654 /// Private endpoint
655 #[serde(skip_serializing_if = "Option::is_none")]
656 pub private_endpoint: Option<String>,
657
658 /// Memory limit in GB
659 #[serde(skip_serializing_if = "Option::is_none")]
660 pub memory_limit_in_gb: Option<f64>,
661
662 /// Dataset size in GB
663 #[serde(skip_serializing_if = "Option::is_none")]
664 pub dataset_size_in_gb: Option<f64>,
665
666 /// Memory used in MB
667 #[serde(skip_serializing_if = "Option::is_none")]
668 pub memory_used_in_mb: Option<f64>,
669
670 /// Read operations per second
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub read_operations_per_second: Option<i32>,
673
674 /// Write operations per second
675 #[serde(skip_serializing_if = "Option::is_none")]
676 pub write_operations_per_second: Option<i32>,
677
678 /// Data persistence setting for this region
679 #[serde(skip_serializing_if = "Option::is_none")]
680 pub data_persistence: Option<String>,
681
682 /// Alert configurations for this region
683 #[serde(skip_serializing_if = "Option::is_none")]
684 pub alerts: Option<Vec<DatabaseAlertSpec>>,
685
686 /// Security configuration for this region
687 #[serde(skip_serializing_if = "Option::is_none")]
688 pub security: Option<Security>,
689
690 /// Backup configuration for this region
691 #[serde(skip_serializing_if = "Option::is_none")]
692 pub backup: Option<Backup>,
693
694 /// Query performance factor
695 #[serde(skip_serializing_if = "Option::is_none")]
696 pub query_performance_factor: Option<String>,
697}
698
699/// Database backup request message
700///
701/// # Example
702///
703/// ```
704/// use redis_cloud::flexible::databases::DatabaseBackupRequest;
705///
706/// let request = DatabaseBackupRequest::builder()
707/// .region_name("us-east-1")
708/// .build();
709/// ```
710#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
711#[serde(rename_all = "camelCase")]
712pub struct DatabaseBackupRequest {
713 #[serde(skip_serializing_if = "Option::is_none")]
714 #[builder(default, setter(strip_option))]
715 pub subscription_id: Option<i32>,
716
717 #[serde(skip_serializing_if = "Option::is_none")]
718 #[builder(default, setter(strip_option))]
719 pub database_id: Option<i32>,
720
721 /// Required for Active-Active databases. Name of the cloud provider region to back up. When backing up an Active-Active database, you must back up each region separately.
722 #[serde(skip_serializing_if = "Option::is_none")]
723 #[builder(default, setter(strip_option, into))]
724 pub region_name: Option<String>,
725
726 /// Optional. Manually backs up data to this location, instead of the set 'remoteBackup' location.
727 #[serde(skip_serializing_if = "Option::is_none")]
728 #[builder(default, setter(strip_option, into))]
729 pub adhoc_backup_path: Option<String>,
730
731 #[serde(skip_serializing_if = "Option::is_none")]
732 #[builder(default, setter(strip_option, into))]
733 pub command_type: Option<String>,
734}
735
736/// Database
737///
738/// Represents a Redis Cloud database with all known API fields as first-class struct members.
739/// The `extra` field is reserved only for truly unknown/future fields that may be added to the API.
740#[derive(Debug, Clone, Serialize, Deserialize)]
741#[serde(rename_all = "camelCase")]
742pub struct Database {
743 /// Database ID - always present in API responses
744 pub database_id: i32,
745
746 /// Database name
747 #[serde(skip_serializing_if = "Option::is_none")]
748 pub name: Option<String>,
749
750 /// Database status (e.g., "active", "pending", "error", "draft")
751 #[serde(skip_serializing_if = "Option::is_none")]
752 pub status: Option<String>,
753
754 /// Cloud provider (e.g., "AWS", "GCP", "Azure")
755 #[serde(skip_serializing_if = "Option::is_none")]
756 pub provider: Option<String>,
757
758 /// Cloud region (e.g., "us-east-1", "europe-west1")
759 #[serde(skip_serializing_if = "Option::is_none")]
760 pub region: Option<String>,
761
762 /// Redis version (e.g., "7.2", "7.0")
763 #[serde(skip_serializing_if = "Option::is_none")]
764 pub redis_version: Option<String>,
765
766 /// Redis Serialization Protocol version
767 #[serde(skip_serializing_if = "Option::is_none")]
768 pub resp_version: Option<String>,
769
770 /// Total memory limit in GB (including replication and overhead)
771 #[serde(skip_serializing_if = "Option::is_none")]
772 pub memory_limit_in_gb: Option<f64>,
773
774 /// Dataset size in GB (actual data size, excluding replication)
775 #[serde(skip_serializing_if = "Option::is_none")]
776 pub dataset_size_in_gb: Option<f64>,
777
778 /// Memory used in MB
779 #[serde(skip_serializing_if = "Option::is_none")]
780 pub memory_used_in_mb: Option<f64>,
781
782 /// Private endpoint for database connections
783 #[serde(skip_serializing_if = "Option::is_none")]
784 pub private_endpoint: Option<String>,
785
786 /// Public endpoint for database connections (if enabled)
787 #[serde(skip_serializing_if = "Option::is_none")]
788 pub public_endpoint: Option<String>,
789
790 /// TCP port on which the database is available
791 #[serde(skip_serializing_if = "Option::is_none")]
792 pub port: Option<i32>,
793
794 /// Data eviction policy (e.g., "volatile-lru", "allkeys-lru", "noeviction")
795 #[serde(skip_serializing_if = "Option::is_none")]
796 pub data_eviction_policy: Option<String>,
797
798 /// Data persistence setting (e.g., "aof-every-1-sec", "snapshot-every-1-hour", "none")
799 #[serde(skip_serializing_if = "Option::is_none")]
800 pub data_persistence: Option<String>,
801
802 /// Whether replication is enabled
803 #[serde(skip_serializing_if = "Option::is_none")]
804 pub replication: Option<bool>,
805
806 /// Protocol used (e.g., "redis", "memcached")
807 #[serde(skip_serializing_if = "Option::is_none")]
808 pub protocol: Option<String>,
809
810 /// Support for OSS Cluster API
811 #[serde(skip_serializing_if = "Option::is_none")]
812 pub support_oss_cluster_api: Option<bool>,
813
814 /// Use external endpoint for OSS Cluster API
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
817
818 /// Whether TLS is enabled for connections
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub enable_tls: Option<bool>,
821
822 /// Throughput measurement configuration
823 #[serde(skip_serializing_if = "Option::is_none")]
824 pub throughput_measurement: Option<DatabaseThroughputSpec>,
825
826 /// Local throughput measurement for Active-Active databases
827 #[serde(skip_serializing_if = "Option::is_none")]
828 pub local_throughput_measurement: Option<Vec<LocalThroughput>>,
829
830 /// Average item size in bytes (for Auto Tiering)
831 #[serde(skip_serializing_if = "Option::is_none")]
832 pub average_item_size_in_bytes: Option<i64>,
833
834 /// Path to periodic backup storage location
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub periodic_backup_path: Option<String>,
837
838 /// Remote backup configuration
839 #[serde(skip_serializing_if = "Option::is_none")]
840 pub remote_backup: Option<DatabaseBackupConfig>,
841
842 /// List of source IP addresses or subnet masks allowed to connect
843 #[serde(skip_serializing_if = "Option::is_none")]
844 pub source_ip: Option<Vec<String>>,
845
846 /// Client TLS/SSL certificate (deprecated, use `client_tls_certificates`)
847 #[serde(skip_serializing_if = "Option::is_none")]
848 pub client_ssl_certificate: Option<String>,
849
850 /// List of client TLS/SSL certificates for mTLS authentication
851 #[serde(skip_serializing_if = "Option::is_none")]
852 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
853
854 /// Database password (masked in responses for security)
855 #[serde(skip_serializing_if = "Option::is_none")]
856 pub password: Option<String>,
857
858 /// Memcached SASL username
859 #[serde(skip_serializing_if = "Option::is_none")]
860 pub sasl_username: Option<String>,
861
862 /// Memcached SASL password (masked in responses)
863 #[serde(skip_serializing_if = "Option::is_none")]
864 pub sasl_password: Option<String>,
865
866 /// Database alert configurations
867 #[serde(skip_serializing_if = "Option::is_none")]
868 pub alerts: Option<Vec<DatabaseAlertSpec>>,
869
870 /// Redis modules/capabilities enabled on this database
871 #[serde(skip_serializing_if = "Option::is_none")]
872 pub modules: Option<Vec<DatabaseModuleSpec>>,
873
874 /// Database hashing policy for clustering
875 #[serde(skip_serializing_if = "Option::is_none")]
876 pub sharding_type: Option<String>,
877
878 /// Query performance factor (for search and query databases)
879 #[serde(skip_serializing_if = "Option::is_none")]
880 pub query_performance_factor: Option<String>,
881
882 /// List of databases this database is a replica of
883 #[serde(skip_serializing_if = "Option::is_none")]
884 pub replica_of: Option<Vec<String>>,
885
886 /// Replica configuration
887 #[serde(skip_serializing_if = "Option::is_none")]
888 pub replica: Option<ReplicaOfSpec>,
889
890 /// Whether default Redis user is enabled
891 #[serde(skip_serializing_if = "Option::is_none")]
892 pub enable_default_user: Option<bool>,
893
894 /// Whether this is an Active-Active (CRDB) database
895 #[serde(skip_serializing_if = "Option::is_none")]
896 pub active_active_redis: Option<bool>,
897
898 /// Memory storage type: "ram" or "ram-and-flash" (Auto Tiering)
899 #[serde(skip_serializing_if = "Option::is_none")]
900 pub memory_storage: Option<String>,
901
902 /// Redis version compliance status
903 #[serde(skip_serializing_if = "Option::is_none")]
904 pub redis_version_compliance: Option<String>,
905
906 /// Whether automatic minor version upgrades are enabled
907 #[serde(skip_serializing_if = "Option::is_none")]
908 pub auto_minor_version_upgrade: Option<bool>,
909
910 /// Number of shards in the database cluster
911 #[serde(skip_serializing_if = "Option::is_none")]
912 pub number_of_shards: Option<i32>,
913
914 /// Regex rules for custom hashing policy
915 #[serde(skip_serializing_if = "Option::is_none")]
916 pub regex_rules: Option<Vec<RegexRule>>,
917
918 /// Whether SSL client authentication is enabled
919 #[serde(skip_serializing_if = "Option::is_none")]
920 pub ssl_client_authentication: Option<bool>,
921
922 /// Whether TLS client authentication is enabled
923 #[serde(skip_serializing_if = "Option::is_none")]
924 pub tls_client_authentication: Option<bool>,
925
926 /// Timestamp when database was activated
927 #[serde(skip_serializing_if = "Option::is_none")]
928 pub activated: Option<String>,
929
930 /// Timestamp of last modification
931 #[serde(skip_serializing_if = "Option::is_none")]
932 pub last_modified: Option<String>,
933
934 /// HATEOAS links for API navigation
935 #[serde(skip_serializing_if = "Option::is_none")]
936 pub links: Option<Vec<Link>>,
937}
938
939/// Optional. Changes Redis database alert details.
940#[derive(Debug, Clone, Serialize, Deserialize)]
941pub struct DatabaseAlertSpec {
942 /// Alert type. Available options depend on Plan type. See [Configure alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance/#configure-metric-alerts) for more information.
943 pub name: String,
944
945 /// Value over which an alert will be sent. Default values and range depend on the alert type. See [Configure alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance/#configure-metric-alerts) for more information.
946 pub value: i32,
947}
948
949/// Request structure for creating a new Pro database
950///
951/// Contains all configuration options for creating a database in a Pro subscription,
952/// including memory settings, replication, persistence, modules, and networking.
953///
954/// # Example
955///
956/// ```
957/// use redis_cloud::flexible::databases::DatabaseCreateRequest;
958///
959/// let request = DatabaseCreateRequest::builder()
960/// .name("my-database")
961/// .memory_limit_in_gb(1.0)
962/// .replication(true)
963/// .build();
964/// ```
965#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
966#[serde(rename_all = "camelCase")]
967pub struct DatabaseCreateRequest {
968 #[serde(skip_serializing_if = "Option::is_none")]
969 #[builder(default, setter(strip_option))]
970 pub subscription_id: Option<i32>,
971
972 /// Optional. When 'false': Creates a deployment plan and deploys it, creating any resources required by the plan. When 'true': creates a read-only deployment plan and does not create any resources. Default: 'false'
973 #[serde(skip_serializing_if = "Option::is_none")]
974 #[builder(default, setter(strip_option))]
975 pub dry_run: Option<bool>,
976
977 /// Name of the database. Database name is limited to 40 characters or less and must include only letters, digits, and hyphens ('-'). It must start with a letter and end with a letter or digit.
978 #[builder(setter(into))]
979 pub name: String,
980
981 /// Optional. Database protocol. Only set to 'memcached' if you have a legacy application. Default: 'redis'
982 #[serde(skip_serializing_if = "Option::is_none")]
983 #[builder(default, setter(strip_option, into))]
984 pub protocol: Option<String>,
985
986 /// Optional. TCP port on which the database is available (10000-19999). Generated automatically if not set.
987 #[serde(skip_serializing_if = "Option::is_none")]
988 #[builder(default, setter(strip_option))]
989 pub port: Option<i32>,
990
991 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
992 #[serde(skip_serializing_if = "Option::is_none")]
993 #[builder(default, setter(strip_option))]
994 pub memory_limit_in_gb: Option<f64>,
995
996 /// Optional. The maximum amount of data in the dataset for this database in GB. You cannot set both datasetSizeInGb and totalMemoryInGb. If 'replication' is 'true', the database's total memory will be twice as large as the datasetSizeInGb. If 'replication' is false, the database's total memory will be the datasetSizeInGb value.
997 #[serde(skip_serializing_if = "Option::is_none")]
998 #[builder(default, setter(strip_option))]
999 pub dataset_size_in_gb: Option<f64>,
1000
1001 /// Optional. If specified, redisVersion defines the Redis database version. If omitted, the Redis version will be set to the default version (available in 'GET /subscriptions/redis-versions')
1002 #[serde(skip_serializing_if = "Option::is_none")]
1003 #[builder(default, setter(strip_option, into))]
1004 pub redis_version: Option<String>,
1005
1006 /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
1007 #[serde(skip_serializing_if = "Option::is_none")]
1008 #[builder(default, setter(strip_option, into))]
1009 pub resp_version: Option<String>,
1010
1011 /// Optional. Support [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
1012 #[serde(skip_serializing_if = "Option::is_none")]
1013 #[builder(default, setter(strip_option))]
1014 pub support_oss_cluster_api: Option<bool>,
1015
1016 /// Optional. If set to 'true', the database will use the external endpoint for OSS Cluster API. This setting blocks the database's private endpoint. Can only be set if 'supportOSSClusterAPI' is 'true'. Default: 'false'
1017 #[serde(skip_serializing_if = "Option::is_none")]
1018 #[builder(default, setter(strip_option))]
1019 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
1020
1021 /// Optional. Type and rate of data persistence in persistent storage. Default: 'none'
1022 #[serde(skip_serializing_if = "Option::is_none")]
1023 #[builder(default, setter(strip_option, into))]
1024 pub data_persistence: Option<String>,
1025
1026 /// Optional. Data eviction policy. Default: 'volatile-lru'
1027 #[serde(skip_serializing_if = "Option::is_none")]
1028 #[builder(default, setter(strip_option, into))]
1029 pub data_eviction_policy: Option<String>,
1030
1031 /// Optional. Sets database replication. Default: 'true'
1032 #[serde(skip_serializing_if = "Option::is_none")]
1033 #[builder(default, setter(strip_option))]
1034 pub replication: Option<bool>,
1035
1036 /// Optional. This database will be a replica of the specified Redis databases provided as one or more URI(s). Example: 'redis://user:password@host:port'. If the URI provided is a Redis Cloud database, only host and port should be provided. Example: ['<redis://endpoint1:6379>', '<redis://endpoint2:6380>'].
1037 #[serde(skip_serializing_if = "Option::is_none")]
1038 #[builder(default, setter(strip_option))]
1039 pub replica_of: Option<Vec<String>>,
1040
1041 #[serde(skip_serializing_if = "Option::is_none")]
1042 #[builder(default, setter(strip_option))]
1043 pub replica: Option<ReplicaOfSpec>,
1044
1045 #[serde(skip_serializing_if = "Option::is_none")]
1046 #[builder(default, setter(strip_option))]
1047 pub throughput_measurement: Option<DatabaseThroughputSpec>,
1048
1049 /// Optional. Expected throughput per region for an Active-Active database. Default: 1000 read and write ops/sec for each region
1050 #[serde(skip_serializing_if = "Option::is_none")]
1051 #[builder(default, setter(strip_option))]
1052 pub local_throughput_measurement: Option<Vec<LocalThroughput>>,
1053
1054 /// Optional. Relevant only to ram-and-flash (also known as Auto Tiering) subscriptions. Estimated average size in bytes of the items stored in the database. Default: 1000
1055 #[serde(skip_serializing_if = "Option::is_none")]
1056 #[builder(default, setter(strip_option))]
1057 pub average_item_size_in_bytes: Option<i64>,
1058
1059 /// Optional. The path to a backup storage location. If specified, the database will back up every 24 hours to this location, and you can manually back up the database to this location at any time.
1060 #[serde(skip_serializing_if = "Option::is_none")]
1061 #[builder(default, setter(strip_option, into))]
1062 pub periodic_backup_path: Option<String>,
1063
1064 #[serde(skip_serializing_if = "Option::is_none")]
1065 #[builder(default, setter(strip_option))]
1066 pub remote_backup: Option<DatabaseBackupConfig>,
1067
1068 /// Optional. List of source IP addresses or subnet masks to allow. If specified, Redis clients will be able to connect to this database only from within the specified source IP addresses ranges. Example: '['192.168.10.0/32', '192.168.12.0/24']'
1069 #[serde(skip_serializing_if = "Option::is_none")]
1070 #[builder(default, setter(strip_option))]
1071 pub source_ip: Option<Vec<String>>,
1072
1073 /// Optional. A public key client TLS/SSL certificate with new line characters replaced with '\n'. If specified, mTLS authentication will be required to authenticate user connections. Default: 'null'
1074 #[serde(skip_serializing_if = "Option::is_none")]
1075 #[builder(default, setter(strip_option, into))]
1076 pub client_ssl_certificate: Option<String>,
1077
1078 /// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections.
1079 #[serde(skip_serializing_if = "Option::is_none")]
1080 #[builder(default, setter(strip_option))]
1081 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
1082
1083 /// Optional. When 'true', requires TLS authentication for all connections - mTLS with valid clientTlsCertificates, regular TLS when clientTlsCertificates is not provided. Default: 'false'
1084 #[serde(skip_serializing_if = "Option::is_none")]
1085 #[builder(default, setter(strip_option))]
1086 pub enable_tls: Option<bool>,
1087
1088 /// Optional. Password to access the database. If not set, a random 32-character alphanumeric password will be automatically generated. Can only be set if 'protocol' is 'redis'.
1089 #[serde(skip_serializing_if = "Option::is_none")]
1090 #[builder(default, setter(strip_option, into))]
1091 pub password: Option<String>,
1092
1093 /// Optional. Memcached (SASL) Username to access the database. If not set, the username will be set to a 'mc-' prefix followed by a random 5 character long alphanumeric. Can only be set if 'protocol' is 'memcached'.
1094 #[serde(skip_serializing_if = "Option::is_none")]
1095 #[builder(default, setter(strip_option, into))]
1096 pub sasl_username: Option<String>,
1097
1098 /// Optional. Memcached (SASL) Password to access the database. If not set, a random 32 character long alphanumeric password will be automatically generated. Can only be set if 'protocol' is 'memcached'.
1099 #[serde(skip_serializing_if = "Option::is_none")]
1100 #[builder(default, setter(strip_option, into))]
1101 pub sasl_password: Option<String>,
1102
1103 /// Optional. Redis database alert details.
1104 #[serde(skip_serializing_if = "Option::is_none")]
1105 #[builder(default, setter(strip_option))]
1106 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1107
1108 /// Optional. Redis advanced capabilities (also known as modules) to be provisioned in the database. Use GET /database-modules to get a list of available advanced capabilities.
1109 #[serde(skip_serializing_if = "Option::is_none")]
1110 #[builder(default, setter(strip_option))]
1111 pub modules: Option<Vec<DatabaseModuleSpec>>,
1112
1113 /// Optional. Database [Hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#manage-the-hashing-policy).
1114 #[serde(skip_serializing_if = "Option::is_none")]
1115 #[builder(default, setter(strip_option, into))]
1116 pub sharding_type: Option<String>,
1117
1118 #[serde(skip_serializing_if = "Option::is_none")]
1119 #[builder(default, setter(strip_option, into))]
1120 pub command_type: Option<String>,
1121
1122 /// Optional. The query performance factor adds extra compute power specifically for search and query databases. You can increase your queries per second by the selected factor.
1123 #[serde(skip_serializing_if = "Option::is_none")]
1124 #[builder(default, setter(strip_option, into))]
1125 pub query_performance_factor: Option<String>,
1126}
1127
1128/// Database import request
1129///
1130/// # Example
1131///
1132/// ```
1133/// use redis_cloud::flexible::databases::DatabaseImportRequest;
1134///
1135/// let request = DatabaseImportRequest::builder()
1136/// .source_type("aws-s3")
1137/// .import_from_uri(vec!["s3://bucket/backup.rdb".to_string()])
1138/// .build();
1139/// ```
1140#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
1141#[serde(rename_all = "camelCase")]
1142pub struct DatabaseImportRequest {
1143 #[serde(skip_serializing_if = "Option::is_none")]
1144 #[builder(default, setter(strip_option))]
1145 pub subscription_id: Option<i32>,
1146
1147 #[serde(skip_serializing_if = "Option::is_none")]
1148 #[builder(default, setter(strip_option))]
1149 pub database_id: Option<i32>,
1150
1151 /// Type of storage from which to import the database RDB file or Redis data.
1152 #[builder(setter(into))]
1153 pub source_type: String,
1154
1155 /// One or more paths to source data files or Redis databases, as appropriate to specified source type.
1156 pub import_from_uri: Vec<String>,
1157
1158 #[serde(skip_serializing_if = "Option::is_none")]
1159 #[builder(default, setter(strip_option, into))]
1160 pub command_type: Option<String>,
1161}
1162
1163/// Redis list of database tags
1164#[derive(Debug, Clone, Serialize, Deserialize)]
1165#[serde(rename_all = "camelCase")]
1166pub struct CloudTags {
1167 #[serde(skip_serializing_if = "Option::is_none")]
1168 pub account_id: Option<i32>,
1169
1170 /// HATEOAS links
1171 #[serde(skip_serializing_if = "Option::is_none")]
1172 pub links: Option<Vec<Link>>,
1173}
1174
1175/// Upgrades the specified Pro database to a later Redis version.
1176#[derive(Debug, Clone, Serialize, Deserialize)]
1177#[serde(rename_all = "camelCase")]
1178pub struct DatabaseUpgradeRedisVersionRequest {
1179 #[serde(skip_serializing_if = "Option::is_none")]
1180 pub database_id: Option<i32>,
1181
1182 #[serde(skip_serializing_if = "Option::is_none")]
1183 pub subscription_id: Option<i32>,
1184
1185 /// The target Redis version the database will be upgraded to. Use GET /subscriptions/redis-versions to get a list of available Redis versions.
1186 pub target_redis_version: String,
1187
1188 #[serde(skip_serializing_if = "Option::is_none")]
1189 pub command_type: Option<String>,
1190}
1191
1192/// `DatabaseSlowLogEntries`
1193#[derive(Debug, Clone, Serialize, Deserialize)]
1194pub struct DatabaseSlowLogEntries {
1195 #[serde(skip_serializing_if = "Option::is_none")]
1196 pub entries: Option<Vec<DatabaseSlowLogEntry>>,
1197
1198 /// HATEOAS links
1199 #[serde(skip_serializing_if = "Option::is_none")]
1200 pub links: Option<Vec<Link>>,
1201}
1202
1203/// Optional. A list of regions and local settings to update.
1204#[derive(Debug, Clone, Serialize, Deserialize)]
1205#[serde(rename_all = "camelCase")]
1206pub struct LocalRegionProperties {
1207 /// Required. Name of the region to update.
1208 #[serde(skip_serializing_if = "Option::is_none")]
1209 pub region: Option<String>,
1210
1211 #[serde(skip_serializing_if = "Option::is_none")]
1212 pub remote_backup: Option<DatabaseBackupConfig>,
1213
1214 #[serde(skip_serializing_if = "Option::is_none")]
1215 pub local_throughput_measurement: Option<LocalThroughput>,
1216
1217 /// Optional. Type and rate of data persistence for this region. If set, 'globalDataPersistence' will not apply to this region.
1218 #[serde(skip_serializing_if = "Option::is_none")]
1219 pub data_persistence: Option<String>,
1220
1221 /// Optional. Changes the password used to access the database in this region. If set, 'globalPassword' will not apply to this region.
1222 #[serde(skip_serializing_if = "Option::is_none")]
1223 pub password: Option<String>,
1224
1225 /// Optional. List of source IP addresses or subnet masks to allow in this region. If set, Redis clients will be able to connect to the database in this region only from within the specified source IP addresses ranges, and 'globalSourceIp' will not apply to this region. Example: ['192.168.10.0/32', '192.168.12.0/24']
1226 #[serde(skip_serializing_if = "Option::is_none")]
1227 pub source_ip: Option<Vec<String>>,
1228
1229 /// Optional. Redis database alert settings for this region. If set, 'glboalAlerts' will not apply to this region.
1230 #[serde(skip_serializing_if = "Option::is_none")]
1231 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1232
1233 /// Optional. Redis Serialization Protocol version for this region. Must be compatible with Redis version.
1234 #[serde(skip_serializing_if = "Option::is_none")]
1235 pub resp_version: Option<String>,
1236}
1237
1238/// `TaskStateUpdate`
1239#[derive(Debug, Clone, Serialize, Deserialize)]
1240#[serde(rename_all = "camelCase")]
1241pub struct TaskStateUpdate {
1242 #[serde(skip_serializing_if = "Option::is_none")]
1243 pub task_id: Option<String>,
1244
1245 #[serde(skip_serializing_if = "Option::is_none")]
1246 pub command_type: Option<String>,
1247
1248 #[serde(skip_serializing_if = "Option::is_none")]
1249 pub status: Option<String>,
1250
1251 #[serde(skip_serializing_if = "Option::is_none")]
1252 pub description: Option<String>,
1253
1254 #[serde(skip_serializing_if = "Option::is_none")]
1255 pub timestamp: Option<String>,
1256
1257 #[serde(skip_serializing_if = "Option::is_none")]
1258 pub response: Option<ProcessorResponse>,
1259
1260 /// HATEOAS links
1261 #[serde(skip_serializing_if = "Option::is_none")]
1262 pub links: Option<Vec<Link>>,
1263}
1264
1265/// Database update request
1266///
1267/// # Example
1268///
1269/// ```
1270/// use redis_cloud::flexible::databases::DatabaseUpdateRequest;
1271///
1272/// let request = DatabaseUpdateRequest::builder()
1273/// .name("updated-name")
1274/// .memory_limit_in_gb(2.0)
1275/// .build();
1276/// ```
1277#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
1278#[serde(rename_all = "camelCase")]
1279pub struct DatabaseUpdateRequest {
1280 #[serde(skip_serializing_if = "Option::is_none")]
1281 #[builder(default, setter(strip_option))]
1282 pub subscription_id: Option<i32>,
1283
1284 #[serde(skip_serializing_if = "Option::is_none")]
1285 #[builder(default, setter(strip_option))]
1286 pub database_id: Option<i32>,
1287
1288 /// Optional. When 'false': Creates a deployment plan and deploys it, updating any resources required by the plan. When 'true': creates a read-only deployment plan and does not update any resources. Default: 'false'
1289 #[serde(skip_serializing_if = "Option::is_none")]
1290 #[builder(default, setter(strip_option))]
1291 pub dry_run: Option<bool>,
1292
1293 /// Optional. Updated database name.
1294 #[serde(skip_serializing_if = "Option::is_none")]
1295 #[builder(default, setter(strip_option, into))]
1296 pub name: Option<String>,
1297
1298 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
1299 #[serde(skip_serializing_if = "Option::is_none")]
1300 #[builder(default, setter(strip_option))]
1301 pub memory_limit_in_gb: Option<f64>,
1302
1303 /// Optional. The maximum amount of data in the dataset for this database in GB. You cannot set both datasetSizeInGb and totalMemoryInGb. If 'replication' is 'true', the database's total memory will be twice as large as the datasetSizeInGb.If 'replication' is false, the database's total memory will be the datasetSizeInGb value.
1304 #[serde(skip_serializing_if = "Option::is_none")]
1305 #[builder(default, setter(strip_option))]
1306 pub dataset_size_in_gb: Option<f64>,
1307
1308 /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
1309 #[serde(skip_serializing_if = "Option::is_none")]
1310 #[builder(default, setter(strip_option, into))]
1311 pub resp_version: Option<String>,
1312
1313 #[serde(skip_serializing_if = "Option::is_none")]
1314 #[builder(default, setter(strip_option))]
1315 pub throughput_measurement: Option<DatabaseThroughputSpec>,
1316
1317 /// Optional. Type and rate of data persistence in persistent storage.
1318 #[serde(skip_serializing_if = "Option::is_none")]
1319 #[builder(default, setter(strip_option, into))]
1320 pub data_persistence: Option<String>,
1321
1322 /// Optional. Data eviction policy.
1323 #[serde(skip_serializing_if = "Option::is_none")]
1324 #[builder(default, setter(strip_option, into))]
1325 pub data_eviction_policy: Option<String>,
1326
1327 /// Optional. Turns database replication on or off.
1328 #[serde(skip_serializing_if = "Option::is_none")]
1329 #[builder(default, setter(strip_option))]
1330 pub replication: Option<bool>,
1331
1332 /// Optional. Hashing policy Regex rules. Used only if 'shardingType' is 'custom-regex-rules'.
1333 #[serde(skip_serializing_if = "Option::is_none")]
1334 #[builder(default, setter(strip_option))]
1335 pub regex_rules: Option<Vec<String>>,
1336
1337 /// Optional. This database will be a replica of the specified Redis databases provided as one or more URI(s). Example: 'redis://user:password@host:port'. If the URI provided is a Redis Cloud database, only host and port should be provided. Example: ['<redis://endpoint1:6379>', '<redis://endpoint2:6380>'].
1338 #[serde(skip_serializing_if = "Option::is_none")]
1339 #[builder(default, setter(strip_option))]
1340 pub replica_of: Option<Vec<String>>,
1341
1342 #[serde(skip_serializing_if = "Option::is_none")]
1343 #[builder(default, setter(strip_option))]
1344 pub replica: Option<ReplicaOfSpec>,
1345
1346 /// Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api).
1347 #[serde(skip_serializing_if = "Option::is_none")]
1348 #[builder(default, setter(strip_option))]
1349 pub support_oss_cluster_api: Option<bool>,
1350
1351 /// Optional. If set to 'true', the database will use the external endpoint for OSS Cluster API. This setting blocks the database's private endpoint. Can only be set if 'supportOSSClusterAPI' is 'true'.
1352 #[serde(skip_serializing_if = "Option::is_none")]
1353 #[builder(default, setter(strip_option))]
1354 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
1355
1356 /// Optional. Changes the password used to access the database with the 'default' user. Can only be set if 'protocol' is 'redis'.
1357 #[serde(skip_serializing_if = "Option::is_none")]
1358 #[builder(default, setter(strip_option, into))]
1359 pub password: Option<String>,
1360
1361 /// Optional. Changes the Memcached (SASL) username to access the database. Can only be set if 'protocol' is 'memcached'.
1362 #[serde(skip_serializing_if = "Option::is_none")]
1363 #[builder(default, setter(strip_option, into))]
1364 pub sasl_username: Option<String>,
1365
1366 /// Optional. Changes the Memcached (SASL) password to access the database. Can only be set if 'protocol' is 'memcached'.
1367 #[serde(skip_serializing_if = "Option::is_none")]
1368 #[builder(default, setter(strip_option, into))]
1369 pub sasl_password: Option<String>,
1370
1371 /// Optional. List of source IP addresses or subnet masks to allow. If specified, Redis clients will be able to connect to this database only from within the specified source IP addresses ranges. Example: '['192.168.10.0/32', '192.168.12.0/24']'
1372 #[serde(skip_serializing_if = "Option::is_none")]
1373 #[builder(default, setter(strip_option))]
1374 pub source_ip: Option<Vec<String>>,
1375
1376 /// Optional. A public key client TLS/SSL certificate with new line characters replaced with '\n'. If specified, mTLS authentication will be required to authenticate user connections if it is not already required. If set to an empty string, TLS client certificates will be removed and mTLS will not be required. TLS connection may still apply, depending on the value of 'enableTls'.
1377 #[serde(skip_serializing_if = "Option::is_none")]
1378 #[builder(default, setter(strip_option, into))]
1379 pub client_ssl_certificate: Option<String>,
1380
1381 /// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections. If set to an empty list, TLS client certificates will be removed and mTLS will not be required. TLS connection may still apply, depending on the value of 'enableTls'.
1382 #[serde(skip_serializing_if = "Option::is_none")]
1383 #[builder(default, setter(strip_option))]
1384 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
1385
1386 /// Optional. When 'true', requires TLS authentication for all connections - mTLS with valid clientTlsCertificates, regular TLS when clientTlsCertificates is not provided. If enableTls is set to 'false' while mTLS is required, it will remove the mTLS requirement and erase previously provided clientTlsCertificates.
1387 #[serde(skip_serializing_if = "Option::is_none")]
1388 #[builder(default, setter(strip_option))]
1389 pub enable_tls: Option<bool>,
1390
1391 /// Optional. When 'true', allows connecting to the database with the 'default' user. When 'false', only defined access control users can connect to the database. Can only be set if 'protocol' is 'redis'.
1392 #[serde(skip_serializing_if = "Option::is_none")]
1393 #[builder(default, setter(strip_option))]
1394 pub enable_default_user: Option<bool>,
1395
1396 /// Optional. Changes the backup location path. If specified, the database will back up every 24 hours to this location, and you can manually back up the database to this location at any time. If set to an empty string, the backup path will be removed.
1397 #[serde(skip_serializing_if = "Option::is_none")]
1398 #[builder(default, setter(strip_option, into))]
1399 pub periodic_backup_path: Option<String>,
1400
1401 #[serde(skip_serializing_if = "Option::is_none")]
1402 #[builder(default, setter(strip_option))]
1403 pub remote_backup: Option<DatabaseBackupConfig>,
1404
1405 /// Optional. Changes Redis database alert details.
1406 #[serde(skip_serializing_if = "Option::is_none")]
1407 #[builder(default, setter(strip_option))]
1408 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1409
1410 #[serde(skip_serializing_if = "Option::is_none")]
1411 #[builder(default, setter(strip_option, into))]
1412 pub command_type: Option<String>,
1413
1414 /// Optional. Changes the query performance factor. The query performance factor adds extra compute power specifically for search and query databases. You can increase your queries per second by the selected factor.
1415 #[serde(skip_serializing_if = "Option::is_none")]
1416 #[builder(default, setter(strip_option, into))]
1417 pub query_performance_factor: Option<String>,
1418}
1419
1420// ============================================================================
1421// Handler
1422// ============================================================================
1423
1424/// Handler for Pro database operations
1425///
1426/// Manages database lifecycle, configuration, backup/restore, import/export,
1427/// and monitoring for Redis Cloud Pro subscriptions.
1428pub struct DatabaseHandler {
1429 client: CloudClient,
1430}
1431
1432impl DatabaseHandler {
1433 /// Create a new handler
1434 #[must_use]
1435 pub fn new(client: CloudClient) -> Self {
1436 Self { client }
1437 }
1438
1439 /// Get all databases in a Pro subscription
1440 ///
1441 /// Gets a list of all databases in the specified Pro subscription.
1442 ///
1443 /// GET /subscriptions/{subscriptionId}/databases
1444 ///
1445 /// # Arguments
1446 ///
1447 /// * `subscription_id` - The subscription ID
1448 /// * `offset` - Optional offset for pagination
1449 /// * `limit` - Optional limit for pagination
1450 ///
1451 /// # Example
1452 ///
1453 /// ```no_run
1454 /// use redis_cloud::CloudClient;
1455 ///
1456 /// # async fn example() -> redis_cloud::Result<()> {
1457 /// let client = CloudClient::builder()
1458 /// .api_key("your-api-key")
1459 /// .api_secret("your-api-secret")
1460 /// .build()?;
1461 ///
1462 /// // Get all databases in subscription 123
1463 /// let databases = client.databases().get_subscription_databases(123, None, None).await?;
1464 ///
1465 /// // With pagination
1466 /// let page = client.databases().get_subscription_databases(123, Some(0), Some(10)).await?;
1467 /// # Ok(())
1468 /// # }
1469 /// ```
1470 pub async fn get_subscription_databases(
1471 &self,
1472 subscription_id: i32,
1473 offset: Option<i32>,
1474 limit: Option<i32>,
1475 ) -> Result<AccountSubscriptionDatabases> {
1476 let mut query = Vec::new();
1477 if let Some(v) = offset {
1478 query.push(format!("offset={v}"));
1479 }
1480 if let Some(v) = limit {
1481 query.push(format!("limit={v}"));
1482 }
1483 let query_string = if query.is_empty() {
1484 String::new()
1485 } else {
1486 format!("?{}", query.join("&"))
1487 };
1488 self.client
1489 .get(&format!(
1490 "/subscriptions/{subscription_id}/databases{query_string}"
1491 ))
1492 .await
1493 }
1494
1495 /// Create Pro database in existing subscription
1496 /// Creates a new database in an existing Pro subscription.
1497 ///
1498 /// POST /subscriptions/{subscriptionId}/databases
1499 pub async fn create_database(
1500 &self,
1501 subscription_id: i32,
1502 request: &DatabaseCreateRequest,
1503 ) -> Result<TaskStateUpdate> {
1504 self.client
1505 .post(
1506 &format!("/subscriptions/{subscription_id}/databases"),
1507 request,
1508 )
1509 .await
1510 }
1511
1512 /// Delete Pro database
1513 /// Deletes a database from a Pro subscription.
1514 ///
1515 /// DELETE /subscriptions/{subscriptionId}/databases/{databaseId}
1516 pub async fn delete_database_by_id(
1517 &self,
1518 subscription_id: i32,
1519 database_id: i32,
1520 ) -> Result<TaskStateUpdate> {
1521 let response = self
1522 .client
1523 .delete_raw(&format!(
1524 "/subscriptions/{subscription_id}/databases/{database_id}"
1525 ))
1526 .await?;
1527 serde_json::from_value(response).map_err(Into::into)
1528 }
1529
1530 /// Get a single Pro database
1531 ///
1532 /// Gets details and settings of a single database in a Pro subscription.
1533 ///
1534 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}
1535 ///
1536 /// # Example
1537 ///
1538 /// ```no_run
1539 /// use redis_cloud::CloudClient;
1540 ///
1541 /// # async fn example() -> redis_cloud::Result<()> {
1542 /// let client = CloudClient::builder()
1543 /// .api_key("your-api-key")
1544 /// .api_secret("your-api-secret")
1545 /// .build()?;
1546 ///
1547 /// let database = client.databases().get_subscription_database_by_id(123, 456).await?;
1548 ///
1549 /// println!("Database: {} (status: {:?})",
1550 /// database.name.unwrap_or_default(),
1551 /// database.status);
1552 /// # Ok(())
1553 /// # }
1554 /// ```
1555 pub async fn get_subscription_database_by_id(
1556 &self,
1557 subscription_id: i32,
1558 database_id: i32,
1559 ) -> Result<Database> {
1560 self.client
1561 .get(&format!(
1562 "/subscriptions/{subscription_id}/databases/{database_id}"
1563 ))
1564 .await
1565 }
1566
1567 /// Update Pro database
1568 /// Updates an existing Pro database.
1569 ///
1570 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}
1571 pub async fn update_database(
1572 &self,
1573 subscription_id: i32,
1574 database_id: i32,
1575 request: &DatabaseUpdateRequest,
1576 ) -> Result<TaskStateUpdate> {
1577 self.client
1578 .put(
1579 &format!("/subscriptions/{subscription_id}/databases/{database_id}"),
1580 request,
1581 )
1582 .await
1583 }
1584
1585 /// Get Pro database backup status
1586 /// Gets information on the latest backup attempt for this Pro database.
1587 ///
1588 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/backup
1589 pub async fn get_database_backup_status(
1590 &self,
1591 subscription_id: i32,
1592 database_id: i32,
1593 region_name: Option<String>,
1594 ) -> Result<TaskStateUpdate> {
1595 let mut query = Vec::new();
1596 if let Some(v) = region_name {
1597 query.push(format!("regionName={v}"));
1598 }
1599 let query_string = if query.is_empty() {
1600 String::new()
1601 } else {
1602 format!("?{}", query.join("&"))
1603 };
1604 self.client
1605 .get(&format!(
1606 "/subscriptions/{subscription_id}/databases/{database_id}/backup{query_string}"
1607 ))
1608 .await
1609 }
1610
1611 /// Back up Pro database
1612 /// Manually back up the specified Pro database to a backup path. By default, backups will be stored in the 'remoteBackup' location for this database.
1613 ///
1614 /// POST /subscriptions/{subscriptionId}/databases/{databaseId}/backup
1615 pub async fn backup_database(
1616 &self,
1617 subscription_id: i32,
1618 database_id: i32,
1619 request: &DatabaseBackupRequest,
1620 ) -> Result<TaskStateUpdate> {
1621 self.client
1622 .post(
1623 &format!("/subscriptions/{subscription_id}/databases/{database_id}/backup"),
1624 request,
1625 )
1626 .await
1627 }
1628
1629 /// Get Pro database TLS certificate
1630 /// Gets the X.509 PEM (base64) encoded server certificate for TLS connection to the database. Requires 'enableTLS' to be 'true' for the database.
1631 ///
1632 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/certificate
1633 pub async fn get_subscription_database_certificate(
1634 &self,
1635 subscription_id: i32,
1636 database_id: i32,
1637 ) -> Result<DatabaseCertificate> {
1638 self.client
1639 .get(&format!(
1640 "/subscriptions/{subscription_id}/databases/{database_id}/certificate"
1641 ))
1642 .await
1643 }
1644
1645 /// Flush Pro database
1646 /// Deletes all data from the specified Pro database.
1647 ///
1648 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}/flush
1649 pub async fn flush_crdb(
1650 &self,
1651 subscription_id: i32,
1652 database_id: i32,
1653 request: &CrdbFlushRequest,
1654 ) -> Result<TaskStateUpdate> {
1655 self.client
1656 .put(
1657 &format!("/subscriptions/{subscription_id}/databases/{database_id}/flush"),
1658 request,
1659 )
1660 .await
1661 }
1662
1663 /// Get Pro database import status
1664 /// Gets information on the latest import attempt for this Pro database.
1665 ///
1666 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/import
1667 pub async fn get_database_import_status(
1668 &self,
1669 subscription_id: i32,
1670 database_id: i32,
1671 ) -> Result<TaskStateUpdate> {
1672 self.client
1673 .get(&format!(
1674 "/subscriptions/{subscription_id}/databases/{database_id}/import"
1675 ))
1676 .await
1677 }
1678
1679 /// Import data to a Pro database
1680 /// Imports data from an RDB file or from a different Redis database into this Pro database. WARNING: Importing data into a database removes all existing data from the database.
1681 ///
1682 /// POST /subscriptions/{subscriptionId}/databases/{databaseId}/import
1683 pub async fn import_database(
1684 &self,
1685 subscription_id: i32,
1686 database_id: i32,
1687 request: &DatabaseImportRequest,
1688 ) -> Result<TaskStateUpdate> {
1689 self.client
1690 .post(
1691 &format!("/subscriptions/{subscription_id}/databases/{database_id}/import"),
1692 request,
1693 )
1694 .await
1695 }
1696
1697 /// Update Active-Active database
1698 /// (Active-Active databases only) Updates database properties for an Active-Active database.
1699 ///
1700 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}/regions
1701 pub async fn update_crdb_local_properties(
1702 &self,
1703 subscription_id: i32,
1704 database_id: i32,
1705 request: &CrdbUpdatePropertiesRequest,
1706 ) -> Result<TaskStateUpdate> {
1707 self.client
1708 .put(
1709 &format!("/subscriptions/{subscription_id}/databases/{database_id}/regions"),
1710 request,
1711 )
1712 .await
1713 }
1714
1715 /// Get database slowlog
1716 /// Gets the slowlog for a specific database.
1717 ///
1718 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/slow-log
1719 pub async fn get_slow_log(
1720 &self,
1721 subscription_id: i32,
1722 database_id: i32,
1723 region_name: Option<String>,
1724 ) -> Result<DatabaseSlowLogEntries> {
1725 let mut query = Vec::new();
1726 if let Some(v) = region_name {
1727 query.push(format!("regionName={v}"));
1728 }
1729 let query_string = if query.is_empty() {
1730 String::new()
1731 } else {
1732 format!("?{}", query.join("&"))
1733 };
1734 self.client
1735 .get(&format!(
1736 "/subscriptions/{subscription_id}/databases/{database_id}/slow-log{query_string}"
1737 ))
1738 .await
1739 }
1740
1741 /// Get database tags
1742 /// Gets a list of all database tags.
1743 ///
1744 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/tags
1745 pub async fn get_tags(&self, subscription_id: i32, database_id: i32) -> Result<CloudTags> {
1746 self.client
1747 .get(&format!(
1748 "/subscriptions/{subscription_id}/databases/{database_id}/tags"
1749 ))
1750 .await
1751 }
1752
1753 /// Add a database tag
1754 /// Adds a single database tag to a database.
1755 ///
1756 /// POST /subscriptions/{subscriptionId}/databases/{databaseId}/tags
1757 pub async fn create_tag(
1758 &self,
1759 subscription_id: i32,
1760 database_id: i32,
1761 request: &DatabaseTagCreateRequest,
1762 ) -> Result<CloudTag> {
1763 self.client
1764 .post(
1765 &format!("/subscriptions/{subscription_id}/databases/{database_id}/tags"),
1766 request,
1767 )
1768 .await
1769 }
1770
1771 /// Overwrite database tags
1772 /// Overwrites all tags on the database.
1773 ///
1774 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}/tags
1775 pub async fn update_tags(
1776 &self,
1777 subscription_id: i32,
1778 database_id: i32,
1779 request: &DatabaseTagsUpdateRequest,
1780 ) -> Result<CloudTags> {
1781 self.client
1782 .put(
1783 &format!("/subscriptions/{subscription_id}/databases/{database_id}/tags"),
1784 request,
1785 )
1786 .await
1787 }
1788
1789 /// Delete database tag
1790 /// Removes the specified tag from the database.
1791 ///
1792 /// DELETE /subscriptions/{subscriptionId}/databases/{databaseId}/tags/{tagKey}
1793 pub async fn delete_tag(
1794 &self,
1795 subscription_id: i32,
1796 database_id: i32,
1797 tag_key: String,
1798 ) -> Result<HashMap<String, Value>> {
1799 let response = self
1800 .client
1801 .delete_raw(&format!(
1802 "/subscriptions/{subscription_id}/databases/{database_id}/tags/{tag_key}"
1803 ))
1804 .await?;
1805 serde_json::from_value(response).map_err(Into::into)
1806 }
1807
1808 /// Update database tag value
1809 /// Updates the value of the specified database tag.
1810 ///
1811 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}/tags/{tagKey}
1812 pub async fn update_tag(
1813 &self,
1814 subscription_id: i32,
1815 database_id: i32,
1816 tag_key: String,
1817 request: &DatabaseTagUpdateRequest,
1818 ) -> Result<CloudTag> {
1819 self.client
1820 .put(
1821 &format!("/subscriptions/{subscription_id}/databases/{database_id}/tags/{tag_key}"),
1822 request,
1823 )
1824 .await
1825 }
1826
1827 /// Get Pro database version upgrade status
1828 /// Gets information on the latest upgrade attempt for this Pro database.
1829 ///
1830 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/upgrade
1831 pub async fn get_database_redis_version_upgrade_status(
1832 &self,
1833 subscription_id: i32,
1834 database_id: i32,
1835 ) -> Result<BdbVersionUpgradeStatus> {
1836 self.client
1837 .get(&format!(
1838 "/subscriptions/{subscription_id}/databases/{database_id}/upgrade"
1839 ))
1840 .await
1841 }
1842
1843 /// Upgrade Pro database version
1844 ///
1845 /// POST /subscriptions/{subscriptionId}/databases/{databaseId}/upgrade
1846 pub async fn upgrade_database_redis_version(
1847 &self,
1848 subscription_id: i32,
1849 database_id: i32,
1850 request: &DatabaseUpgradeRedisVersionRequest,
1851 ) -> Result<TaskStateUpdate> {
1852 self.client
1853 .post(
1854 &format!("/subscriptions/{subscription_id}/databases/{database_id}/upgrade"),
1855 request,
1856 )
1857 .await
1858 }
1859
1860 /// Get available target Redis versions for upgrade
1861 /// Gets a list of Redis versions that the database can be upgraded to.
1862 ///
1863 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/available-target-versions
1864 pub async fn get_available_target_versions(
1865 &self,
1866 subscription_id: i32,
1867 database_id: i32,
1868 ) -> Result<Value> {
1869 self.client
1870 .get_raw(&format!(
1871 "/subscriptions/{subscription_id}/databases/{database_id}/available-target-versions"
1872 ))
1873 .await
1874 }
1875
1876 /// Flush Pro database (standard, non-Active-Active)
1877 /// Deletes all data from the specified Pro database.
1878 ///
1879 /// PUT /subscriptions/{subscriptionId}/databases/{databaseId}/flush
1880 pub async fn flush_database(
1881 &self,
1882 subscription_id: i32,
1883 database_id: i32,
1884 ) -> Result<TaskStateUpdate> {
1885 // Empty body for standard flush
1886 self.client
1887 .put_raw(
1888 &format!("/subscriptions/{subscription_id}/databases/{database_id}/flush"),
1889 serde_json::json!({}),
1890 )
1891 .await
1892 .and_then(|v| serde_json::from_value(v).map_err(Into::into))
1893 }
1894
1895 // ========================================================================
1896 // Pagination Helpers
1897 // ========================================================================
1898
1899 /// Stream all databases in a Pro subscription
1900 ///
1901 /// Returns an async stream that automatically handles pagination, yielding
1902 /// individual [`Database`] items. This is useful when you need to process
1903 /// a large number of databases without loading them all into memory at once.
1904 ///
1905 /// # Arguments
1906 ///
1907 /// * `subscription_id` - The subscription ID
1908 ///
1909 /// # Example
1910 ///
1911 /// ```no_run
1912 /// use redis_cloud::CloudClient;
1913 /// use futures::StreamExt;
1914 /// use std::pin::pin;
1915 ///
1916 /// # async fn example() -> redis_cloud::Result<()> {
1917 /// let client = CloudClient::builder()
1918 /// .api_key("your-api-key")
1919 /// .api_secret("your-api-secret")
1920 /// .build()?;
1921 ///
1922 /// let handler = client.databases();
1923 /// let mut stream = pin!(handler.stream_databases(123));
1924 /// while let Some(result) = stream.next().await {
1925 /// let database = result?;
1926 /// println!("Database: {} (ID: {})", database.name.unwrap_or_default(), database.database_id);
1927 /// }
1928 /// # Ok(())
1929 /// # }
1930 /// ```
1931 pub fn stream_databases(
1932 &self,
1933 subscription_id: i32,
1934 ) -> impl Stream<Item = Result<Database>> + '_ {
1935 self.stream_databases_with_page_size(subscription_id, 100)
1936 }
1937
1938 /// Stream all databases with custom page size
1939 ///
1940 /// Like [`stream_databases`](Self::stream_databases), but allows specifying
1941 /// the page size for API requests.
1942 ///
1943 /// # Arguments
1944 ///
1945 /// * `subscription_id` - The subscription ID
1946 /// * `page_size` - Number of databases to fetch per API request
1947 pub fn stream_databases_with_page_size(
1948 &self,
1949 subscription_id: i32,
1950 page_size: i32,
1951 ) -> impl Stream<Item = Result<Database>> + '_ {
1952 try_stream! {
1953 let mut offset = 0;
1954
1955 loop {
1956 let response = self
1957 .get_subscription_databases(subscription_id, Some(offset), Some(page_size))
1958 .await?;
1959
1960 // Extract databases from the response
1961 let databases = Self::extract_databases_from_response(&response);
1962
1963 if databases.is_empty() {
1964 break;
1965 }
1966
1967 let count = databases.len();
1968 for db in databases {
1969 yield db;
1970 }
1971
1972 // If we got fewer than page_size, we've reached the end
1973 #[allow(clippy::cast_sign_loss)]
1974 if count < page_size as usize {
1975 break;
1976 }
1977
1978 offset += page_size;
1979 }
1980 }
1981 }
1982
1983 /// Get all databases in a subscription (collected)
1984 ///
1985 /// Fetches all databases by automatically handling pagination and returns
1986 /// them as a single vector. Use [`stream_databases`](Self::stream_databases)
1987 /// if you prefer to process databases one at a time.
1988 ///
1989 /// # Arguments
1990 ///
1991 /// * `subscription_id` - The subscription ID
1992 ///
1993 /// # Example
1994 ///
1995 /// ```no_run
1996 /// use redis_cloud::CloudClient;
1997 ///
1998 /// # async fn example() -> redis_cloud::Result<()> {
1999 /// let client = CloudClient::builder()
2000 /// .api_key("your-api-key")
2001 /// .api_secret("your-api-secret")
2002 /// .build()?;
2003 ///
2004 /// let all_databases = client.databases().get_all_databases(123).await?;
2005 /// println!("Total databases: {}", all_databases.len());
2006 /// # Ok(())
2007 /// # }
2008 /// ```
2009 pub async fn get_all_databases(&self, subscription_id: i32) -> Result<Vec<Database>> {
2010 let mut databases = Vec::new();
2011 let mut offset = 0;
2012 let page_size = 100;
2013
2014 loop {
2015 let response = self
2016 .get_subscription_databases(subscription_id, Some(offset), Some(page_size))
2017 .await?;
2018
2019 let page = Self::extract_databases_from_response(&response);
2020 let count = page.len();
2021 databases.extend(page);
2022
2023 #[allow(clippy::cast_sign_loss)]
2024 if count < page_size as usize {
2025 break;
2026 }
2027 offset += page_size;
2028 }
2029
2030 Ok(databases)
2031 }
2032
2033 /// Extract databases from an `AccountSubscriptionDatabases` response
2034 fn extract_databases_from_response(response: &AccountSubscriptionDatabases) -> Vec<Database> {
2035 response
2036 .subscription
2037 .first()
2038 .map(|sub| sub.databases.clone())
2039 .unwrap_or_default()
2040 }
2041
2042 // ========================================================================
2043 // Simplified API Methods
2044 // ========================================================================
2045
2046 /// List databases in a subscription (simplified)
2047 ///
2048 /// Returns a flat list of databases, unwrapping the nested API response.
2049 /// This is a convenience method that wraps [`get_subscription_databases`](Self::get_subscription_databases).
2050 ///
2051 /// # Arguments
2052 ///
2053 /// * `subscription_id` - The subscription ID
2054 ///
2055 /// # Example
2056 ///
2057 /// ```no_run
2058 /// use redis_cloud::CloudClient;
2059 ///
2060 /// # async fn example() -> redis_cloud::Result<()> {
2061 /// let client = CloudClient::builder()
2062 /// .api_key("your-api-key")
2063 /// .api_secret("your-api-secret")
2064 /// .build()?;
2065 ///
2066 /// let databases = client.databases().list(123).await?;
2067 /// for db in databases {
2068 /// println!("Database: {} (ID: {})", db.name.unwrap_or_default(), db.database_id);
2069 /// }
2070 /// # Ok(())
2071 /// # }
2072 /// ```
2073 pub async fn list(&self, subscription_id: i32) -> Result<Vec<Database>> {
2074 let response = self
2075 .get_subscription_databases(subscription_id, None, None)
2076 .await?;
2077 Ok(Self::extract_databases_from_response(&response))
2078 }
2079
2080 /// Get a database by ID (simplified)
2081 ///
2082 /// Alias for [`get_subscription_database_by_id`](Self::get_subscription_database_by_id).
2083 ///
2084 /// # Arguments
2085 ///
2086 /// * `subscription_id` - The subscription ID
2087 /// * `database_id` - The database ID
2088 ///
2089 /// # Example
2090 ///
2091 /// ```no_run
2092 /// use redis_cloud::CloudClient;
2093 ///
2094 /// # async fn example() -> redis_cloud::Result<()> {
2095 /// let client = CloudClient::builder()
2096 /// .api_key("your-api-key")
2097 /// .api_secret("your-api-secret")
2098 /// .build()?;
2099 ///
2100 /// let database = client.databases().get(123, 456).await?;
2101 /// println!("Database: {} (status: {:?})", database.name.unwrap_or_default(), database.status);
2102 /// # Ok(())
2103 /// # }
2104 /// ```
2105 pub async fn get(&self, subscription_id: i32, database_id: i32) -> Result<Database> {
2106 self.get_subscription_database_by_id(subscription_id, database_id)
2107 .await
2108 }
2109
2110 /// Create a database (simplified)
2111 ///
2112 /// Alias for [`create_database`](Self::create_database).
2113 ///
2114 /// # Arguments
2115 ///
2116 /// * `subscription_id` - The subscription ID
2117 /// * `request` - The database creation request
2118 ///
2119 /// # Example
2120 ///
2121 /// ```no_run
2122 /// use redis_cloud::CloudClient;
2123 /// use redis_cloud::flexible::databases::DatabaseCreateRequest;
2124 ///
2125 /// # async fn example() -> redis_cloud::Result<()> {
2126 /// let client = CloudClient::builder()
2127 /// .api_key("your-api-key")
2128 /// .api_secret("your-api-secret")
2129 /// .build()?;
2130 ///
2131 /// let request = DatabaseCreateRequest::builder()
2132 /// .name("my-database")
2133 /// .memory_limit_in_gb(1.0)
2134 /// .build();
2135 ///
2136 /// let task = client.databases().create(123, &request).await?;
2137 /// println!("Task ID: {:?}", task.task_id);
2138 /// # Ok(())
2139 /// # }
2140 /// ```
2141 pub async fn create(
2142 &self,
2143 subscription_id: i32,
2144 request: &DatabaseCreateRequest,
2145 ) -> Result<TaskStateUpdate> {
2146 self.create_database(subscription_id, request).await
2147 }
2148
2149 /// Update a database (simplified)
2150 ///
2151 /// Alias for [`update_database`](Self::update_database).
2152 ///
2153 /// # Arguments
2154 ///
2155 /// * `subscription_id` - The subscription ID
2156 /// * `database_id` - The database ID
2157 /// * `request` - The database update request
2158 ///
2159 /// # Example
2160 ///
2161 /// ```no_run
2162 /// use redis_cloud::CloudClient;
2163 /// use redis_cloud::flexible::databases::DatabaseUpdateRequest;
2164 ///
2165 /// # async fn example() -> redis_cloud::Result<()> {
2166 /// let client = CloudClient::builder()
2167 /// .api_key("your-api-key")
2168 /// .api_secret("your-api-secret")
2169 /// .build()?;
2170 ///
2171 /// let request = DatabaseUpdateRequest::builder()
2172 /// .name("updated-name")
2173 /// .build();
2174 ///
2175 /// let task = client.databases().update(123, 456, &request).await?;
2176 /// println!("Task ID: {:?}", task.task_id);
2177 /// # Ok(())
2178 /// # }
2179 /// ```
2180 pub async fn update(
2181 &self,
2182 subscription_id: i32,
2183 database_id: i32,
2184 request: &DatabaseUpdateRequest,
2185 ) -> Result<TaskStateUpdate> {
2186 self.update_database(subscription_id, database_id, request)
2187 .await
2188 }
2189
2190 /// Delete a database (simplified)
2191 ///
2192 /// Alias for [`delete_database_by_id`](Self::delete_database_by_id).
2193 ///
2194 /// # Arguments
2195 ///
2196 /// * `subscription_id` - The subscription ID
2197 /// * `database_id` - The database ID
2198 ///
2199 /// # Example
2200 ///
2201 /// ```no_run
2202 /// use redis_cloud::CloudClient;
2203 ///
2204 /// # async fn example() -> redis_cloud::Result<()> {
2205 /// let client = CloudClient::builder()
2206 /// .api_key("your-api-key")
2207 /// .api_secret("your-api-secret")
2208 /// .build()?;
2209 ///
2210 /// let task = client.databases().delete(123, 456).await?;
2211 /// println!("Task ID: {:?}", task.task_id);
2212 /// # Ok(())
2213 /// # }
2214 /// ```
2215 pub async fn delete(&self, subscription_id: i32, database_id: i32) -> Result<TaskStateUpdate> {
2216 self.delete_database_by_id(subscription_id, database_id)
2217 .await
2218 }
2219}