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;
53pub use crate::types::TaskStateUpdate;
54use crate::{CloudClient, Result};
55use serde::{Deserialize, Serialize};
56use typed_builder::TypedBuilder;
57
58// ============================================================================
59// Models
60// ============================================================================
61
62/// `RedisVersions`
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct RedisVersions {
66 /// List of Redis versions available for the account.
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub redis_versions: Option<Vec<RedisVersion>>,
69}
70
71/// Redis list of Essentials subscriptions plans
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct FixedSubscriptionsPlans {
74 /// HATEOAS links
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub links: Option<Vec<Link>>,
77}
78
79/// Essentials subscription update request
80///
81/// # Example
82///
83/// ```
84/// use redis_cloud::fixed::subscriptions::FixedSubscriptionUpdateRequest;
85///
86/// let request = FixedSubscriptionUpdateRequest::builder()
87/// .name("updated-subscription")
88/// .build();
89/// ```
90#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
91#[serde(rename_all = "camelCase")]
92pub struct FixedSubscriptionUpdateRequest {
93 /// Subscription ID being updated. Server-populated from the path.
94 #[serde(skip_serializing_if = "Option::is_none")]
95 #[builder(default, setter(strip_option))]
96 pub subscription_id: Option<i32>,
97
98 /// Optional. Updated subscription name.
99 #[serde(skip_serializing_if = "Option::is_none")]
100 #[builder(default, setter(strip_option, into))]
101 pub name: Option<String>,
102
103 /// 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.
104 #[serde(skip_serializing_if = "Option::is_none")]
105 #[builder(default, setter(strip_option))]
106 pub plan_id: Option<i32>,
107
108 /// Optional. The payment method for the subscription. If set to 'credit-card' , 'paymentMethodId' must be defined.
109 #[serde(skip_serializing_if = "Option::is_none")]
110 #[builder(default, setter(strip_option, into))]
111 pub payment_method: Option<String>,
112
113 /// 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'.
114 #[serde(skip_serializing_if = "Option::is_none")]
115 #[builder(default, setter(strip_option))]
116 pub payment_method_id: Option<i32>,
117
118 /// Read-only on the response; populated by the server with the
119 /// operation type (e.g. `"UPDATE_FIXED_SUBSCRIPTION"`).
120 #[serde(skip_serializing_if = "Option::is_none")]
121 #[builder(default, setter(strip_option, into))]
122 pub command_type: Option<String>,
123}
124
125/// Redis Essentials subscription plan information
126#[derive(Debug, Clone, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct FixedSubscriptionsPlan {
129 /// Plan identifier.
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub id: Option<i32>,
132
133 /// Plan name.
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub name: Option<String>,
136
137 /// Total memory size of the plan in the plan's measurement unit.
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub size: Option<f64>,
140
141 /// Dataset size of the plan in the plan's measurement unit.
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub dataset_size: Option<f64>,
144
145 /// Measurement unit for `size`/`dataset_size` (e.g. `"GB"`, `"MB"`).
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub size_measurement_unit: Option<String>,
148
149 /// Cloud provider (e.g. `"AWS"`, `"GCP"`, `"Azure"`).
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub provider: Option<String>,
152
153 /// Cloud region for the plan.
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub region: Option<String>,
156
157 /// Region identifier.
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub region_id: Option<i32>,
160
161 /// Plan price in the plan's currency.
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub price: Option<i32>,
164
165 /// ISO currency code for the plan price (e.g. `"USD"`).
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub price_currency: Option<String>,
168
169 /// Billing period for the plan price (e.g. `"Month"`, `"Hour"`).
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub price_period: Option<String>,
172
173 /// Maximum number of databases allowed under this plan.
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub maximum_databases: Option<i32>,
176
177 /// Maximum throughput (ops/sec) allowed under this plan.
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub maximum_throughput: Option<i32>,
180
181 /// Maximum monthly bandwidth, in GB.
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub maximum_bandwidth_gb: Option<i32>,
184
185 /// Availability tier (e.g. `"Single-zone"`, `"Multi-zone"`).
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub availability: Option<String>,
188
189 /// Connection limit description for this plan.
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub connections: Option<String>,
192
193 /// Number of CIDR allow rules supported by this plan.
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub cidr_allow_rules: Option<i32>,
196
197 /// Whether the plan supports data persistence.
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub support_data_persistence: Option<bool>,
200
201 /// Whether the plan supports Redis Flex (auto-tiering).
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub redis_flex: Option<bool>,
204
205 /// Whether the plan supports instant and daily backups.
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub support_instant_and_daily_backups: Option<bool>,
208
209 /// Whether the plan supports replication.
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub support_replication: Option<bool>,
212
213 /// Whether the plan supports clustering.
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub support_clustering: Option<bool>,
216
217 /// Whether the plan supports SSL/TLS connections.
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub support_ssl: Option<bool>,
220
221 /// List of supported alert types for this plan
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub supported_alerts: Option<Vec<String>>,
224
225 /// Customer support tier included with this plan.
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub customer_support: Option<String>,
228
229 /// HATEOAS links
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub links: Option<Vec<Link>>,
232}
233
234/// Essentials subscription create request
235///
236/// # Example
237///
238/// ```
239/// use redis_cloud::fixed::subscriptions::FixedSubscriptionCreateRequest;
240///
241/// let request = FixedSubscriptionCreateRequest::builder()
242/// .name("my-subscription")
243/// .plan_id(123)
244/// .build();
245/// ```
246#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
247#[serde(rename_all = "camelCase")]
248pub struct FixedSubscriptionCreateRequest {
249 /// New Essentials subscription name.
250 #[builder(setter(into))]
251 pub name: String,
252
253 /// 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.
254 pub plan_id: i32,
255
256 /// Optional. The payment method for the subscription. If set to 'credit-card', 'paymentMethodId' must be defined. Default: 'credit-card'
257 #[serde(skip_serializing_if = "Option::is_none")]
258 #[builder(default, setter(strip_option, into))]
259 pub payment_method: Option<String>,
260
261 /// 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.
262 #[serde(skip_serializing_if = "Option::is_none")]
263 #[builder(default, setter(strip_option))]
264 pub payment_method_id: Option<i32>,
265
266 /// Read-only on the response; populated by the server with the
267 /// operation type (e.g. `"CREATE_FIXED_SUBSCRIPTION"`).
268 #[serde(skip_serializing_if = "Option::is_none")]
269 #[builder(default, setter(strip_option, into))]
270 pub command_type: Option<String>,
271}
272
273/// Redis list of Essentials subscriptions in current account
274#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(rename_all = "camelCase")]
276pub struct FixedSubscriptions {
277 /// Account identifier owning these subscriptions.
278 #[serde(skip_serializing_if = "Option::is_none")]
279 pub account_id: Option<i32>,
280
281 /// List of Essentials subscriptions
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub subscriptions: Option<Vec<FixedSubscription>>,
284
285 /// HATEOAS links
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub links: Option<Vec<Link>>,
288}
289
290/// `RedisVersion`
291#[derive(Debug, Clone, Serialize, Deserialize)]
292#[serde(rename_all = "camelCase")]
293pub struct RedisVersion {
294 /// Redis version string (e.g. `"7.2"`).
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub version: Option<String>,
297
298 /// End-of-life date for this Redis version.
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub eol_date: Option<String>,
301
302 /// Whether this Redis version is a preview/early-access release.
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub is_preview: Option<bool>,
305
306 /// Whether this Redis version is the default for new databases.
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub is_default: Option<bool>,
309}
310
311/// Redis Essentials Subscription information
312#[derive(Debug, Clone, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct FixedSubscription {
315 /// Subscription identifier.
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub id: Option<i32>,
318
319 /// Subscription name.
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub name: Option<String>,
322
323 /// Current subscription status (e.g. `"active"`, `"pending"`).
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub status: Option<String>,
326
327 /// Payment method identifier for this subscription.
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub payment_method_id: Option<i32>,
330
331 /// Payment method type (e.g. `"credit-card"`, `"marketplace"`).
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub payment_method_type: Option<String>,
334
335 /// Identifier of the Essentials plan for this subscription.
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub plan_id: Option<i32>,
338
339 /// Name of the Essentials plan for this subscription.
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub plan_name: Option<String>,
342
343 /// Plan type (e.g. `"single-region"`).
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub plan_type: Option<String>,
346
347 /// Plan size in the plan's measurement unit.
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub size: Option<f64>,
350
351 /// Measurement unit for `size` (e.g. `"GB"`, `"MB"`).
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub size_measurement_unit: Option<String>,
354
355 /// Cloud provider (e.g. `"AWS"`, `"GCP"`, `"Azure"`).
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub provider: Option<String>,
358
359 /// Cloud region for the subscription.
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub region: Option<String>,
362
363 /// Subscription price in the configured currency.
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub price: Option<i32>,
366
367 /// Billing period for the subscription price (e.g. `"Month"`).
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub price_period: Option<String>,
370
371 /// ISO currency code for the subscription price (e.g. `"USD"`).
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub price_currency: Option<String>,
374
375 /// Maximum number of databases allowed under this subscription.
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub maximum_databases: Option<i32>,
378
379 /// Availability tier (e.g. `"Single-zone"`, `"Multi-zone"`).
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub availability: Option<String>,
382
383 /// Connection limit description.
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub connections: Option<String>,
386
387 /// Number of CIDR allow rules supported by this subscription.
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub cidr_allow_rules: Option<i32>,
390
391 /// Whether data persistence is supported.
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub support_data_persistence: Option<bool>,
394
395 /// Whether instant and daily backups are supported.
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub support_instant_and_daily_backups: Option<bool>,
398
399 /// Whether replication is supported.
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub support_replication: Option<bool>,
402
403 /// Whether clustering is supported.
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub support_clustering: Option<bool>,
406
407 /// Customer support tier included with this subscription.
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub customer_support: Option<String>,
410
411 /// Timestamp when the subscription was created.
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub creation_date: Option<String>,
414
415 /// Aggregate status of databases in this subscription.
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub database_status: Option<String>,
418
419 /// HATEOAS links
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub links: Option<Vec<Link>>,
422}
423
424// ============================================================================
425// Handler
426// ============================================================================
427
428/// Handler for Essentials subscription operations
429///
430/// Manages fixed-capacity subscriptions with pre-defined plans,
431/// simplified pricing, and streamlined configuration options.
432pub struct FixedSubscriptionHandler {
433 client: CloudClient,
434}
435
436impl FixedSubscriptionHandler {
437 /// Create a new handler
438 #[must_use]
439 pub fn new(client: CloudClient) -> Self {
440 Self { client }
441 }
442
443 /// Get Essentials plans
444 /// 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.
445 ///
446 /// GET /fixed/plans
447 pub async fn list_plans(
448 &self,
449 provider: Option<String>,
450 redis_flex: Option<bool>,
451 ) -> Result<FixedSubscriptionsPlans> {
452 let mut query = Vec::new();
453 if let Some(v) = provider {
454 query.push(format!("provider={v}"));
455 }
456 if let Some(v) = redis_flex {
457 query.push(format!("redisFlex={v}"));
458 }
459 let query_string = if query.is_empty() {
460 String::new()
461 } else {
462 format!("?{}", query.join("&"))
463 };
464 self.client
465 .get(&format!("/fixed/plans{query_string}"))
466 .await
467 }
468
469 /// Get Essentials plans for a subscription
470 /// Gets a list of compatible Essentials plans for the specified Essentials subscription.
471 ///
472 /// GET /fixed/plans/subscriptions/{subscriptionId}
473 pub async fn get_plans_by_subscription_id(
474 &self,
475 subscription_id: i32,
476 ) -> Result<FixedSubscriptionsPlans> {
477 self.client
478 .get(&format!("/fixed/plans/subscriptions/{subscription_id}"))
479 .await
480 }
481
482 /// Get a single Essentials plan
483 /// Gets information on the specified Essentials plan.
484 ///
485 /// GET /fixed/plans/{planId}
486 pub async fn get_plan_by_id(&self, plan_id: i32) -> Result<FixedSubscriptionsPlan> {
487 self.client.get(&format!("/fixed/plans/{plan_id}")).await
488 }
489
490 /// Get available Redis database versions for specific Essentials subscription
491 /// Gets a list of all available Redis database versions for a specific Essentials subscription.
492 ///
493 /// GET /fixed/redis-versions
494 pub async fn get_redis_versions(&self, subscription_id: i32) -> Result<RedisVersions> {
495 let mut query = Vec::new();
496 query.push(format!("subscriptionId={subscription_id}"));
497 let query_string = if query.is_empty() {
498 String::new()
499 } else {
500 format!("?{}", query.join("&"))
501 };
502 self.client
503 .get(&format!("/fixed/redis-versions{query_string}"))
504 .await
505 }
506
507 /// Get Essentials subscriptions
508 /// Gets a list of all Essentials subscriptions in the current account.
509 ///
510 /// GET /fixed/subscriptions
511 pub async fn list(&self) -> Result<FixedSubscriptions> {
512 self.client.get("/fixed/subscriptions").await
513 }
514
515 /// Create Essentials subscription
516 /// Creates a new Essentials subscription.
517 ///
518 /// POST /fixed/subscriptions
519 pub async fn create(
520 &self,
521 request: &FixedSubscriptionCreateRequest,
522 ) -> Result<TaskStateUpdate> {
523 self.client.post("/fixed/subscriptions", request).await
524 }
525
526 /// Delete Essentials subscription
527 /// Deletes the specified Essentials subscription. All databases in the subscription must be deleted before deleting it.
528 ///
529 /// DELETE /fixed/subscriptions/{subscriptionId}
530 pub async fn delete_by_id(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
531 let response = self
532 .client
533 .delete_raw(&format!("/fixed/subscriptions/{subscription_id}"))
534 .await?;
535 serde_json::from_value(response).map_err(Into::into)
536 }
537
538 /// Get a single Essentials subscription
539 /// Gets information on the specified Essentials subscription.
540 ///
541 /// GET /fixed/subscriptions/{subscriptionId}
542 pub async fn get_by_id(&self, subscription_id: i32) -> Result<FixedSubscription> {
543 self.client
544 .get(&format!("/fixed/subscriptions/{subscription_id}"))
545 .await
546 }
547
548 /// Update Essentials subscription
549 /// Updates the specified Essentials subscription.
550 ///
551 /// PUT /fixed/subscriptions/{subscriptionId}
552 pub async fn update(
553 &self,
554 subscription_id: i32,
555 request: &FixedSubscriptionUpdateRequest,
556 ) -> Result<TaskStateUpdate> {
557 self.client
558 .put(&format!("/fixed/subscriptions/{subscription_id}"), request)
559 .await
560 }
561}