koprogo_api/infrastructure/database/repositories/
poll_vote_repository_impl.rs

1use crate::application::ports::PollVoteRepository;
2use crate::domain::entities::PollVote;
3use crate::infrastructure::database::pool::DbPool;
4use async_trait::async_trait;
5use serde_json;
6use sqlx::Row;
7use uuid::Uuid;
8
9pub struct PostgresPollVoteRepository {
10    pool: DbPool,
11}
12
13impl PostgresPollVoteRepository {
14    pub fn new(pool: DbPool) -> Self {
15        Self { pool }
16    }
17
18    /// Map database row to PollVote entity
19    fn row_to_poll_vote(&self, row: &sqlx::postgres::PgRow) -> Result<PollVote, String> {
20        // Parse selected_option_ids from JSONB
21        let selected_ids_json: serde_json::Value = row
22            .try_get("selected_option_ids")
23            .map_err(|e| format!("Failed to get selected_option_ids: {}", e))?;
24
25        let selected_ids: Vec<Uuid> = serde_json::from_value(selected_ids_json)
26            .map_err(|e| format!("Failed to deserialize selected_option_ids: {}", e))?;
27
28        Ok(PollVote {
29            id: row
30                .try_get("id")
31                .map_err(|e| format!("Failed to get id: {}", e))?,
32            poll_id: row
33                .try_get("poll_id")
34                .map_err(|e| format!("Failed to get poll_id: {}", e))?,
35            owner_id: row
36                .try_get("owner_id")
37                .map_err(|e| format!("Failed to get owner_id: {}", e))?,
38            building_id: row
39                .try_get("building_id")
40                .map_err(|e| format!("Failed to get building_id: {}", e))?,
41            selected_option_ids: selected_ids,
42            rating_value: row
43                .try_get("rating_value")
44                .map_err(|e| format!("Failed to get rating_value: {}", e))?,
45            open_text: row
46                .try_get("open_text")
47                .map_err(|e| format!("Failed to get open_text: {}", e))?,
48            voted_at: row
49                .try_get("voted_at")
50                .map_err(|e| format!("Failed to get voted_at: {}", e))?,
51            ip_address: row
52                .try_get("ip_address")
53                .map_err(|e| format!("Failed to get ip_address: {}", e))?,
54        })
55    }
56}
57
58#[async_trait]
59impl PollVoteRepository for PostgresPollVoteRepository {
60    async fn create(&self, vote: &PollVote) -> Result<PollVote, String> {
61        // Serialize selected_option_ids to JSONB
62        let selected_ids_json = serde_json::to_value(&vote.selected_option_ids)
63            .map_err(|e| format!("Failed to serialize selected_option_ids: {}", e))?;
64
65        sqlx::query(
66            r#"
67            INSERT INTO poll_votes (
68                id, poll_id, owner_id, building_id, selected_option_ids,
69                rating_value, open_text, voted_at, ip_address
70            )
71            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
72            "#,
73        )
74        .bind(vote.id)
75        .bind(vote.poll_id)
76        .bind(vote.owner_id)
77        .bind(vote.building_id)
78        .bind(selected_ids_json)
79        .bind(vote.rating_value)
80        .bind(&vote.open_text)
81        .bind(vote.voted_at)
82        .bind(&vote.ip_address)
83        .execute(&self.pool)
84        .await
85        .map_err(|e| format!("Failed to insert poll vote: {}", e))?;
86
87        Ok(vote.clone())
88    }
89
90    async fn find_by_id(&self, id: Uuid) -> Result<Option<PollVote>, String> {
91        let row = sqlx::query(
92            r#"
93            SELECT * FROM poll_votes WHERE id = $1
94            "#,
95        )
96        .bind(id)
97        .fetch_optional(&self.pool)
98        .await
99        .map_err(|e| format!("Failed to fetch poll vote: {}", e))?;
100
101        match row {
102            Some(r) => Ok(Some(self.row_to_poll_vote(&r)?)),
103            None => Ok(None),
104        }
105    }
106
107    async fn find_by_poll(&self, poll_id: Uuid) -> Result<Vec<PollVote>, String> {
108        let rows = sqlx::query(
109            r#"
110            SELECT * FROM poll_votes 
111            WHERE poll_id = $1
112            ORDER BY voted_at DESC
113            "#,
114        )
115        .bind(poll_id)
116        .fetch_all(&self.pool)
117        .await
118        .map_err(|e| format!("Failed to fetch poll votes: {}", e))?;
119
120        rows.iter()
121            .map(|row| self.row_to_poll_vote(row))
122            .collect::<Result<Vec<PollVote>, String>>()
123    }
124
125    async fn find_by_poll_and_owner(
126        &self,
127        poll_id: Uuid,
128        owner_id: Uuid,
129    ) -> Result<Option<PollVote>, String> {
130        let row = sqlx::query(
131            r#"
132            SELECT * FROM poll_votes 
133            WHERE poll_id = $1 AND owner_id = $2
134            LIMIT 1
135            "#,
136        )
137        .bind(poll_id)
138        .bind(owner_id)
139        .fetch_optional(&self.pool)
140        .await
141        .map_err(|e| format!("Failed to fetch poll vote: {}", e))?;
142
143        match row {
144            Some(r) => Ok(Some(self.row_to_poll_vote(&r)?)),
145            None => Ok(None),
146        }
147    }
148
149    async fn find_by_owner(&self, owner_id: Uuid) -> Result<Vec<PollVote>, String> {
150        let rows = sqlx::query(
151            r#"
152            SELECT * FROM poll_votes 
153            WHERE owner_id = $1
154            ORDER BY voted_at DESC
155            "#,
156        )
157        .bind(owner_id)
158        .fetch_all(&self.pool)
159        .await
160        .map_err(|e| format!("Failed to fetch poll votes: {}", e))?;
161
162        rows.iter()
163            .map(|row| self.row_to_poll_vote(row))
164            .collect::<Result<Vec<PollVote>, String>>()
165    }
166
167    async fn delete(&self, id: Uuid) -> Result<bool, String> {
168        let result = sqlx::query(
169            r#"
170            DELETE FROM poll_votes WHERE id = $1
171            "#,
172        )
173        .bind(id)
174        .execute(&self.pool)
175        .await
176        .map_err(|e| format!("Failed to delete poll vote: {}", e))?;
177
178        Ok(result.rows_affected() > 0)
179    }
180}