Skip to main content

resourcespace_client/api/
user.rs

1use serde::Serialize;
2use serde_with::json::JsonString;
3use serde_with::{serde_as, skip_serializing_none};
4
5use crate::client::Client;
6use crate::error::RsError;
7
8use super::List;
9
10#[derive(Debug)]
11pub struct UserApi<'a> {
12    client: &'a Client,
13}
14
15/// Sub-API for user endpoints.
16impl<'a> UserApi<'a> {
17    pub(crate) fn new(client: &'a Client) -> Self {
18        Self { client }
19    }
20
21    /// Find out if the current user has a particular permission. The permission strings are shown in the ResourceSpace UI when managing group permissions.
22    ///
23    /// ## Arguments
24    /// * `request` - Parameters built via [`CheckpermRequest`]
25    ///
26    /// ## Returns
27    ///
28    /// TRUE if the user has the permission, FALSE if they don't.
29    ///
30    /// ## TODO: Errors
31    ///
32    /// ## TODO: Examples
33    pub async fn checkperm(&self, request: CheckpermRequest) -> Result<serde_json::Value, RsError> {
34        self.client
35            .send_request("checkperm", reqwest::Method::GET, request)
36            .await
37    }
38
39    /// Retrieve a list of users
40    ///
41    /// Permissions are always honoured so users from other groups to which this user does not have access will be omitted.
42    ///
43    /// ## Arguments
44    /// * `request` - Parameters built via [`GetUsersRequest`]
45    ///
46    /// ## Returns
47    ///
48    /// An array of matching user records include ID ("ref"), username, full name and user group ID.
49    ///
50    /// ## TODO: Errors
51    ///
52    /// ## TODO: Examples
53    pub async fn get_users(&self, request: GetUsersRequest) -> Result<serde_json::Value, RsError> {
54        self.client
55            .send_request("get_users", reqwest::Method::GET, request)
56            .await
57    }
58
59    /// Retrieve information on all users with the given permissions
60    ///
61    /// Permissions are always honoured so users from groups to which this user does not have access will be omitted.
62    ///
63    /// ## Arguments
64    /// * `request` - Parameters built via [`GetUsersByPermissionRequest`]
65    ///
66    /// ## Returns
67    ///
68    /// An array of matching user records with a subset of information from the user record
69    ///
70    /// ## TODO: Errors
71    ///
72    /// ## TODO: Examples
73    pub async fn get_users_by_permission(
74        &self,
75        request: GetUsersByPermissionRequest,
76    ) -> Result<serde_json::Value, RsError> {
77        self.client
78            .send_request("get_users_by_permission", reqwest::Method::GET, request)
79            .await
80    }
81
82    /// Mark a specified email address as invalid.
83    ///
84    /// Email addresses marked as invalid will be blocked before send_mail() tries to dispatch any emails, this will be applied to any users with this email address.
85    ///
86    /// ## Arguments
87    /// * `request` - Parameters built via [`GetUsersByPermissionRequest`]
88    ///
89    /// ## Returns
90    ///
91    /// Boolean - true if one or more users are found and mark as having invalid adresses, false otherwise.
92    ///
93    /// ## TODO: Errors
94    ///
95    /// ## TODO: Examples
96    pub async fn mark_email_as_invalid(
97        &self,
98        request: MarkEmailAsInvalidRequest,
99    ) -> Result<serde_json::Value, RsError> {
100        self.client
101            .send_request("mark_email_as_invalid", reqwest::Method::POST, request)
102            .await
103    }
104
105    /// Save a user record.
106    ///
107    /// Use [`new_user`](Self::new_user) first to create the user, then call this with the
108    /// returned ID to populate the user's details.
109    ///
110    /// ## Arguments
111    /// * `request` - Parameters built via [`SaveUserRequest`]
112    ///
113    /// ## Returns
114    ///
115    /// Returns `Ok(())` on success (HTTP 200). Returns an error on HTTP 409 (e.g. missing
116    /// required fields) or HTTP 403 (permission denied).
117    ///
118    /// ## TODO: Errors
119    ///
120    /// ## TODO: Examples
121    pub async fn save_user(&self, request: SaveUserRequest) -> Result<serde_json::Value, RsError> {
122        self.client
123            .send_request("save_user", reqwest::Method::POST, request)
124            .await
125    }
126
127    /// Create a new user record.
128    ///
129    /// Create a user record. Use the returned ID to then call save_user() with the user details.
130    ///
131    /// ## Arguments
132    /// * `request` - Parameters built via [`NewUserRequest`]
133    ///
134    /// ## Returns
135    ///
136    /// The new user ID in `data.ref` on success (HTTP 200).
137    /// HTTP 409 if the username already exists (`data.ref = false`) or the user limit has
138    /// been reached (`data.ref = -2`). HTTP 403 on permission failure.
139    ///
140    /// ## TODO: Errors
141    ///
142    /// ## TODO: Examples
143    pub async fn new_user(&self, request: NewUserRequest) -> Result<serde_json::Value, RsError> {
144        self.client
145            .send_request("new_user", reqwest::Method::POST, request)
146            .await
147    }
148}
149
150#[derive(Clone, Debug, PartialEq, Serialize)]
151pub struct CheckpermRequest {
152    /// The permission string to check (e.g. `"a"` for admin, `"e"` for edit).
153    pub perm: String,
154}
155
156impl CheckpermRequest {
157    pub fn new(perm: impl Into<String>) -> Self {
158        Self { perm: perm.into() }
159    }
160}
161
162#[skip_serializing_none]
163#[derive(Clone, Debug, Default, PartialEq, Serialize)]
164pub struct GetUsersRequest {
165    /// Search string to filter users by name or username.
166    pub find: Option<String>,
167    /// If set, only returns users whose username exactly matches `find`.
168    pub exact_username_match: Option<bool>,
169}
170
171impl GetUsersRequest {
172    pub fn new() -> Self {
173        Self::default()
174    }
175
176    pub fn find(mut self, find: impl Into<String>) -> Self {
177        self.find = Some(find.into());
178        self
179    }
180
181    pub fn exact_username_match(mut self, exact_username_match: bool) -> Self {
182        self.exact_username_match = Some(exact_username_match);
183        self
184    }
185}
186
187#[derive(Clone, Debug, PartialEq, Serialize)]
188pub struct GetUsersByPermissionRequest {
189    /// List of permission strings; only users holding all of these are returned.
190    pub permissions: List<String>,
191}
192
193impl GetUsersByPermissionRequest {
194    pub fn new(permissions: impl Into<List<String>>) -> Self {
195        Self {
196            permissions: permissions.into(),
197        }
198    }
199}
200
201#[derive(Clone, Debug, PartialEq, Serialize)]
202pub struct MarkEmailAsInvalidRequest {
203    /// The email address to mark as invalid.
204    pub email: String,
205}
206
207impl MarkEmailAsInvalidRequest {
208    pub fn new(email: impl Into<String>) -> Self {
209        Self {
210            email: email.into(),
211        }
212    }
213}
214
215#[skip_serializing_none]
216#[derive(Clone, Debug, PartialEq, Serialize)]
217pub struct NewUserRequest {
218    /// The username for the new user account.
219    pub username: String,
220    /// The ID of the user group to assign this user to.
221    pub usergroup: Option<u32>,
222}
223
224impl NewUserRequest {
225    pub fn new(username: impl Into<String>) -> Self {
226        Self {
227            username: username.into(),
228            usergroup: None,
229        }
230    }
231
232    pub fn usergroup(mut self, usergroup: u32) -> Self {
233        self.usergroup = Some(usergroup);
234        self
235    }
236}
237
238#[serde_as]
239#[derive(Clone, Debug, PartialEq, Serialize)]
240pub struct SaveUserRequest {
241    /// The ID of the user to update.
242    #[serde(rename = "ref")]
243    pub r#ref: u32,
244    /// JSON object containing the user fields to save (e.g. fullname, email, usergroup).
245    #[serde_as(as = "JsonString")]
246    pub data: SaveUserData,
247}
248
249impl SaveUserRequest {
250    pub fn new(r#ref: u32, data: SaveUserData) -> Self {
251        Self { r#ref, data }
252    }
253}
254
255#[skip_serializing_none]
256#[derive(Clone, Debug, PartialEq, Serialize)]
257pub struct SaveUserData {
258    /// Username used to log into the account.
259    pub username: Option<String>,
260    /// Password for the account in plain text.
261    pub password: Option<String>,
262    /// Full display name of the user.
263    pub fullname: Option<String>,
264    /// Email address associated with the account.
265    pub email: Option<String>,
266    /// ID of the user group to assign the user to.
267    pub usergroup: Option<u32>,
268    /// Optional IP restriction for the account. Can contain a single IP, or a wildcard pattern.
269    pub ip_restrict: Option<String>,
270    // pub search_filter_override: ?
271    // pub search_filter_o_id: ?
272    /// Administrative comments or notes about the user.
273    pub comments: Option<String>,
274    /// Whether the user should receive content suggestions.
275    pub suggest: Option<bool>,
276    /// Whether to send the user a password reset link by email instead of setting a password directly.
277    pub emailresetlink: Option<bool>,
278    /// Approval state of the account.
279    pub approved: Option<u8>,
280    /// Account expiry date in `YYYY-MM-DD` format, e.g. `"2026-12-31"`.
281    pub expires: Option<String>,
282}