tetratto_core/database/
requests.rs

1use oiseau::cache::Cache;
2use crate::model::requests::ActionType;
3use crate::model::{Error, Result, requests::ActionRequest, auth::User, permissions::FinePermission};
4use crate::DataManager;
5
6use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
7
8impl DataManager {
9    /// Get an [`ActionRequest`] from an SQL row.
10    pub(crate) fn get_request_from_row(x: &PostgresRow) -> ActionRequest {
11        ActionRequest {
12            id: get!(x->0(i64)) as usize,
13            created: get!(x->1(i64)) as usize,
14            owner: get!(x->2(i64)) as usize,
15            action_type: serde_json::from_str(&get!(x->3(String))).unwrap(),
16            linked_asset: get!(x->4(i64)) as usize,
17            data: serde_json::from_str(&get!(x->5(String))).unwrap(),
18        }
19    }
20
21    pub async fn get_request_by_id_linked_asset(
22        &self,
23        id: usize,
24        linked_asset: usize,
25    ) -> Result<ActionRequest> {
26        if let Some(cached) = self
27            .0
28            .1
29            .get(format!("atto.request:{}:{}", id, linked_asset))
30            .await
31        {
32            if let Ok(x) = serde_json::from_str(&cached) {
33                return Ok(x);
34            } else {
35                self.0
36                    .1
37                    .remove(format!("atto.request:{}:{}", id, linked_asset))
38                    .await;
39            }
40        }
41
42        let conn = match self.0.connect().await {
43            Ok(c) => c,
44            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
45        };
46
47        let res = query_row!(
48            &conn,
49            "SELECT * FROM requests WHERE id = $1 AND linked_asset = $2",
50            &[&(id as i64), &(linked_asset as i64)],
51            |x| { Ok(Self::get_request_from_row(x)) }
52        );
53
54        if res.is_err() {
55            return Err(Error::GeneralNotFound("request".to_string()));
56        }
57
58        let x = res.unwrap();
59        self.0
60            .1
61            .set(
62                format!("atto.request:{}:{}", id, linked_asset),
63                serde_json::to_string(&x).unwrap(),
64            )
65            .await;
66
67        Ok(x)
68    }
69
70    /// Get all action requests by `owner`.
71    pub async fn get_requests_by_owner(&self, owner: usize) -> Result<Vec<ActionRequest>> {
72        let conn = match self.0.connect().await {
73            Ok(c) => c,
74            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
75        };
76
77        let res = query_rows!(
78            &conn,
79            "SELECT * FROM requests WHERE owner = $1 ORDER BY created DESC",
80            &[&(owner as i64)],
81            |x| { Self::get_request_from_row(x) }
82        );
83
84        if res.is_err() {
85            return Err(Error::GeneralNotFound("request".to_string()));
86        }
87
88        Ok(res.unwrap())
89    }
90
91    /// Get all action requests by `owner` (paginated).
92    pub async fn get_requests_by_owner_paginated(
93        &self,
94        owner: usize,
95        batch: usize,
96        page: usize,
97    ) -> Result<Vec<ActionRequest>> {
98        let conn = match self.0.connect().await {
99            Ok(c) => c,
100            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
101        };
102
103        let res = query_rows!(
104            &conn,
105            "SELECT * FROM requests WHERE owner = $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
106            &[&(owner as i64), &(batch as i64), &((page * batch) as i64)],
107            |x| { Self::get_request_from_row(x) }
108        );
109
110        if res.is_err() {
111            return Err(Error::GeneralNotFound("request".to_string()));
112        }
113
114        Ok(res.unwrap())
115    }
116
117    /// Create a new request in the database.
118    ///
119    /// # Arguments
120    /// * `data` - a mock [`ActionRequest`] object to insert
121    pub async fn create_request(&self, data: ActionRequest) -> Result<()> {
122        let conn = match self.0.connect().await {
123            Ok(c) => c,
124            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
125        };
126
127        let res = execute!(
128            &conn,
129            "INSERT INTO requests VALUES ($1, $2, $3, $4, $5, $6)",
130            params![
131                &(data.id as i64),
132                &(data.created as i64),
133                &(data.owner as i64),
134                &serde_json::to_string(&data.action_type).unwrap().as_str(),
135                &(data.linked_asset as i64),
136                &serde_json::to_string(&data.data).unwrap().as_str(),
137            ]
138        );
139
140        if let Err(e) = res {
141            return Err(Error::DatabaseError(e.to_string()));
142        }
143
144        // incr request count
145        self.incr_user_request_count(data.owner).await.unwrap();
146
147        // return
148        Ok(())
149    }
150
151    pub async fn delete_request(
152        &self,
153        id: usize,
154        linked_asset: usize,
155        user: &User,
156        force: bool,
157    ) -> Result<()> {
158        let y = self
159            .get_request_by_id_linked_asset(id, linked_asset)
160            .await?;
161
162        if !force
163            && (user.id != y.owner && user.id != y.linked_asset)
164            && !user.permissions.check(FinePermission::MANAGE_REQUESTS)
165        {
166            return Err(Error::NotAllowed);
167        }
168
169        let conn = match self.0.connect().await {
170            Ok(c) => c,
171            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
172        };
173
174        let res = execute!(
175            &conn,
176            "DELETE FROM requests WHERE id = $1",
177            &[&(y.id as i64)]
178        );
179
180        if let Err(e) = res {
181            return Err(Error::DatabaseError(e.to_string()));
182        }
183
184        self.0.1.remove(format!("atto.request:{}", y.id)).await;
185
186        self.0
187            .1
188            .remove(format!("atto.request:{}:{}", id, linked_asset))
189            .await;
190
191        // decr request count
192        let owner = self.get_user_by_id(y.owner).await?;
193        if owner.request_count > 0 {
194            self.decr_user_request_count(y.owner).await.unwrap();
195        }
196
197        // return
198        Ok(())
199    }
200
201    pub async fn delete_all_requests(&self, user: &User) -> Result<()> {
202        let y = self.get_requests_by_owner(user.id).await?;
203
204        for x in y {
205            if user.id != x.owner && !user.permissions.check(FinePermission::MANAGE_REQUESTS) {
206                return Err(Error::NotAllowed);
207            }
208
209            self.delete_request(x.id, x.linked_asset, user, false)
210                .await?;
211
212            // delete question
213            if x.action_type == ActionType::Answer {
214                self.delete_question(x.linked_asset, user).await?;
215            }
216        }
217
218        self.update_user_request_count(user.id, 0).await?;
219
220        Ok(())
221    }
222}