headless_lms_server/controllers/course_material/
code_giveaways.rs

1//! Controllers for requests starting with `/api/v0/course-material/code-giveaways`.
2
3use crate::{domain::authorization::skip_authorize, prelude::*};
4use models::code_giveaways::CodeGiveawayStatus;
5
6/**
7 GET /api/v0/course-material/code-giveaways/:id/status - Returns information about a code giveaway.
8*/
9#[instrument(skip(pool))]
10async fn get_giveaway_status(
11    user: AuthUser,
12    code_giveaway_id: web::Path<Uuid>,
13    pool: web::Data<PgPool>,
14) -> ControllerResult<web::Json<CodeGiveawayStatus>> {
15    let mut conn = pool.acquire().await?;
16    let token = skip_authorize();
17    let res =
18        models::code_giveaways::get_code_giveaway_status(&mut conn, *code_giveaway_id, user.id)
19            .await?;
20    token.authorized_ok(web::Json(res))
21}
22
23/**
24 POST /api/v0/course-material/code-giveaways/:id/claim - Claim a code from a code giveaway. If user has not completed the course module that is a requirement for the code, returns an error.
25*/
26#[instrument(skip(pool))]
27async fn claim_code_from_code_giveaway(
28    user: AuthUser,
29    code_giveaway_id: web::Path<Uuid>,
30    pool: web::Data<PgPool>,
31) -> ControllerResult<web::Json<String>> {
32    let mut conn = pool.acquire().await?;
33    let token = skip_authorize();
34    let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *code_giveaway_id).await?;
35    if !code_giveaway.enabled {
36        return Err(ControllerError::new(
37            ControllerErrorType::Forbidden,
38            "Code giveaway is not enabled.".to_string(),
39            None,
40        ));
41    }
42    if let Some(course_module_id) = code_giveaway.course_module_id {
43        let course_module_completions =
44            models::course_module_completions::get_all_by_user_id_and_course_module_id(
45                &mut conn,
46                user.id,
47                course_module_id,
48            )
49            .await?;
50
51        course_module_completions
52            .iter()
53            .find(|c| c.passed)
54            .ok_or_else(|| {
55                ControllerError::new(
56                    ControllerErrorType::BadRequest,
57                    "You have not completed the required course module.".to_string(),
58                    None,
59                )
60            })?;
61    } else {
62        return Err(ControllerError::new(
63            ControllerErrorType::BadRequest,
64            "The required course module has not been configured to this code giveaway.".to_string(),
65            None,
66        ));
67    }
68
69    if let Some(question_id) = code_giveaway.require_course_specific_consent_form_question_id {
70        let answers =
71            models::research_forms::get_all_research_form_answers_with_user_course_and_question_id(
72                &mut conn,
73                user.id,
74                code_giveaway.course_id,
75                question_id,
76            )
77            .await
78            .optional()?;
79        if let Some(answers) = answers {
80            let consented = answers.iter().any(|a| a.research_consent);
81            if !consented {
82                return Err(ControllerError::new(
83                    ControllerErrorType::BadRequest,
84                    "You're not eligible for the code.".to_string(),
85                    None,
86                ));
87            }
88        } else {
89            return Err(ControllerError::new(
90                ControllerErrorType::BadRequest,
91                "You have not completed the required research form.".to_string(),
92                None,
93            ));
94        }
95    }
96
97    let already_given_code =
98        models::code_giveaway_codes::get_code_given_to_user(&mut conn, *code_giveaway_id, user.id)
99            .await?
100            .map(|o| o.code);
101
102    if let Some(code) = already_given_code {
103        // This is for a pretty message, in the end a database constraint ensures that only one code can be given to a user.
104        return token.authorized_ok(web::Json(code));
105    }
106
107    let give_code_result =
108        models::code_giveaway_codes::give_some_code_to_user(&mut conn, *code_giveaway_id, user.id)
109            .await;
110
111    if let Err(_e) = &give_code_result {
112        let codes_left =
113            models::code_giveaway_codes::are_any_codes_left(&mut conn, *code_giveaway_id).await?;
114        if !codes_left {
115            return Err(ControllerError::new(
116                ControllerErrorType::BadRequest,
117                "The giveaway has ran out of codes.".to_string(),
118                None,
119            ));
120        }
121    }
122
123    let code = give_code_result?.code;
124    token.authorized_ok(web::Json(code))
125}
126
127pub fn _add_routes(cfg: &mut ServiceConfig) {
128    cfg.route("/{id}/status", web::get().to(get_giveaway_status))
129        .route("/{id}/claim", web::post().to(claim_code_from_code_giveaway));
130}