use crate::client::Client;
use crate::errors::Error;
use crate::insert_optional;
use crate::token::{token_request_attributes, CreateTokenRequest, Token, TokenResponse};
use crate::KeygenResponseData;
use serde::{Deserialize, Serialize, Serializer};
use std::collections::HashMap;
fn serialize_metadata_filter<S>(
metadata: &Option<HashMap<String, serde_json::Value>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeMap;
match metadata {
Some(map) => {
let mut ser_map = serializer.serialize_map(Some(map.len()))?;
for (key, value) in map {
ser_map.serialize_entry(&format!("metadata[{key}]"), value)?;
}
ser_map.end()
}
None => serializer.serialize_none(),
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum UserStatus {
Active,
Inactive,
Banned,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum UserRole {
Admin,
Developer,
Environment,
Product,
License,
#[serde(rename = "sales-agent")]
SalesAgent,
#[serde(rename = "support-agent")]
SupportAgent,
#[serde(rename = "read-only")]
ReadOnly,
User,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserAttributes {
pub email: String,
#[serde(rename = "firstName")]
pub first_name: Option<String>,
#[serde(rename = "lastName")]
pub last_name: Option<String>,
#[serde(rename = "fullName")]
pub full_name: Option<String>,
pub status: UserStatus,
pub role: UserRole,
pub permissions: Option<Vec<String>>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
#[serde(rename = "lastSeenAt")]
pub last_seen_at: Option<String>,
#[serde(rename = "banReason")]
pub ban_reason: Option<String>,
pub created: String,
pub updated: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub email: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub full_name: Option<String>,
pub status: UserStatus,
pub role: UserRole,
pub permissions: Option<Vec<String>>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
pub last_seen_at: Option<String>,
pub ban_reason: Option<String>,
pub created: String,
pub updated: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct UserResponse {
pub data: KeygenResponseData<UserAttributes>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct UsersResponse {
pub data: Vec<KeygenResponseData<UserAttributes>>,
pub meta: Option<serde_json::Value>,
pub links: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsersListResult {
pub users: Vec<User>,
pub meta: Option<serde_json::Value>,
pub links: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateUserRequest {
pub email: String,
#[serde(rename = "firstName")]
pub first_name: Option<String>,
#[serde(rename = "lastName")]
pub last_name: Option<String>,
pub role: Option<UserRole>,
pub permissions: Option<Vec<String>>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateUserRequest {
pub email: Option<String>,
#[serde(rename = "firstName")]
pub first_name: Option<String>,
#[serde(rename = "lastName")]
pub last_name: Option<String>,
pub role: Option<UserRole>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UpdatePasswordRequest {
#[serde(rename = "oldPassword")]
pub old_password: Option<String>,
#[serde(rename = "newPassword")]
pub new_password: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResetPasswordRequest {
#[serde(rename = "passwordResetToken")]
pub password_reset_token: String,
#[serde(rename = "newPassword")]
pub new_password: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListUsersOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(rename = "page[size]", skip_serializing_if = "Option::is_none")]
pub page_size: Option<u32>,
#[serde(rename = "page[number]", skip_serializing_if = "Option::is_none")]
pub page_number: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<UserStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assigned: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub product: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub roles: Option<Vec<UserRole>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include: Option<String>,
#[serde(
flatten,
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_metadata_filter"
)]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
impl User {
pub(crate) fn from(data: KeygenResponseData<UserAttributes>) -> User {
User {
id: data.id,
email: data.attributes.email,
first_name: data.attributes.first_name,
last_name: data.attributes.last_name,
full_name: data.attributes.full_name,
status: data.attributes.status,
role: data.attributes.role,
permissions: data.attributes.permissions,
metadata: data.attributes.metadata,
last_seen_at: data.attributes.last_seen_at,
ban_reason: data.attributes.ban_reason,
created: data.attributes.created,
updated: data.attributes.updated,
}
}
pub async fn create(request: CreateUserRequest) -> Result<User, Error> {
let client = Client::from_global_config()?;
let mut attributes = serde_json::Map::new();
attributes.insert("email".to_string(), serde_json::json!(request.email));
insert_optional(&mut attributes, "firstName", request.first_name)?;
insert_optional(&mut attributes, "lastName", request.last_name)?;
insert_optional(&mut attributes, "role", request.role)?;
insert_optional(&mut attributes, "permissions", request.permissions)?;
insert_optional(&mut attributes, "metadata", request.metadata)?;
let body = serde_json::json!({
"data": {
"type": "users",
"attributes": attributes
}
});
let response = client.post("users", Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn list(options: Option<ListUsersOptions>) -> Result<UsersListResult, Error> {
let client = Client::from_global_config()?;
let response = client.get("users", options.as_ref()).await?;
let users_response: UsersResponse = serde_json::from_value(response.body)?;
Ok(UsersListResult {
users: users_response.data.into_iter().map(User::from).collect(),
meta: users_response.meta,
links: users_response.links,
})
}
pub async fn get(user_id: &str) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{user_id}");
let response = client.get(&endpoint, None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn update(&self, request: UpdateUserRequest) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}", self.id);
let mut attributes = serde_json::Map::new();
insert_optional(&mut attributes, "email", request.email)?;
insert_optional(&mut attributes, "firstName", request.first_name)?;
insert_optional(&mut attributes, "lastName", request.last_name)?;
insert_optional(&mut attributes, "role", request.role)?;
insert_optional(&mut attributes, "metadata", request.metadata)?;
let body = serde_json::json!({
"data": {
"type": "users",
"id": self.id,
"attributes": attributes
}
});
let response = client.patch(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn delete(&self) -> Result<(), Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}", self.id);
client.delete::<(), ()>(&endpoint, None::<&()>).await?;
Ok(())
}
pub async fn ban(&self) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/actions/ban", self.id);
let body = serde_json::json!({ "meta": {} });
let response = client.post(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn unban(&self) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/actions/unban", self.id);
let body = serde_json::json!({ "meta": {} });
let response = client.post(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn generate_token(
&self,
request: Option<CreateTokenRequest>,
) -> Result<Token, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/tokens", self.id);
let attributes = token_request_attributes(request.as_ref())?;
let body = serde_json::json!({
"data": {
"type": "tokens",
"attributes": attributes
}
});
let response = client.post(&endpoint, Some(&body), None::<&()>).await?;
let token_response: TokenResponse = serde_json::from_value(response.body)?;
Ok(Token::from(token_response.data))
}
pub async fn change_group(&self, group_id: &str) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/group", self.id);
let body = serde_json::json!({
"data": {
"type": "groups",
"id": group_id
}
});
let response = client.put(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn update_password(&self, request: UpdatePasswordRequest) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/actions/update-password", self.id);
let mut meta = serde_json::Map::new();
insert_optional(&mut meta, "oldPassword", request.old_password)?;
meta.insert(
"newPassword".to_string(),
serde_json::Value::String(request.new_password),
);
let body = serde_json::json!({ "meta": meta });
let response = client.post(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
pub async fn reset_password(&self, request: ResetPasswordRequest) -> Result<User, Error> {
let client = Client::from_global_config()?;
let endpoint = format!("users/{}/actions/reset-password", self.id);
let body = serde_json::json!({
"meta": {
"passwordResetToken": request.password_reset_token,
"newPassword": request.new_password
}
});
let response = client.post(&endpoint, Some(&body), None::<&()>).await?;
let user_response: UserResponse = serde_json::from_value(response.body)?;
Ok(User::from(user_response.data))
}
}