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