headless_lms_server/controllers/course_material/
code_giveaways.rs1use crate::{domain::authorization::skip_authorize, prelude::*};
4use models::code_giveaways::CodeGiveawayStatus;
5use utoipa::OpenApi;
6
7#[derive(OpenApi)]
8#[openapi(paths(get_giveaway_status, claim_code_from_code_giveaway))]
9pub(crate) struct CourseMaterialCodeGiveawaysApiDoc;
10
11#[utoipa::path(
15 get,
16 path = "/{id}/status",
17 operation_id = "getCodeGiveawayStatus",
18 tag = "course-material-code-giveaways",
19 params(
20 ("id" = Uuid, Path, description = "Code giveaway id")
21 ),
22 responses(
23 (status = 200, description = "Code giveaway status", body = CodeGiveawayStatus)
24 )
25)]
26#[instrument(skip(pool))]
27async fn get_giveaway_status(
28 user: AuthUser,
29 code_giveaway_id: web::Path<Uuid>,
30 pool: web::Data<PgPool>,
31) -> ControllerResult<web::Json<CodeGiveawayStatus>> {
32 let mut conn = pool.acquire().await?;
33 let token = skip_authorize();
34 let res =
35 models::code_giveaways::get_code_giveaway_status(&mut conn, *code_giveaway_id, user.id)
36 .await?;
37 token.authorized_ok(web::Json(res))
38}
39
40#[utoipa::path(
44 post,
45 path = "/{id}/claim",
46 operation_id = "claimCodeFromCodeGiveaway",
47 tag = "course-material-code-giveaways",
48 params(
49 ("id" = Uuid, Path, description = "Code giveaway id")
50 ),
51 responses(
52 (status = 200, description = "Claimed code", body = String)
53 )
54)]
55#[instrument(skip(pool))]
56async fn claim_code_from_code_giveaway(
57 user: AuthUser,
58 code_giveaway_id: web::Path<Uuid>,
59 pool: web::Data<PgPool>,
60) -> ControllerResult<web::Json<String>> {
61 let mut conn = pool.acquire().await?;
62 let token = skip_authorize();
63 let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *code_giveaway_id).await?;
64 if !code_giveaway.enabled {
65 return Err(ControllerError::new(
66 ControllerErrorType::Forbidden,
67 "Code giveaway is not enabled.".to_string(),
68 None,
69 ));
70 }
71 if let Some(course_module_id) = code_giveaway.course_module_id {
72 let course_module_completions =
73 models::course_module_completions::get_all_by_user_id_and_course_module_id(
74 &mut conn,
75 user.id,
76 course_module_id,
77 )
78 .await?;
79
80 course_module_completions
81 .iter()
82 .find(|c| c.passed)
83 .ok_or_else(|| {
84 ControllerError::new(
85 ControllerErrorType::BadRequest,
86 "You have not completed the required course module.".to_string(),
87 None,
88 )
89 })?;
90 } else {
91 return Err(ControllerError::new(
92 ControllerErrorType::BadRequest,
93 "The required course module has not been configured to this code giveaway.".to_string(),
94 None,
95 ));
96 }
97
98 if let Some(question_id) = code_giveaway.require_course_specific_consent_form_question_id {
99 let answers =
100 models::research_forms::get_all_research_form_answers_with_user_course_and_question_id(
101 &mut conn,
102 user.id,
103 code_giveaway.course_id,
104 question_id,
105 )
106 .await
107 .optional()?;
108 if let Some(answers) = answers {
109 let consented = answers.iter().any(|a| a.research_consent);
110 if !consented {
111 return Err(ControllerError::new(
112 ControllerErrorType::BadRequest,
113 "You're not eligible for the code.".to_string(),
114 None,
115 ));
116 }
117 } else {
118 return Err(ControllerError::new(
119 ControllerErrorType::BadRequest,
120 "You have not completed the required research form.".to_string(),
121 None,
122 ));
123 }
124 }
125
126 let already_given_code =
127 models::code_giveaway_codes::get_code_given_to_user(&mut conn, *code_giveaway_id, user.id)
128 .await?
129 .map(|o| o.code);
130
131 if let Some(code) = already_given_code {
132 return token.authorized_ok(web::Json(code));
134 }
135
136 let give_code_result =
137 models::code_giveaway_codes::give_some_code_to_user(&mut conn, *code_giveaway_id, user.id)
138 .await;
139
140 if let Err(_e) = &give_code_result {
141 let codes_left =
142 models::code_giveaway_codes::are_any_codes_left(&mut conn, *code_giveaway_id).await?;
143 if !codes_left {
144 return Err(ControllerError::new(
145 ControllerErrorType::BadRequest,
146 "The giveaway has ran out of codes.".to_string(),
147 None,
148 ));
149 }
150 }
151
152 let code = give_code_result?.code;
153 token.authorized_ok(web::Json(code))
154}
155
156pub fn _add_routes(cfg: &mut ServiceConfig) {
157 cfg.route("/{id}/status", web::get().to(get_giveaway_status))
158 .route("/{id}/claim", web::post().to(claim_code_from_code_giveaway));
159}