autter_core/database/
organizations.rs

1use std::collections::HashMap;
2
3use crate::{
4    DataManager,
5    model::{
6        Error, Result, User, UserPermission,
7        organizations::{Organization, OrganizationMembership, OrganizationRole},
8    },
9};
10use oiseau::{PostgresRow, cache::Cache, execute, get, params, query_rows};
11use tetratto_core::auto_method;
12
13impl DataManager {
14    /// Get a [`Organization`] from an SQL row.
15    pub(crate) fn get_organization_from_row(x: &PostgresRow) -> Organization {
16        Organization {
17            id: get!(x->0(i64)) as usize,
18            created: get!(x->1(i64)) as usize,
19            owner: get!(x->2(i64)) as usize,
20            name: get!(x->3(String)),
21            members: get!(x->4(i32)),
22            roles: serde_json::from_str(&get!(x->5(String))).unwrap(),
23        }
24    }
25
26    auto_method!(get_organization_by_id(usize as i64)@get_organization_from_row -> "SELECT * FROM a_organizations WHERE id = $1" --name="organization" --returns=Organization --cache-key-tmpl="srmp.organization:{}");
27    auto_method!(get_organization_by_name(&str)@get_organization_from_row -> "SELECT * FROM a_organizations WHERE name = $1" --name="organization" --returns=Organization);
28
29    /// Get all organizations by user.
30    ///
31    /// # Arguments
32    /// * `user` - the ID of the user to fetch organizations for
33    pub async fn get_organizations_by_user(&self, user: usize) -> Result<Vec<Organization>> {
34        let conn = match self.0.connect().await {
35            Ok(c) => c,
36            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
37        };
38
39        let res = query_rows!(
40            &conn,
41            "SELECT * FROM a_organizations WHERE owner = $1 ORDER BY created",
42            &[&(user as i64)],
43            |x| { Self::get_organization_from_row(x) }
44        );
45
46        if res.is_err() {
47            return Err(Error::GeneralNotFound("organization".to_string()));
48        }
49
50        Ok(res.unwrap())
51    }
52
53    /// Create a new organization in the database.
54    ///
55    /// # Arguments
56    /// * `data` - a mock [`Organization`] object to insert
57    pub async fn create_organization(&self, data: Organization) -> Result<usize> {
58        if self.get_organization_by_name(&data.name).await.is_ok() {
59            return Err(Error::MiscError("Name already in use".to_string()));
60        }
61
62        // ...
63        let conn = match self.0.connect().await {
64            Ok(c) => c,
65            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
66        };
67
68        let res = execute!(
69            &conn,
70            "INSERT INTO a_organizations VALUES ($1, $2, $3, $4, $5, $6)",
71            params![
72                &(data.id as i64),
73                &(data.created as i64),
74                &(data.owner as i64),
75                &data.name,
76                &data.members,
77                &serde_json::to_string(&data.roles).unwrap()
78            ]
79        );
80
81        if let Err(e) = res {
82            return Err(Error::DatabaseError(e.to_string()));
83        }
84
85        // add membership
86        let mut membership = OrganizationMembership::new(data.id, data.owner);
87        membership.role = OrganizationRole::Owner;
88        self.create_organization_membership(membership).await?;
89
90        // return
91        Ok(data.id)
92    }
93
94    pub async fn delete_organization(&self, id: usize, user: User) -> Result<()> {
95        let organization = self.get_organization_by_id(id).await?;
96
97        if user.id != organization.owner
98            && !user
99                .permissions
100                .contains(&UserPermission::ManageOrganizations)
101        {
102            return Err(Error::NotAllowed);
103        }
104
105        if let Ok(u) = self.1.get_upload_by_id(organization.id).await {
106            self.1
107                .delete_upload(u.id)
108                .await
109                .expect("failed to delete organization avatar");
110        }
111
112        let conn = match self.0.connect().await {
113            Ok(c) => c,
114            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
115        };
116
117        let res = execute!(
118            &conn,
119            "DELETE FROM a_organizations WHERE id = $1",
120            &[&(id as i64)]
121        );
122
123        if let Err(e) = res {
124            return Err(Error::DatabaseError(e.to_string()));
125        }
126
127        self.0.1.remove(format!("srmp.organization:{}", id)).await;
128
129        // return
130        Ok(())
131    }
132
133    auto_method!(update_organization_name(&str) -> "UPDATE a_organizations SET name = $1 WHERE id = $2" --cache-key-tmpl="srmp.organization:{}");
134    auto_method!(update_organization_roles(HashMap<usize, String>) -> "UPDATE a_organizations SET roles = $1 WHERE id = $2" --serde --cache-key-tmpl="srmp.organization:{}");
135
136    auto_method!(incr_organization_members() -> "UPDATE a_organizations SET members = members + 1 WHERE id = $2" --cache-key-tmpl="srmp.organization:{}" --incr);
137    auto_method!(decr_organization_members() -> "UPDATE a_organizations SET members = members + 1 WHERE id = $2" --cache-key-tmpl="srmp.organization:{}" --decr);
138}