anthropic_api/admin/
members.rs

1//! # Organization Members Admin API
2//!
3//! This module provides a Rust interface to Anthropic's Admin API for managing organization members, which allows you to
4//! list, get, update, and remove users from your organization.
5//!
6//! ## Key Features
7//!
8//! - List all users with pagination and filtering support
9//! - Get detailed information about a specific user
10//! - Update user roles within the organization
11//! - Remove users from the organization
12//!
13//! ## Basic Usage
14//!
15//! ```no_run
16//! use anthropic_api::{admin::members::*, Credentials};
17//!
18//! #[tokio::main]
19//! async fn main() {
20//!     let credentials = Credentials::from_env();
21//!
22//!     // List users
23//!     let users = UserList::builder()
24//!         .credentials(credentials.clone())
25//!         .create()
26//!         .await
27//!         .unwrap();
28//!
29//!     println!("Organization members: {:?}", users.data);
30//!
31//!     // Get a specific user
32//!     if let Some(user) = users.data.first() {
33//!         let user_details = User::builder(&user.id)
34//!             .credentials(credentials.clone())
35//!             .create()
36//!             .await
37//!             .unwrap();
38//!
39//!         println!("User details: {:?}", user_details);
40//!     }
41//! }
42//! ```
43
44use crate::{anthropic_request_json, ApiResponseOrError, Credentials};
45use derive_builder::Builder;
46use reqwest::Method;
47use serde::{Deserialize, Serialize};
48
49/// Organization role of a user
50#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
51#[serde(rename_all = "lowercase")]
52pub enum UserRole {
53    /// Regular user
54    User,
55    /// Developer role
56    Developer,
57    /// Billing administrator
58    Billing,
59    /// Organization administrator
60    Admin,
61}
62
63/// A user in the organization
64#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
65pub struct User {
66    /// Unique user identifier
67    pub id: String,
68    /// User's email address
69    pub email: String,
70    /// User's name
71    pub name: String,
72    /// RFC 3339 datetime string indicating when the user joined the organization
73    pub added_at: String,
74    /// User's role in the organization
75    pub role: UserRole,
76    /// Object type (always "user" for Users)
77    #[serde(rename = "type")]
78    pub user_type: String,
79}
80
81/// Response from the List Users API
82#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
83pub struct UserList {
84    /// List of users in the organization
85    pub data: Vec<User>,
86    /// First ID in the data list (for pagination)
87    pub first_id: Option<String>,
88    /// Last ID in the data list (for pagination)
89    pub last_id: Option<String>,
90    /// Indicates if there are more results in the requested page direction
91    pub has_more: bool,
92}
93
94/// Response from the Remove User API
95#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
96pub struct UserDeleted {
97    /// ID of the deleted user
98    pub id: String,
99    /// Object type (always "user_deleted" for deleted users)
100    #[serde(rename = "type")]
101    pub deleted_type: String,
102}
103
104/// Request parameters for listing users
105#[derive(Serialize, Builder, Debug, Clone)]
106#[builder(derive(Clone, Debug, PartialEq))]
107#[builder(pattern = "owned")]
108#[builder(name = "UserListBuilder")]
109#[builder(setter(strip_option, into))]
110pub struct UserListRequest {
111    /// ID of the object to use as a cursor for pagination (previous page)
112    #[builder(default)]
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub before_id: Option<String>,
115
116    /// ID of the object to use as a cursor for pagination (next page)
117    #[builder(default)]
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub after_id: Option<String>,
120
121    /// Number of items to return per page (1-1000)
122    #[builder(default)]
123    #[serde(skip_serializing_if = "Option::is_none")]
124    pub limit: Option<u32>,
125
126    /// Filter by user email
127    #[builder(default)]
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub email: Option<String>,
130
131    /// Credentials for authentication (not serialized)
132    #[serde(skip_serializing)]
133    #[builder(default)]
134    pub credentials: Option<Credentials>,
135}
136
137/// Request parameters for getting a specific user
138#[derive(Serialize, Builder, Debug, Clone)]
139#[builder(derive(Clone, Debug, PartialEq))]
140#[builder(pattern = "owned")]
141#[builder(name = "UserBuilder")]
142#[builder(setter(strip_option, into))]
143pub struct UserRequest {
144    /// User identifier
145    pub user_id: String,
146
147    /// Credentials for authentication (not serialized)
148    #[serde(skip_serializing)]
149    #[builder(default)]
150    pub credentials: Option<Credentials>,
151}
152
153/// Request parameters for updating a user
154#[derive(Serialize, Builder, Debug, Clone)]
155#[builder(derive(Clone, Debug, PartialEq))]
156#[builder(pattern = "owned")]
157#[builder(name = "UserUpdateBuilder")]
158#[builder(setter(strip_option, into))]
159pub struct UserUpdateRequest {
160    /// User identifier (not serialized)
161    #[serde(skip_serializing)]
162    pub user_id: String,
163
164    /// New role for the user (cannot be "admin")
165    pub role: UserRole,
166
167    /// Credentials for authentication (not serialized)
168    #[serde(skip_serializing)]
169    #[builder(default)]
170    pub credentials: Option<Credentials>,
171}
172
173/// Request parameters for removing a user
174#[derive(Serialize, Builder, Debug, Clone)]
175#[builder(derive(Clone, Debug, PartialEq))]
176#[builder(pattern = "owned")]
177#[builder(name = "UserRemoveBuilder")]
178#[builder(setter(strip_option, into))]
179pub struct UserRemoveRequest {
180    /// User identifier
181    pub user_id: String,
182
183    /// Credentials for authentication (not serialized)
184    #[serde(skip_serializing)]
185    #[builder(default)]
186    pub credentials: Option<Credentials>,
187}
188
189impl UserList {
190    /// Creates a builder for listing users.
191    ///
192    /// # Example
193    ///
194    /// ```no_run
195    /// # use anthropic_api::{admin::members::*, Credentials};
196    /// # #[tokio::main]
197    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
198    /// let credentials = Credentials::from_env();
199    ///
200    /// let users = UserList::builder()
201    ///     .credentials(credentials)
202    ///     .limit(10u32)
203    ///     .create()
204    ///     .await?;
205    /// # Ok(())
206    /// # }
207    /// ```
208    pub fn builder() -> UserListBuilder {
209        UserListBuilder::create_empty()
210    }
211
212    /// Lists users in the organization with the given request parameters.
213    ///
214    /// # Example
215    ///
216    /// ```no_run
217    /// # use anthropic_api::{admin::members::*, Credentials};
218    /// # #[tokio::main]
219    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
220    /// let credentials = Credentials::from_env();
221    /// let request = UserListRequest {
222    ///     before_id: None,
223    ///     after_id: None,
224    ///     limit: Some(20),
225    ///     email: None,
226    ///     credentials: Some(credentials),
227    /// };
228    ///
229    /// let users = UserList::create(request).await?;
230    /// # Ok(())
231    /// # }
232    /// ```
233    pub async fn create(request: UserListRequest) -> ApiResponseOrError<Self> {
234        let credentials_opt = request.credentials.clone();
235
236        // Build query parameters
237        let mut query_params = Vec::new();
238        if let Some(before_id) = &request.before_id {
239            query_params.push(("before_id", before_id.clone()));
240        }
241        if let Some(after_id) = &request.after_id {
242            query_params.push(("after_id", after_id.clone()));
243        }
244        if let Some(limit) = request.limit {
245            query_params.push(("limit", limit.to_string()));
246        }
247        if let Some(email) = &request.email {
248            query_params.push(("email", email.clone()));
249        }
250
251        anthropic_request_json(
252            Method::GET,
253            "organizations/users",
254            |r| r.query(&query_params),
255            credentials_opt,
256        )
257        .await
258    }
259}
260
261impl User {
262    /// Creates a builder for getting a specific user.
263    ///
264    /// # Example
265    ///
266    /// ```no_run
267    /// # use anthropic_api::{admin::members::*, Credentials};
268    /// # #[tokio::main]
269    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
270    /// let credentials = Credentials::from_env();
271    ///
272    /// let user = User::builder("user_123456789")
273    ///     .credentials(credentials)
274    ///     .create()
275    ///     .await?;
276    /// # Ok(())
277    /// # }
278    /// ```
279    pub fn builder(user_id: impl Into<String>) -> UserBuilder {
280        UserBuilder::create_empty().user_id(user_id)
281    }
282
283    /// Gets information about a specific user.
284    ///
285    /// # Example
286    ///
287    /// ```no_run
288    /// # use anthropic_api::{admin::members::*, Credentials};
289    /// # #[tokio::main]
290    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
291    /// let credentials = Credentials::from_env();
292    /// let request = UserRequest {
293    ///     user_id: "user_123456789".to_string(),
294    ///     credentials: Some(credentials),
295    /// };
296    ///
297    /// let user = User::create(request).await?;
298    /// # Ok(())
299    /// # }
300    /// ```
301    pub async fn create(request: UserRequest) -> ApiResponseOrError<Self> {
302        let credentials_opt = request.credentials.clone();
303        let route = format!("organizations/users/{}", request.user_id);
304
305        anthropic_request_json(Method::GET, &route, |r| r, credentials_opt).await
306    }
307
308    /// Creates a builder for updating a user.
309    ///
310    /// # Example
311    ///
312    /// ```no_run
313    /// # use anthropic_api::{admin::members::*, Credentials};
314    /// # #[tokio::main]
315    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
316    /// let credentials = Credentials::from_env();
317    ///
318    /// let updated_user = User::update_builder("user_123456789")
319    ///     .credentials(credentials)
320    ///     .role(UserRole::Developer)
321    ///     .create()
322    ///     .await?;
323    /// # Ok(())
324    /// # }
325    /// ```
326    pub fn update_builder(user_id: impl Into<String>) -> UserUpdateBuilder {
327        UserUpdateBuilder::create_empty().user_id(user_id)
328    }
329
330    /// Updates a user with the given request parameters.
331    ///
332    /// # Example
333    ///
334    /// ```no_run
335    /// # use anthropic_api::{admin::members::*, Credentials};
336    /// # #[tokio::main]
337    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
338    /// let credentials = Credentials::from_env();
339    /// let request = UserUpdateRequest {
340    ///     user_id: "user_123456789".to_string(),
341    ///     role: UserRole::Developer,
342    ///     credentials: Some(credentials),
343    /// };
344    ///
345    /// let updated_user = User::update(request).await?;
346    /// # Ok(())
347    /// # }
348    /// ```
349    pub async fn update(request: UserUpdateRequest) -> ApiResponseOrError<Self> {
350        let credentials_opt = request.credentials.clone();
351        let route = format!("organizations/users/{}", request.user_id);
352
353        anthropic_request_json(Method::POST, &route, |r| r.json(&request), credentials_opt).await
354    }
355
356    /// Creates a builder for removing a user.
357    ///
358    /// # Example
359    ///
360    /// ```no_run
361    /// # use anthropic_api::{admin::members::*, Credentials};
362    /// # #[tokio::main]
363    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
364    /// let credentials = Credentials::from_env();
365    ///
366    /// let removed_user = User::remove_builder("user_123456789")
367    ///     .credentials(credentials)
368    ///     .create()
369    ///     .await?;
370    /// # Ok(())
371    /// # }
372    /// ```
373    pub fn remove_builder(user_id: impl Into<String>) -> UserRemoveBuilder {
374        UserRemoveBuilder::create_empty().user_id(user_id)
375    }
376
377    /// Removes a user from the organization.
378    ///
379    /// # Example
380    ///
381    /// ```no_run
382    /// # use anthropic_api::{admin::members::*, Credentials};
383    /// # #[tokio::main]
384    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
385    /// let credentials = Credentials::from_env();
386    /// let request = UserRemoveRequest {
387    ///     user_id: "user_123456789".to_string(),
388    ///     credentials: Some(credentials),
389    /// };
390    ///
391    /// let removed_user = User::remove(request).await?;
392    /// # Ok(())
393    /// # }
394    /// ```
395    pub async fn remove(request: UserRemoveRequest) -> ApiResponseOrError<UserDeleted> {
396        let credentials_opt = request.credentials.clone();
397        let route = format!("organizations/users/{}", request.user_id);
398
399        anthropic_request_json(Method::DELETE, &route, |r| r, credentials_opt).await
400    }
401}
402
403// Builder convenience methods
404impl UserListBuilder {
405    /// Creates a new user list request and returns the response.
406    ///
407    /// This is a convenience method that builds the request from the builder
408    /// and sends it to the Users API.
409    ///
410    /// # Example
411    ///
412    /// ```no_run
413    /// # use anthropic_api::{admin::members::*, Credentials};
414    /// # #[tokio::main]
415    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
416    /// let credentials = Credentials::from_env();
417    ///
418    /// let users = UserList::builder()
419    ///     .credentials(credentials)
420    ///     .limit(10u32)
421    ///     .create()
422    ///     .await?;
423    /// # Ok(())
424    /// # }
425    /// ```
426    pub async fn create(self) -> ApiResponseOrError<UserList> {
427        let request = self.build().unwrap();
428        UserList::create(request).await
429    }
430}
431
432impl UserBuilder {
433    /// Creates a new user request and returns the response.
434    ///
435    /// This is a convenience method that builds the request from the builder
436    /// and sends it to the Users API.
437    ///
438    /// # Example
439    ///
440    /// ```no_run
441    /// # use anthropic_api::{admin::members::*, Credentials};
442    /// # #[tokio::main]
443    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
444    /// let credentials = Credentials::from_env();
445    ///
446    /// let user = User::builder("user_123456789")
447    ///     .credentials(credentials)
448    ///     .create()
449    ///     .await?;
450    /// # Ok(())
451    /// # }
452    /// ```
453    pub async fn create(self) -> ApiResponseOrError<User> {
454        let request = self.build().unwrap();
455        User::create(request).await
456    }
457}
458
459impl UserUpdateBuilder {
460    /// Creates a new user update request and returns the response.
461    ///
462    /// This is a convenience method that builds the request from the builder
463    /// and sends it to the Users API.
464    ///
465    /// # Example
466    ///
467    /// ```no_run
468    /// # use anthropic_api::{admin::members::*, Credentials};
469    /// # #[tokio::main]
470    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
471    /// let credentials = Credentials::from_env();
472    ///
473    /// let updated_user = User::update_builder("user_123456789")
474    ///     .credentials(credentials)
475    ///     .role(UserRole::Developer)
476    ///     .create()
477    ///     .await?;
478    /// # Ok(())
479    /// # }
480    /// ```
481    pub async fn create(self) -> ApiResponseOrError<User> {
482        let request = self.build().unwrap();
483        User::update(request).await
484    }
485}
486
487impl UserRemoveBuilder {
488    /// Creates a new user remove request and returns the response.
489    ///
490    /// This is a convenience method that builds the request from the builder
491    /// and sends it to the Users API.
492    ///
493    /// # Example
494    ///
495    /// ```no_run
496    /// # use anthropic_api::{admin::members::*, Credentials};
497    /// # #[tokio::main]
498    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
499    /// let credentials = Credentials::from_env();
500    ///
501    /// let removed_user = User::remove_builder("user_123456789")
502    ///     .credentials(credentials)
503    ///     .create()
504    ///     .await?;
505    /// # Ok(())
506    /// # }
507    /// ```
508    pub async fn create(self) -> ApiResponseOrError<UserDeleted> {
509        let request = self.build().unwrap();
510        User::remove(request).await
511    }
512}
513
514#[cfg(test)]
515mod tests {
516    use super::*;
517    use crate::Credentials;
518
519    #[tokio::test]
520    #[ignore] // Requires admin API key
521    async fn test_list_users() {
522        let credentials = Credentials::from_env();
523
524        let users = UserList::builder()
525            .credentials(credentials)
526            .create()
527            .await
528            .unwrap();
529
530        assert!(users.data.len() > 0);
531    }
532
533    #[tokio::test]
534    #[ignore] // Requires admin API key
535    async fn test_get_user() {
536        let credentials = Credentials::from_env();
537
538        // First get a user ID from the list
539        let users = UserList::builder()
540            .credentials(credentials.clone())
541            .create()
542            .await
543            .unwrap();
544
545        if let Some(user) = users.data.first() {
546            let user_id = &user.id;
547
548            // Then get that specific user
549            let user_details = User::builder(user_id)
550                .credentials(credentials)
551                .create()
552                .await
553                .unwrap();
554
555            assert_eq!(user_details.id, *user_id);
556        }
557    }
558}