headless_lms_models/
organizations.rs

1use std::path::PathBuf;
2
3use headless_lms_utils::{ApplicationConfiguration, file_store::FileStore};
4
5use crate::prelude::*;
6
7#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
8pub struct DatabaseOrganization {
9    pub id: Uuid,
10    pub slug: String,
11    pub created_at: DateTime<Utc>,
12    pub updated_at: DateTime<Utc>,
13    pub name: String,
14    pub description: Option<String>,
15    pub organization_image_path: Option<String>,
16    pub deleted_at: Option<DateTime<Utc>>,
17}
18
19#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
20#[cfg_attr(feature = "ts_rs", derive(TS))]
21pub struct Organization {
22    pub id: Uuid,
23    pub slug: String,
24    pub created_at: DateTime<Utc>,
25    pub updated_at: DateTime<Utc>,
26    pub name: String,
27    pub description: Option<String>,
28    pub organization_image_url: Option<String>,
29    pub deleted_at: Option<DateTime<Utc>>,
30}
31
32impl Organization {
33    pub fn from_database_organization(
34        organization: DatabaseOrganization,
35        file_store: &dyn FileStore,
36        app_conf: &ApplicationConfiguration,
37    ) -> Self {
38        let organization_image_url = organization.organization_image_path.as_ref().map(|image| {
39            let path = PathBuf::from(image);
40            file_store.get_download_url(path.as_path(), app_conf)
41        });
42        Self {
43            id: organization.id,
44            created_at: organization.created_at,
45            updated_at: organization.updated_at,
46            name: organization.name,
47            slug: organization.slug,
48            deleted_at: organization.deleted_at,
49            organization_image_url,
50            description: organization.description,
51        }
52    }
53}
54
55pub async fn insert(
56    conn: &mut PgConnection,
57    pkey_policy: PKeyPolicy<Uuid>,
58    name: &str,
59    slug: &str,
60    description: &str,
61) -> ModelResult<Uuid> {
62    let res = sqlx::query!(
63        "
64INSERT INTO organizations (id, name, slug, description)
65VALUES ($1, $2, $3, $4)
66RETURNING id
67",
68        pkey_policy.into_uuid(),
69        name,
70        slug,
71        description
72    )
73    .fetch_one(conn)
74    .await?;
75    Ok(res.id)
76}
77
78pub async fn all_organizations(conn: &mut PgConnection) -> ModelResult<Vec<DatabaseOrganization>> {
79    let organizations = sqlx::query_as!(
80        DatabaseOrganization,
81        "SELECT * FROM organizations WHERE deleted_at IS NULL ORDER BY name;"
82    )
83    .fetch_all(conn)
84    .await?;
85    Ok(organizations)
86}
87
88pub async fn get_organization(
89    conn: &mut PgConnection,
90    organization_id: Uuid,
91) -> ModelResult<DatabaseOrganization> {
92    let org = sqlx::query_as!(
93        DatabaseOrganization,
94        "
95SELECT *
96from organizations
97where id = $1;",
98        organization_id,
99    )
100    .fetch_one(conn)
101    .await?;
102    Ok(org)
103}
104
105pub async fn get_organization_by_slug(
106    conn: &mut PgConnection,
107    organization_slug: &str,
108) -> ModelResult<DatabaseOrganization> {
109    let organization = sqlx::query_as!(
110        DatabaseOrganization,
111        "
112SELECT *
113FROM organizations
114WHERE slug = $1;
115        ",
116        organization_slug
117    )
118    .fetch_one(conn)
119    .await?;
120    Ok(organization)
121}
122
123pub async fn update_organization_image_path(
124    conn: &mut PgConnection,
125    organization_id: Uuid,
126    organization_image_path: Option<String>,
127) -> ModelResult<DatabaseOrganization> {
128    let updated_organization = sqlx::query_as!(
129        DatabaseOrganization,
130        "
131UPDATE organizations
132SET organization_image_path = $1
133WHERE id = $2
134RETURNING *;",
135        organization_image_path,
136        organization_id
137    )
138    .fetch_one(conn)
139    .await?;
140    Ok(updated_organization)
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use crate::test_helper::Conn;
147
148    #[tokio::test]
149    async fn gets_organizations() {
150        let mut conn = Conn::init().await;
151        let mut tx = conn.begin().await;
152        let orgs_before = all_organizations(tx.as_mut()).await.unwrap();
153        insert(
154            tx.as_mut(),
155            PKeyPolicy::Fixed(Uuid::parse_str("8c34e601-b5db-4b33-a588-57cb6a5b1669").unwrap()),
156            "org",
157            "slug",
158            "description",
159        )
160        .await
161        .unwrap();
162        let orgs_after = all_organizations(tx.as_mut()).await.unwrap();
163        assert_eq!(orgs_before.len() + 1, orgs_after.len());
164    }
165}