redis_cloud/fixed/
databases.rs

1//! Database management operations for Essentials (Fixed) subscriptions
2//!
3//! This module provides database management functionality for Redis Cloud Essentials
4//! (formerly Fixed) subscriptions, which offer a simplified, cost-effective option
5//! for smaller workloads with predictable capacity requirements.
6//!
7//! # Overview
8//!
9//! Essentials databases are pre-configured Redis instances with fixed memory allocations
10//! and simplified pricing. They're ideal for development, testing, and production
11//! workloads that don't require auto-scaling or advanced clustering features.
12//!
13//! # Key Features
14//!
15//! - **Fixed Capacity**: Pre-defined memory sizes from 250MB to 12GB
16//! - **Simple Pricing**: Predictable monthly costs
17//! - **Essential Features**: Replication, persistence, and backup support
18//! - **Module Support**: Limited module availability based on plan
19//! - **Quick Setup**: Simplified configuration for faster deployment
20//!
21//! # Differences from Pro Databases
22//!
23//! - Fixed memory allocations (no auto-scaling)
24//! - Limited to single-region deployments
25//! - Simplified module selection
26//! - No clustering support
27//! - Predictable pricing model
28//!
29//! # Example Usage
30//!
31//! ```no_run
32//! use redis_cloud::{CloudClient, FixedDatabaseHandler};
33//!
34//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
35//! let client = CloudClient::builder()
36//!     .api_key("your-api-key")
37//!     .api_secret("your-api-secret")
38//!     .build()?;
39//!
40//! let handler = FixedDatabaseHandler::new(client);
41//!
42//! // Example: List databases in a fixed subscription (ID 123)
43//! let databases = handler.list(123, None, None).await?;
44//! # Ok(())
45//! # }
46//! ```
47
48use crate::{CloudClient, Result};
49use serde::{Deserialize, Serialize};
50use serde_json::Value;
51use std::collections::HashMap;
52
53// ============================================================================
54// Models
55// ============================================================================
56
57/// RedisLabs Account Subscription Databases information
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct AccountFixedSubscriptionDatabases {
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub account_id: Option<i32>,
63
64    /// HATEOAS links
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub links: Option<Vec<HashMap<String, Value>>>,
67
68    /// Additional fields from the API
69    #[serde(flatten)]
70    pub extra: Value,
71}
72
73/// Database import request
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct FixedDatabaseImportRequest {
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub subscription_id: Option<i32>,
79
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub database_id: Option<i32>,
82
83    /// Type of storage from which to import the database RDB file or Redis data.
84    pub source_type: String,
85
86    /// One or more paths to source data files or Redis databases, as appropriate to specified source type.
87    pub import_from_uri: Vec<String>,
88
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub command_type: Option<String>,
91
92    /// Additional fields from the API
93    #[serde(flatten)]
94    pub extra: Value,
95}
96
97/// ProcessorResponse
98#[derive(Debug, Clone, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct ProcessorResponse {
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub resource_id: Option<i32>,
103
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub additional_resource_id: Option<i32>,
106
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub resource: Option<HashMap<String, Value>>,
109
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub error: Option<String>,
112
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub additional_info: Option<String>,
115
116    /// Additional fields from the API
117    #[serde(flatten)]
118    pub extra: Value,
119}
120
121/// Database tag update request message
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct DatabaseTagUpdateRequest {
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub subscription_id: Option<i32>,
127
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub database_id: Option<i32>,
130
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub key: Option<String>,
133
134    /// Database tag value
135    pub value: String,
136
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub command_type: Option<String>,
139
140    /// Additional fields from the API
141    #[serde(flatten)]
142    pub extra: Value,
143}
144
145/// DynamicEndpoints
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct DynamicEndpoints {
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub public: Option<String>,
150
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub private: Option<String>,
153
154    /// Additional fields from the API
155    #[serde(flatten)]
156    pub extra: Value,
157}
158
159/// Database tag
160#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct Tag {
163    /// Database tag key.
164    pub key: String,
165
166    /// Database tag value.
167    pub value: String,
168
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub command_type: Option<String>,
171
172    /// Additional fields from the API
173    #[serde(flatten)]
174    pub extra: Value,
175}
176
177/// Database tags update request message
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct DatabaseTagsUpdateRequest {
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub subscription_id: Option<i32>,
183
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub database_id: Option<i32>,
186
187    /// List of database tags.
188    pub tags: Vec<Tag>,
189
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub command_type: Option<String>,
192
193    /// Additional fields from the API
194    #[serde(flatten)]
195    pub extra: Value,
196}
197
198/// Optional. This database will be a replica of the specified Redis databases, provided as a list of objects with endpoint and certificate details.
199#[derive(Debug, Clone, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct DatabaseSyncSourceSpec {
202    /// 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'.
203    pub endpoint: String,
204
205    /// 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.
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub encryption: Option<bool>,
208
209    /// 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.
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub server_cert: Option<String>,
212
213    /// Additional fields from the API
214    #[serde(flatten)]
215    pub extra: Value,
216}
217
218/// 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'.
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct DatabaseCertificateSpec {
222    /// Client certificate public key in PEM format, with new line characters replaced with '\n'.
223    pub public_certificate_pem_string: String,
224
225    /// Additional fields from the API
226    #[serde(flatten)]
227    pub extra: Value,
228}
229
230/// Database tag
231#[derive(Debug, Clone, Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233pub struct CloudTag {
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub key: Option<String>,
236
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub value: Option<String>,
239
240    #[serde(skip_serializing_if = "Option::is_none")]
241    pub created_at: Option<String>,
242
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub updated_at: Option<String>,
245
246    /// HATEOAS links
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub links: Option<Vec<HashMap<String, Value>>>,
249
250    /// Additional fields from the API
251    #[serde(flatten)]
252    pub extra: Value,
253}
254
255/// Database slowlog entry
256#[derive(Debug, Clone, Serialize, Deserialize)]
257#[serde(rename_all = "camelCase")]
258pub struct DatabaseSlowLogEntry {
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub id: Option<i32>,
261
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub start_time: Option<String>,
264
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub duration: Option<i32>,
267
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub arguments: Option<String>,
270
271    /// Additional fields from the API
272    #[serde(flatten)]
273    pub extra: Value,
274}
275
276/// Database tag
277#[derive(Debug, Clone, Serialize, Deserialize)]
278#[serde(rename_all = "camelCase")]
279pub struct DatabaseTagCreateRequest {
280    /// Database tag key.
281    pub key: String,
282
283    /// Database tag value.
284    pub value: String,
285
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub subscription_id: Option<i32>,
288
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub database_id: Option<i32>,
291
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub command_type: Option<String>,
294
295    /// Additional fields from the API
296    #[serde(flatten)]
297    pub extra: Value,
298}
299
300/// Essentials database backup request message
301#[derive(Debug, Clone, Serialize, Deserialize)]
302#[serde(rename_all = "camelCase")]
303pub struct FixedDatabaseBackupRequest {
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub subscription_id: Option<i32>,
306
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub database_id: Option<i32>,
309
310    /// Optional. Manually backs up data to this location, instead of the set 'periodicBackupPath' location.
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub adhoc_backup_path: Option<String>,
313
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub command_type: Option<String>,
316
317    /// Additional fields from the API
318    #[serde(flatten)]
319    pub extra: Value,
320}
321
322/// 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.
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct DatabaseModuleSpec {
325    /// Redis advanced capability name. Use GET /database-modules for a list of available capabilities.
326    pub name: String,
327
328    /// Optional. Redis advanced capability parameters. Use GET /database-modules to get the available capabilities and their parameters.
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub parameters: Option<HashMap<String, Value>>,
331
332    /// Additional fields from the API
333    #[serde(flatten)]
334    pub extra: Value,
335}
336
337/// Optional. Changes Replica Of (also known as Active-Passive) configuration details.
338#[derive(Debug, Clone, Serialize, Deserialize)]
339#[serde(rename_all = "camelCase")]
340pub struct ReplicaOfSpec {
341    /// Optional. This database will be a replica of the specified Redis databases, provided as a list of objects with endpoint and certificate details.
342    pub sync_sources: Vec<DatabaseSyncSourceSpec>,
343
344    /// Additional fields from the API
345    #[serde(flatten)]
346    pub extra: Value,
347}
348
349/// Optional. Changes Redis database alert details.
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct DatabaseAlertSpec {
352    /// 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.
353    pub name: String,
354
355    /// 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.
356    pub value: i32,
357
358    /// Additional fields from the API
359    #[serde(flatten)]
360    pub extra: Value,
361}
362
363/// Redis list of database tags
364#[derive(Debug, Clone, Serialize, Deserialize)]
365#[serde(rename_all = "camelCase")]
366pub struct CloudTags {
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub account_id: Option<i32>,
369
370    /// HATEOAS links
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub links: Option<Vec<HashMap<String, Value>>>,
373
374    /// Additional fields from the API
375    #[serde(flatten)]
376    pub extra: Value,
377}
378
379/// FixedDatabase
380#[derive(Debug, Clone, Serialize, Deserialize)]
381#[serde(rename_all = "camelCase")]
382pub struct FixedDatabase {
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub database_id: Option<i32>,
385
386    #[serde(skip_serializing_if = "Option::is_none")]
387    pub name: Option<String>,
388
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub protocol: Option<String>,
391
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub provider: Option<String>,
394
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub region: Option<String>,
397
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub redis_version: Option<String>,
400
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub redis_version_compliance: Option<String>,
403
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub resp_version: Option<String>,
406
407    #[serde(skip_serializing_if = "Option::is_none")]
408    pub status: Option<String>,
409
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub plan_memory_limit: Option<f64>,
412
413    #[serde(skip_serializing_if = "Option::is_none")]
414    pub plan_dataset_size: Option<f64>,
415
416    #[serde(skip_serializing_if = "Option::is_none")]
417    pub memory_limit_measurement_unit: Option<String>,
418
419    #[serde(skip_serializing_if = "Option::is_none")]
420    pub memory_limit_in_gb: Option<f64>,
421
422    #[serde(skip_serializing_if = "Option::is_none")]
423    pub dataset_size_in_gb: Option<f64>,
424
425    #[serde(skip_serializing_if = "Option::is_none")]
426    pub memory_used_in_mb: Option<f64>,
427
428    #[serde(skip_serializing_if = "Option::is_none")]
429    pub network_monthly_usage_in_byte: Option<f64>,
430
431    #[serde(skip_serializing_if = "Option::is_none")]
432    pub memory_storage: Option<String>,
433
434    #[serde(skip_serializing_if = "Option::is_none")]
435    pub redis_flex: Option<bool>,
436
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub support_oss_cluster_api: Option<bool>,
439
440    #[serde(skip_serializing_if = "Option::is_none")]
441    pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
442
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub data_persistence: Option<String>,
445
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub replication: Option<bool>,
448
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub data_eviction_policy: Option<String>,
451
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub activated_on: Option<String>,
454
455    #[serde(skip_serializing_if = "Option::is_none")]
456    pub last_modified: Option<String>,
457
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub public_endpoint: Option<String>,
460
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub private_endpoint: Option<String>,
463
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub dynamic_endpoints: Option<DynamicEndpoints>,
466
467    /// HATEOAS links
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub links: Option<Vec<HashMap<String, Value>>>,
470
471    /// Additional fields from the API
472    #[serde(flatten)]
473    pub extra: Value,
474}
475
476/// DatabaseSlowLogEntries
477#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct DatabaseSlowLogEntries {
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub entries: Option<Vec<DatabaseSlowLogEntry>>,
481
482    /// HATEOAS links
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub links: Option<Vec<HashMap<String, Value>>>,
485
486    /// Additional fields from the API
487    #[serde(flatten)]
488    pub extra: Value,
489}
490
491/// TaskStateUpdate
492#[derive(Debug, Clone, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct TaskStateUpdate {
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub task_id: Option<String>,
497
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub command_type: Option<String>,
500
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub status: Option<String>,
503
504    #[serde(skip_serializing_if = "Option::is_none")]
505    pub description: Option<String>,
506
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub timestamp: Option<String>,
509
510    #[serde(skip_serializing_if = "Option::is_none")]
511    pub response: Option<ProcessorResponse>,
512
513    /// HATEOAS links
514    #[serde(skip_serializing_if = "Option::is_none")]
515    pub links: Option<Vec<HashMap<String, Value>>>,
516
517    /// Additional fields from the API
518    #[serde(flatten)]
519    pub extra: Value,
520}
521
522/// Essentials database definition
523#[derive(Debug, Clone, Serialize, Deserialize)]
524#[serde(rename_all = "camelCase")]
525pub struct FixedDatabaseCreateRequest {
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub subscription_id: Option<i32>,
528
529    /// 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.
530    pub name: String,
531
532    /// Optional. Database protocol. Use 'stack' to get all of Redis' advanced capabilities. Only use 'redis' for Pay-as-you-go or Redis Flex subscriptions. Default: 'stack' for most subscriptions, 'redis' for Redis Flex subscriptions.
533    #[serde(skip_serializing_if = "Option::is_none")]
534    pub protocol: Option<String>,
535
536    /// (Pay-as-you-go subscriptions only) Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub memory_limit_in_gb: Option<f64>,
539
540    /// (Pay-as-you-go subscriptions only) 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.
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub dataset_size_in_gb: Option<f64>,
543
544    /// (Pay-as-you-go subscriptions only) Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub support_oss_cluster_api: Option<bool>,
547
548    /// Optional. If specified, redisVersion defines the Redis database version. If omitted, the Redis version will be set to the default version.  (available in 'GET /fixed/redis-versions')
549    #[serde(skip_serializing_if = "Option::is_none")]
550    pub redis_version: Option<String>,
551
552    /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
553    #[serde(skip_serializing_if = "Option::is_none")]
554    pub resp_version: Option<String>,
555
556    /// (Pay-as-you-go subscriptions only) 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'
557    #[serde(skip_serializing_if = "Option::is_none")]
558    pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
559
560    /// (Pay-as-you-go subscriptions only) Optional. Distributes database data to different cloud instances. Default: 'false'
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub enable_database_clustering: Option<bool>,
563
564    /// (Pay-as-you-go subscriptions only) Optional. Specifies the number of master shards.
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub number_of_shards: Option<i32>,
567
568    /// Optional. Type and rate of data persistence in persistent storage. Use GET /fixed/plans/{planId} to see if your plan supports data persistence.
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub data_persistence: Option<String>,
571
572    /// Optional. Data eviction policy.
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub data_eviction_policy: Option<String>,
575
576    /// Optional. Sets database replication. Use GET /fixed/plans/{planId} to see if your plan supports database replication.
577    #[serde(skip_serializing_if = "Option::is_none")]
578    pub replication: Option<bool>,
579
580    /// 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. Use GET /fixed/plans/{planId} to see if your plan supports database backups.
581    #[serde(skip_serializing_if = "Option::is_none")]
582    pub periodic_backup_path: Option<String>,
583
584    /// 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. Use GET /fixed/plans/{planId} to see how many CIDR allow rules your plan supports. Example: '['192.168.10.0/32', '192.168.12.0/24']'
585    #[serde(skip_serializing_if = "Option::is_none")]
586    pub source_ips: Option<Vec<String>>,
587
588    /// (Pay-as-you-go subscriptions only) Optional. Hashing policy Regex rules. Used only if 'enableDatabaseClustering' is set to 'true' and .
589    #[serde(skip_serializing_if = "Option::is_none")]
590    pub regex_rules: Option<Vec<String>>,
591
592    /// 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'].
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub replica_of: Option<Vec<String>>,
595
596    #[serde(skip_serializing_if = "Option::is_none")]
597    pub replica: Option<ReplicaOfSpec>,
598
599    /// 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'
600    #[serde(skip_serializing_if = "Option::is_none")]
601    pub client_ssl_certificate: Option<String>,
602
603    /// Optional. A list of client TLS/SSL certificates. If specified, mTLS authentication will be required to authenticate user connections.
604    #[serde(skip_serializing_if = "Option::is_none")]
605    pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
606
607    /// Optional. When 'true', requires TLS authentication for all connections - mTLS with valid clientTlsCertificates, regular TLS when clientTlsCertificates is not provided. Default: 'false'
608    #[serde(skip_serializing_if = "Option::is_none")]
609    pub enable_tls: Option<bool>,
610
611    /// Optional. Password to access the database. If not set, a random 32-character alphanumeric password will be automatically generated.
612    #[serde(skip_serializing_if = "Option::is_none")]
613    pub password: Option<String>,
614
615    /// Optional. Redis database alert details.
616    #[serde(skip_serializing_if = "Option::is_none")]
617    pub alerts: Option<Vec<DatabaseAlertSpec>>,
618
619    /// 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. Can only be set if 'protocol' is 'redis'.
620    #[serde(skip_serializing_if = "Option::is_none")]
621    pub modules: Option<Vec<DatabaseModuleSpec>>,
622
623    #[serde(skip_serializing_if = "Option::is_none")]
624    pub command_type: Option<String>,
625
626    /// Additional fields from the API
627    #[serde(flatten)]
628    pub extra: Value,
629}
630
631/// Essentials database update request
632#[derive(Debug, Clone, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct FixedDatabaseUpdateRequest {
635    #[serde(skip_serializing_if = "Option::is_none")]
636    pub subscription_id: Option<i32>,
637
638    #[serde(skip_serializing_if = "Option::is_none")]
639    pub database_id: Option<i32>,
640
641    /// Optional. Updated database name.
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub name: Option<String>,
644
645    /// (Pay-as-you-go subscriptions only) Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
646    #[serde(skip_serializing_if = "Option::is_none")]
647    pub memory_limit_in_gb: Option<f64>,
648
649    /// (Pay-as-you-go subscriptions only) 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.
650    #[serde(skip_serializing_if = "Option::is_none")]
651    pub dataset_size_in_gb: Option<f64>,
652
653    /// (Pay-as-you-go subscriptions only) Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api).
654    #[serde(skip_serializing_if = "Option::is_none")]
655    pub support_oss_cluster_api: Option<bool>,
656
657    /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub resp_version: Option<String>,
660
661    /// (Pay-as-you-go subscriptions only) 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'
662    #[serde(skip_serializing_if = "Option::is_none")]
663    pub use_external_endpoint_for_oss_cluster_api: Option<bool>,
664
665    /// (Pay-as-you-go subscriptions only) Optional. Distributes database data to different cloud instances.
666    #[serde(skip_serializing_if = "Option::is_none")]
667    pub enable_database_clustering: Option<bool>,
668
669    /// (Pay-as-you-go subscriptions only) Optional. Changes the number of master shards.
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub number_of_shards: Option<i32>,
672
673    /// Optional. Type and rate of data persistence in persistent storage. Use GET /fixed/plans/{planId} to see if your plan supports data persistence.
674    #[serde(skip_serializing_if = "Option::is_none")]
675    pub data_persistence: Option<String>,
676
677    /// Optional. Turns database replication on or off.
678    #[serde(skip_serializing_if = "Option::is_none")]
679    pub data_eviction_policy: Option<String>,
680
681    /// Optional. Sets database replication. Use GET /fixed/plans/{planId} to see if your plan supports database replication.
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub replication: Option<bool>,
684
685    /// 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. Use GET /fixed/plans/{planId} to see if your plan supports database backups. If set to an empty string, the backup path will be removed.
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub periodic_backup_path: Option<String>,
688
689    /// 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']'
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub source_ips: Option<Vec<String>>,
692
693    /// Optional. This database will be a replica of the specified Redis databases provided as one or more URI (sample format: 'redis://user:password@host:port)'. If the URI provided is Redis Cloud instance, only host and port should be provided (using the format: ['redis://endpoint1:6379', 'redis://endpoint2:6380'] ).
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub replica_of: Option<Vec<String>>,
696
697    #[serde(skip_serializing_if = "Option::is_none")]
698    pub replica: Option<ReplicaOfSpec>,
699
700    /// (Pay-as-you-go subscriptions only) Optional. Hashing policy Regex rules. Used only if 'shardingType' is 'custom-regex-rules'.
701    #[serde(skip_serializing_if = "Option::is_none")]
702    pub regex_rules: Option<Vec<String>>,
703
704    /// 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'.
705    #[serde(skip_serializing_if = "Option::is_none")]
706    pub client_ssl_certificate: Option<String>,
707
708    /// 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'.
709    #[serde(skip_serializing_if = "Option::is_none")]
710    pub client_tls_certificates: Option<Vec<DatabaseCertificateSpec>>,
711
712    /// 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.
713    #[serde(skip_serializing_if = "Option::is_none")]
714    pub enable_tls: Option<bool>,
715
716    /// Optional. Changes the password used to access the database with the 'default' user.
717    #[serde(skip_serializing_if = "Option::is_none")]
718    pub password: Option<String>,
719
720    /// Optional. When 'true', allows connecting to the database with the 'default' user. When 'false', only defined access control users can connect to the database.
721    #[serde(skip_serializing_if = "Option::is_none")]
722    pub enable_default_user: Option<bool>,
723
724    /// Optional. Changes Redis database alert details.
725    #[serde(skip_serializing_if = "Option::is_none")]
726    pub alerts: Option<Vec<DatabaseAlertSpec>>,
727
728    #[serde(skip_serializing_if = "Option::is_none")]
729    pub command_type: Option<String>,
730
731    /// Additional fields from the API
732    #[serde(flatten)]
733    pub extra: Value,
734}
735
736// ============================================================================
737// Handler
738// ============================================================================
739
740/// Handler for Essentials database operations
741///
742/// Manages fixed-capacity databases with simplified configuration
743/// and predictable pricing for Redis Cloud Essentials subscriptions.
744pub struct FixedDatabaseHandler {
745    client: CloudClient,
746}
747
748impl FixedDatabaseHandler {
749    /// Create a new handler
750    pub fn new(client: CloudClient) -> Self {
751        Self { client }
752    }
753
754    /// Get all databases in an Essentials subscription
755    /// Gets a list of all databases in the specified Essentials subscription.
756    ///
757    /// GET /fixed/subscriptions/{subscriptionId}/databases
758    pub async fn list(
759        &self,
760        subscription_id: i32,
761        offset: Option<i32>,
762        limit: Option<i32>,
763    ) -> Result<AccountFixedSubscriptionDatabases> {
764        let mut query = Vec::new();
765        if let Some(v) = offset {
766            query.push(format!("offset={}", v));
767        }
768        if let Some(v) = limit {
769            query.push(format!("limit={}", v));
770        }
771        let query_string = if query.is_empty() {
772            String::new()
773        } else {
774            format!("?{}", query.join("&"))
775        };
776        self.client
777            .get(&format!(
778                "/fixed/subscriptions/{}/databases{}",
779                subscription_id, query_string
780            ))
781            .await
782    }
783
784    /// Create Essentials database
785    /// Creates a new database in the specified Essentials subscription.
786    ///
787    /// POST /fixed/subscriptions/{subscriptionId}/databases
788    pub async fn create(
789        &self,
790        subscription_id: i32,
791        request: &FixedDatabaseCreateRequest,
792    ) -> Result<TaskStateUpdate> {
793        self.client
794            .post(
795                &format!("/fixed/subscriptions/{}/databases", subscription_id),
796                request,
797            )
798            .await
799    }
800
801    /// Delete Essentials database
802    /// Deletes a database from an Essentials subscription.
803    ///
804    /// DELETE /fixed/subscriptions/{subscriptionId}/databases/{databaseId}
805    pub async fn delete_by_id(
806        &self,
807        subscription_id: i32,
808        database_id: i32,
809    ) -> Result<TaskStateUpdate> {
810        let response = self
811            .client
812            .delete_raw(&format!(
813                "/fixed/subscriptions/{}/databases/{}",
814                subscription_id, database_id
815            ))
816            .await?;
817        serde_json::from_value(response).map_err(Into::into)
818    }
819
820    /// Get a single Essentials database
821    /// Gets details and settings of a single database in an Essentials subscription.
822    ///
823    /// GET /fixed/subscriptions/{subscriptionId}/databases/{databaseId}
824    pub async fn get_by_id(&self, subscription_id: i32, database_id: i32) -> Result<FixedDatabase> {
825        self.client
826            .get(&format!(
827                "/fixed/subscriptions/{}/databases/{}",
828                subscription_id, database_id
829            ))
830            .await
831    }
832
833    /// Update Essentials database
834    /// Updates the specified Essentials database.
835    ///
836    /// PUT /fixed/subscriptions/{subscriptionId}/databases/{databaseId}
837    pub async fn update(
838        &self,
839        subscription_id: i32,
840        database_id: i32,
841        request: &FixedDatabaseUpdateRequest,
842    ) -> Result<TaskStateUpdate> {
843        self.client
844            .put(
845                &format!(
846                    "/fixed/subscriptions/{}/databases/{}",
847                    subscription_id, database_id
848                ),
849                request,
850            )
851            .await
852    }
853
854    /// Backup Essentials database status
855    /// Information on the latest database backup status identified by Essentials subscription Id and Essentials database Id
856    ///
857    /// GET /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/backup
858    pub async fn get_backup_status(
859        &self,
860        subscription_id: i32,
861        database_id: i32,
862    ) -> Result<TaskStateUpdate> {
863        self.client
864            .get(&format!(
865                "/fixed/subscriptions/{}/databases/{}/backup",
866                subscription_id, database_id
867            ))
868            .await
869    }
870
871    /// Back up Essentials database
872    /// Manually back up the specified Essentials database to a backup path. By default, backups will be stored in the 'periodicBackupPath' location for this database.
873    ///
874    /// POST /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/backup
875    pub async fn backup(
876        &self,
877        subscription_id: i32,
878        database_id: i32,
879        request: &FixedDatabaseBackupRequest,
880    ) -> Result<TaskStateUpdate> {
881        self.client
882            .post(
883                &format!(
884                    "/fixed/subscriptions/{}/databases/{}/backup",
885                    subscription_id, database_id
886                ),
887                request,
888            )
889            .await
890    }
891
892    /// Get Essentials database import status
893    /// Gets information on the latest import attempt for this Essentials database.
894    ///
895    /// GET /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/import
896    pub async fn get_import_status(
897        &self,
898        subscription_id: i32,
899        database_id: i32,
900    ) -> Result<TaskStateUpdate> {
901        self.client
902            .get(&format!(
903                "/fixed/subscriptions/{}/databases/{}/import",
904                subscription_id, database_id
905            ))
906            .await
907    }
908
909    /// Import data to an Essentials database
910    /// Imports data from an RDB file or from a different Redis database into this Essentials database. WARNING: Importing data into a database removes all existing data from the database.
911    ///
912    /// POST /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/import
913    pub async fn import(
914        &self,
915        subscription_id: i32,
916        database_id: i32,
917        request: &FixedDatabaseImportRequest,
918    ) -> Result<TaskStateUpdate> {
919        self.client
920            .post(
921                &format!(
922                    "/fixed/subscriptions/{}/databases/{}/import",
923                    subscription_id, database_id
924                ),
925                request,
926            )
927            .await
928    }
929
930    /// Get Essentials database slow-log by database id
931    /// Get slow-log for a specific database identified by Essentials subscription Id and database Id
932    ///
933    /// GET /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/slow-log
934    pub async fn get_slow_log(
935        &self,
936        subscription_id: i32,
937        database_id: i32,
938    ) -> Result<DatabaseSlowLogEntries> {
939        self.client
940            .get(&format!(
941                "/fixed/subscriptions/{}/databases/{}/slow-log",
942                subscription_id, database_id
943            ))
944            .await
945    }
946
947    /// Get database tags
948    /// Gets a list of all database tags.
949    ///
950    /// GET /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/tags
951    pub async fn get_tags(&self, subscription_id: i32, database_id: i32) -> Result<CloudTags> {
952        self.client
953            .get(&format!(
954                "/fixed/subscriptions/{}/databases/{}/tags",
955                subscription_id, database_id
956            ))
957            .await
958    }
959
960    /// Add a database tag
961    /// Adds a single database tag to a database.
962    ///
963    /// POST /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/tags
964    pub async fn create_tag(
965        &self,
966        subscription_id: i32,
967        database_id: i32,
968        request: &DatabaseTagCreateRequest,
969    ) -> Result<CloudTag> {
970        self.client
971            .post(
972                &format!(
973                    "/fixed/subscriptions/{}/databases/{}/tags",
974                    subscription_id, database_id
975                ),
976                request,
977            )
978            .await
979    }
980
981    /// Overwrite database tags
982    /// Overwrites all tags on the database.
983    ///
984    /// PUT /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/tags
985    pub async fn update_tags(
986        &self,
987        subscription_id: i32,
988        database_id: i32,
989        request: &DatabaseTagsUpdateRequest,
990    ) -> Result<CloudTags> {
991        self.client
992            .put(
993                &format!(
994                    "/fixed/subscriptions/{}/databases/{}/tags",
995                    subscription_id, database_id
996                ),
997                request,
998            )
999            .await
1000    }
1001
1002    /// Delete database tag
1003    /// Removes the specified tag from the database.
1004    ///
1005    /// DELETE /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/tags/{tagKey}
1006    pub async fn delete_tag(
1007        &self,
1008        subscription_id: i32,
1009        database_id: i32,
1010        tag_key: String,
1011    ) -> Result<HashMap<String, Value>> {
1012        let response = self
1013            .client
1014            .delete_raw(&format!(
1015                "/fixed/subscriptions/{}/databases/{}/tags/{}",
1016                subscription_id, database_id, tag_key
1017            ))
1018            .await?;
1019        serde_json::from_value(response).map_err(Into::into)
1020    }
1021
1022    /// Update database tag value
1023    /// Updates the value of the specified database tag.
1024    ///
1025    /// PUT /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/tags/{tagKey}
1026    pub async fn update_tag(
1027        &self,
1028        subscription_id: i32,
1029        database_id: i32,
1030        tag_key: String,
1031        request: &DatabaseTagUpdateRequest,
1032    ) -> Result<CloudTag> {
1033        self.client
1034            .put(
1035                &format!(
1036                    "/fixed/subscriptions/{}/databases/{}/tags/{}",
1037                    subscription_id, database_id, tag_key
1038                ),
1039                request,
1040            )
1041            .await
1042    }
1043
1044    // ========================================================================
1045    // Backward compatibility wrapper methods
1046    // ========================================================================
1047    // NOTE: These methods are deprecated in favor of the shorter, more idiomatic names.
1048    // They will be removed in a future version.
1049
1050    /// Create fixed database (backward compatibility)
1051    ///
1052    /// **Deprecated**: Use [`create`](Self::create) instead
1053    #[deprecated(since = "0.8.0", note = "Use `create` instead")]
1054    pub async fn create_fixed_database(
1055        &self,
1056        subscription_id: i32,
1057        request: &FixedDatabaseCreateRequest,
1058    ) -> Result<TaskStateUpdate> {
1059        self.create(subscription_id, request).await
1060    }
1061
1062    /// Get fixed database (backward compatibility)
1063    ///
1064    /// **Deprecated**: Use [`get_by_id`](Self::get_by_id) instead
1065    #[deprecated(since = "0.8.0", note = "Use `get_by_id` instead")]
1066    pub async fn get_fixed_database(
1067        &self,
1068        subscription_id: i32,
1069        database_id: i32,
1070    ) -> Result<TaskStateUpdate> {
1071        self.get_by_id(subscription_id, database_id)
1072            .await
1073            .map(|db| serde_json::from_value(serde_json::json!(db)).unwrap())
1074    }
1075
1076    /// Update fixed database (backward compatibility)
1077    ///
1078    /// **Deprecated**: Use [`update`](Self::update) instead
1079    #[deprecated(since = "0.8.0", note = "Use `update` instead")]
1080    pub async fn update_fixed_database(
1081        &self,
1082        subscription_id: i32,
1083        database_id: i32,
1084        request: &FixedDatabaseUpdateRequest,
1085    ) -> Result<TaskStateUpdate> {
1086        self.update(subscription_id, database_id, request).await
1087    }
1088
1089    /// Delete fixed database (backward compatibility)
1090    ///
1091    /// **Deprecated**: Use [`delete_by_id`](Self::delete_by_id) instead
1092    #[deprecated(since = "0.8.0", note = "Use `delete_by_id` instead")]
1093    pub async fn delete_fixed_database(
1094        &self,
1095        subscription_id: i32,
1096        database_id: i32,
1097    ) -> Result<TaskStateUpdate> {
1098        self.delete_by_id(subscription_id, database_id).await
1099    }
1100
1101    /// Backup fixed database (backward compatibility)
1102    ///
1103    /// **Deprecated**: Use [`backup`](Self::backup) instead
1104    #[deprecated(since = "0.8.0", note = "Use `backup` instead")]
1105    pub async fn backup_fixed_database(
1106        &self,
1107        subscription_id: i32,
1108        database_id: i32,
1109        request: &FixedDatabaseBackupRequest,
1110    ) -> Result<TaskStateUpdate> {
1111        self.backup(subscription_id, database_id, request).await
1112    }
1113
1114    /// Get fixed subscription databases (backward compatibility)
1115    ///
1116    /// **Deprecated**: Use [`list`](Self::list) instead
1117    #[deprecated(since = "0.8.0", note = "Use `list` instead")]
1118    pub async fn get_fixed_subscription_databases(
1119        &self,
1120        subscription_id: i32,
1121        offset: Option<i32>,
1122        limit: Option<i32>,
1123    ) -> Result<AccountFixedSubscriptionDatabases> {
1124        self.list(subscription_id, offset, limit).await
1125    }
1126
1127    /// Get fixed database by id (backward compatibility)
1128    ///
1129    /// **Deprecated**: Use [`get_by_id`](Self::get_by_id) instead
1130    #[deprecated(since = "0.8.0", note = "Use `get_by_id` instead")]
1131    pub async fn fixed_database_by_id(
1132        &self,
1133        subscription_id: i32,
1134        database_id: i32,
1135    ) -> Result<FixedDatabase> {
1136        self.get_by_id(subscription_id, database_id).await
1137    }
1138
1139    /// Get fixed subscription database by id (backward compatibility)
1140    ///
1141    /// **Deprecated**: Use [`get_by_id`](Self::get_by_id) instead
1142    #[deprecated(since = "0.8.0", note = "Use `get_by_id` instead")]
1143    pub async fn get_fixed_subscription_database_by_id(
1144        &self,
1145        subscription_id: i32,
1146        database_id: i32,
1147    ) -> Result<FixedDatabase> {
1148        self.get_by_id(subscription_id, database_id).await
1149    }
1150
1151    /// Delete fixed database by id (backward compatibility)
1152    ///
1153    /// **Deprecated**: Use [`delete_by_id`](Self::delete_by_id) instead
1154    #[deprecated(since = "0.8.0", note = "Use `delete_by_id` instead")]
1155    pub async fn delete_fixed_database_by_id(
1156        &self,
1157        subscription_id: i32,
1158        database_id: i32,
1159    ) -> Result<TaskStateUpdate> {
1160        self.delete_by_id(subscription_id, database_id).await
1161    }
1162
1163    /// Import fixed database (backward compatibility)
1164    ///
1165    /// **Deprecated**: Use [`import`](Self::import) instead
1166    #[deprecated(since = "0.8.0", note = "Use `import` instead")]
1167    pub async fn import_fixed_database(
1168        &self,
1169        subscription_id: i32,
1170        database_id: i32,
1171        request: &FixedDatabaseImportRequest,
1172    ) -> Result<TaskStateUpdate> {
1173        self.import(subscription_id, database_id, request).await
1174    }
1175
1176    /// Create fixed database tag (backward compatibility)
1177    ///
1178    /// **Deprecated**: Use [`create_tag`](Self::create_tag) instead
1179    #[deprecated(since = "0.8.0", note = "Use `create_tag` instead")]
1180    pub async fn create_fixed_database_tag(
1181        &self,
1182        subscription_id: i32,
1183        database_id: i32,
1184        request: &DatabaseTagCreateRequest,
1185    ) -> Result<CloudTag> {
1186        self.create_tag(subscription_id, database_id, request).await
1187    }
1188
1189    /// Get fixed database tags (backward compatibility)
1190    ///
1191    /// **Deprecated**: Use [`get_tags`](Self::get_tags) instead
1192    #[deprecated(since = "0.8.0", note = "Use `get_tags` instead")]
1193    pub async fn get_fixed_database_tags(
1194        &self,
1195        subscription_id: i32,
1196        database_id: i32,
1197    ) -> Result<CloudTags> {
1198        self.get_tags(subscription_id, database_id).await
1199    }
1200}