tetratto_core/database/
pollvotes.rs

1use oiseau::cache::Cache;
2use tetratto_shared::unix_epoch_timestamp;
3use crate::model::communities::{PollOption, PollVote};
4use crate::model::moderation::AuditLogEntry;
5use crate::model::{Error, Result, auth::User, permissions::FinePermission};
6use crate::{auto_method, DataManager};
7
8use oiseau::PostgresRow;
9
10use oiseau::{execute, get, query_row, params};
11
12impl DataManager {
13    /// Get a [`PollVote`] from an SQL row.
14    pub(crate) fn get_pollvote_from_row(x: &PostgresRow) -> PollVote {
15        PollVote {
16            id: get!(x->0(i64)) as usize,
17            owner: get!(x->1(i64)) as usize,
18            created: get!(x->2(i64)) as usize,
19            poll_id: get!(x->3(i64)) as usize,
20            vote: (get!(x->4(i32)) as u8).into(),
21        }
22    }
23
24    auto_method!(get_pollvote_by_id()@get_pollvote_from_row -> "SELECT * FROM pollvotes WHERE id = $1" --name="poll vote" --returns=PollVote --cache-key-tmpl="atto.pollvote:{}");
25
26    pub async fn get_pollvote_by_owner_poll(&self, id: usize, poll_id: usize) -> Result<PollVote> {
27        if let Some(cached) = self
28            .0
29            .1
30            .get(format!("atto.pollvote:{}:{}", id, poll_id))
31            .await
32        {
33            return Ok(serde_json::from_str(&cached).unwrap());
34        }
35
36        let conn = match self.0.connect().await {
37            Ok(c) => c,
38            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
39        };
40
41        let res = query_row!(
42            &conn,
43            "SELECT * FROM pollvotes WHERE owner = $1 AND poll_id = $2",
44            &[&(id as i64), &(poll_id as i64)],
45            |x| { Ok(Self::get_pollvote_from_row(x)) }
46        );
47
48        if res.is_err() {
49            return Err(Error::GeneralNotFound("poll vote".to_string()));
50        }
51
52        let x = res.unwrap();
53        self.0
54            .1
55            .set(
56                format!("atto.pollvote:{}:{}", id, poll_id),
57                serde_json::to_string(&x).unwrap(),
58            )
59            .await;
60
61        Ok(x)
62    }
63
64    /// Create a new poll vote in the database.
65    ///
66    /// # Arguments
67    /// * `data` - a mock [`PollVote`] object to insert
68    pub async fn create_pollvote(&self, data: PollVote) -> Result<usize> {
69        // get poll and check permission
70        let poll = self.get_poll_by_id(data.poll_id).await?;
71
72        let now = unix_epoch_timestamp();
73        let diff = now - poll.created;
74
75        if diff > poll.expires {
76            return Err(Error::MiscError("Poll is closed".to_string()));
77        };
78
79        // ...
80        let conn = match self.0.connect().await {
81            Ok(c) => c,
82            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
83        };
84
85        let vote_u8: u8 = data.vote.into();
86        let res = execute!(
87            &conn,
88            "INSERT INTO pollvotes VALUES ($1, $2, $3, $4, $5)",
89            params![
90                &(data.id as i64),
91                &(data.owner as i64),
92                &(data.created as i64),
93                &(data.poll_id as i64),
94                &(vote_u8 as i32),
95            ]
96        );
97
98        if let Err(e) = res {
99            return Err(Error::DatabaseError(e.to_string()));
100        }
101
102        // update poll
103        match data.vote {
104            PollOption::A => {
105                self.incr_votes_a_count(poll.id).await?;
106            }
107            PollOption::B => {
108                self.incr_votes_b_count(poll.id).await?;
109            }
110            PollOption::C => {
111                self.incr_votes_c_count(poll.id).await?;
112            }
113            PollOption::D => {
114                self.incr_votes_d_count(poll.id).await?;
115            }
116        };
117
118        // ...
119        Ok(data.id)
120    }
121
122    pub async fn delete_pollvote(&self, id: usize, user: User) -> Result<()> {
123        let y = self.get_pollvote_by_id(id).await?;
124
125        if user.id != y.owner {
126            if !user.permissions.check(FinePermission::MANAGE_POSTS) {
127                return Err(Error::NotAllowed);
128            } else {
129                self.create_audit_log_entry(AuditLogEntry::new(
130                    user.id,
131                    format!("invoked `delete_pollvote` with x value `{id}`"),
132                ))
133                .await?
134            }
135        }
136        let conn = match self.0.connect().await {
137            Ok(c) => c,
138            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
139        };
140
141        let res = execute!(
142            &conn,
143            "DELETE FROM pollvotes WHERE id = $1",
144            &[&(id as i64)]
145        );
146
147        if let Err(e) = res {
148            return Err(Error::DatabaseError(e.to_string()));
149        }
150
151        self.0.1.remove(format!("atto.pollvote:{}", id)).await;
152
153        Ok(())
154    }
155}