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;
55pub use crate::types::{CloudTag, CloudTags, DatabaseTrafficStateResponse, Tag, TaskStateUpdate};
56use crate::{CloudClient, Result};
57use async_stream::try_stream;
58use futures_core::Stream;
59use serde::{Deserialize, Deserializer, Serialize};
60use serde_json::Value;
61use std::collections::HashMap;
62use typed_builder::TypedBuilder;
63
64// ============================================================================
65// Models
66// ============================================================================
67
68/// `RedisLabs` Account Subscription Databases information
69///
70/// Response from GET /subscriptions/{subscriptionId}/databases
71#[derive(Debug, Clone, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase")]
73pub struct AccountSubscriptionDatabases {
74 /// Account ID
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub account_id: Option<i32>,
77
78 /// Subscription information with nested databases array.
79 /// The API returns this as an array, each element containing subscriptionId, databases, and links.
80 #[serde(default, deserialize_with = "deserialize_subscription_info")]
81 pub subscription: Vec<SubscriptionDatabasesInfo>,
82
83 /// HATEOAS links for API navigation
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub links: Option<Vec<Link>>,
86}
87
88/// Subscription databases info returned within `AccountSubscriptionDatabases`
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct SubscriptionDatabasesInfo {
92 /// Subscription ID
93 pub subscription_id: i32,
94
95 /// Number of databases (may not always be present)
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub number_of_databases: Option<i32>,
98
99 /// List of databases in this subscription
100 #[serde(default)]
101 pub databases: Vec<Database>,
102
103 /// HATEOAS links
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub links: Option<Vec<Link>>,
106}
107
108/// Custom deserializer that handles both object and array formats for subscription field.
109/// The API returns an array, but some test mocks use an object format.
110fn deserialize_subscription_info<'de, D>(
111 deserializer: D,
112) -> std::result::Result<Vec<SubscriptionDatabasesInfo>, D::Error>
113where
114 D: Deserializer<'de>,
115{
116 let value: Option<Value> = Option::deserialize(deserializer)?;
117
118 match value {
119 None => Ok(Vec::new()),
120 Some(Value::Array(arr)) => {
121 serde_json::from_value(Value::Array(arr)).map_err(serde::de::Error::custom)
122 }
123 Some(Value::Object(obj)) => {
124 // Single object - wrap in array
125 let item: SubscriptionDatabasesInfo =
126 serde_json::from_value(Value::Object(obj)).map_err(serde::de::Error::custom)?;
127 Ok(vec![item])
128 }
129 Some(other) => Err(serde::de::Error::custom(format!(
130 "expected array or object for subscription, got {other:?}"
131 ))),
132 }
133}
134
135/// Optional. Expected read and write throughput for this region.
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct LocalThroughput {
139 /// Specify one of the selected cloud provider regions for the subscription.
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub region: Option<String>,
142
143 /// Write operations for this region per second. Default: 1000 ops/sec
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub write_operations_per_second: Option<i64>,
146
147 /// Read operations for this region per second. Default: 1000 ops/sec
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub read_operations_per_second: Option<i64>,
150}
151
152/// Database tag update request message
153#[derive(Debug, Clone, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct DatabaseTagUpdateRequest {
156 /// Subscription ID being updated. Server-populated from the path.
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub subscription_id: Option<i32>,
159
160 /// Database ID being updated. Server-populated from the path.
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub database_id: Option<i32>,
163
164 /// Tag key being updated. Server-populated from the path.
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub key: Option<String>,
167
168 /// Database tag value
169 pub value: String,
170
171 /// Read-only on the response; populated by the server with the
172 /// operation type (e.g. `"UPDATE_DATABASE_TAG"`).
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub command_type: Option<String>,
175}
176
177/// Active-Active database flush request message
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct CrdbFlushRequest {
181 /// Subscription ID being updated. Server-populated from the path.
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub subscription_id: Option<i32>,
184
185 /// Database ID being updated. Server-populated from the path.
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub database_id: Option<i32>,
188
189 /// Read-only on the response; populated by the server with the
190 /// operation type (e.g. `"FLUSH_CRDB"`).
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub command_type: Option<String>,
193}
194
195/// Database certificate
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct DatabaseCertificate {
199 /// An X.509 PEM (base64) encoded server certificate with new line characters replaced by '\n'.
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub public_certificate_pem_string: Option<String>,
202}
203
204/// Database tags update request message
205#[derive(Debug, Clone, Serialize, Deserialize)]
206#[serde(rename_all = "camelCase")]
207pub struct DatabaseTagsUpdateRequest {
208 /// Subscription ID being updated. Server-populated from the path.
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub subscription_id: Option<i32>,
211
212 /// Database ID being updated. Server-populated from the path.
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub database_id: Option<i32>,
215
216 /// List of database tags.
217 pub tags: Vec<Tag>,
218
219 /// Read-only on the response; populated by the server with the
220 /// operation type (e.g. `"UPDATE_DATABASE_TAGS"`).
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/// `BdbVersionUpgradeStatus`
250#[derive(Debug, Clone, Serialize, Deserialize)]
251#[serde(rename_all = "camelCase")]
252pub struct BdbVersionUpgradeStatus {
253 /// Database identifier.
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub database_id: Option<i32>,
256
257 /// Target Redis version of the upgrade.
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub target_redis_version: Option<String>,
260
261 /// Upgrade progress (0.0 - 100.0).
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub progress: Option<f64>,
264
265 /// Current upgrade status (e.g. `"in-progress"`, `"completed"`, `"failed"`).
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub upgrade_status: Option<String>,
268}
269
270/// Active-Active database update local properties request message
271#[derive(Debug, Clone, Serialize, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct CrdbUpdatePropertiesRequest {
274 /// Subscription ID being updated. Server-populated from the path.
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub subscription_id: Option<i32>,
277
278 /// Database ID being updated. Server-populated from the path.
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub database_id: Option<i32>,
281
282 /// 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.
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub name: Option<String>,
285
286 /// 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'
287 #[serde(skip_serializing_if = "Option::is_none")]
288 pub dry_run: Option<bool>,
289
290 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
291 #[serde(skip_serializing_if = "Option::is_none")]
292 pub memory_limit_in_gb: Option<f64>,
293
294 /// 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.
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub dataset_size_in_gb: Option<f64>,
297
298 /// Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub support_oss_cluster_api: Option<bool>,
301
302 /// 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'.
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
305
306 /// 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'.
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub client_ssl_certificate: Option<String>,
309
310 /// 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'.
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
313
314 /// 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.
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub enable_tls: Option<bool>,
317
318 /// Optional. Type and rate of data persistence in all regions that don't set local 'dataPersistence'.
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub global_data_persistence: Option<String>,
321
322 /// Optional. Changes the password used to access the database in all regions that don't set a local 'password'.
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub global_password: Option<String>,
325
326 /// 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']
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub global_source_ip: Option<Vec<String>>,
329
330 /// Optional. Redis database alert settings in all regions that don't set local 'alerts'.
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub global_alerts: Option<Vec<DatabaseAlertSpec>>,
333
334 /// Optional. A list of regions and local settings to update.
335 #[serde(skip_serializing_if = "Option::is_none")]
336 pub regions: Option<Vec<LocalRegionProperties>>,
337
338 /// Optional. Data eviction policy.
339 #[serde(skip_serializing_if = "Option::is_none")]
340 pub data_eviction_policy: Option<String>,
341
342 /// Read-only on the response; populated by the server with the
343 /// operation type (e.g. `"UPDATE_CRDB"`).
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub command_type: Option<String>,
346}
347
348/// Database slowlog entry
349#[derive(Debug, Clone, Serialize, Deserialize)]
350#[serde(rename_all = "camelCase")]
351pub struct DatabaseSlowLogEntry {
352 /// Slowlog entry identifier.
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub id: Option<i32>,
355
356 /// Timestamp when the command began executing.
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub start_time: Option<String>,
359
360 /// Execution duration in microseconds.
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub duration: Option<i32>,
363
364 /// Command arguments captured for this slowlog entry.
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub arguments: Option<String>,
367}
368
369/// Database tag
370#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct DatabaseTagCreateRequest {
373 /// Database tag key.
374 pub key: String,
375
376 /// Database tag value.
377 pub value: String,
378
379 /// Subscription ID being updated. Server-populated from the path.
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub subscription_id: Option<i32>,
382
383 /// Database ID being updated. Server-populated from the path.
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub database_id: Option<i32>,
386
387 /// Read-only on the response; populated by the server with the
388 /// operation type (e.g. `"CREATE_DATABASE_TAG"`).
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub command_type: Option<String>,
391}
392
393/// Optional. Throughput measurement method.
394#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct DatabaseThroughputSpec {
396 /// Throughput measurement method. Use 'operations-per-second' for all new databases.
397 pub by: String,
398
399 /// Throughput value in the selected measurement method.
400 pub value: i64,
401}
402
403/// Optional. Changes Remote backup configuration details.
404#[derive(Debug, Clone, Serialize, Deserialize)]
405#[serde(rename_all = "camelCase")]
406pub struct DatabaseBackupConfig {
407 /// Optional. Determine if backup should be active. Default: null
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub active: Option<bool>,
410
411 /// 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"
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub interval: Option<String>,
414
415 /// Alternate request field name for [`Self::interval`] that the API also
416 /// accepts (`backupInterval`). Prefer `interval`, which is the documented
417 /// form in the OpenAPI spec.
418 #[serde(skip_serializing_if = "Option::is_none")]
419 pub backup_interval: Option<String>,
420
421 /// 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.
422 #[serde(rename = "timeUTC", skip_serializing_if = "Option::is_none")]
423 pub time_utc: Option<String>,
424
425 /// Alternate request field name for [`Self::time_utc`] that the API also
426 /// accepts (`databaseBackupTimeUTC`). Prefer `time_utc`, which is the
427 /// documented form in the OpenAPI spec.
428 #[serde(
429 rename = "databaseBackupTimeUTC",
430 skip_serializing_if = "Option::is_none"
431 )]
432 pub database_backup_time_utc: Option<String>,
433
434 /// 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.
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub storage_type: Option<String>,
437
438 /// Alternate request field name for [`Self::storage_type`] that the API
439 /// also accepts (`backupStorageType`). Prefer `storage_type`, which is the
440 /// documented form in the OpenAPI spec.
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub backup_storage_type: Option<String>,
443
444 /// Required when active is 'true'. Path to the backup storage location.
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub storage_path: Option<String>,
447}
448
449/// 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.
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct DatabaseModuleSpec {
452 /// Redis advanced capability name. Use GET /database-modules for a list of available capabilities.
453 pub name: String,
454
455 /// Optional. Redis advanced capability parameters. Use GET /database-modules to get the available capabilities and their parameters.
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub parameters: Option<HashMap<String, Value>>,
458}
459
460/// Optional. Changes Replica Of (also known as Active-Passive) configuration details.
461#[derive(Debug, Clone, Serialize, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct ReplicaOfSpec {
464 /// Optional. This database will be a replica of the specified Redis databases, provided as a list of objects with endpoint and certificate details.
465 pub sync_sources: Vec<DatabaseSyncSourceSpec>,
466}
467
468/// Regex rule for custom hashing policy
469#[derive(Debug, Clone, Serialize, Deserialize)]
470#[serde(rename_all = "camelCase")]
471pub struct RegexRule {
472 /// The ordinal/order of this rule
473 pub ordinal: i32,
474
475 /// The regex pattern for this rule
476 pub pattern: String,
477}
478
479/// Backup configuration status (response)
480#[derive(Debug, Clone, Serialize, Deserialize)]
481#[serde(rename_all = "camelCase")]
482pub struct Backup {
483 /// Whether remote backup is enabled
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub enable_remote_backup: Option<bool>,
486
487 /// Backup time in UTC
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub time_utc: Option<String>,
490
491 /// Backup interval (e.g., "every-24-hours", "every-12-hours")
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub interval: Option<String>,
494
495 /// Backup destination path
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub destination: Option<String>,
498}
499
500/// Security configuration (response)
501#[derive(Debug, Clone, Serialize, Deserialize)]
502#[serde(rename_all = "camelCase")]
503pub struct Security {
504 /// Whether default Redis user is enabled
505 #[serde(skip_serializing_if = "Option::is_none")]
506 pub enable_default_user: Option<bool>,
507
508 /// Whether SSL client authentication is enabled
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub ssl_client_authentication: Option<bool>,
511
512 /// Whether TLS client authentication is enabled
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub tls_client_authentication: Option<bool>,
515
516 /// List of source IP addresses allowed to connect
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub source_ips: Option<Vec<String>>,
519
520 /// Database password (masked in responses)
521 #[serde(skip_serializing_if = "Option::is_none")]
522 pub password: Option<String>,
523
524 /// Whether TLS is enabled
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub enable_tls: Option<bool>,
527}
528
529/// Clustering configuration (response)
530#[derive(Debug, Clone, Serialize, Deserialize)]
531#[serde(rename_all = "camelCase")]
532pub struct Clustering {
533 /// Number of shards
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub number_of_shards: Option<i32>,
536
537 /// Regex rules for custom hashing
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub regex_rules: Option<Vec<RegexRule>>,
540
541 /// Hashing policy
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub hashing_policy: Option<String>,
544}
545
546/// Active-Active (CRDB) database information
547///
548/// Represents an Active-Active database with global settings and per-region configurations.
549#[derive(Debug, Clone, Serialize, Deserialize)]
550#[serde(rename_all = "camelCase")]
551pub struct ActiveActiveDatabase {
552 /// Database ID
553 #[serde(skip_serializing_if = "Option::is_none")]
554 pub database_id: Option<i32>,
555
556 /// Database name
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub name: Option<String>,
559
560 /// Database protocol
561 #[serde(skip_serializing_if = "Option::is_none")]
562 pub protocol: Option<String>,
563
564 /// Database status
565 #[serde(skip_serializing_if = "Option::is_none")]
566 pub status: Option<String>,
567
568 /// Redis version
569 #[serde(skip_serializing_if = "Option::is_none")]
570 pub redis_version: Option<String>,
571
572 /// Memory storage type
573 #[serde(skip_serializing_if = "Option::is_none")]
574 pub memory_storage: Option<String>,
575
576 /// Whether this is an Active-Active database
577 #[serde(skip_serializing_if = "Option::is_none")]
578 pub active_active_redis: Option<bool>,
579
580 /// Timestamp when database was activated
581 #[serde(skip_serializing_if = "Option::is_none")]
582 pub activated_on: Option<String>,
583
584 /// Timestamp of last modification
585 #[serde(skip_serializing_if = "Option::is_none")]
586 pub last_modified: Option<String>,
587
588 /// Support for OSS Cluster API
589 #[serde(skip_serializing_if = "Option::is_none")]
590 pub support_oss_cluster_api: Option<bool>,
591
592 /// Use external endpoint for OSS Cluster API
593 #[serde(skip_serializing_if = "Option::is_none")]
594 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
595
596 /// Whether replication is enabled
597 #[serde(skip_serializing_if = "Option::is_none")]
598 pub replication: Option<bool>,
599
600 /// Data eviction policy
601 #[serde(skip_serializing_if = "Option::is_none")]
602 pub data_eviction_policy: Option<String>,
603
604 /// Security configuration
605 #[serde(skip_serializing_if = "Option::is_none")]
606 pub security: Option<Security>,
607
608 /// Redis modules enabled
609 #[serde(skip_serializing_if = "Option::is_none")]
610 pub modules: Option<Vec<DatabaseModuleSpec>>,
611
612 /// Global data persistence setting
613 #[serde(skip_serializing_if = "Option::is_none")]
614 pub global_data_persistence: Option<String>,
615
616 /// Global source IP allowlist
617 #[serde(skip_serializing_if = "Option::is_none")]
618 pub global_source_ip: Option<Vec<String>>,
619
620 /// Global password
621 #[serde(skip_serializing_if = "Option::is_none")]
622 pub global_password: Option<String>,
623
624 /// Global alert configurations
625 #[serde(skip_serializing_if = "Option::is_none")]
626 pub global_alerts: Option<Vec<DatabaseAlertSpec>>,
627
628 /// Global enable default user setting
629 #[serde(skip_serializing_if = "Option::is_none")]
630 pub global_enable_default_user: Option<bool>,
631
632 /// Per-region CRDB database configurations
633 #[serde(skip_serializing_if = "Option::is_none")]
634 pub crdb_databases: Option<Vec<CrdbDatabase>>,
635
636 /// Whether automatic minor version upgrades are enabled
637 #[serde(skip_serializing_if = "Option::is_none")]
638 pub auto_minor_version_upgrade: Option<bool>,
639}
640
641/// Per-region configuration for an Active-Active (CRDB) database
642#[derive(Debug, Clone, Serialize, Deserialize)]
643#[serde(rename_all = "camelCase")]
644pub struct CrdbDatabase {
645 /// Cloud provider
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub provider: Option<String>,
648
649 /// Cloud region
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub region: Option<String>,
652
653 /// Redis version compliance
654 #[serde(skip_serializing_if = "Option::is_none")]
655 pub redis_version_compliance: Option<String>,
656
657 /// Public endpoint
658 #[serde(skip_serializing_if = "Option::is_none")]
659 pub public_endpoint: Option<String>,
660
661 /// Private endpoint
662 #[serde(skip_serializing_if = "Option::is_none")]
663 pub private_endpoint: Option<String>,
664
665 /// Memory limit in GB
666 #[serde(skip_serializing_if = "Option::is_none")]
667 pub memory_limit_in_gb: Option<f64>,
668
669 /// Dataset size in GB
670 #[serde(skip_serializing_if = "Option::is_none")]
671 pub dataset_size_in_gb: Option<f64>,
672
673 /// Memory used in MB
674 #[serde(skip_serializing_if = "Option::is_none")]
675 pub memory_used_in_mb: Option<f64>,
676
677 /// Read operations per second
678 #[serde(skip_serializing_if = "Option::is_none")]
679 pub read_operations_per_second: Option<i32>,
680
681 /// Write operations per second
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub write_operations_per_second: Option<i32>,
684
685 /// Data persistence setting for this region
686 #[serde(skip_serializing_if = "Option::is_none")]
687 pub data_persistence: Option<String>,
688
689 /// Alert configurations for this region
690 #[serde(skip_serializing_if = "Option::is_none")]
691 pub alerts: Option<Vec<DatabaseAlertSpec>>,
692
693 /// Security configuration for this region
694 #[serde(skip_serializing_if = "Option::is_none")]
695 pub security: Option<Security>,
696
697 /// Backup configuration for this region
698 #[serde(skip_serializing_if = "Option::is_none")]
699 pub backup: Option<Backup>,
700
701 /// Query performance factor
702 #[serde(skip_serializing_if = "Option::is_none")]
703 pub query_performance_factor: Option<String>,
704}
705
706/// Database backup request message
707///
708/// # Example
709///
710/// ```
711/// use redis_cloud::flexible::databases::DatabaseBackupRequest;
712///
713/// let request = DatabaseBackupRequest::builder()
714/// .region_name("us-east-1")
715/// .build();
716/// ```
717#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
718#[serde(rename_all = "camelCase")]
719pub struct DatabaseBackupRequest {
720 /// Subscription ID being updated. Server-populated from the path.
721 #[serde(skip_serializing_if = "Option::is_none")]
722 #[builder(default, setter(strip_option))]
723 pub subscription_id: Option<i32>,
724
725 /// Database ID being updated. Server-populated from the path.
726 #[serde(skip_serializing_if = "Option::is_none")]
727 #[builder(default, setter(strip_option))]
728 pub database_id: Option<i32>,
729
730 /// 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.
731 #[serde(skip_serializing_if = "Option::is_none")]
732 #[builder(default, setter(strip_option, into))]
733 pub region_name: Option<String>,
734
735 /// Optional. Manually backs up data to this location, instead of the set 'remoteBackup' location.
736 #[serde(skip_serializing_if = "Option::is_none")]
737 #[builder(default, setter(strip_option, into))]
738 pub adhoc_backup_path: Option<String>,
739
740 /// Read-only on the response; populated by the server with the
741 /// operation type (e.g. `"BACKUP_DATABASE"`).
742 #[serde(skip_serializing_if = "Option::is_none")]
743 #[builder(default, setter(strip_option, into))]
744 pub command_type: Option<String>,
745}
746
747/// Database
748///
749/// Represents a Redis Cloud database with all known API fields as first-class struct members.
750/// The `extra` field is reserved only for truly unknown/future fields that may be added to the API.
751#[derive(Debug, Clone, Serialize, Deserialize)]
752#[serde(rename_all = "camelCase")]
753pub struct Database {
754 /// Database ID - always present in API responses
755 pub database_id: i32,
756
757 /// Database name
758 #[serde(skip_serializing_if = "Option::is_none")]
759 pub name: Option<String>,
760
761 /// Database status (e.g., "active", "pending", "error", "draft")
762 #[serde(skip_serializing_if = "Option::is_none")]
763 pub status: Option<String>,
764
765 /// Cloud provider (e.g., "AWS", "GCP", "Azure")
766 #[serde(skip_serializing_if = "Option::is_none")]
767 pub provider: Option<String>,
768
769 /// Cloud region (e.g., "us-east-1", "europe-west1")
770 #[serde(skip_serializing_if = "Option::is_none")]
771 pub region: Option<String>,
772
773 /// Redis version (e.g., "7.2", "7.0")
774 #[serde(skip_serializing_if = "Option::is_none")]
775 pub redis_version: Option<String>,
776
777 /// Redis Serialization Protocol version
778 #[serde(skip_serializing_if = "Option::is_none")]
779 pub resp_version: Option<String>,
780
781 /// Total memory limit in GB (including replication and overhead)
782 #[serde(skip_serializing_if = "Option::is_none")]
783 pub memory_limit_in_gb: Option<f64>,
784
785 /// Dataset size in GB (actual data size, excluding replication)
786 #[serde(skip_serializing_if = "Option::is_none")]
787 pub dataset_size_in_gb: Option<f64>,
788
789 /// Memory used in MB
790 #[serde(skip_serializing_if = "Option::is_none")]
791 pub memory_used_in_mb: Option<f64>,
792
793 /// Private endpoint for database connections
794 #[serde(skip_serializing_if = "Option::is_none")]
795 pub private_endpoint: Option<String>,
796
797 /// Public endpoint for database connections (if enabled)
798 #[serde(skip_serializing_if = "Option::is_none")]
799 pub public_endpoint: Option<String>,
800
801 /// TCP port on which the database is available
802 #[serde(skip_serializing_if = "Option::is_none")]
803 pub port: Option<i32>,
804
805 /// Data eviction policy (e.g., "volatile-lru", "allkeys-lru", "noeviction")
806 #[serde(skip_serializing_if = "Option::is_none")]
807 pub data_eviction_policy: Option<String>,
808
809 /// Data persistence setting (e.g., "aof-every-1-sec", "snapshot-every-1-hour", "none")
810 #[serde(skip_serializing_if = "Option::is_none")]
811 pub data_persistence: Option<String>,
812
813 /// Whether replication is enabled
814 #[serde(skip_serializing_if = "Option::is_none")]
815 pub replication: Option<bool>,
816
817 /// Protocol used (e.g., "redis", "memcached")
818 #[serde(skip_serializing_if = "Option::is_none")]
819 pub protocol: Option<String>,
820
821 /// Support for OSS Cluster API
822 #[serde(skip_serializing_if = "Option::is_none")]
823 pub support_oss_cluster_api: Option<bool>,
824
825 /// Use external endpoint for OSS Cluster API
826 #[serde(skip_serializing_if = "Option::is_none")]
827 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
828
829 /// Whether TLS is enabled for connections
830 #[serde(skip_serializing_if = "Option::is_none")]
831 pub enable_tls: Option<bool>,
832
833 /// Throughput measurement configuration
834 #[serde(skip_serializing_if = "Option::is_none")]
835 pub throughput_measurement: Option<DatabaseThroughputSpec>,
836
837 /// Local throughput measurement for Active-Active databases
838 #[serde(skip_serializing_if = "Option::is_none")]
839 pub local_throughput_measurement: Option<Vec<LocalThroughput>>,
840
841 /// Average item size in bytes (for Auto Tiering)
842 #[serde(skip_serializing_if = "Option::is_none")]
843 pub average_item_size_in_bytes: Option<i64>,
844
845 /// Path to periodic backup storage location
846 #[serde(skip_serializing_if = "Option::is_none")]
847 pub periodic_backup_path: Option<String>,
848
849 /// Remote backup configuration
850 #[serde(skip_serializing_if = "Option::is_none")]
851 pub remote_backup: Option<DatabaseBackupConfig>,
852
853 /// List of source IP addresses or subnet masks allowed to connect
854 #[serde(skip_serializing_if = "Option::is_none")]
855 pub source_ip: Option<Vec<String>>,
856
857 /// Client TLS/SSL certificate (deprecated, use `client_tls_certificates`)
858 #[serde(skip_serializing_if = "Option::is_none")]
859 pub client_ssl_certificate: Option<String>,
860
861 /// List of client TLS/SSL certificates for mTLS authentication
862 #[serde(skip_serializing_if = "Option::is_none")]
863 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
864
865 /// Database password (masked in responses for security)
866 #[serde(skip_serializing_if = "Option::is_none")]
867 pub password: Option<String>,
868
869 /// Memcached SASL username
870 #[serde(skip_serializing_if = "Option::is_none")]
871 pub sasl_username: Option<String>,
872
873 /// Memcached SASL password (masked in responses)
874 #[serde(skip_serializing_if = "Option::is_none")]
875 pub sasl_password: Option<String>,
876
877 /// Database alert configurations
878 #[serde(skip_serializing_if = "Option::is_none")]
879 pub alerts: Option<Vec<DatabaseAlertSpec>>,
880
881 /// Redis modules/capabilities enabled on this database
882 #[serde(skip_serializing_if = "Option::is_none")]
883 pub modules: Option<Vec<DatabaseModuleSpec>>,
884
885 /// Database hashing policy for clustering
886 #[serde(skip_serializing_if = "Option::is_none")]
887 pub sharding_type: Option<String>,
888
889 /// Query performance factor (for search and query databases)
890 #[serde(skip_serializing_if = "Option::is_none")]
891 pub query_performance_factor: Option<String>,
892
893 /// List of databases this database is a replica of
894 #[serde(skip_serializing_if = "Option::is_none")]
895 pub replica_of: Option<Vec<String>>,
896
897 /// Replica configuration
898 #[serde(skip_serializing_if = "Option::is_none")]
899 pub replica: Option<ReplicaOfSpec>,
900
901 /// Whether default Redis user is enabled
902 #[serde(skip_serializing_if = "Option::is_none")]
903 pub enable_default_user: Option<bool>,
904
905 /// Whether this is an Active-Active (CRDB) database
906 #[serde(skip_serializing_if = "Option::is_none")]
907 pub active_active_redis: Option<bool>,
908
909 /// Memory storage type: "ram" or "ram-and-flash" (Auto Tiering)
910 #[serde(skip_serializing_if = "Option::is_none")]
911 pub memory_storage: Option<String>,
912
913 /// Redis version compliance status
914 #[serde(skip_serializing_if = "Option::is_none")]
915 pub redis_version_compliance: Option<String>,
916
917 /// Whether automatic minor version upgrades are enabled
918 #[serde(skip_serializing_if = "Option::is_none")]
919 pub auto_minor_version_upgrade: Option<bool>,
920
921 /// Number of shards in the database cluster
922 #[serde(skip_serializing_if = "Option::is_none")]
923 pub number_of_shards: Option<i32>,
924
925 /// Regex rules for custom hashing policy
926 #[serde(skip_serializing_if = "Option::is_none")]
927 pub regex_rules: Option<Vec<RegexRule>>,
928
929 /// Whether SSL client authentication is enabled
930 #[serde(skip_serializing_if = "Option::is_none")]
931 pub ssl_client_authentication: Option<bool>,
932
933 /// Whether TLS client authentication is enabled
934 #[serde(skip_serializing_if = "Option::is_none")]
935 pub tls_client_authentication: Option<bool>,
936
937 /// Timestamp when the database was activated.
938 ///
939 /// Wire field is `activatedOn` per the OpenAPI spec. The previous Rust
940 /// name (`activated`) serialized as `activated`, so real responses
941 /// silently deserialized to `None`.
942 #[serde(skip_serializing_if = "Option::is_none")]
943 pub activated_on: Option<String>,
944
945 /// Timestamp of last modification
946 #[serde(skip_serializing_if = "Option::is_none")]
947 pub last_modified: Option<String>,
948
949 /// HATEOAS links for API navigation
950 #[serde(skip_serializing_if = "Option::is_none")]
951 pub links: Option<Vec<Link>>,
952}
953
954/// Optional. Changes Redis database alert details.
955#[derive(Debug, Clone, Serialize, Deserialize)]
956pub struct DatabaseAlertSpec {
957 /// 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.
958 pub name: String,
959
960 /// 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.
961 pub value: i32,
962}
963
964/// Request structure for creating a new Pro database
965///
966/// Contains all configuration options for creating a database in a Pro subscription,
967/// including memory settings, replication, persistence, modules, and networking.
968///
969/// # Example
970///
971/// ```
972/// use redis_cloud::flexible::databases::DatabaseCreateRequest;
973///
974/// let request = DatabaseCreateRequest::builder()
975/// .name("my-database")
976/// .memory_limit_in_gb(1.0)
977/// .replication(true)
978/// .build();
979/// ```
980#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
981#[serde(rename_all = "camelCase")]
982pub struct DatabaseCreateRequest {
983 /// Subscription ID being updated. Server-populated from the path.
984 #[serde(skip_serializing_if = "Option::is_none")]
985 #[builder(default, setter(strip_option))]
986 pub subscription_id: Option<i32>,
987
988 /// 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'
989 #[serde(skip_serializing_if = "Option::is_none")]
990 #[builder(default, setter(strip_option))]
991 pub dry_run: Option<bool>,
992
993 /// 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.
994 #[builder(setter(into))]
995 pub name: String,
996
997 /// Optional. Database protocol. Only set to 'memcached' if you have a legacy application. Default: 'redis'
998 #[serde(skip_serializing_if = "Option::is_none")]
999 #[builder(default, setter(strip_option, into))]
1000 pub protocol: Option<String>,
1001
1002 /// Optional. TCP port on which the database is available (10000-19999). Generated automatically if not set.
1003 #[serde(skip_serializing_if = "Option::is_none")]
1004 #[builder(default, setter(strip_option))]
1005 pub port: Option<i32>,
1006
1007 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
1008 #[serde(skip_serializing_if = "Option::is_none")]
1009 #[builder(default, setter(strip_option))]
1010 pub memory_limit_in_gb: Option<f64>,
1011
1012 /// 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.
1013 #[serde(skip_serializing_if = "Option::is_none")]
1014 #[builder(default, setter(strip_option))]
1015 pub dataset_size_in_gb: Option<f64>,
1016
1017 /// 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')
1018 #[serde(skip_serializing_if = "Option::is_none")]
1019 #[builder(default, setter(strip_option, into))]
1020 pub redis_version: Option<String>,
1021
1022 /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
1023 #[serde(skip_serializing_if = "Option::is_none")]
1024 #[builder(default, setter(strip_option, into))]
1025 pub resp_version: Option<String>,
1026
1027 /// Optional. Support [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
1028 #[serde(skip_serializing_if = "Option::is_none")]
1029 #[builder(default, setter(strip_option))]
1030 pub support_oss_cluster_api: Option<bool>,
1031
1032 /// 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'
1033 #[serde(skip_serializing_if = "Option::is_none")]
1034 #[builder(default, setter(strip_option))]
1035 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
1036
1037 /// Optional. Type and rate of data persistence in persistent storage. Default: 'none'
1038 #[serde(skip_serializing_if = "Option::is_none")]
1039 #[builder(default, setter(strip_option, into))]
1040 pub data_persistence: Option<String>,
1041
1042 /// Optional. Data eviction policy. Default: 'volatile-lru'
1043 #[serde(skip_serializing_if = "Option::is_none")]
1044 #[builder(default, setter(strip_option, into))]
1045 pub data_eviction_policy: Option<String>,
1046
1047 /// Optional. Sets database replication. Default: 'true'
1048 #[serde(skip_serializing_if = "Option::is_none")]
1049 #[builder(default, setter(strip_option))]
1050 pub replication: Option<bool>,
1051
1052 /// 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>'].
1053 #[serde(skip_serializing_if = "Option::is_none")]
1054 #[builder(default, setter(strip_option))]
1055 pub replica_of: Option<Vec<String>>,
1056
1057 /// Optional. Replica-of (Active-Passive) configuration. See [`ReplicaOfSpec`].
1058 #[serde(skip_serializing_if = "Option::is_none")]
1059 #[builder(default, setter(strip_option))]
1060 pub replica: Option<ReplicaOfSpec>,
1061
1062 /// Optional. Throughput measurement spec. See [`DatabaseThroughputSpec`].
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 #[builder(default, setter(strip_option))]
1065 pub throughput_measurement: Option<DatabaseThroughputSpec>,
1066
1067 /// Optional. Expected throughput per region for an Active-Active database. Default: 1000 read and write ops/sec for each region
1068 #[serde(skip_serializing_if = "Option::is_none")]
1069 #[builder(default, setter(strip_option))]
1070 pub local_throughput_measurement: Option<Vec<LocalThroughput>>,
1071
1072 /// 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
1073 #[serde(skip_serializing_if = "Option::is_none")]
1074 #[builder(default, setter(strip_option))]
1075 pub average_item_size_in_bytes: Option<i64>,
1076
1077 /// 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.
1078 #[serde(skip_serializing_if = "Option::is_none")]
1079 #[builder(default, setter(strip_option, into))]
1080 pub periodic_backup_path: Option<String>,
1081
1082 /// Optional. Remote backup configuration. See [`DatabaseBackupConfig`].
1083 #[serde(skip_serializing_if = "Option::is_none")]
1084 #[builder(default, setter(strip_option))]
1085 pub remote_backup: Option<DatabaseBackupConfig>,
1086
1087 /// 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']'
1088 #[serde(skip_serializing_if = "Option::is_none")]
1089 #[builder(default, setter(strip_option))]
1090 pub source_ip: Option<Vec<String>>,
1091
1092 /// 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'
1093 #[serde(skip_serializing_if = "Option::is_none")]
1094 #[builder(default, setter(strip_option, into))]
1095 pub client_ssl_certificate: Option<String>,
1096
1097 /// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections.
1098 #[serde(skip_serializing_if = "Option::is_none")]
1099 #[builder(default, setter(strip_option))]
1100 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
1101
1102 /// Optional. When 'true', requires TLS authentication for all connections - mTLS with valid clientTlsCertificates, regular TLS when clientTlsCertificates is not provided. Default: 'false'
1103 #[serde(skip_serializing_if = "Option::is_none")]
1104 #[builder(default, setter(strip_option))]
1105 pub enable_tls: Option<bool>,
1106
1107 /// 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'.
1108 #[serde(skip_serializing_if = "Option::is_none")]
1109 #[builder(default, setter(strip_option, into))]
1110 pub password: Option<String>,
1111
1112 /// 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'.
1113 #[serde(skip_serializing_if = "Option::is_none")]
1114 #[builder(default, setter(strip_option, into))]
1115 pub sasl_username: Option<String>,
1116
1117 /// 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'.
1118 #[serde(skip_serializing_if = "Option::is_none")]
1119 #[builder(default, setter(strip_option, into))]
1120 pub sasl_password: Option<String>,
1121
1122 /// Optional. Redis database alert details.
1123 #[serde(skip_serializing_if = "Option::is_none")]
1124 #[builder(default, setter(strip_option))]
1125 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1126
1127 /// 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.
1128 #[serde(skip_serializing_if = "Option::is_none")]
1129 #[builder(default, setter(strip_option))]
1130 pub modules: Option<Vec<DatabaseModuleSpec>>,
1131
1132 /// Optional. Database [Hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#manage-the-hashing-policy).
1133 #[serde(skip_serializing_if = "Option::is_none")]
1134 #[builder(default, setter(strip_option, into))]
1135 pub sharding_type: Option<String>,
1136
1137 /// Read-only on the response; populated by the server with the
1138 /// operation type (e.g. `"CREATE_DATABASE"`).
1139 #[serde(skip_serializing_if = "Option::is_none")]
1140 #[builder(default, setter(strip_option, into))]
1141 pub command_type: Option<String>,
1142
1143 /// 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.
1144 #[serde(skip_serializing_if = "Option::is_none")]
1145 #[builder(default, setter(strip_option, into))]
1146 pub query_performance_factor: Option<String>,
1147}
1148
1149/// Database import request
1150///
1151/// # Example
1152///
1153/// ```
1154/// use redis_cloud::flexible::databases::DatabaseImportRequest;
1155///
1156/// let request = DatabaseImportRequest::builder()
1157/// .source_type("aws-s3")
1158/// .import_from_uri(vec!["s3://bucket/backup.rdb".to_string()])
1159/// .build();
1160/// ```
1161#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
1162#[serde(rename_all = "camelCase")]
1163pub struct DatabaseImportRequest {
1164 /// Subscription ID being updated. Server-populated from the path.
1165 #[serde(skip_serializing_if = "Option::is_none")]
1166 #[builder(default, setter(strip_option))]
1167 pub subscription_id: Option<i32>,
1168
1169 /// Database ID being updated. Server-populated from the path.
1170 #[serde(skip_serializing_if = "Option::is_none")]
1171 #[builder(default, setter(strip_option))]
1172 pub database_id: Option<i32>,
1173
1174 /// Type of storage from which to import the database RDB file or Redis data.
1175 #[builder(setter(into))]
1176 pub source_type: String,
1177
1178 /// One or more paths to source data files or Redis databases, as appropriate to specified source type.
1179 pub import_from_uri: Vec<String>,
1180
1181 /// Read-only on the response; populated by the server with the
1182 /// operation type (e.g. `"IMPORT_DATABASE"`).
1183 #[serde(skip_serializing_if = "Option::is_none")]
1184 #[builder(default, setter(strip_option, into))]
1185 pub command_type: Option<String>,
1186}
1187
1188/// Upgrades the specified Pro database to a later Redis version.
1189#[derive(Debug, Clone, Serialize, Deserialize)]
1190#[serde(rename_all = "camelCase")]
1191pub struct DatabaseUpgradeRedisVersionRequest {
1192 /// Database ID being updated. Server-populated from the path.
1193 #[serde(skip_serializing_if = "Option::is_none")]
1194 pub database_id: Option<i32>,
1195
1196 /// Subscription ID being updated. Server-populated from the path.
1197 #[serde(skip_serializing_if = "Option::is_none")]
1198 pub subscription_id: Option<i32>,
1199
1200 /// The target Redis version the database will be upgraded to. Use GET /subscriptions/redis-versions to get a list of available Redis versions.
1201 pub target_redis_version: String,
1202
1203 /// Read-only on the response; populated by the server with the
1204 /// operation type (e.g. `"UPGRADE_DATABASE_REDIS_VERSION"`).
1205 #[serde(skip_serializing_if = "Option::is_none")]
1206 pub command_type: Option<String>,
1207}
1208
1209/// `DatabaseSlowLogEntries`
1210#[derive(Debug, Clone, Serialize, Deserialize)]
1211pub struct DatabaseSlowLogEntries {
1212 /// Slowlog entries returned for the database.
1213 #[serde(skip_serializing_if = "Option::is_none")]
1214 pub entries: Option<Vec<DatabaseSlowLogEntry>>,
1215
1216 /// HATEOAS links
1217 #[serde(skip_serializing_if = "Option::is_none")]
1218 pub links: Option<Vec<Link>>,
1219}
1220
1221/// Optional. A list of regions and local settings to update.
1222#[derive(Debug, Clone, Serialize, Deserialize)]
1223#[serde(rename_all = "camelCase")]
1224pub struct LocalRegionProperties {
1225 /// Required. Name of the region to update.
1226 #[serde(skip_serializing_if = "Option::is_none")]
1227 pub region: Option<String>,
1228
1229 /// Optional. Remote backup configuration for this region. See [`DatabaseBackupConfig`].
1230 #[serde(skip_serializing_if = "Option::is_none")]
1231 pub remote_backup: Option<DatabaseBackupConfig>,
1232
1233 /// Optional. Local throughput settings for this region. See [`LocalThroughput`].
1234 #[serde(skip_serializing_if = "Option::is_none")]
1235 pub local_throughput_measurement: Option<LocalThroughput>,
1236
1237 /// Optional. Type and rate of data persistence for this region. If set, 'globalDataPersistence' will not apply to this region.
1238 #[serde(skip_serializing_if = "Option::is_none")]
1239 pub data_persistence: Option<String>,
1240
1241 /// Optional. Changes the password used to access the database in this region. If set, 'globalPassword' will not apply to this region.
1242 #[serde(skip_serializing_if = "Option::is_none")]
1243 pub password: Option<String>,
1244
1245 /// 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']
1246 #[serde(skip_serializing_if = "Option::is_none")]
1247 pub source_ip: Option<Vec<String>>,
1248
1249 /// Optional. Redis database alert settings for this region. If set, 'glboalAlerts' will not apply to this region.
1250 #[serde(skip_serializing_if = "Option::is_none")]
1251 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1252
1253 /// Optional. Redis Serialization Protocol version for this region. Must be compatible with Redis version.
1254 #[serde(skip_serializing_if = "Option::is_none")]
1255 pub resp_version: Option<String>,
1256}
1257
1258/// Database update request
1259///
1260/// # Example
1261///
1262/// ```
1263/// use redis_cloud::flexible::databases::DatabaseUpdateRequest;
1264///
1265/// let request = DatabaseUpdateRequest::builder()
1266/// .name("updated-name")
1267/// .memory_limit_in_gb(2.0)
1268/// .build();
1269/// ```
1270#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
1271#[serde(rename_all = "camelCase")]
1272pub struct DatabaseUpdateRequest {
1273 /// Subscription ID being updated. Server-populated from the path.
1274 #[serde(skip_serializing_if = "Option::is_none")]
1275 #[builder(default, setter(strip_option))]
1276 pub subscription_id: Option<i32>,
1277
1278 /// Database ID being updated. Server-populated from the path.
1279 #[serde(skip_serializing_if = "Option::is_none")]
1280 #[builder(default, setter(strip_option))]
1281 pub database_id: Option<i32>,
1282
1283 /// 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'
1284 #[serde(skip_serializing_if = "Option::is_none")]
1285 #[builder(default, setter(strip_option))]
1286 pub dry_run: Option<bool>,
1287
1288 /// Optional. Updated database name.
1289 #[serde(skip_serializing_if = "Option::is_none")]
1290 #[builder(default, setter(strip_option, into))]
1291 pub name: Option<String>,
1292
1293 /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
1294 #[serde(skip_serializing_if = "Option::is_none")]
1295 #[builder(default, setter(strip_option))]
1296 pub memory_limit_in_gb: Option<f64>,
1297
1298 /// 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.
1299 #[serde(skip_serializing_if = "Option::is_none")]
1300 #[builder(default, setter(strip_option))]
1301 pub dataset_size_in_gb: Option<f64>,
1302
1303 /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
1304 #[serde(skip_serializing_if = "Option::is_none")]
1305 #[builder(default, setter(strip_option, into))]
1306 pub resp_version: Option<String>,
1307
1308 /// Optional. Throughput measurement spec. See [`DatabaseThroughputSpec`].
1309 #[serde(skip_serializing_if = "Option::is_none")]
1310 #[builder(default, setter(strip_option))]
1311 pub throughput_measurement: Option<DatabaseThroughputSpec>,
1312
1313 /// Optional. Type and rate of data persistence in persistent storage.
1314 #[serde(skip_serializing_if = "Option::is_none")]
1315 #[builder(default, setter(strip_option, into))]
1316 pub data_persistence: Option<String>,
1317
1318 /// Optional. Data eviction policy.
1319 #[serde(skip_serializing_if = "Option::is_none")]
1320 #[builder(default, setter(strip_option, into))]
1321 pub data_eviction_policy: Option<String>,
1322
1323 /// Optional. Turns database replication on or off.
1324 #[serde(skip_serializing_if = "Option::is_none")]
1325 #[builder(default, setter(strip_option))]
1326 pub replication: Option<bool>,
1327
1328 /// Optional. Hashing policy Regex rules. Used only if 'shardingType' is 'custom-regex-rules'.
1329 #[serde(skip_serializing_if = "Option::is_none")]
1330 #[builder(default, setter(strip_option))]
1331 pub regex_rules: Option<Vec<String>>,
1332
1333 /// 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>'].
1334 #[serde(skip_serializing_if = "Option::is_none")]
1335 #[builder(default, setter(strip_option))]
1336 pub replica_of: Option<Vec<String>>,
1337
1338 /// Optional. Replica-of (Active-Passive) configuration. See [`ReplicaOfSpec`].
1339 #[serde(skip_serializing_if = "Option::is_none")]
1340 #[builder(default, setter(strip_option))]
1341 pub replica: Option<ReplicaOfSpec>,
1342
1343 /// Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api).
1344 #[serde(skip_serializing_if = "Option::is_none")]
1345 #[builder(default, setter(strip_option))]
1346 pub support_oss_cluster_api: Option<bool>,
1347
1348 /// 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'.
1349 #[serde(skip_serializing_if = "Option::is_none")]
1350 #[builder(default, setter(strip_option))]
1351 pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
1352
1353 /// Optional. Changes the password used to access the database with the 'default' user. Can only be set if 'protocol' is 'redis'.
1354 #[serde(skip_serializing_if = "Option::is_none")]
1355 #[builder(default, setter(strip_option, into))]
1356 pub password: Option<String>,
1357
1358 /// Optional. Changes the Memcached (SASL) username to access the database. Can only be set if 'protocol' is 'memcached'.
1359 #[serde(skip_serializing_if = "Option::is_none")]
1360 #[builder(default, setter(strip_option, into))]
1361 pub sasl_username: Option<String>,
1362
1363 /// Optional. Changes the Memcached (SASL) password to access the database. Can only be set if 'protocol' is 'memcached'.
1364 #[serde(skip_serializing_if = "Option::is_none")]
1365 #[builder(default, setter(strip_option, into))]
1366 pub sasl_password: Option<String>,
1367
1368 /// 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']'
1369 #[serde(skip_serializing_if = "Option::is_none")]
1370 #[builder(default, setter(strip_option))]
1371 pub source_ip: Option<Vec<String>>,
1372
1373 /// 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'.
1374 #[serde(skip_serializing_if = "Option::is_none")]
1375 #[builder(default, setter(strip_option, into))]
1376 pub client_ssl_certificate: Option<String>,
1377
1378 /// 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'.
1379 #[serde(skip_serializing_if = "Option::is_none")]
1380 #[builder(default, setter(strip_option))]
1381 pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
1382
1383 /// 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.
1384 #[serde(skip_serializing_if = "Option::is_none")]
1385 #[builder(default, setter(strip_option))]
1386 pub enable_tls: Option<bool>,
1387
1388 /// 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'.
1389 #[serde(skip_serializing_if = "Option::is_none")]
1390 #[builder(default, setter(strip_option))]
1391 pub enable_default_user: Option<bool>,
1392
1393 /// 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.
1394 #[serde(skip_serializing_if = "Option::is_none")]
1395 #[builder(default, setter(strip_option, into))]
1396 pub periodic_backup_path: Option<String>,
1397
1398 /// Optional. Remote backup configuration. See [`DatabaseBackupConfig`].
1399 #[serde(skip_serializing_if = "Option::is_none")]
1400 #[builder(default, setter(strip_option))]
1401 pub remote_backup: Option<DatabaseBackupConfig>,
1402
1403 /// Optional. Changes Redis database alert details.
1404 #[serde(skip_serializing_if = "Option::is_none")]
1405 #[builder(default, setter(strip_option))]
1406 pub alerts: Option<Vec<DatabaseAlertSpec>>,
1407
1408 /// Read-only on the response; populated by the server with the
1409 /// operation type (e.g. `"UPDATE_DATABASE"`).
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
2220 /// Get Pro database traffic state
2221 /// Gets the current traffic state for this Pro database, including whether
2222 /// traffic is stopped and whether it can be resumed.
2223 ///
2224 /// GET /subscriptions/{subscriptionId}/databases/{databaseId}/traffic
2225 pub async fn get_traffic(
2226 &self,
2227 subscription_id: i32,
2228 database_id: i32,
2229 ) -> Result<DatabaseTrafficStateResponse> {
2230 self.client
2231 .get(&format!(
2232 "/subscriptions/{subscription_id}/databases/{database_id}/traffic"
2233 ))
2234 .await
2235 }
2236
2237 /// Resume Pro database traffic
2238 /// Resumes traffic to this Pro database after it has been stopped.
2239 ///
2240 /// POST /subscriptions/{subscriptionId}/databases/{databaseId}/traffic/resume
2241 pub async fn resume_traffic(&self, subscription_id: i32, database_id: i32) -> Result<()> {
2242 self.client
2243 .post(
2244 &format!("/subscriptions/{subscription_id}/databases/{database_id}/traffic/resume"),
2245 &serde_json::json!({}),
2246 )
2247 .await
2248 }
2249}