Skip to main content

redis_cloud/fixed/
subscriptions.rs

1//! Subscription management for Essentials (Fixed) plans
2//!
3//! This module manages Redis Cloud Essentials subscriptions, which provide
4//! simplified, fixed-capacity Redis deployments with predictable pricing.
5//! Essentials subscriptions are ideal for smaller, stable workloads.
6//!
7//! # Overview
8//!
9//! Essentials subscriptions offer a streamlined experience with pre-defined
10//! plans that include specific memory allocations, regions, and feature sets.
11//! Unlike Pro subscriptions, they don't support auto-scaling or multi-region
12//! deployments.
13//!
14//! # Key Features
15//!
16//! - **Fixed Plans**: Pre-defined subscription plans with set resources
17//! - **Simple Management**: Create, update, and delete subscriptions
18//! - **Plan Discovery**: Browse available plans by region and size
19//! - **Redis Versions**: Access supported Redis versions for the subscription
20//! - **Cost Predictability**: Fixed monthly pricing based on plan selection
21//!
22//! # Plan Structure
23//!
24//! Essentials plans are defined by:
25//! - Memory size (250MB to 12GB)
26//! - Cloud provider and region
27//! - Included features and modules
28//! - Fixed monthly price
29//!
30//! # Example Usage
31//!
32//! ```no_run
33//! use redis_cloud::{CloudClient, FixedSubscriptionHandler};
34//!
35//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
36//! let client = CloudClient::builder()
37//!     .api_key("your-api-key")
38//!     .api_secret("your-api-secret")
39//!     .build()?;
40//!
41//! let handler = FixedSubscriptionHandler::new(client);
42//!
43//! // List available plans
44//! let plans = handler.list_plans(None, None).await?;
45//!
46//! // Get all fixed subscriptions
47//! let subscriptions = handler.list().await?;
48//! # Ok(())
49//! # }
50//! ```
51
52use crate::types::{Link, ProcessorResponse};
53use crate::{CloudClient, Result};
54use serde::{Deserialize, Serialize};
55
56// ============================================================================
57// Models
58// ============================================================================
59
60/// `RedisVersions`
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct RedisVersions {
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub redis_versions: Option<Vec<RedisVersion>>,
66}
67
68/// Redis list of Essentials subscriptions plans
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct FixedSubscriptionsPlans {
71    /// HATEOAS links
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub links: Option<Vec<Link>>,
74}
75
76/// Essentials subscription update request
77#[derive(Debug, Clone, Serialize, Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub struct FixedSubscriptionUpdateRequest {
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub subscription_id: Option<i32>,
82
83    /// Optional. Updated subscription name.
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub name: Option<String>,
86
87    /// Optional. An Essentials plan ID. The plan describes the dataset size, cloud provider and region, and available database configuration options. Use GET /fixed/plans/subscriptions/{subscriptionId} to get a list of compatible options for the specified subscription.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub plan_id: Option<i32>,
90
91    /// Optional. The payment method for the subscription. If set to ‘credit-card’ , ‘paymentMethodId’ must be defined.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub payment_method: Option<String>,
94
95    /// 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 a list of payment methods for your account. This value is optional if ‘paymentMethod’ is ‘marketplace’, but required if 'paymentMethod' is 'credit-card'.
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub payment_method_id: Option<i32>,
98
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub command_type: Option<String>,
101}
102
103/// Redis Essentials subscription plan information
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct FixedSubscriptionsPlan {
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub id: Option<i32>,
109
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub name: Option<String>,
112
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub size: Option<f64>,
115
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub dataset_size: Option<f64>,
118
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub size_measurement_unit: Option<String>,
121
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub provider: Option<String>,
124
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub region: Option<String>,
127
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub region_id: Option<i32>,
130
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub price: Option<i32>,
133
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub price_currency: Option<String>,
136
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub price_period: Option<String>,
139
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub maximum_databases: Option<i32>,
142
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub maximum_throughput: Option<i32>,
145
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub maximum_bandwidth_gb: Option<i32>,
148
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub availability: Option<String>,
151
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub connections: Option<String>,
154
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub cidr_allow_rules: Option<i32>,
157
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub support_data_persistence: Option<bool>,
160
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub redis_flex: Option<bool>,
163
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub support_instant_and_daily_backups: Option<bool>,
166
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub support_replication: Option<bool>,
169
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub support_clustering: Option<bool>,
172
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub support_ssl: Option<bool>,
175
176    /// List of supported alert types for this plan
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub supported_alerts: Option<Vec<String>>,
179
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub customer_support: Option<String>,
182
183    /// HATEOAS links
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub links: Option<Vec<Link>>,
186}
187
188/// Essentials subscription create request
189#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct FixedSubscriptionCreateRequest {
192    /// New Essentials subscription name.
193    pub name: String,
194
195    /// An Essentials plan ID. The plan describes the dataset size, cloud provider and region, and available database configuration options. Use GET /fixed/plans to get a list of available options.
196    pub plan_id: i32,
197
198    /// Optional. The payment method for the subscription. If set to ‘credit-card’, ‘paymentMethodId’ must be defined. Default: 'credit-card'
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub payment_method: Option<String>,
201
202    /// 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.
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub payment_method_id: Option<i32>,
205
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub command_type: Option<String>,
208}
209
210/// Redis list of Essentials subscriptions in current account
211#[derive(Debug, Clone, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct FixedSubscriptions {
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub account_id: Option<i32>,
216
217    /// List of Essentials subscriptions
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub subscriptions: Option<Vec<FixedSubscription>>,
220
221    /// HATEOAS links
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub links: Option<Vec<Link>>,
224}
225
226/// `RedisVersion`
227#[derive(Debug, Clone, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct RedisVersion {
230    #[serde(skip_serializing_if = "Option::is_none")]
231    pub version: Option<String>,
232
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub eol_date: Option<String>,
235
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub is_preview: Option<bool>,
238
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub is_default: Option<bool>,
241}
242
243/// Redis Essentials Subscription information
244#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct FixedSubscription {
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub id: Option<i32>,
249
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub name: Option<String>,
252
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub status: Option<String>,
255
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub payment_method_id: Option<i32>,
258
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub payment_method_type: Option<String>,
261
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub plan_id: Option<i32>,
264
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub plan_name: Option<String>,
267
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub plan_type: Option<String>,
270
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub size: Option<f64>,
273
274    #[serde(skip_serializing_if = "Option::is_none")]
275    pub size_measurement_unit: Option<String>,
276
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub provider: Option<String>,
279
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub region: Option<String>,
282
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub price: Option<i32>,
285
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub price_period: Option<String>,
288
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub price_currency: Option<String>,
291
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub maximum_databases: Option<i32>,
294
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub availability: Option<String>,
297
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub connections: Option<String>,
300
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub cidr_allow_rules: Option<i32>,
303
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub support_data_persistence: Option<bool>,
306
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub support_instant_and_daily_backups: Option<bool>,
309
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub support_replication: Option<bool>,
312
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub support_clustering: Option<bool>,
315
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub customer_support: Option<String>,
318
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub creation_date: Option<String>,
321
322    #[serde(skip_serializing_if = "Option::is_none")]
323    pub database_status: Option<String>,
324
325    /// HATEOAS links
326    #[serde(skip_serializing_if = "Option::is_none")]
327    pub links: Option<Vec<Link>>,
328}
329
330/// `TaskStateUpdate`
331#[derive(Debug, Clone, Serialize, Deserialize)]
332#[serde(rename_all = "camelCase")]
333pub struct TaskStateUpdate {
334    #[serde(skip_serializing_if = "Option::is_none")]
335    pub task_id: Option<String>,
336
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub command_type: Option<String>,
339
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub status: Option<String>,
342
343    #[serde(skip_serializing_if = "Option::is_none")]
344    pub description: Option<String>,
345
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub timestamp: Option<String>,
348
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub response: Option<ProcessorResponse>,
351
352    /// HATEOAS links
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub links: Option<Vec<Link>>,
355}
356
357// ============================================================================
358// Handler
359// ============================================================================
360
361/// Handler for Essentials subscription operations
362///
363/// Manages fixed-capacity subscriptions with pre-defined plans,
364/// simplified pricing, and streamlined configuration options.
365pub struct FixedSubscriptionHandler {
366    client: CloudClient,
367}
368
369impl FixedSubscriptionHandler {
370    /// Create a new handler
371    #[must_use]
372    pub fn new(client: CloudClient) -> Self {
373        Self { client }
374    }
375
376    /// Get Essentials plans
377    /// Gets a list of Essentials plans. The plan describes the dataset size, cloud provider and region, and available database configuration options for an Essentials database.
378    ///
379    /// GET /fixed/plans
380    pub async fn list_plans(
381        &self,
382        provider: Option<String>,
383        redis_flex: Option<bool>,
384    ) -> Result<FixedSubscriptionsPlans> {
385        let mut query = Vec::new();
386        if let Some(v) = provider {
387            query.push(format!("provider={v}"));
388        }
389        if let Some(v) = redis_flex {
390            query.push(format!("redisFlex={v}"));
391        }
392        let query_string = if query.is_empty() {
393            String::new()
394        } else {
395            format!("?{}", query.join("&"))
396        };
397        self.client
398            .get(&format!("/fixed/plans{query_string}"))
399            .await
400    }
401
402    /// Get Essentials plans for a subscription
403    /// Gets a list of compatible Essentials plans for the specified Essentials subscription.
404    ///
405    /// GET /fixed/plans/subscriptions/{subscriptionId}
406    pub async fn get_plans_by_subscription_id(
407        &self,
408        subscription_id: i32,
409    ) -> Result<FixedSubscriptionsPlans> {
410        self.client
411            .get(&format!("/fixed/plans/subscriptions/{subscription_id}"))
412            .await
413    }
414
415    /// Get a single Essentials plan
416    /// Gets information on the specified Essentials plan.
417    ///
418    /// GET /fixed/plans/{planId}
419    pub async fn get_plan_by_id(&self, plan_id: i32) -> Result<FixedSubscriptionsPlan> {
420        self.client.get(&format!("/fixed/plans/{plan_id}")).await
421    }
422
423    /// Get available Redis database versions for specific Essentials subscription
424    /// Gets a list of all available Redis database versions for a specific Essentials subscription.
425    ///
426    /// GET /fixed/redis-versions
427    pub async fn get_redis_versions(&self, subscription_id: i32) -> Result<RedisVersions> {
428        let mut query = Vec::new();
429        query.push(format!("subscriptionId={subscription_id}"));
430        let query_string = if query.is_empty() {
431            String::new()
432        } else {
433            format!("?{}", query.join("&"))
434        };
435        self.client
436            .get(&format!("/fixed/redis-versions{query_string}"))
437            .await
438    }
439
440    /// Get Essentials subscriptions
441    /// Gets a list of all Essentials subscriptions in the current account.
442    ///
443    /// GET /fixed/subscriptions
444    pub async fn list(&self) -> Result<FixedSubscriptions> {
445        self.client.get("/fixed/subscriptions").await
446    }
447
448    /// Create Essentials subscription
449    /// Creates a new Essentials subscription.
450    ///
451    /// POST /fixed/subscriptions
452    pub async fn create(
453        &self,
454        request: &FixedSubscriptionCreateRequest,
455    ) -> Result<TaskStateUpdate> {
456        self.client.post("/fixed/subscriptions", request).await
457    }
458
459    /// Delete Essentials subscription
460    /// Deletes the specified Essentials subscription. All databases in the subscription must be deleted before deleting it.
461    ///
462    /// DELETE /fixed/subscriptions/{subscriptionId}
463    pub async fn delete_by_id(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
464        let response = self
465            .client
466            .delete_raw(&format!("/fixed/subscriptions/{subscription_id}"))
467            .await?;
468        serde_json::from_value(response).map_err(Into::into)
469    }
470
471    /// Get a single Essentials subscription
472    /// Gets information on the specified Essentials subscription.
473    ///
474    /// GET /fixed/subscriptions/{subscriptionId}
475    pub async fn get_by_id(&self, subscription_id: i32) -> Result<FixedSubscription> {
476        self.client
477            .get(&format!("/fixed/subscriptions/{subscription_id}"))
478            .await
479    }
480
481    /// Update Essentials subscription
482    /// Updates the specified Essentials subscription.
483    ///
484    /// PUT /fixed/subscriptions/{subscriptionId}
485    pub async fn update(
486        &self,
487        subscription_id: i32,
488        request: &FixedSubscriptionUpdateRequest,
489    ) -> Result<TaskStateUpdate> {
490        self.client
491            .put(&format!("/fixed/subscriptions/{subscription_id}"), request)
492            .await
493    }
494}