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}