Skip to main content

redis_cloud/
account.rs

1//! Account management operations and models
2//!
3//! This module provides comprehensive account management functionality for Redis Cloud,
4//! including account information retrieval, settings management, API keys, owners,
5//! payment methods, SSO/SAML configuration, and billing address management.
6//!
7//! # Overview
8//!
9//! The account module is the central point for managing organization-wide settings and
10//! configurations in Redis Cloud. It handles everything from basic account information
11//! to advanced features like SSO integration and API key management.
12//!
13//! # Key Features
14//!
15//! - **Account Information**: Get current account details and metadata
16//! - **API Key Management**: Create, list, and manage API keys for programmatic access
17//! - **Owner Management**: Manage account owners and their permissions
18//! - **Payment Methods**: Handle payment methods and billing configuration
19//! - **SSO/SAML**: Configure single sign-on and SAML integration
20//! - **Billing Address**: Manage billing address information
21//!
22//! # Example Usage
23//!
24//! ```no_run
25//! use redis_cloud::{CloudClient, AccountHandler};
26//!
27//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
28//! let client = CloudClient::builder()
29//!     .api_key("your-api-key")
30//!     .api_secret("your-api-secret")
31//!     .build()?;
32//!
33//! let handler = AccountHandler::new(client);
34//!
35//! // Get current account information
36//! let account = handler.get_current_account().await?;
37//! println!("Account info: {:?}", account);
38//!
39//! // Get payment methods
40//! let payment_methods = handler.get_account_payment_methods().await?;
41//! println!("Payment methods: {:?}", payment_methods);
42//! # Ok(())
43//! # }
44//! ```
45
46use crate::types::Link;
47use crate::{CloudClient, Result};
48use serde::{Deserialize, Serialize};
49
50// ============================================================================
51// Models
52// ============================================================================
53
54/// Database modules/capabilities response
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ModulesData {
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub modules: Option<Vec<Module>>,
59
60    /// HATEOAS links
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub links: Option<Vec<Link>>,
63}
64
65/// Root account response from GET /
66#[derive(Debug, Clone, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase")]
68pub struct RootAccount {
69    /// Account information
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub account: Option<Account>,
72
73    /// HATEOAS links
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub links: Option<Vec<Link>>,
76}
77
78/// Account information
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct Account {
82    /// Account ID
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub id: Option<i32>,
85
86    /// Account name
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub name: Option<String>,
89
90    /// Timestamp when the account was created
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub created_timestamp: Option<String>,
93
94    /// Timestamp when the account was last updated
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub updated_timestamp: Option<String>,
97
98    /// Marketplace status (e.g., "active", "deleted")
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub marketplace_status: Option<String>,
101
102    /// API key information used for this request
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub key: Option<AccountApiKeyInfo>,
105}
106
107/// API key information returned in account response
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct AccountApiKeyInfo {
111    /// API key name
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub name: Option<String>,
114
115    /// Account ID this key belongs to
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub account_id: Option<i32>,
118
119    /// Account name
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub account_name: Option<String>,
122
123    /// Allowed source IP addresses/CIDRs
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub allowed_source_ips: Option<Vec<String>>,
126
127    /// Owner information
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub owner: Option<AccountApiKeyOwner>,
130
131    /// User account ID
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub user_account_id: Option<i32>,
134
135    /// HTTP source IP of the current request
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub http_source_ip: Option<String>,
138
139    /// Account marketplace ID
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub account_marketplace_id: Option<String>,
142}
143
144/// API key owner information
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct AccountApiKeyOwner {
148    /// Owner's name
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub name: Option<String>,
151
152    /// Owner's email
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub email: Option<String>,
155}
156
157/// Account system log entry
158#[derive(Debug, Clone, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub struct AccountSystemLogEntry {
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub id: Option<i32>,
163
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub time: Option<String>,
166
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub originator: Option<String>,
169
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub api_key_name: Option<String>,
172
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub resource: Option<String>,
175
176    /// Resource ID associated with this log entry
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub resource_id: Option<i32>,
179
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub r#type: Option<String>,
182
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub description: Option<String>,
185}
186
187/// Available regions response
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct Regions {
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub regions: Option<Vec<Region>>,
192
193    /// HATEOAS links
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub links: Option<Vec<Link>>,
196}
197
198/// Region information
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Region {
201    /// Region ID
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub id: Option<i32>,
204
205    /// Region name (e.g., "us-east-1")
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub name: Option<String>,
208
209    /// Cloud provider (e.g., "AWS", "GCP", "Azure")
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub provider: Option<String>,
212}
213
214/// Account payment methods response
215#[derive(Debug, Clone, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct PaymentMethods {
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub account_id: Option<i32>,
220
221    /// List of payment methods
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub payment_methods: Option<Vec<PaymentMethod>>,
224
225    /// HATEOAS links
226    #[serde(skip_serializing_if = "Option::is_none")]
227    pub links: Option<Vec<Link>>,
228}
229
230/// Payment method information
231#[derive(Debug, Clone, Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233pub struct PaymentMethod {
234    /// Payment method ID
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub id: Option<i32>,
237
238    /// Card type (e.g., "Mastercard", "Visa")
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub r#type: Option<String>,
241
242    /// Last 4 digits of the credit card
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub credit_card_ends_with: Option<i32>,
245
246    /// Name on the card
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub name_on_card: Option<String>,
249
250    /// Expiration month (1-12)
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub expiration_month: Option<i32>,
253
254    /// Expiration year
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub expiration_year: Option<i32>,
257
258    /// HATEOAS links
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub links: Option<Vec<Link>>,
261}
262
263/// Database module/capability information
264#[derive(Debug, Clone, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct Module {
267    /// Module name (e.g., "`RedisJSON`", "`RediSearch`")
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub name: Option<String>,
270
271    /// Capability name (e.g., "JSON", "Search and query")
272    #[serde(skip_serializing_if = "Option::is_none")]
273    pub capability_name: Option<String>,
274
275    /// Module description
276    #[serde(skip_serializing_if = "Option::is_none")]
277    pub description: Option<String>,
278
279    /// Module parameters configuration
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub parameters: Option<Vec<ModuleParameter>>,
282}
283
284/// Module parameter configuration
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287pub struct ModuleParameter {
288    /// Parameter name
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub name: Option<String>,
291
292    /// Parameter description
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub description: Option<String>,
295
296    /// Parameter type (e.g., "integer")
297    #[serde(skip_serializing_if = "Option::is_none")]
298    pub r#type: Option<String>,
299
300    /// Default value for the parameter
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub default_value: Option<i64>,
303
304    /// Whether this parameter is required
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub required: Option<bool>,
307}
308
309/// Account system log entries response
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct AccountSystemLogEntries {
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub entries: Option<Vec<AccountSystemLogEntry>>,
314
315    /// HATEOAS links
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub links: Option<Vec<Link>>,
318}
319
320/// Query performance factors (search scaling) response
321#[derive(Debug, Clone, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct SearchScalingFactorsData {
324    /// Available query performance factors (e.g., "Standard", "2x", "4x")
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub query_performance_factors: Option<Vec<String>>,
327
328    /// HATEOAS links
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub links: Option<Vec<Link>>,
331}
332
333/// Account session log entry
334#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336pub struct AccountSessionLogEntry {
337    /// Session log entry ID (UUID)
338    #[serde(skip_serializing_if = "Option::is_none")]
339    pub id: Option<String>,
340
341    /// Timestamp of the session event
342    #[serde(skip_serializing_if = "Option::is_none")]
343    pub time: Option<String>,
344
345    /// User who performed the action
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub user: Option<String>,
348
349    /// User agent string
350    #[serde(skip_serializing_if = "Option::is_none")]
351    pub user_agent: Option<String>,
352
353    /// IP address of the session
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub ip_address: Option<String>,
356
357    /// User role (e.g., "owner")
358    #[serde(skip_serializing_if = "Option::is_none")]
359    pub user_role: Option<String>,
360
361    /// Session type (e.g., "sso")
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub r#type: Option<String>,
364
365    /// Action performed (e.g., "Successful login", "Successful logout")
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub action: Option<String>,
368}
369
370/// Data persistence option entry
371#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct DataPersistenceEntry {
373    /// Persistence option name (e.g., "none", "aof-every-1-second")
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub name: Option<String>,
376
377    /// Human-readable description
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub description: Option<String>,
380}
381
382/// Data persistence options response
383#[derive(Debug, Clone, Serialize, Deserialize)]
384#[serde(rename_all = "camelCase")]
385pub struct DataPersistenceOptions {
386    /// Available data persistence options
387    #[serde(skip_serializing_if = "Option::is_none")]
388    pub data_persistence: Option<Vec<DataPersistenceEntry>>,
389
390    /// HATEOAS links
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub links: Option<Vec<Link>>,
393}
394
395/// Account session log entries response
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct AccountSessionLogEntries {
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub entries: Option<Vec<AccountSessionLogEntry>>,
400
401    /// HATEOAS links
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub links: Option<Vec<Link>>,
404}
405
406// ============================================================================
407// Handler
408// ============================================================================
409
410/// Account operations handler
411/// Handler for account management operations
412///
413/// Provides methods for managing account information, API keys, owners,
414/// payment methods, SSO/SAML configuration, and billing addresses.
415pub struct AccountHandler {
416    client: CloudClient,
417}
418
419impl AccountHandler {
420    /// Create a new handler
421    #[must_use]
422    pub fn new(client: CloudClient) -> Self {
423        Self { client }
424    }
425
426    /// Get current account
427    /// Gets information on this account.
428    ///
429    /// GET /
430    ///
431    /// # Example
432    ///
433    /// ```no_run
434    /// use redis_cloud::CloudClient;
435    ///
436    /// # async fn example() -> redis_cloud::Result<()> {
437    /// let client = CloudClient::builder()
438    ///     .api_key("your-api-key")
439    ///     .api_secret("your-api-secret")
440    ///     .build()?;
441    ///
442    /// let root = client.account().get_current_account().await?;
443    /// if let Some(account) = &root.account {
444    ///     println!("Account ID: {:?}", account.id);
445    /// }
446    /// # Ok(())
447    /// # }
448    /// ```
449    pub async fn get_current_account(&self) -> Result<RootAccount> {
450        self.client.get("/").await
451    }
452
453    /// Get data persistence options
454    /// Gets a list of all [data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence/) options for this account.
455    ///
456    /// GET /data-persistence
457    pub async fn get_data_persistence_options(&self) -> Result<DataPersistenceOptions> {
458        self.client.get("/data-persistence").await
459    }
460
461    /// Get advanced capabilities
462    /// Gets a list of Redis [advanced capabilities](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities/) (also known as modules) available for this account. Advanced capability support may differ based on subscription and database settings.
463    ///
464    /// GET /database-modules
465    pub async fn get_supported_database_modules(&self) -> Result<ModulesData> {
466        self.client.get("/database-modules").await
467    }
468
469    /// Get system logs
470    /// Gets [system logs](https://redis.io/docs/latest/operate/rc/api/examples/audit-system-logs/) for this account.
471    ///
472    /// GET /logs
473    pub async fn get_account_system_logs(
474        &self,
475        offset: Option<i32>,
476        limit: Option<i32>,
477    ) -> Result<AccountSystemLogEntries> {
478        let mut query = Vec::new();
479        if let Some(v) = offset {
480            query.push(format!("offset={v}"));
481        }
482        if let Some(v) = limit {
483            query.push(format!("limit={v}"));
484        }
485        let query_string = if query.is_empty() {
486            String::new()
487        } else {
488            format!("?{}", query.join("&"))
489        };
490        self.client.get(&format!("/logs{query_string}")).await
491    }
492
493    /// Get payment methods
494    /// Gets a list of all payment methods for this account.
495    ///
496    /// GET /payment-methods
497    pub async fn get_account_payment_methods(&self) -> Result<PaymentMethods> {
498        self.client.get("/payment-methods").await
499    }
500
501    /// Get query performance factors
502    /// Gets a list of available [query performance factors](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities/#query-performance-factor).
503    ///
504    /// GET /query-performance-factors
505    pub async fn get_supported_search_scaling_factors(&self) -> Result<SearchScalingFactorsData> {
506        self.client.get("/query-performance-factors").await
507    }
508
509    /// Get available Pro plan regions
510    /// Gets a list of available regions for Pro subscriptions. For Essentials subscriptions, use 'GET /fixed/plans'.
511    ///
512    /// GET /regions
513    pub async fn get_supported_regions(&self, provider: Option<String>) -> Result<Regions> {
514        let mut query = Vec::new();
515        if let Some(v) = provider {
516            query.push(format!("provider={v}"));
517        }
518        let query_string = if query.is_empty() {
519            String::new()
520        } else {
521            format!("?{}", query.join("&"))
522        };
523        self.client.get(&format!("/regions{query_string}")).await
524    }
525
526    /// Get session logs
527    /// Gets session logs for this account.
528    ///
529    /// GET /session-logs
530    pub async fn get_account_session_logs(
531        &self,
532        offset: Option<i32>,
533        limit: Option<i32>,
534    ) -> Result<AccountSessionLogEntries> {
535        let mut query = Vec::new();
536        if let Some(v) = offset {
537            query.push(format!("offset={v}"));
538        }
539        if let Some(v) = limit {
540            query.push(format!("limit={v}"));
541        }
542        let query_string = if query.is_empty() {
543            String::new()
544        } else {
545            format!("?{}", query.join("&"))
546        };
547        self.client
548            .get(&format!("/session-logs{query_string}"))
549            .await
550    }
551}