Skip to main content

tetratto_core2/database/
requests.rs

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