files_sdk/users/
api_keys.rs

1//! API Key management operations
2//!
3//! This module provides API key management functionality including:
4//! - List API keys
5//! - Create new API keys
6//! - Update API key details
7//! - Delete API keys
8//!
9//! API keys are the recommended authentication method for programmatic access.
10
11use crate::{FilesClient, PaginationInfo, Result};
12use serde::{Deserialize, Serialize};
13use serde_json::json;
14
15/// API Key entity from Files.com API
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ApiKeyEntity {
18    /// API Key ID
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub id: Option<i64>,
21
22    /// API Key name
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub name: Option<String>,
25
26    /// API Key description
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub description: Option<String>,
29
30    /// Descriptive label
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub descriptive_label: Option<String>,
33
34    /// The actual API key (only returned on creation)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub key: Option<String>,
37
38    /// User ID this key belongs to
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub user_id: Option<i64>,
41
42    /// Platform this key is for
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub platform: Option<String>,
45
46    /// Permission set for this key
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub permission_set: Option<String>,
49
50    /// URL this key is associated with
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub url: Option<String>,
53
54    /// Created at timestamp
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub created_at: Option<String>,
57
58    /// Expires at timestamp
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub expires_at: Option<String>,
61
62    /// Last use at timestamp
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub last_use_at: Option<String>,
65}
66
67/// Handler for API key operations
68#[derive(Debug, Clone)]
69pub struct ApiKeyHandler {
70    client: FilesClient,
71}
72
73impl ApiKeyHandler {
74    /// Creates a new ApiKeyHandler
75    pub fn new(client: FilesClient) -> Self {
76        Self { client }
77    }
78
79    /// List API keys
80    ///
81    /// # Arguments
82    ///
83    /// * `user_id` - Filter by user ID (optional)
84    /// * `cursor` - Pagination cursor (optional)
85    /// * `per_page` - Results per page (optional)
86    ///
87    /// # Examples
88    ///
89    /// ```rust,no_run
90    /// # use files_sdk::{FilesClient, ApiKeyHandler};
91    /// # #[tokio::main]
92    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
93    /// # let client = FilesClient::builder().api_key("key").build()?;
94    /// let handler = ApiKeyHandler::new(client);
95    /// let (keys, pagination) = handler.list(None, None, Some(10)).await?;
96    ///
97    /// for key in keys {
98    ///     println!("API Key: {:?}", key.name);
99    /// }
100    /// # Ok(())
101    /// # }
102    /// ```
103    pub async fn list(
104        &self,
105        user_id: Option<i64>,
106        cursor: Option<String>,
107        per_page: Option<i32>,
108    ) -> Result<(Vec<ApiKeyEntity>, PaginationInfo)> {
109        let mut path = "/api_keys?".to_string();
110
111        if let Some(uid) = user_id {
112            path.push_str(&format!("user_id={}&", uid));
113        }
114        if let Some(c) = cursor {
115            path.push_str(&format!("cursor={}&", c));
116        }
117        if let Some(pp) = per_page {
118            path.push_str(&format!("per_page={}&", pp));
119        }
120
121        let response = self.client.get_raw(&path).await?;
122        let keys: Vec<ApiKeyEntity> = serde_json::from_value(response)?;
123
124        let pagination = PaginationInfo {
125            cursor_next: None,
126            cursor_prev: None,
127        };
128
129        Ok((keys, pagination))
130    }
131
132    /// Get a specific API key by ID
133    ///
134    /// # Arguments
135    ///
136    /// * `id` - API Key ID
137    pub async fn get(&self, id: i64) -> Result<ApiKeyEntity> {
138        let path = format!("/api_keys/{}", id);
139        let response = self.client.get_raw(&path).await?;
140        Ok(serde_json::from_value(response)?)
141    }
142
143    /// Create a new API key
144    ///
145    /// # Arguments
146    ///
147    /// * `name` - Name for the API key (optional)
148    /// * `description` - Description (optional)
149    /// * `expires_at` - Expiration timestamp (optional)
150    /// * `permission_set` - Permission set name (optional)
151    ///
152    /// # Examples
153    ///
154    /// ```rust,no_run
155    /// # use files_sdk::{FilesClient, ApiKeyHandler};
156    /// # #[tokio::main]
157    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
158    /// # let client = FilesClient::builder().api_key("key").build()?;
159    /// let handler = ApiKeyHandler::new(client);
160    /// let key = handler.create(
161    ///     Some("My API Key"),
162    ///     Some("For automated uploads"),
163    ///     None,
164    ///     None
165    /// ).await?;
166    /// println!("Created key: {:?}", key.key);
167    /// # Ok(())
168    /// # }
169    /// ```
170    pub async fn create(
171        &self,
172        name: Option<&str>,
173        description: Option<&str>,
174        expires_at: Option<&str>,
175        permission_set: Option<&str>,
176    ) -> Result<ApiKeyEntity> {
177        let mut body = json!({});
178
179        if let Some(n) = name {
180            body["name"] = json!(n);
181        }
182        if let Some(d) = description {
183            body["description"] = json!(d);
184        }
185        if let Some(e) = expires_at {
186            body["expires_at"] = json!(e);
187        }
188        if let Some(p) = permission_set {
189            body["permission_set"] = json!(p);
190        }
191
192        let response = self.client.post_raw("/api_keys", body).await?;
193        Ok(serde_json::from_value(response)?)
194    }
195
196    /// Update an API key
197    ///
198    /// # Arguments
199    ///
200    /// * `id` - API Key ID
201    /// * `name` - New name (optional)
202    /// * `description` - New description (optional)
203    /// * `expires_at` - New expiration (optional)
204    pub async fn update(
205        &self,
206        id: i64,
207        name: Option<&str>,
208        description: Option<&str>,
209        expires_at: Option<&str>,
210    ) -> Result<ApiKeyEntity> {
211        let mut body = json!({});
212
213        if let Some(n) = name {
214            body["name"] = json!(n);
215        }
216        if let Some(d) = description {
217            body["description"] = json!(d);
218        }
219        if let Some(e) = expires_at {
220            body["expires_at"] = json!(e);
221        }
222
223        let path = format!("/api_keys/{}", id);
224        let response = self.client.patch_raw(&path, body).await?;
225        Ok(serde_json::from_value(response)?)
226    }
227
228    /// Delete an API key
229    ///
230    /// # Arguments
231    ///
232    /// * `id` - API Key ID
233    pub async fn delete(&self, id: i64) -> Result<()> {
234        let path = format!("/api_keys/{}", id);
235        self.client.delete_raw(&path).await?;
236        Ok(())
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    #[test]
245    fn test_handler_creation() {
246        let client = FilesClient::builder().api_key("test-key").build().unwrap();
247        let _handler = ApiKeyHandler::new(client);
248    }
249}