mod meta;
mod verbose_status;
pub use meta::AccountMetaKey;
pub use verbose_status::{FlagResponse, VerboseStatus};
use super::{
account_type::AccountType, guest_user::GuestUser, tag::Tag, user::User,
};
use crate::domain::{actors::SystemActor, dtos::written_by::WrittenBy};
use chrono::{DateTime, Local};
use mycelium_base::dtos::Children;
use serde::{Deserialize, Serialize};
use slugify::slugify;
use std::collections::HashMap;
use utoipa::{ToResponse, ToSchema};
use uuid::Uuid;
pub type AccountMeta = HashMap<AccountMetaKey, String>;
#[derive(
Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema, ToResponse,
)]
#[serde(rename_all = "camelCase")]
pub struct Account {
pub id: Option<Uuid>,
pub name: String,
pub slug: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<Tag>>,
pub is_active: bool,
pub is_checked: bool,
pub is_archived: bool,
pub is_deleted: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub verbose_status: Option<VerboseStatus>,
pub is_system_account: bool,
pub owners: Children<User, Uuid>,
pub account_type: AccountType,
#[serde(skip_serializing_if = "Option::is_none")]
pub guest_users: Option<Children<GuestUser, Uuid>>,
#[serde(alias = "created")]
pub created_at: DateTime<Local>,
pub created_by: Option<WrittenBy>,
#[serde(alias = "updated", skip_serializing_if = "Option::is_none")]
pub updated_at: Option<DateTime<Local>>,
pub updated_by: Option<WrittenBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<HashMap<AccountMetaKey, String>>,
}
impl Default for Account {
fn default() -> Self {
Self {
id: None,
name: String::new(),
slug: String::new(),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account: false,
owners: Children::Ids([].to_vec()),
account_type: AccountType::User,
guest_users: None,
created_at: Local::now(),
created_by: None,
updated_at: None,
updated_by: None,
meta: None,
}
}
}
impl Account {
pub fn new_subscription_account(
account_name: String,
tenant_id: Uuid,
created_by: Option<WrittenBy>,
) -> Self {
Self {
id: None,
name: account_name.to_owned(),
slug: slugify!(account_name.as_str()),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account: false,
owners: Children::Ids([].to_vec()),
account_type: AccountType::Subscription { tenant_id },
guest_users: None,
created_at: Local::now(),
created_by,
updated_at: None,
updated_by: None,
meta: None,
}
}
pub fn new_role_related_account<T: ToString>(
account_name: String,
tenant_id: Uuid,
read_role_id: Uuid,
write_role_id: Uuid,
role_name: T,
is_system_account: bool,
created_by: Option<WrittenBy>,
) -> Self {
Self {
id: None,
name: account_name.to_owned(),
slug: slugify!(account_name.as_str()),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account,
owners: Children::Ids([].to_vec()),
account_type: AccountType::RoleAssociated {
tenant_id,
role_name: role_name.to_string(),
read_role_id,
write_role_id,
},
guest_users: None,
created_at: Local::now(),
created_by,
updated_at: None,
updated_by: None,
meta: None,
}
}
pub fn new_actor_related_account(
name: String,
actor: SystemActor,
is_system_account: bool,
created_by: Option<WrittenBy>,
) -> Self {
Self {
id: None,
name: name.to_owned(),
slug: slugify!(name.as_str()),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account,
owners: Children::Ids([].to_vec()),
account_type: AccountType::ActorAssociated { actor },
guest_users: None,
created_at: Local::now(),
created_by,
updated_at: None,
updated_by: None,
meta: None,
}
}
pub fn new_tenant_management_account(
account_name: String,
tenant_id: Uuid,
created_by: Option<WrittenBy>,
) -> Self {
Self {
id: None,
name: account_name.to_owned(),
slug: slugify!(account_name.as_str()),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account: false,
owners: Children::Ids([].to_vec()),
account_type: AccountType::TenantManager { tenant_id },
guest_users: None,
created_at: Local::now(),
created_by,
updated_at: None,
updated_by: None,
meta: None,
}
}
pub fn with_id(&mut self) -> Self {
self.id = Some(Uuid::new_v4());
self.clone()
}
pub fn new(
account_name: String,
principal_owner: User,
account_type: AccountType,
created_by: Option<WrittenBy>,
) -> Self {
Self {
id: None,
name: account_name.to_owned(),
slug: slugify!(account_name.as_str()),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account: false,
owners: Children::Records([principal_owner].to_vec()),
account_type,
guest_users: None,
created_at: Local::now(),
created_by,
updated_at: None,
updated_by: None,
meta: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::dtos::email::Email;
use chrono::Local;
use mycelium_base::dtos::Parent;
#[test]
fn test_if_account_works() {
let account = Account {
id: None,
name: String::from("Account Name"),
slug: String::from("account-name"),
tags: None,
is_active: true,
is_checked: false,
is_archived: false,
is_deleted: false,
verbose_status: None,
is_system_account: false,
owners: Children::Records([].to_vec()),
account_type: AccountType::User,
guest_users: None,
created_at: Local::now(),
created_by: None,
updated_at: Some(Local::now()),
updated_by: None,
meta: None,
};
User::new(
None,
"username".to_string(),
Email::from_string("username@email.domain".to_string()).unwrap(),
Some("first_name".to_string()),
Some("last_name".to_string()),
true,
Local::now(),
Some(Local::now()),
Some(Parent::Record(account)),
None,
)
.with_principal(false);
}
#[test]
fn test_if_verbose_status_works() {
[
((false, true, true, false), VerboseStatus::Inactive),
((false, false, true, false), VerboseStatus::Inactive),
((false, true, false, false), VerboseStatus::Inactive),
((false, false, false, false), VerboseStatus::Inactive),
((true, false, false, false), VerboseStatus::Unverified),
((true, false, true, false), VerboseStatus::Unverified),
((true, true, true, false), VerboseStatus::Archived),
((true, true, false, false), VerboseStatus::Verified),
((true, true, true, true), VerboseStatus::Deleted),
((false, true, true, false), VerboseStatus::Unknown),
((false, false, true, false), VerboseStatus::Unknown),
((false, true, false, false), VerboseStatus::Unknown),
((false, false, false, false), VerboseStatus::Unknown),
((true, false, false, false), VerboseStatus::Unknown),
((true, false, true, false), VerboseStatus::Unknown),
((true, true, true, false), VerboseStatus::Unknown),
((true, true, false, false), VerboseStatus::Unknown),
]
.into_iter()
.for_each(|(flags, expected_value)| {
let (is_active, is_checked, is_archived, is_deleted) = flags;
let status = VerboseStatus::from_flags(
is_active,
is_checked,
is_archived,
is_deleted,
);
if let VerboseStatus::Unknown = expected_value {
assert_ne!(status, expected_value);
} else {
assert_eq!(status, expected_value);
}
let flags_response = status.to_flags().unwrap();
match expected_value {
VerboseStatus::Inactive => {
assert_eq!(
VerboseStatus::from_flags(
flags_response.is_active.unwrap(),
flags_response.is_checked.unwrap_or(is_checked),
flags_response.is_archived.unwrap_or(is_archived),
flags_response.is_deleted.unwrap_or(is_deleted)
),
expected_value
);
}
VerboseStatus::Unverified => {
assert_eq!(
VerboseStatus::from_flags(
flags_response.is_active.unwrap(),
flags_response.is_checked.unwrap(),
flags_response.is_archived.unwrap_or(is_archived),
flags_response.is_deleted.unwrap_or(is_deleted)
),
expected_value
);
}
VerboseStatus::Archived => {
assert_eq!(
VerboseStatus::from_flags(
flags_response.is_active.unwrap(),
flags_response.is_checked.unwrap(),
flags_response.is_archived.unwrap(),
flags_response.is_deleted.unwrap_or(is_deleted)
),
expected_value
);
}
VerboseStatus::Verified => {
assert_eq!(
VerboseStatus::from_flags(
flags_response.is_active.unwrap(),
flags_response.is_checked.unwrap(),
flags_response.is_archived.unwrap(),
flags_response.is_deleted.unwrap_or(is_deleted)
),
expected_value
);
}
VerboseStatus::Deleted => {
assert_eq!(
VerboseStatus::from_flags(
flags_response.is_active.unwrap(),
flags_response.is_checked.unwrap(),
flags_response.is_archived.unwrap_or(is_archived),
flags_response.is_deleted.unwrap(),
),
expected_value
);
}
VerboseStatus::Unknown => (),
};
});
}
}