files_sdk/users/
groups.rs

1//! Group management operations
2//!
3//! This module provides group management functionality including:
4//! - List groups
5//! - Create new groups
6//! - Update group settings
7//! - Delete groups
8//! - Manage group memberships
9
10use crate::{FilesClient, PaginationInfo, Result};
11use serde::{Deserialize, Serialize};
12use serde_json::json;
13
14/// Group entity from Files.com API
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct GroupEntity {
17    /// Group ID
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub id: Option<i64>,
20
21    /// Group name
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub name: Option<String>,
24
25    /// Notes about the group
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub notes: Option<String>,
28
29    /// Admin user IDs (comma-separated string)
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub admin_ids: Option<String>,
32
33    /// User IDs in this group (comma-separated string)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub user_ids: Option<String>,
36
37    /// Usernames in this group (comma-separated string)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub usernames: Option<String>,
40
41    /// Allowed IP addresses
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub allowed_ips: Option<String>,
44
45    /// FTP permission
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub ftp_permission: Option<bool>,
48
49    /// SFTP permission
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub sftp_permission: Option<bool>,
52
53    /// WebDAV permission
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub dav_permission: Option<bool>,
56
57    /// REST API permission
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub restapi_permission: Option<bool>,
60
61    /// Site ID
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub site_id: Option<i64>,
64}
65
66/// Handler for group operations
67#[derive(Debug, Clone)]
68pub struct GroupHandler {
69    client: FilesClient,
70}
71
72impl GroupHandler {
73    /// Creates a new GroupHandler
74    pub fn new(client: FilesClient) -> Self {
75        Self { client }
76    }
77
78    /// List groups
79    ///
80    /// # Arguments
81    ///
82    /// * `cursor` - Pagination cursor (optional)
83    /// * `per_page` - Results per page (optional)
84    ///
85    /// # Examples
86    ///
87    /// ```rust,no_run
88    /// # use files_sdk::{FilesClient, GroupHandler};
89    /// # #[tokio::main]
90    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
91    /// # let client = FilesClient::builder().api_key("key").build()?;
92    /// let handler = GroupHandler::new(client);
93    /// let (groups, pagination) = handler.list(None, Some(10)).await?;
94    ///
95    /// for group in groups {
96    ///     println!("Group: {:?}", group.name);
97    /// }
98    /// # Ok(())
99    /// # }
100    /// ```
101    pub async fn list(
102        &self,
103        cursor: Option<String>,
104        per_page: Option<i32>,
105    ) -> Result<(Vec<GroupEntity>, PaginationInfo)> {
106        let mut path = "/groups?".to_string();
107
108        if let Some(c) = cursor {
109            path.push_str(&format!("cursor={}&", c));
110        }
111        if let Some(pp) = per_page {
112            path.push_str(&format!("per_page={}&", pp));
113        }
114
115        let response = self.client.get_raw(&path).await?;
116        let groups: Vec<GroupEntity> = serde_json::from_value(response)?;
117
118        let pagination = PaginationInfo {
119            cursor_next: None,
120            cursor_prev: None,
121        };
122
123        Ok((groups, pagination))
124    }
125
126    /// Get a specific group by ID
127    ///
128    /// # Arguments
129    ///
130    /// * `id` - Group ID
131    pub async fn get(&self, id: i64) -> Result<GroupEntity> {
132        let path = format!("/groups/{}", id);
133        let response = self.client.get_raw(&path).await?;
134        Ok(serde_json::from_value(response)?)
135    }
136
137    /// Create a new group
138    ///
139    /// # Arguments
140    ///
141    /// * `name` - Group name (required)
142    /// * `notes` - Notes about the group (optional)
143    /// * `user_ids` - User IDs to add to group (optional)
144    ///
145    /// # Examples
146    ///
147    /// ```rust,no_run
148    /// # use files_sdk::{FilesClient, GroupHandler};
149    /// # #[tokio::main]
150    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
151    /// # let client = FilesClient::builder().api_key("key").build()?;
152    /// let handler = GroupHandler::new(client);
153    /// let group = handler.create(
154    ///     "Developers",
155    ///     Some("Development team members"),
156    ///     None
157    /// ).await?;
158    /// # Ok(())
159    /// # }
160    /// ```
161    pub async fn create(
162        &self,
163        name: &str,
164        notes: Option<&str>,
165        user_ids: Option<Vec<i64>>,
166    ) -> Result<GroupEntity> {
167        let mut body = json!({
168            "name": name,
169        });
170
171        if let Some(n) = notes {
172            body["notes"] = json!(n);
173        }
174        if let Some(uids) = user_ids {
175            body["user_ids"] = json!(uids);
176        }
177
178        let response = self.client.post_raw("/groups", body).await?;
179        Ok(serde_json::from_value(response)?)
180    }
181
182    /// Update a group
183    ///
184    /// # Arguments
185    ///
186    /// * `id` - Group ID
187    /// * `name` - New name (optional)
188    /// * `notes` - New notes (optional)
189    pub async fn update(
190        &self,
191        id: i64,
192        name: Option<&str>,
193        notes: Option<&str>,
194    ) -> Result<GroupEntity> {
195        let mut body = json!({});
196
197        if let Some(n) = name {
198            body["name"] = json!(n);
199        }
200        if let Some(nt) = notes {
201            body["notes"] = json!(nt);
202        }
203
204        let path = format!("/groups/{}", id);
205        let response = self.client.patch_raw(&path, body).await?;
206        Ok(serde_json::from_value(response)?)
207    }
208
209    /// Delete a group
210    ///
211    /// # Arguments
212    ///
213    /// * `id` - Group ID
214    pub async fn delete(&self, id: i64) -> Result<()> {
215        let path = format!("/groups/{}", id);
216        self.client.delete_raw(&path).await?;
217        Ok(())
218    }
219
220    /// Add a user to a group
221    ///
222    /// # Arguments
223    ///
224    /// * `group_id` - Group ID
225    /// * `user_id` - User ID to add
226    pub async fn add_user(&self, group_id: i64, user_id: i64) -> Result<()> {
227        let path = format!("/groups/{}/users", group_id);
228        let body = json!({
229            "user_id": user_id,
230        });
231        self.client.post_raw(&path, body).await?;
232        Ok(())
233    }
234
235    /// Remove a user from a group
236    ///
237    /// # Arguments
238    ///
239    /// * `group_id` - Group ID
240    /// * `user_id` - User ID to remove
241    pub async fn remove_user(&self, group_id: i64, user_id: i64) -> Result<()> {
242        let path = format!("/groups/{}/memberships/{}", group_id, user_id);
243        self.client.delete_raw(&path).await?;
244        Ok(())
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251
252    #[test]
253    fn test_handler_creation() {
254        let client = FilesClient::builder().api_key("test-key").build().unwrap();
255        let _handler = GroupHandler::new(client);
256    }
257}