use crate::{FilesClient, PaginationInfo, Result};
use futures::stream::Stream;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserEntity {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_home: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_root: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub site_admin: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub readonly_site_admin: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled_expired_or_inactive: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ssl_required: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_zone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allowed_ips: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bypass_site_allowed_ips: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_ids: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub admin_group_ids: Option<Vec<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ftp_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sftp_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dav_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub restapi_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub require_2fa: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active_2fa: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_login_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password_set_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password_validity_days: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_keys_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_keys_count: Option<i64>,
}
#[derive(Debug, Clone)]
pub struct UserHandler {
client: FilesClient,
}
impl UserHandler {
pub fn new(client: FilesClient) -> Self {
Self { client }
}
pub async fn list(
&self,
cursor: Option<String>,
per_page: Option<i32>,
) -> Result<(Vec<UserEntity>, PaginationInfo)> {
let mut path = "/users?".to_string();
if let Some(c) = cursor {
path.push_str(&format!("cursor={}&", c));
}
if let Some(pp) = per_page {
path.push_str(&format!("per_page={}&", pp));
}
let response = self.client.get_raw(&path).await?;
let users: Vec<UserEntity> = serde_json::from_value(response)?;
let pagination = PaginationInfo {
cursor_next: None,
cursor_prev: None,
};
Ok((users, pagination))
}
pub fn list_stream(
&self,
per_page: Option<i32>,
) -> impl Stream<Item = Result<UserEntity>> + '_ {
let per_page = per_page.unwrap_or(1000);
async_stream::try_stream! {
let mut cursor: Option<String> = None;
loop {
let (users, pagination) = self
.list(cursor.clone(), Some(per_page))
.await?;
for user in users {
yield user;
}
match pagination.cursor_next {
Some(next) => cursor = Some(next),
None => break,
}
}
}
}
pub async fn get(&self, id: i64) -> Result<UserEntity> {
let path = format!("/users/{}", id);
let response = self.client.get_raw(&path).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn create(
&self,
username: &str,
email: Option<&str>,
password: Option<&str>,
name: Option<&str>,
) -> Result<UserEntity> {
let mut body = json!({
"username": username,
});
if let Some(e) = email {
body["email"] = json!(e);
}
if let Some(p) = password {
body["password"] = json!(p);
}
if let Some(n) = name {
body["name"] = json!(n);
}
let response = self.client.post_raw("/users", body).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn update(
&self,
id: i64,
email: Option<&str>,
name: Option<&str>,
company: Option<&str>,
notes: Option<&str>,
) -> Result<UserEntity> {
let mut body = json!({});
if let Some(e) = email {
body["email"] = json!(e);
}
if let Some(n) = name {
body["name"] = json!(n);
}
if let Some(c) = company {
body["company"] = json!(c);
}
if let Some(nt) = notes {
body["notes"] = json!(nt);
}
let path = format!("/users/{}", id);
let response = self.client.patch_raw(&path, body).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete(&self, id: i64) -> Result<()> {
let path = format!("/users/{}", id);
self.client.delete_raw(&path).await?;
Ok(())
}
pub async fn unlock(&self, id: i64) -> Result<()> {
let path = format!("/users/{}/unlock", id);
self.client.post_raw(&path, json!({})).await?;
Ok(())
}
pub async fn reset_2fa(&self, id: i64) -> Result<()> {
let path = format!("/users/{}/2fa/reset", id);
self.client.post_raw(&path, json!({})).await?;
Ok(())
}
pub async fn resend_welcome_email(&self, id: i64) -> Result<()> {
let path = format!("/users/{}/resend_welcome_email", id);
self.client.post_raw(&path, json!({})).await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handler_creation() {
let client = FilesClient::builder().api_key("test-key").build().unwrap();
let _handler = UserHandler::new(client);
}
}