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