Skip to main content

redis_cloud/flexible/
subscriptions.rs

1//! Subscription management for Pro (Flexible) plans
2//!
3//! This module provides comprehensive management of Redis Cloud Pro subscriptions,
4//! which offer flexible, scalable Redis deployments with advanced features like
5//! auto-scaling, multi-region support, and Active-Active configurations.
6//!
7//! # Overview
8//!
9//! Pro subscriptions are Redis Cloud's most flexible offering, supporting everything
10//! from small development instances to large-scale production deployments with
11//! automatic scaling, clustering, and global distribution.
12//!
13//! # Key Features
14//!
15//! - **Flexible Scaling**: Auto-scaling based on usage patterns
16//! - **Multi-Region**: Deploy across multiple regions and cloud providers
17//! - **Active-Active**: Global database replication with local reads/writes
18//! - **Advanced Networking**: VPC peering, Transit Gateway, Private endpoints
19//! - **Maintenance Windows**: Configurable maintenance scheduling
20//! - **CIDR Management**: IP allowlist and security group configuration
21//! - **Custom Pricing**: Usage-based pricing with detailed cost tracking
22//!
23//! # Subscription Types
24//!
25//! - **Single-Region**: Standard deployment in one region
26//! - **Multi-Region**: Replicated across multiple regions
27//! - **Active-Active**: CRDB with conflict-free replicated data types
28//!
29//! # Example Usage
30//!
31//! ```no_run
32//! use redis_cloud::{CloudClient, SubscriptionHandler};
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 = SubscriptionHandler::new(client);
41//!
42//! // List all Pro subscriptions
43//! let subscriptions = handler.get_all_subscriptions().await?;
44//!
45//! // Get subscription details (subscription ID 123)
46//! let subscription = handler.get_subscription_by_id(123).await?;
47//!
48//! // Manage maintenance windows
49//! let windows = handler.get_subscription_maintenance_windows(123).await?;
50//! # Ok(())
51//! # }
52//! ```
53
54pub use crate::types::TaskStateUpdate;
55use crate::types::{Link, Tag};
56use crate::{CloudClient, Result};
57use serde::{Deserialize, Serialize};
58use serde_json::Value;
59use std::collections::HashMap;
60use typed_builder::TypedBuilder;
61
62// ============================================================================
63// Models
64// ============================================================================
65
66/// Subscription update request message
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct BaseSubscriptionUpdateRequest {
70    /// Subscription ID being updated. Server-populated from the path.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub subscription_id: Option<i32>,
73
74    /// Read-only on the response; populated by the server with the
75    /// operation type (e.g. `"UPDATE_SUBSCRIPTION"`).
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub command_type: Option<String>,
78}
79
80/// Subscription update request message
81///
82/// # Example
83///
84/// ```
85/// use redis_cloud::flexible::subscriptions::SubscriptionUpdateRequest;
86///
87/// let request = SubscriptionUpdateRequest::builder()
88///     .name("updated-subscription")
89///     .build();
90/// ```
91#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
92#[serde(rename_all = "camelCase")]
93pub struct SubscriptionUpdateRequest {
94    /// Subscription ID being updated. Server-populated from the path.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    #[builder(default, setter(strip_option))]
97    pub subscription_id: Option<i32>,
98
99    /// Optional. Updated subscription name.
100    #[serde(skip_serializing_if = "Option::is_none")]
101    #[builder(default, setter(strip_option, into))]
102    pub name: Option<String>,
103
104    /// Optional. The payment method ID you'd like to use for this subscription. Must be a valid payment method ID for this account. Use GET /payment-methods to get all payment methods for your account. This value is optional if 'paymentMethod' is 'marketplace', but required if 'paymentMethod' is 'credit-card'.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    #[builder(default, setter(strip_option))]
107    pub payment_method_id: Option<i32>,
108
109    /// Optional. The payment method for the subscription. If set to 'credit-card' , 'paymentMethodId' must be defined.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    #[builder(default, setter(strip_option, into))]
112    pub payment_method: Option<String>,
113
114    /// Read-only on the response; populated by the server with the
115    /// operation type (e.g. `"UPDATE_SUBSCRIPTION"`).
116    #[serde(skip_serializing_if = "Option::is_none")]
117    #[builder(default, setter(strip_option, into))]
118    pub command_type: Option<String>,
119}
120
121/// Cloud provider, region, and networking details.
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct SubscriptionSpec {
125    /// Optional. Cloud provider. Default: 'AWS'
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub provider: Option<String>,
128
129    /// Optional. Cloud account identifier. Default: Redis internal cloud account (Cloud Account ID = 1). Use GET /cloud-accounts to list all available cloud accounts. Note: A subscription on Google Cloud can be created only with Redis internal cloud account.
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub cloud_account_id: Option<i32>,
132
133    /// The cloud provider region or list of regions (Active-Active only) and networking details.
134    pub regions: Vec<SubscriptionRegionSpec>,
135}
136
137/// Object representing a customer managed key (CMK), along with the region it is associated to.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct CustomerManagedKey {
141    /// Required. Resource name of the customer managed key as defined by the cloud provider.
142    pub resource_name: String,
143
144    /// Name of region to for the customer managed key as defined by the cloud provider. Required for active-active subscriptions.
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub region: Option<String>,
147}
148
149/// Optional. Expected read and write throughput for this region.
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct LocalThroughput {
153    /// Specify one of the selected cloud provider regions for the subscription.
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub region: Option<String>,
156
157    /// Write operations for this region per second. Default: 1000 ops/sec
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub write_operations_per_second: Option<i64>,
160
161    /// Read operations for this region per second. Default: 1000 ops/sec
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub read_operations_per_second: Option<i64>,
164}
165
166/// List of databases in the subscription with local throughput details. Default: 1000 read and write ops/sec for each database
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct CrdbRegionSpec {
170    /// Database name.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub name: Option<String>,
173
174    /// Optional. Local throughput settings for this region. See [`LocalThroughput`].
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub local_throughput_measurement: Option<LocalThroughput>,
177}
178
179/// Subscription update request message
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct SubscriptionUpdateCMKRequest {
183    /// Subscription ID being updated. Server-populated from the path.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub subscription_id: Option<i32>,
186
187    /// Read-only on the response; populated by the server with the
188    /// operation type (e.g. `"UPDATE_SUBSCRIPTION_CMK"`).
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub command_type: Option<String>,
191
192    /// Optional. The grace period for deleting the subscription. If not set, will default to immediate deletion grace period.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub deletion_grace_period: Option<String>,
195
196    /// The customer managed keys (CMK) to use for this subscription. If is active-active subscription, must set a key for each region.
197    pub customer_managed_keys: Vec<CustomerManagedKey>,
198}
199
200/// `SubscriptionPricings`
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct SubscriptionPricings {
203    /// Pricing breakdown entries for the subscription.
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub pricing: Option<Vec<SubscriptionPricing>>,
206}
207
208/// Optional. Throughput measurement method.
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct DatabaseThroughputSpec {
211    /// Throughput measurement method. Use 'operations-per-second' for all new databases.
212    pub by: String,
213
214    /// Throughput value in the selected measurement method.
215    pub value: i64,
216}
217
218/// 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.
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct DatabaseModuleSpec {
221    /// Redis advanced capability name. Use GET /database-modules for a list of available capabilities.
222    pub name: String,
223
224    /// Optional. Redis advanced capability parameters. Use GET /database-modules to get the available capabilities and their parameters.
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub parameters: Option<HashMap<String, Value>>,
227}
228
229/// Update Pro subscription
230#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct CidrAllowlistUpdateRequest {
233    /// Subscription ID being updated. Server-populated from the path.
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub subscription_id: Option<i32>,
236
237    /// List of CIDR values. Example: ['10.1.1.0/32']
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub cidr_ips: Option<Vec<String>>,
240
241    /// List of AWS Security group IDs.
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub security_group_ids: Option<Vec<String>>,
244
245    /// Read-only on the response; populated by the server with the
246    /// operation type (e.g. `"UPDATE_SUBSCRIPTION_CIDR_ALLOWLIST"`).
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub command_type: Option<String>,
249}
250
251/// `SubscriptionMaintenanceWindowsSpec`
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct SubscriptionMaintenanceWindowsSpec {
254    /// Maintenance window mode: either 'manual' or 'automatic'. Must provide 'windows' if manual.
255    pub mode: String,
256
257    /// Maintenance window timeframes if mode is set to 'manual'. Up to 7 maintenance windows can be provided.
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub windows: Option<Vec<MaintenanceWindowSpec>>,
260}
261
262/// `MaintenanceWindowSkipStatus`
263#[derive(Debug, Clone, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct MaintenanceWindowSkipStatus {
266    /// Number of remaining maintenance-window skips available.
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub remaining_skips: Option<i32>,
269
270    /// Timestamp marking the end of the currently skipped window, if any.
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub current_skip_end: Option<String>,
273}
274
275/// List of active-active subscription regions
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct ActiveActiveSubscriptionRegions {
279    /// Subscription identifier.
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub subscription_id: Option<i32>,
282
283    /// HATEOAS links
284    #[serde(skip_serializing_if = "Option::is_none")]
285    pub links: Option<Vec<Link>>,
286}
287
288/// `SubscriptionPricing`
289#[derive(Debug, Clone, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct SubscriptionPricing {
292    /// Database name this pricing applies to
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub database_name: Option<String>,
295
296    /// Pricing line type (e.g. `"Shards"`, `"EBSVolume"`).
297    #[serde(skip_serializing_if = "Option::is_none")]
298    pub r#type: Option<String>,
299
300    /// Additional details about the pricing line type.
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub type_details: Option<String>,
303
304    /// Quantity of the priced unit.
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub quantity: Option<i32>,
307
308    /// Unit used to measure `quantity` (e.g. `"shards"`, `"GB"`).
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub quantity_measurement: Option<String>,
311
312    /// Price per unit in the configured currency.
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub price_per_unit: Option<f64>,
315
316    /// ISO currency code for `price_per_unit` (e.g. `"USD"`).
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub price_currency: Option<String>,
319
320    /// Billing period for the price (e.g. `"Month"`, `"Hour"`).
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub price_period: Option<String>,
323
324    /// Cloud region this pricing entry applies to.
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub region: Option<String>,
327}
328
329/// Request structure for creating a new Pro subscription
330///
331/// Defines configuration for flexible subscriptions including cloud providers,
332/// regions, deployment type, and initial database specifications.
333///
334/// # Example
335///
336/// ```
337/// use redis_cloud::flexible::subscriptions::{SubscriptionCreateRequest, SubscriptionSpec, SubscriptionDatabaseSpec, SubscriptionRegionSpec};
338///
339/// let request = SubscriptionCreateRequest::builder()
340///     .name("my-subscription")
341///     .cloud_providers(vec![
342///         SubscriptionSpec {
343///             provider: Some("AWS".to_string()),
344///             cloud_account_id: Some(1),
345///             regions: vec![SubscriptionRegionSpec {
346///                 region: "us-east-1".to_string(),
347///                 multiple_availability_zones: None,
348///                 preferred_availability_zones: None,
349///                 networking: None,
350///             }],
351///         }
352///     ])
353///     .databases(vec![
354///         SubscriptionDatabaseSpec {
355///             name: "my-database".to_string(),
356///             protocol: "redis".to_string(),
357///             memory_limit_in_gb: Some(1.0),
358///             dataset_size_in_gb: None,
359///             support_oss_cluster_api: None,
360///             data_persistence: None,
361///             replication: None,
362///             throughput_measurement: None,
363///             local_throughput_measurement: None,
364///             modules: None,
365///             quantity: None,
366///             average_item_size_in_bytes: None,
367///             resp_version: None,
368///             redis_version: None,
369///             sharding_type: None,
370///             query_performance_factor: None,
371///         }
372///     ])
373///     .build();
374/// ```
375#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
376#[serde(rename_all = "camelCase")]
377pub struct SubscriptionCreateRequest {
378    /// Optional. New subscription name.
379    #[serde(skip_serializing_if = "Option::is_none")]
380    #[builder(default, setter(strip_option, into))]
381    pub name: Option<String>,
382
383    /// 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'
384    #[serde(skip_serializing_if = "Option::is_none")]
385    #[builder(default, setter(strip_option))]
386    pub dry_run: Option<bool>,
387
388    /// Optional. When 'single-region' or not set: Creates a single region subscription. When 'active-active': creates an Active-Active (multi-region) subscription.
389    #[serde(skip_serializing_if = "Option::is_none")]
390    #[builder(default, setter(strip_option, into))]
391    pub deployment_type: Option<String>,
392
393    /// Optional. The payment method for the subscription. If set to 'credit-card', 'paymentMethodId' must be defined. Default: 'credit-card'
394    #[serde(skip_serializing_if = "Option::is_none")]
395    #[builder(default, setter(strip_option, into))]
396    pub payment_method: Option<String>,
397
398    /// Optional. A valid payment method ID for this account. Use GET /payment-methods to get a list of all payment methods for your account. This value is optional if 'paymentMethod' is 'marketplace', but required for all other account types.
399    #[serde(skip_serializing_if = "Option::is_none")]
400    #[builder(default, setter(strip_option))]
401    pub payment_method_id: Option<i32>,
402
403    /// Optional. Memory storage preference: either 'ram' or a combination of 'ram-and-flash' (also known as Auto Tiering). Default: 'ram'
404    #[serde(skip_serializing_if = "Option::is_none")]
405    #[builder(default, setter(strip_option, into))]
406    pub memory_storage: Option<String>,
407
408    /// Optional. Persistent storage encryption secures data-at-rest for database persistence. You can use 'cloud-provider-managed-key' or 'customer-managed-key'.  Default: 'cloud-provider-managed-key'
409    #[serde(skip_serializing_if = "Option::is_none")]
410    #[builder(default, setter(strip_option, into))]
411    pub persistent_storage_encryption_type: Option<String>,
412
413    /// Cloud provider, region, and networking details.
414    pub cloud_providers: Vec<SubscriptionSpec>,
415
416    /// One or more database specification(s) to create in this subscription.
417    pub databases: Vec<SubscriptionDatabaseSpec>,
418
419    /// Optional. Defines the Redis version of the databases created in this specific request. It doesn't determine future databases associated with this subscription. If not set, databases will use the default Redis version. This field is deprecated and will be removed in a future API version - use the database-level redisVersion property instead.
420    #[serde(skip_serializing_if = "Option::is_none")]
421    #[builder(default, setter(strip_option, into))]
422    pub redis_version: Option<String>,
423
424    /// Read-only on the response; populated by the server with the
425    /// operation type (e.g. `"CREATE_SUBSCRIPTION"`).
426    #[serde(skip_serializing_if = "Option::is_none")]
427    #[builder(default, setter(strip_option, into))]
428    pub command_type: Option<String>,
429}
430
431/// Configuration regarding customer managed persistent storage encryption
432#[derive(Debug, Clone, Serialize, Deserialize)]
433#[serde(rename_all = "camelCase")]
434pub struct CustomerManagedKeyAccessDetails {
435    /// Redis service account that requires CMK access (GCP).
436    #[serde(skip_serializing_if = "Option::is_none")]
437    pub redis_service_account: Option<String>,
438
439    /// GCP predefined roles the service account must be granted.
440    #[serde(skip_serializing_if = "Option::is_none")]
441    pub google_predefined_roles: Option<Vec<String>>,
442
443    /// GCP custom permissions required on the customer managed key.
444    #[serde(skip_serializing_if = "Option::is_none")]
445    pub google_custom_permissions: Option<Vec<String>>,
446
447    /// AWS IAM role used by Redis to access the customer managed key.
448    #[serde(skip_serializing_if = "Option::is_none")]
449    pub redis_iam_role: Option<String>,
450
451    /// AWS KMS key-policy statements required for Redis to use the CMK.
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub required_key_policy_statements: Option<HashMap<String, Value>>,
454
455    /// Supported deletion grace period options for the CMK.
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub deletion_grace_period_options: Option<Vec<String>>,
458}
459
460/// One or more database specification(s) to create in this subscription.
461#[derive(Debug, Clone, Serialize, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct SubscriptionDatabaseSpec {
464    /// 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.
465    pub name: String,
466
467    /// Optional. Database protocol. Only set to 'memcached' if you have a legacy application. Default: 'redis'
468    pub protocol: String,
469
470    /// Optional. Total memory in GB, including replication and other overhead. You cannot set both datasetSizeInGb and totalMemoryInGb.
471    #[serde(skip_serializing_if = "Option::is_none")]
472    pub memory_limit_in_gb: Option<f64>,
473
474    /// 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.
475    #[serde(skip_serializing_if = "Option::is_none")]
476    pub dataset_size_in_gb: Option<f64>,
477
478    /// Optional. Support Redis [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#oss-cluster-api). Default: 'false'
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub support_oss_cluster_api: Option<bool>,
481
482    /// Optional. Type and rate of data persistence in persistent storage. Default: 'none'
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub data_persistence: Option<String>,
485
486    /// Optional. Databases replication. Default: 'true'
487    #[serde(skip_serializing_if = "Option::is_none")]
488    pub replication: Option<bool>,
489
490    /// Optional. Throughput measurement spec. See [`DatabaseThroughputSpec`].
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub throughput_measurement: Option<DatabaseThroughputSpec>,
493
494    /// Optional. Expected throughput per region for an Active-Active database. Default: 1000 read and write ops/sec for each region
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub local_throughput_measurement: Option<Vec<LocalThroughput>>,
497
498    /// 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.
499    #[serde(skip_serializing_if = "Option::is_none")]
500    pub modules: Option<Vec<DatabaseModuleSpec>>,
501
502    /// Optional. Number of databases that will be created with these settings. Default: 1
503    #[serde(skip_serializing_if = "Option::is_none")]
504    pub quantity: Option<i32>,
505
506    /// 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
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub average_item_size_in_bytes: Option<i64>,
509
510    /// Optional. Redis Serialization Protocol version. Must be compatible with Redis version.
511    #[serde(skip_serializing_if = "Option::is_none")]
512    pub resp_version: Option<String>,
513
514    /// 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')
515    #[serde(skip_serializing_if = "Option::is_none")]
516    pub redis_version: Option<String>,
517
518    /// Optional. Database [Hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/#manage-the-hashing-policy).
519    #[serde(skip_serializing_if = "Option::is_none")]
520    pub sharding_type: Option<String>,
521
522    /// 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.
523    #[serde(skip_serializing_if = "Option::is_none")]
524    pub query_performance_factor: Option<String>,
525}
526
527/// Optional. Cloud networking details, per region. Required if creating an Active-Active subscription.
528#[derive(Debug, Clone, Serialize, Deserialize)]
529#[serde(rename_all = "camelCase")]
530pub struct SubscriptionRegionNetworkingSpec {
531    /// Optional. Deployment CIDR mask. Must be a valid CIDR format with a range of 256 IP addresses. Default for single-region subscriptions: If using Redis internal cloud account, 192.168.0.0/24
532    #[serde(skip_serializing_if = "Option::is_none")]
533    pub deployment_cidr: Option<String>,
534
535    /// Optional. Enter a VPC identifier that exists in the hosted AWS account. Creates a new VPC if not set. VPC Identifier must be in a valid format (for example: 'vpc-0125be68a4625884ad') and must exist within the hosting account.
536    #[serde(skip_serializing_if = "Option::is_none")]
537    pub vpc_id: Option<String>,
538
539    /// Optional. Enter a list of subnets identifiers that exists in the hosted AWS account. Subnet Identifier must exist within the hosting account.
540    #[serde(skip_serializing_if = "Option::is_none")]
541    pub subnet_ids: Option<Vec<String>>,
542
543    /// Optional. Enter a security group identifier that exists in the hosted AWS account. Security group Identifier must be in a valid format (for example: 'sg-0125be68a4625884ad') and must exist within the hosting account.
544    #[serde(skip_serializing_if = "Option::is_none")]
545    pub security_group_id: Option<String>,
546}
547
548/// `RedisVersion`
549#[derive(Debug, Clone, Serialize, Deserialize)]
550#[serde(rename_all = "camelCase")]
551pub struct RedisVersion {
552    /// Redis version string (e.g. `"7.2"`).
553    #[serde(skip_serializing_if = "Option::is_none")]
554    pub version: Option<String>,
555
556    /// End-of-life date for this Redis version.
557    #[serde(skip_serializing_if = "Option::is_none")]
558    pub eol_date: Option<String>,
559
560    /// Whether this Redis version is a preview/early-access release.
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub is_preview: Option<bool>,
563
564    /// Whether this Redis version is the default for new databases.
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub is_default: Option<bool>,
567}
568
569/// `MaintenanceWindow`
570#[derive(Debug, Clone, Serialize, Deserialize)]
571#[serde(rename_all = "camelCase")]
572pub struct MaintenanceWindow {
573    /// Days of the week the window is active (e.g. `["Monday", "Wednesday"]`).
574    #[serde(skip_serializing_if = "Option::is_none")]
575    pub days: Option<Vec<String>>,
576
577    /// Window start hour in 24-hour UTC time (0-23).
578    #[serde(skip_serializing_if = "Option::is_none")]
579    pub start_hour: Option<i32>,
580
581    /// Window duration in hours.
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub duration_in_hours: Option<i32>,
584}
585
586/// Cloud provider details for a subscription
587#[derive(Debug, Clone, Serialize, Deserialize)]
588#[serde(rename_all = "camelCase")]
589pub struct CloudDetail {
590    /// Cloud provider (e.g., "AWS", "GCP", "Azure")
591    #[serde(skip_serializing_if = "Option::is_none")]
592    pub provider: Option<String>,
593
594    /// Cloud account ID (Redis Cloud internal or BYOA)
595    #[serde(skip_serializing_if = "Option::is_none")]
596    pub cloud_account_id: Option<i32>,
597
598    /// AWS account ID (for AWS deployments)
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub aws_account_id: Option<String>,
601
602    /// Total size of the subscription in GB
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub total_size_in_gb: Option<f64>,
605
606    /// Regions configured for this cloud provider
607    #[serde(skip_serializing_if = "Option::is_none")]
608    pub regions: Option<Vec<SubscriptionRegion>>,
609}
610
611/// Region details in a subscription response
612#[derive(Debug, Clone, Serialize, Deserialize)]
613#[serde(rename_all = "camelCase")]
614pub struct SubscriptionRegion {
615    /// Region name (e.g., "us-east-1")
616    #[serde(skip_serializing_if = "Option::is_none")]
617    pub region: Option<String>,
618
619    /// Networking configuration for this region
620    #[serde(skip_serializing_if = "Option::is_none")]
621    pub networking: Option<Vec<SubscriptionNetworking>>,
622
623    /// Preferred availability zones
624    #[serde(skip_serializing_if = "Option::is_none")]
625    pub preferred_availability_zones: Option<Vec<String>>,
626
627    /// Whether multiple availability zones are enabled
628    #[serde(skip_serializing_if = "Option::is_none")]
629    pub multiple_availability_zones: Option<bool>,
630}
631
632/// Networking configuration in a subscription region
633#[derive(Debug, Clone, Serialize, Deserialize)]
634#[serde(rename_all = "camelCase")]
635pub struct SubscriptionNetworking {
636    /// Deployment CIDR
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub deployment_cidr: Option<String>,
639
640    /// VPC ID
641    #[serde(skip_serializing_if = "Option::is_none")]
642    pub vpc_id: Option<String>,
643
644    /// Subnet ID
645    #[serde(skip_serializing_if = "Option::is_none")]
646    pub subnet_id: Option<String>,
647}
648
649/// `RedisLabs` Subscription information
650#[derive(Debug, Clone, Serialize, Deserialize)]
651#[serde(rename_all = "camelCase")]
652/// Subscription
653///
654/// Represents a Redis Cloud subscription with all known API fields as first-class struct members.
655/// The `extra` field is reserved only for truly unknown/future fields that may be added to the API.
656pub struct Subscription {
657    /// Subscription ID
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub id: Option<i32>,
660
661    /// Subscription name
662    #[serde(skip_serializing_if = "Option::is_none")]
663    pub name: Option<String>,
664
665    /// Subscription status (e.g., "active", "pending", "error")
666    #[serde(skip_serializing_if = "Option::is_none")]
667    pub status: Option<String>,
668
669    /// Payment method ID
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub payment_method_id: Option<i32>,
672
673    /// Payment method type (e.g., "credit-card", "marketplace")
674    #[serde(skip_serializing_if = "Option::is_none")]
675    pub payment_method_type: Option<String>,
676
677    /// Payment method (e.g., "credit-card", "marketplace")
678    #[serde(skip_serializing_if = "Option::is_none")]
679    pub payment_method: Option<String>,
680
681    /// Memory storage type: "ram" or "ram-and-flash" (Auto Tiering)
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub memory_storage: Option<String>,
684
685    /// Persistent storage encryption type
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub persistent_storage_encryption_type: Option<String>,
688
689    /// Deployment type: "single-region" or "active-active"
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub deployment_type: Option<String>,
692
693    /// Number of databases in this subscription
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub number_of_databases: Option<i32>,
696
697    /// Cloud provider details (AWS, GCP, Azure configurations)
698    #[serde(skip_serializing_if = "Option::is_none")]
699    pub cloud_details: Option<Vec<CloudDetail>>,
700
701    /// Pricing details for the subscription
702    #[serde(skip_serializing_if = "Option::is_none")]
703    pub pricing: Option<Vec<SubscriptionPricing>>,
704
705    /// Redis version for databases created in this subscription (deprecated)
706    #[serde(skip_serializing_if = "Option::is_none")]
707    pub redis_version: Option<String>,
708
709    /// Deletion grace period for customer-managed keys
710    #[serde(skip_serializing_if = "Option::is_none")]
711    pub deletion_grace_period: Option<String>,
712
713    /// Customer-managed key access details for encryption
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub customer_managed_key_access_details: Option<CustomerManagedKeyAccessDetails>,
716
717    /// Whether storage encryption is enabled
718    #[serde(skip_serializing_if = "Option::is_none")]
719    pub storage_encryption: Option<bool>,
720
721    /// Whether public endpoint access is enabled
722    #[serde(skip_serializing_if = "Option::is_none")]
723    pub public_endpoint_access: Option<bool>,
724
725    /// Timestamp when subscription was created
726    #[serde(skip_serializing_if = "Option::is_none")]
727    pub created_timestamp: Option<String>,
728
729    /// HATEOAS links for API navigation
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub links: Option<Vec<Link>>,
732}
733
734/// Maintenance window timeframes if mode is set to 'manual'. Up to 7 maintenance windows can be provided.
735#[derive(Debug, Clone, Serialize, Deserialize)]
736#[serde(rename_all = "camelCase")]
737pub struct MaintenanceWindowSpec {
738    /// Starting hour of the maintenance window. Can be between '0' (12 AM in the deployment region's local time) and '23' (11 PM in the deployment region's local time).
739    pub start_hour: i32,
740
741    /// The duration of the maintenance window in hours. Can be between 4-24 hours (or 8-24 hours if using 'ram-and-flash').
742    pub duration_in_hours: i32,
743
744    /// Days where this maintenance window applies. Can contain one or more of: "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", or "Sunday".
745    pub days: Vec<String>,
746}
747
748/// `RedisLabs` list of subscriptions in current account
749///
750/// Response from GET /subscriptions
751#[derive(Debug, Clone, Serialize, Deserialize)]
752#[serde(rename_all = "camelCase")]
753pub struct AccountSubscriptions {
754    /// Account ID
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub account_id: Option<i32>,
757
758    /// List of subscriptions (typically in extra as 'subscriptions' array)
759    #[serde(skip_serializing_if = "Option::is_none")]
760    pub subscriptions: Option<Vec<Subscription>>,
761
762    /// HATEOAS links for API navigation
763    #[serde(skip_serializing_if = "Option::is_none")]
764    pub links: Option<Vec<Link>>,
765}
766
767/// Active active region creation request message
768#[derive(Debug, Clone, Serialize, Deserialize)]
769#[serde(rename_all = "camelCase")]
770pub struct ActiveActiveRegionCreateRequest {
771    /// Subscription ID being updated. Server-populated from the path.
772    #[serde(skip_serializing_if = "Option::is_none")]
773    pub subscription_id: Option<i32>,
774
775    /// Name of region to add as defined by the cloud provider.
776    #[serde(skip_serializing_if = "Option::is_none")]
777    pub region: Option<String>,
778
779    /// Optional. Enter a VPC identifier that exists in the hosted AWS account. Creates a new VPC if not set. VPC Identifier must be in a valid format and must exist within the hosting account.
780    #[serde(skip_serializing_if = "Option::is_none")]
781    pub vpc_id: Option<String>,
782
783    /// Deployment CIDR mask. Must be a valid CIDR format with a range of 256 IP addresses.
784    pub deployment_cidr: String,
785
786    /// 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'
787    #[serde(skip_serializing_if = "Option::is_none")]
788    pub dry_run: Option<bool>,
789
790    /// List of databases in the subscription with local throughput details. Default: 1000 read and write ops/sec for each database
791    #[serde(skip_serializing_if = "Option::is_none")]
792    pub databases: Option<Vec<CrdbRegionSpec>>,
793
794    /// Optional. RESP version must be compatible with Redis version.
795    #[serde(skip_serializing_if = "Option::is_none")]
796    pub resp_version: Option<String>,
797
798    /// Optional. Resource name of the customer managed key as defined by the cloud provider for customer managed subscriptions.
799    #[serde(skip_serializing_if = "Option::is_none")]
800    pub customer_managed_key_resource_name: Option<String>,
801
802    /// Read-only on the response; populated by the server with the
803    /// operation type (e.g. `"CREATE_ACTIVE_ACTIVE_REGION"`).
804    #[serde(skip_serializing_if = "Option::is_none")]
805    pub command_type: Option<String>,
806}
807
808/// `RedisVersions`
809#[derive(Debug, Clone, Serialize, Deserialize)]
810#[serde(rename_all = "camelCase")]
811pub struct RedisVersions {
812    /// List of Redis versions available for the account.
813    #[serde(skip_serializing_if = "Option::is_none")]
814    pub redis_versions: Option<Vec<RedisVersion>>,
815}
816
817/// Active active region deletion request message
818#[derive(Debug, Clone, Serialize, Deserialize)]
819#[serde(rename_all = "camelCase")]
820pub struct ActiveActiveRegionDeleteRequest {
821    /// Subscription ID being updated. Server-populated from the path.
822    #[serde(skip_serializing_if = "Option::is_none")]
823    pub subscription_id: Option<i32>,
824
825    /// The names of the regions to delete.
826    #[serde(skip_serializing_if = "Option::is_none")]
827    pub regions: Option<Vec<ActiveActiveRegionToDelete>>,
828
829    /// Optional. When 'false': Creates a deployment plan and deploys it, deleting any resources required by the plan. When 'true': creates a read-only deployment plan and does not delete or modify any resources. Default: 'false'
830    #[serde(skip_serializing_if = "Option::is_none")]
831    pub dry_run: Option<bool>,
832
833    /// Read-only on the response; populated by the server with the
834    /// operation type (e.g. `"DELETE_ACTIVE_ACTIVE_REGION"`).
835    #[serde(skip_serializing_if = "Option::is_none")]
836    pub command_type: Option<String>,
837}
838
839/// The names of the regions to delete.
840#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct ActiveActiveRegionToDelete {
842    /// Name of the cloud provider region to delete.
843    #[serde(skip_serializing_if = "Option::is_none")]
844    pub region: Option<String>,
845}
846
847/// The cloud provider region or list of regions (Active-Active only) and networking details.
848#[derive(Debug, Clone, Serialize, Deserialize)]
849#[serde(rename_all = "camelCase")]
850pub struct SubscriptionRegionSpec {
851    /// Deployment region as defined by the cloud provider.
852    pub region: String,
853
854    /// Optional. Support deployment on multiple availability zones within the selected region. Default: 'false'
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub multiple_availability_zones: Option<bool>,
857
858    /// Optional. List the zone ID(s) for your preferred availability zone(s) for the cloud provider and region. If ‘multipleAvailabilityZones’ is set to 'true', you must list three availability zones. Otherwise, list one availability zone.
859    #[serde(skip_serializing_if = "Option::is_none")]
860    pub preferred_availability_zones: Option<Vec<String>>,
861
862    /// Optional. Per-region networking configuration. See [`SubscriptionRegionNetworkingSpec`].
863    #[serde(skip_serializing_if = "Option::is_none")]
864    pub networking: Option<SubscriptionRegionNetworkingSpec>,
865}
866
867/// `SubscriptionMaintenanceWindows`
868#[derive(Debug, Clone, Serialize, Deserialize)]
869#[serde(rename_all = "camelCase")]
870pub struct SubscriptionMaintenanceWindows {
871    /// Maintenance window mode (e.g. `"manual"`, `"automatic"`).
872    #[serde(skip_serializing_if = "Option::is_none")]
873    pub mode: Option<String>,
874
875    /// Time zone used to interpret window times (e.g. `"UTC"`).
876    #[serde(skip_serializing_if = "Option::is_none")]
877    pub time_zone: Option<String>,
878
879    /// Configured maintenance windows when `mode` is `"manual"`.
880    #[serde(skip_serializing_if = "Option::is_none")]
881    pub windows: Option<Vec<MaintenanceWindow>>,
882
883    /// Current skip status for upcoming maintenance windows. See [`MaintenanceWindowSkipStatus`].
884    #[serde(skip_serializing_if = "Option::is_none")]
885    pub skip_status: Option<MaintenanceWindowSkipStatus>,
886}
887
888// ============================================================================
889// Handler
890// ============================================================================
891
892/// Request to replace the resource tags on a Pro subscription.
893///
894/// Matches the `SubscriptionResourceTagsUpdateRequest` schema. The supplied
895/// tags replace all existing tags on the subscription.
896#[derive(Debug, Clone, Serialize, Deserialize)]
897#[serde(rename_all = "camelCase")]
898pub struct SubscriptionResourceTagsUpdateRequest {
899    /// Subscription to update. Server-populated from the path.
900    #[serde(skip_serializing_if = "Option::is_none")]
901    pub subscription_id: Option<i32>,
902
903    /// Tags to apply to the subscription. Replaces all existing tags.
904    pub resource_tags: Vec<Tag>,
905
906    /// Read-only on the response; populated by the server with the operation
907    /// type.
908    #[serde(skip_serializing_if = "Option::is_none")]
909    pub command_type: Option<String>,
910}
911
912/// Handler for Pro subscription operations
913///
914/// Manages flexible subscriptions with auto-scaling, multi-region support,
915/// Active-Active configurations, and advanced networking features.
916pub struct SubscriptionHandler {
917    client: CloudClient,
918}
919
920impl SubscriptionHandler {
921    /// Create a new handler
922    #[must_use]
923    pub fn new(client: CloudClient) -> Self {
924        Self { client }
925    }
926
927    /// Get Pro subscriptions
928    ///
929    /// Gets a list of all Pro subscriptions in the current account.
930    ///
931    /// GET /subscriptions
932    ///
933    /// # Example
934    ///
935    /// ```no_run
936    /// use redis_cloud::CloudClient;
937    ///
938    /// # async fn example() -> redis_cloud::Result<()> {
939    /// let client = CloudClient::builder()
940    ///     .api_key("your-api-key")
941    ///     .api_secret("your-api-secret")
942    ///     .build()?;
943    ///
944    /// let subscriptions = client.subscriptions().get_all_subscriptions().await?;
945    ///
946    /// // Access subscription data
947    /// if let Some(subs) = &subscriptions.subscriptions {
948    ///     println!("Found {} subscriptions", subs.len());
949    /// }
950    /// # Ok(())
951    /// # }
952    /// ```
953    pub async fn get_all_subscriptions(&self) -> Result<AccountSubscriptions> {
954        self.client.get("/subscriptions").await
955    }
956
957    /// Create Pro subscription
958    /// Creates a new Redis Cloud Pro subscription.
959    ///
960    /// POST /subscriptions
961    pub async fn create_subscription(
962        &self,
963        request: &SubscriptionCreateRequest,
964    ) -> Result<TaskStateUpdate> {
965        self.client.post("/subscriptions", request).await
966    }
967
968    /// Get available Redis database versions
969    /// Gets a list of all available Redis database versions for Pro subscriptions.
970    ///
971    /// GET /subscriptions/redis-versions
972    pub async fn get_redis_versions(&self, subscription_id: Option<i32>) -> Result<RedisVersions> {
973        let mut query = Vec::new();
974        if let Some(v) = subscription_id {
975            query.push(format!("subscriptionId={v}"));
976        }
977        let query_string = if query.is_empty() {
978            String::new()
979        } else {
980            format!("?{}", query.join("&"))
981        };
982        self.client
983            .get(&format!("/subscriptions/redis-versions{query_string}"))
984            .await
985    }
986
987    /// Delete Pro subscription
988    /// Delete the specified Pro subscription. All databases in the subscription must be deleted before deleting it.
989    ///
990    /// DELETE /subscriptions/{subscriptionId}
991    pub async fn delete_subscription_by_id(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
992        let response = self
993            .client
994            .delete_raw(&format!("/subscriptions/{subscription_id}"))
995            .await?;
996        serde_json::from_value(response).map_err(Into::into)
997    }
998
999    /// Get a single Pro subscription
1000    ///
1001    /// Gets information on the specified Pro subscription.
1002    ///
1003    /// GET /subscriptions/{subscriptionId}
1004    ///
1005    /// # Example
1006    ///
1007    /// ```no_run
1008    /// use redis_cloud::CloudClient;
1009    ///
1010    /// # async fn example() -> redis_cloud::Result<()> {
1011    /// let client = CloudClient::builder()
1012    ///     .api_key("your-api-key")
1013    ///     .api_secret("your-api-secret")
1014    ///     .build()?;
1015    ///
1016    /// let subscription = client.subscriptions().get_subscription_by_id(123).await?;
1017    ///
1018    /// println!("Subscription: {} (status: {:?})",
1019    ///     subscription.name.unwrap_or_default(),
1020    ///     subscription.status);
1021    /// # Ok(())
1022    /// # }
1023    /// ```
1024    pub async fn get_subscription_by_id(&self, subscription_id: i32) -> Result<Subscription> {
1025        self.client
1026            .get(&format!("/subscriptions/{subscription_id}"))
1027            .await
1028    }
1029
1030    /// Update Pro subscription
1031    /// Updates the specified Pro subscription.
1032    ///
1033    /// PUT /subscriptions/{subscriptionId}
1034    pub async fn update_subscription(
1035        &self,
1036        subscription_id: i32,
1037        request: &BaseSubscriptionUpdateRequest,
1038    ) -> Result<TaskStateUpdate> {
1039        self.client
1040            .put(&format!("/subscriptions/{subscription_id}"), request)
1041            .await
1042    }
1043
1044    /// Get Pro subscription CIDR allowlist
1045    /// (Self-hosted AWS subscriptions only) Gets a Pro subscription's CIDR allowlist.
1046    ///
1047    /// GET /subscriptions/{subscriptionId}/cidr
1048    pub async fn get_cidr_allowlist(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
1049        self.client
1050            .get(&format!("/subscriptions/{subscription_id}/cidr"))
1051            .await
1052    }
1053
1054    /// Update Pro subscription CIDR allowlist
1055    /// (Self-hosted AWS subscriptions only) Updates a Pro subscription's CIDR allowlist.
1056    ///
1057    /// PUT /subscriptions/{subscriptionId}/cidr
1058    pub async fn update_subscription_cidr_allowlist(
1059        &self,
1060        subscription_id: i32,
1061        request: &CidrAllowlistUpdateRequest,
1062    ) -> Result<TaskStateUpdate> {
1063        self.client
1064            .put(&format!("/subscriptions/{subscription_id}/cidr"), request)
1065            .await
1066    }
1067
1068    /// Get Pro subscription maintenance windows
1069    /// Gets maintenance windows for the specified Pro subscription.
1070    ///
1071    /// GET /subscriptions/{subscriptionId}/maintenance-windows
1072    pub async fn get_subscription_maintenance_windows(
1073        &self,
1074        subscription_id: i32,
1075    ) -> Result<SubscriptionMaintenanceWindows> {
1076        self.client
1077            .get(&format!(
1078                "/subscriptions/{subscription_id}/maintenance-windows"
1079            ))
1080            .await
1081    }
1082
1083    /// Update Pro subscription maintenance windows
1084    /// Updates maintenance windows for the specified Pro subscription.
1085    ///
1086    /// PUT /subscriptions/{subscriptionId}/maintenance-windows
1087    pub async fn update_subscription_maintenance_windows(
1088        &self,
1089        subscription_id: i32,
1090        request: &SubscriptionMaintenanceWindowsSpec,
1091    ) -> Result<TaskStateUpdate> {
1092        self.client
1093            .put(
1094                &format!("/subscriptions/{subscription_id}/maintenance-windows"),
1095                request,
1096            )
1097            .await
1098    }
1099
1100    /// Get Pro subscription pricing
1101    /// Gets pricing details for the specified Pro subscription.
1102    ///
1103    /// GET /subscriptions/{subscriptionId}/pricing
1104    pub async fn get_subscription_pricing(
1105        &self,
1106        subscription_id: i32,
1107    ) -> Result<SubscriptionPricings> {
1108        self.client
1109            .get(&format!("/subscriptions/{subscription_id}/pricing"))
1110            .await
1111    }
1112
1113    /// Delete regions from an Active-Active subscription
1114    /// (Active-Active subscriptions only) Deletes one or more regions from the specified Active-Active subscription.
1115    ///
1116    /// DELETE /subscriptions/{subscriptionId}/regions
1117    pub async fn delete_regions_from_active_active_subscription(
1118        &self,
1119        subscription_id: i32,
1120        request: &ActiveActiveRegionDeleteRequest,
1121    ) -> Result<TaskStateUpdate> {
1122        self.client
1123            .delete_with_body(
1124                &format!("/subscriptions/{subscription_id}/regions"),
1125                serde_json::to_value(request)?,
1126            )
1127            .await
1128    }
1129
1130    /// Get regions in an Active-Active subscription
1131    /// (Active-Active subscriptions only) Gets a list of regions in the specified Active-Active subscription.
1132    ///
1133    /// GET /subscriptions/{subscriptionId}/regions
1134    pub async fn get_regions_from_active_active_subscription(
1135        &self,
1136        subscription_id: i32,
1137    ) -> Result<ActiveActiveSubscriptionRegions> {
1138        self.client
1139            .get(&format!("/subscriptions/{subscription_id}/regions"))
1140            .await
1141    }
1142
1143    /// Add region to Active-Active subscription
1144    /// Adds a new region to an Active-Active subscription.
1145    ///
1146    /// POST /subscriptions/{subscriptionId}/regions
1147    pub async fn add_new_region_to_active_active_subscription(
1148        &self,
1149        subscription_id: i32,
1150        request: &ActiveActiveRegionCreateRequest,
1151    ) -> Result<TaskStateUpdate> {
1152        self.client
1153            .post(
1154                &format!("/subscriptions/{subscription_id}/regions"),
1155                request,
1156            )
1157            .await
1158    }
1159
1160    /// Update Pro subscription resource tags
1161    /// Replaces all resource tags on the specified Pro subscription with the
1162    /// supplied set.
1163    ///
1164    /// PUT /subscriptions/{subscriptionId}/resource-tags
1165    pub async fn update_resource_tags(
1166        &self,
1167        subscription_id: i32,
1168        request: &SubscriptionResourceTagsUpdateRequest,
1169    ) -> Result<TaskStateUpdate> {
1170        self.client
1171            .put(
1172                &format!("/subscriptions/{subscription_id}/resource-tags"),
1173                request,
1174            )
1175            .await
1176    }
1177
1178    // ============================================================================
1179    // Simplified aliases
1180    // ============================================================================
1181
1182    /// List Pro subscriptions (simplified)
1183    ///
1184    /// Alias for [`get_all_subscriptions`](Self::get_all_subscriptions).
1185    ///
1186    /// # Example
1187    ///
1188    /// ```no_run
1189    /// use redis_cloud::CloudClient;
1190    ///
1191    /// # async fn example() -> redis_cloud::Result<()> {
1192    /// let client = CloudClient::builder()
1193    ///     .api_key("your-api-key")
1194    ///     .api_secret("your-api-secret")
1195    ///     .build()?;
1196    ///
1197    /// let subscriptions = client.subscriptions().list().await?;
1198    /// # Ok(())
1199    /// # }
1200    /// ```
1201    pub async fn list(&self) -> Result<AccountSubscriptions> {
1202        self.get_all_subscriptions().await
1203    }
1204
1205    /// Create a Pro subscription (simplified)
1206    ///
1207    /// Alias for [`create_subscription`](Self::create_subscription).
1208    ///
1209    /// # Arguments
1210    ///
1211    /// * `request` - The subscription creation request
1212    pub async fn create(&self, request: &SubscriptionCreateRequest) -> Result<TaskStateUpdate> {
1213        self.create_subscription(request).await
1214    }
1215
1216    /// Delete a Pro subscription (simplified)
1217    ///
1218    /// Alias for [`delete_subscription_by_id`](Self::delete_subscription_by_id).
1219    ///
1220    /// # Arguments
1221    ///
1222    /// * `subscription_id` - The subscription ID
1223    ///
1224    /// # Example
1225    ///
1226    /// ```no_run
1227    /// use redis_cloud::CloudClient;
1228    ///
1229    /// # async fn example() -> redis_cloud::Result<()> {
1230    /// let client = CloudClient::builder()
1231    ///     .api_key("your-api-key")
1232    ///     .api_secret("your-api-secret")
1233    ///     .build()?;
1234    ///
1235    /// let task = client.subscriptions().delete(123).await?;
1236    /// # Ok(())
1237    /// # }
1238    /// ```
1239    pub async fn delete(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
1240        self.delete_subscription_by_id(subscription_id).await
1241    }
1242
1243    /// Get a Pro subscription by ID (simplified)
1244    ///
1245    /// Alias for [`get_subscription_by_id`](Self::get_subscription_by_id).
1246    ///
1247    /// # Arguments
1248    ///
1249    /// * `subscription_id` - The subscription ID
1250    ///
1251    /// # Example
1252    ///
1253    /// ```no_run
1254    /// use redis_cloud::CloudClient;
1255    ///
1256    /// # async fn example() -> redis_cloud::Result<()> {
1257    /// let client = CloudClient::builder()
1258    ///     .api_key("your-api-key")
1259    ///     .api_secret("your-api-secret")
1260    ///     .build()?;
1261    ///
1262    /// let subscription = client.subscriptions().get(123).await?;
1263    /// # Ok(())
1264    /// # }
1265    /// ```
1266    pub async fn get(&self, subscription_id: i32) -> Result<Subscription> {
1267        self.get_subscription_by_id(subscription_id).await
1268    }
1269
1270    /// Update a Pro subscription (simplified)
1271    ///
1272    /// Alias for [`update_subscription`](Self::update_subscription).
1273    ///
1274    /// # Arguments
1275    ///
1276    /// * `subscription_id` - The subscription ID
1277    /// * `request` - The subscription update request
1278    pub async fn update(
1279        &self,
1280        subscription_id: i32,
1281        request: &BaseSubscriptionUpdateRequest,
1282    ) -> Result<TaskStateUpdate> {
1283        self.update_subscription(subscription_id, request).await
1284    }
1285
1286    /// Get a Pro subscription's CIDR allowlist (simplified)
1287    ///
1288    /// Alias for [`get_cidr_allowlist`](Self::get_cidr_allowlist).
1289    ///
1290    /// # Arguments
1291    ///
1292    /// * `subscription_id` - The subscription ID
1293    ///
1294    /// # Example
1295    ///
1296    /// ```no_run
1297    /// use redis_cloud::CloudClient;
1298    ///
1299    /// # async fn example() -> redis_cloud::Result<()> {
1300    /// let client = CloudClient::builder()
1301    ///     .api_key("your-api-key")
1302    ///     .api_secret("your-api-secret")
1303    ///     .build()?;
1304    ///
1305    /// let allowlist = client.subscriptions().cidr_allowlist(123).await?;
1306    /// # Ok(())
1307    /// # }
1308    /// ```
1309    pub async fn cidr_allowlist(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
1310        self.get_cidr_allowlist(subscription_id).await
1311    }
1312
1313    /// Update a Pro subscription's CIDR allowlist (simplified)
1314    ///
1315    /// Alias for
1316    /// [`update_subscription_cidr_allowlist`](Self::update_subscription_cidr_allowlist).
1317    ///
1318    /// # Arguments
1319    ///
1320    /// * `subscription_id` - The subscription ID
1321    /// * `request` - The CIDR allowlist update request
1322    pub async fn update_cidr_allowlist(
1323        &self,
1324        subscription_id: i32,
1325        request: &CidrAllowlistUpdateRequest,
1326    ) -> Result<TaskStateUpdate> {
1327        self.update_subscription_cidr_allowlist(subscription_id, request)
1328            .await
1329    }
1330
1331    /// Get a Pro subscription's maintenance windows (simplified)
1332    ///
1333    /// Alias for
1334    /// [`get_subscription_maintenance_windows`](Self::get_subscription_maintenance_windows).
1335    ///
1336    /// # Arguments
1337    ///
1338    /// * `subscription_id` - The subscription ID
1339    ///
1340    /// # Example
1341    ///
1342    /// ```no_run
1343    /// use redis_cloud::CloudClient;
1344    ///
1345    /// # async fn example() -> redis_cloud::Result<()> {
1346    /// let client = CloudClient::builder()
1347    ///     .api_key("your-api-key")
1348    ///     .api_secret("your-api-secret")
1349    ///     .build()?;
1350    ///
1351    /// let windows = client.subscriptions().maintenance_windows(123).await?;
1352    /// # Ok(())
1353    /// # }
1354    /// ```
1355    pub async fn maintenance_windows(
1356        &self,
1357        subscription_id: i32,
1358    ) -> Result<SubscriptionMaintenanceWindows> {
1359        self.get_subscription_maintenance_windows(subscription_id)
1360            .await
1361    }
1362
1363    /// Update a Pro subscription's maintenance windows (simplified)
1364    ///
1365    /// Alias for
1366    /// [`update_subscription_maintenance_windows`](Self::update_subscription_maintenance_windows).
1367    ///
1368    /// # Arguments
1369    ///
1370    /// * `subscription_id` - The subscription ID
1371    /// * `request` - The maintenance windows specification
1372    pub async fn update_maintenance_windows(
1373        &self,
1374        subscription_id: i32,
1375        request: &SubscriptionMaintenanceWindowsSpec,
1376    ) -> Result<TaskStateUpdate> {
1377        self.update_subscription_maintenance_windows(subscription_id, request)
1378            .await
1379    }
1380
1381    /// Get a Pro subscription's pricing (simplified)
1382    ///
1383    /// Alias for [`get_subscription_pricing`](Self::get_subscription_pricing).
1384    ///
1385    /// # Arguments
1386    ///
1387    /// * `subscription_id` - The subscription ID
1388    ///
1389    /// # Example
1390    ///
1391    /// ```no_run
1392    /// use redis_cloud::CloudClient;
1393    ///
1394    /// # async fn example() -> redis_cloud::Result<()> {
1395    /// let client = CloudClient::builder()
1396    ///     .api_key("your-api-key")
1397    ///     .api_secret("your-api-secret")
1398    ///     .build()?;
1399    ///
1400    /// let pricing = client.subscriptions().pricing(123).await?;
1401    /// # Ok(())
1402    /// # }
1403    /// ```
1404    pub async fn pricing(&self, subscription_id: i32) -> Result<SubscriptionPricings> {
1405        self.get_subscription_pricing(subscription_id).await
1406    }
1407
1408    /// Get the regions of an Active-Active subscription (simplified)
1409    ///
1410    /// Alias for
1411    /// [`get_regions_from_active_active_subscription`](Self::get_regions_from_active_active_subscription).
1412    ///
1413    /// # Arguments
1414    ///
1415    /// * `subscription_id` - The subscription ID
1416    ///
1417    /// # Example
1418    ///
1419    /// ```no_run
1420    /// use redis_cloud::CloudClient;
1421    ///
1422    /// # async fn example() -> redis_cloud::Result<()> {
1423    /// let client = CloudClient::builder()
1424    ///     .api_key("your-api-key")
1425    ///     .api_secret("your-api-secret")
1426    ///     .build()?;
1427    ///
1428    /// let regions = client.subscriptions().active_active_regions(123).await?;
1429    /// # Ok(())
1430    /// # }
1431    /// ```
1432    pub async fn active_active_regions(
1433        &self,
1434        subscription_id: i32,
1435    ) -> Result<ActiveActiveSubscriptionRegions> {
1436        self.get_regions_from_active_active_subscription(subscription_id)
1437            .await
1438    }
1439
1440    /// Add a region to an Active-Active subscription (simplified)
1441    ///
1442    /// Alias for
1443    /// [`add_new_region_to_active_active_subscription`](Self::add_new_region_to_active_active_subscription).
1444    ///
1445    /// # Arguments
1446    ///
1447    /// * `subscription_id` - The subscription ID
1448    /// * `request` - The region creation request
1449    pub async fn add_active_active_region(
1450        &self,
1451        subscription_id: i32,
1452        request: &ActiveActiveRegionCreateRequest,
1453    ) -> Result<TaskStateUpdate> {
1454        self.add_new_region_to_active_active_subscription(subscription_id, request)
1455            .await
1456    }
1457
1458    /// Delete regions from an Active-Active subscription (simplified)
1459    ///
1460    /// Alias for
1461    /// [`delete_regions_from_active_active_subscription`](Self::delete_regions_from_active_active_subscription).
1462    ///
1463    /// # Arguments
1464    ///
1465    /// * `subscription_id` - The subscription ID
1466    /// * `request` - The region deletion request
1467    pub async fn delete_active_active_regions(
1468        &self,
1469        subscription_id: i32,
1470        request: &ActiveActiveRegionDeleteRequest,
1471    ) -> Result<TaskStateUpdate> {
1472        self.delete_regions_from_active_active_subscription(subscription_id, request)
1473            .await
1474    }
1475
1476    /// Update a Pro subscription's resource tags (simplified)
1477    ///
1478    /// Alias for [`update_resource_tags`](Self::update_resource_tags).
1479    ///
1480    /// # Arguments
1481    ///
1482    /// * `subscription_id` - The subscription ID
1483    /// * `request` - The resource tags update request
1484    pub async fn update_tags(
1485        &self,
1486        subscription_id: i32,
1487        request: &SubscriptionResourceTagsUpdateRequest,
1488    ) -> Result<TaskStateUpdate> {
1489        self.update_resource_tags(subscription_id, request).await
1490    }
1491}