koprogo_api/infrastructure/web/handlers/
poll_handlers.rs1use crate::application::dto::{
2 CastVoteDto, CreatePollDto, PageRequest, PollFilters, SortOrder, UpdatePollDto,
3};
4use crate::application::use_cases::PollUseCases;
5use crate::infrastructure::web::middleware::AuthenticatedUser;
6use actix_web::{delete, get, post, put, web, HttpRequest, HttpResponse};
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10#[post("/polls")]
17pub async fn create_poll(
18 poll_use_cases: web::Data<PollUseCases>,
19 auth_user: AuthenticatedUser,
20 dto: web::Json<CreatePollDto>,
21) -> HttpResponse {
22 match poll_use_cases
23 .create_poll(dto.into_inner(), auth_user.user_id)
24 .await
25 {
26 Ok(poll) => HttpResponse::Created().json(poll),
27 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({
28 "error": e
29 })),
30 }
31}
32
33#[get("/polls/{id}")]
36pub async fn get_poll(
37 poll_use_cases: web::Data<PollUseCases>,
38 _auth_user: AuthenticatedUser,
39 path: web::Path<String>,
40) -> HttpResponse {
41 let poll_id = match Uuid::parse_str(&path.into_inner()) {
42 Ok(id) => id,
43 Err(_) => {
44 return HttpResponse::BadRequest().json(serde_json::json!({
45 "error": "Invalid poll ID format"
46 }))
47 }
48 };
49
50 match poll_use_cases.get_poll(poll_id).await {
51 Ok(poll) => HttpResponse::Ok().json(poll),
52 Err(e) => {
53 if e.contains("not found") {
54 HttpResponse::NotFound().json(serde_json::json!({
55 "error": e
56 }))
57 } else {
58 HttpResponse::InternalServerError().json(serde_json::json!({
59 "error": e
60 }))
61 }
62 }
63 }
64}
65
66#[put("/polls/{id}")]
69pub async fn update_poll(
70 poll_use_cases: web::Data<PollUseCases>,
71 auth_user: AuthenticatedUser,
72 path: web::Path<String>,
73 dto: web::Json<UpdatePollDto>,
74) -> HttpResponse {
75 let poll_id = match Uuid::parse_str(&path.into_inner()) {
76 Ok(id) => id,
77 Err(_) => {
78 return HttpResponse::BadRequest().json(serde_json::json!({
79 "error": "Invalid poll ID format"
80 }))
81 }
82 };
83
84 match poll_use_cases
85 .update_poll(poll_id, dto.into_inner(), auth_user.user_id)
86 .await
87 {
88 Ok(poll) => HttpResponse::Ok().json(poll),
89 Err(e) => {
90 if e.contains("not found") {
91 HttpResponse::NotFound().json(serde_json::json!({
92 "error": e
93 }))
94 } else if e.contains("Only the poll creator") {
95 HttpResponse::Forbidden().json(serde_json::json!({
96 "error": e
97 }))
98 } else {
99 HttpResponse::BadRequest().json(serde_json::json!({
100 "error": e
101 }))
102 }
103 }
104 }
105}
106
107#[get("/polls")]
110pub async fn list_polls(
111 poll_use_cases: web::Data<PollUseCases>,
112 _auth_user: AuthenticatedUser,
113 query: web::Query<ListPollsQuery>,
114) -> HttpResponse {
115 let page_request = PageRequest {
116 page: query.page.unwrap_or(1),
117 per_page: query.per_page.unwrap_or(10),
118 sort_by: None,
119 order: SortOrder::Desc,
120 };
121
122 let filters = PollFilters {
123 building_id: query.building_id.clone(),
124 created_by: query.created_by.clone(),
125 status: None, poll_type: None,
127 ends_before: query.ends_before.clone(),
128 ends_after: query.ends_after.clone(),
129 };
130
131 match poll_use_cases
132 .list_polls_paginated(&page_request, &filters)
133 .await
134 {
135 Ok(response) => HttpResponse::Ok().json(response),
136 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
137 "error": e
138 })),
139 }
140}
141
142#[derive(Debug, Deserialize)]
143pub struct ListPollsQuery {
144 pub page: Option<i64>,
145 pub per_page: Option<i64>,
146 pub building_id: Option<String>,
147 pub created_by: Option<String>,
148 pub ends_before: Option<String>,
149 pub ends_after: Option<String>,
150}
151
152#[get("/buildings/{building_id}/polls/active")]
155pub async fn find_active_polls(
156 poll_use_cases: web::Data<PollUseCases>,
157 _auth_user: AuthenticatedUser,
158 path: web::Path<String>,
159) -> HttpResponse {
160 let building_id = match Uuid::parse_str(&path.into_inner()) {
161 Ok(id) => id,
162 Err(_) => {
163 return HttpResponse::BadRequest().json(serde_json::json!({
164 "error": "Invalid building ID format"
165 }))
166 }
167 };
168
169 match poll_use_cases.find_active_polls(building_id).await {
170 Ok(polls) => HttpResponse::Ok().json(polls),
171 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
172 "error": e
173 })),
174 }
175}
176
177#[post("/polls/{id}/publish")]
180pub async fn publish_poll(
181 poll_use_cases: web::Data<PollUseCases>,
182 auth_user: AuthenticatedUser,
183 path: web::Path<String>,
184) -> HttpResponse {
185 let poll_id = match Uuid::parse_str(&path.into_inner()) {
186 Ok(id) => id,
187 Err(_) => {
188 return HttpResponse::BadRequest().json(serde_json::json!({
189 "error": "Invalid poll ID format"
190 }))
191 }
192 };
193
194 match poll_use_cases
195 .publish_poll(poll_id, auth_user.user_id)
196 .await
197 {
198 Ok(poll) => HttpResponse::Ok().json(poll),
199 Err(e) => {
200 if e.contains("not found") {
201 HttpResponse::NotFound().json(serde_json::json!({
202 "error": e
203 }))
204 } else if e.contains("Only the poll creator") {
205 HttpResponse::Forbidden().json(serde_json::json!({
206 "error": e
207 }))
208 } else {
209 HttpResponse::BadRequest().json(serde_json::json!({
210 "error": e
211 }))
212 }
213 }
214 }
215}
216
217#[post("/polls/{id}/close")]
220pub async fn close_poll(
221 poll_use_cases: web::Data<PollUseCases>,
222 auth_user: AuthenticatedUser,
223 path: web::Path<String>,
224) -> HttpResponse {
225 let poll_id = match Uuid::parse_str(&path.into_inner()) {
226 Ok(id) => id,
227 Err(_) => {
228 return HttpResponse::BadRequest().json(serde_json::json!({
229 "error": "Invalid poll ID format"
230 }))
231 }
232 };
233
234 match poll_use_cases.close_poll(poll_id, auth_user.user_id).await {
235 Ok(poll) => HttpResponse::Ok().json(poll),
236 Err(e) => {
237 if e.contains("not found") {
238 HttpResponse::NotFound().json(serde_json::json!({
239 "error": e
240 }))
241 } else if e.contains("Only the poll creator") {
242 HttpResponse::Forbidden().json(serde_json::json!({
243 "error": e
244 }))
245 } else {
246 HttpResponse::BadRequest().json(serde_json::json!({
247 "error": e
248 }))
249 }
250 }
251 }
252}
253
254#[post("/polls/{id}/cancel")]
257pub async fn cancel_poll(
258 poll_use_cases: web::Data<PollUseCases>,
259 auth_user: AuthenticatedUser,
260 path: web::Path<String>,
261) -> HttpResponse {
262 let poll_id = match Uuid::parse_str(&path.into_inner()) {
263 Ok(id) => id,
264 Err(_) => {
265 return HttpResponse::BadRequest().json(serde_json::json!({
266 "error": "Invalid poll ID format"
267 }))
268 }
269 };
270
271 match poll_use_cases.cancel_poll(poll_id, auth_user.user_id).await {
272 Ok(poll) => HttpResponse::Ok().json(poll),
273 Err(e) => {
274 if e.contains("not found") {
275 HttpResponse::NotFound().json(serde_json::json!({
276 "error": e
277 }))
278 } else if e.contains("Only the poll creator") {
279 HttpResponse::Forbidden().json(serde_json::json!({
280 "error": e
281 }))
282 } else {
283 HttpResponse::BadRequest().json(serde_json::json!({
284 "error": e
285 }))
286 }
287 }
288 }
289}
290
291#[delete("/polls/{id}")]
294pub async fn delete_poll(
295 poll_use_cases: web::Data<PollUseCases>,
296 auth_user: AuthenticatedUser,
297 path: web::Path<String>,
298) -> HttpResponse {
299 let poll_id = match Uuid::parse_str(&path.into_inner()) {
300 Ok(id) => id,
301 Err(_) => {
302 return HttpResponse::BadRequest().json(serde_json::json!({
303 "error": "Invalid poll ID format"
304 }))
305 }
306 };
307
308 match poll_use_cases.delete_poll(poll_id, auth_user.user_id).await {
309 Ok(true) => HttpResponse::NoContent().finish(),
310 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
311 "error": "Poll not found"
312 })),
313 Err(e) => {
314 if e.contains("Only the poll creator") {
315 HttpResponse::Forbidden().json(serde_json::json!({
316 "error": e
317 }))
318 } else {
319 HttpResponse::BadRequest().json(serde_json::json!({
320 "error": e
321 }))
322 }
323 }
324 }
325}
326
327#[post("/polls/vote")]
334pub async fn cast_poll_vote(
335 poll_use_cases: web::Data<PollUseCases>,
336 auth_user: AuthenticatedUser,
337 dto: web::Json<CastVoteDto>,
338 _req: HttpRequest,
339) -> HttpResponse {
340 let owner_id = Some(auth_user.user_id);
344
345 match poll_use_cases.cast_vote(dto.into_inner(), owner_id).await {
346 Ok(message) => HttpResponse::Created().json(serde_json::json!({
347 "message": message
348 })),
349 Err(e) => {
350 if e.contains("not active") || e.contains("expired") {
351 HttpResponse::BadRequest().json(serde_json::json!({
352 "error": e
353 }))
354 } else if e.contains("already voted") {
355 HttpResponse::Conflict().json(serde_json::json!({
356 "error": e
357 }))
358 } else if e.contains("not found") {
359 HttpResponse::NotFound().json(serde_json::json!({
360 "error": e
361 }))
362 } else {
363 HttpResponse::BadRequest().json(serde_json::json!({
364 "error": e
365 }))
366 }
367 }
368 }
369}
370
371#[get("/polls/{id}/results")]
374pub async fn get_poll_results(
375 poll_use_cases: web::Data<PollUseCases>,
376 _auth_user: AuthenticatedUser,
377 path: web::Path<String>,
378) -> HttpResponse {
379 let poll_id = match Uuid::parse_str(&path.into_inner()) {
380 Ok(id) => id,
381 Err(_) => {
382 return HttpResponse::BadRequest().json(serde_json::json!({
383 "error": "Invalid poll ID format"
384 }))
385 }
386 };
387
388 match poll_use_cases.get_poll_results(poll_id).await {
389 Ok(results) => HttpResponse::Ok().json(results),
390 Err(e) => {
391 if e.contains("not found") {
392 HttpResponse::NotFound().json(serde_json::json!({
393 "error": e
394 }))
395 } else {
396 HttpResponse::InternalServerError().json(serde_json::json!({
397 "error": e
398 }))
399 }
400 }
401 }
402}
403
404#[get("/buildings/{building_id}/polls/statistics")]
411pub async fn get_poll_building_statistics(
412 poll_use_cases: web::Data<PollUseCases>,
413 _auth_user: AuthenticatedUser,
414 path: web::Path<String>,
415) -> HttpResponse {
416 let building_id = match Uuid::parse_str(&path.into_inner()) {
417 Ok(id) => id,
418 Err(_) => {
419 return HttpResponse::BadRequest().json(serde_json::json!({
420 "error": "Invalid building ID format"
421 }))
422 }
423 };
424
425 match poll_use_cases.get_building_statistics(building_id).await {
426 Ok(stats) => HttpResponse::Ok().json(stats),
427 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({
428 "error": e
429 })),
430 }
431}
432
433#[derive(Debug, Serialize)]
438pub struct PollStatisticsResponse {
439 pub total_polls: i64,
440 pub active_polls: i64,
441 pub closed_polls: i64,
442 pub average_participation_rate: f64,
443}