headless_lms_server/controllers/main_frontend/
code_giveaways.rs1use chrono::Utc;
3use domain::csv_export::{code_giveaway_codes::CodeGiveawayCodesExportOperation, general_export};
4use headless_lms_models::{
5 code_giveaway_codes::CodeGiveawayCode,
6 code_giveaways::{CodeGiveaway, NewCodeGiveaway},
7};
8use utoipa::OpenApi;
9
10use crate::prelude::*;
11
12#[derive(OpenApi)]
13#[openapi(paths(
14 get_code_giveaways_by_course,
15 create_code_giveaway,
16 get_code_giveaway_by_id,
17 add_codes_to_code_giveaway,
18 get_codes_by_code_giveaway_id,
19 delete_code_giveaway_code,
20 get_codes_by_code_giveaway_id_csv
21))]
22pub(crate) struct MainFrontendCodeGiveawaysApiDoc;
23
24#[utoipa::path(
28 get,
29 path = "/by-course/{course_id}",
30 operation_id = "getCodeGiveawaysByCourse",
31 tag = "code_giveaways",
32 params(
33 ("course_id" = Uuid, Path, description = "Course id")
34 ),
35 responses(
36 (status = 200, description = "Code giveaways for course", body = Vec<CodeGiveaway>)
37 )
38)]
39#[instrument(skip(pool))]
40async fn get_code_giveaways_by_course(
41 pool: web::Data<PgPool>,
42 course_id: web::Path<Uuid>,
43 user: AuthUser,
44) -> ControllerResult<web::Json<Vec<CodeGiveaway>>> {
45 let mut conn = pool.acquire().await?;
46
47 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Course(*course_id)).await?;
48
49 let code_giveaways = models::code_giveaways::get_all_for_course(&mut conn, *course_id).await?;
50
51 token.authorized_ok(web::Json(code_giveaways))
52}
53
54#[utoipa::path(
58 post,
59 path = "",
60 operation_id = "createCodeGiveaway",
61 tag = "code_giveaways",
62 request_body = NewCodeGiveaway,
63 responses(
64 (status = 200, description = "Created code giveaway", body = CodeGiveaway)
65 )
66)]
67#[instrument(skip(pool))]
68async fn create_code_giveaway(
69 pool: web::Data<PgPool>,
70 code_giveaway: web::Json<NewCodeGiveaway>,
71 user: AuthUser,
72) -> ControllerResult<web::Json<CodeGiveaway>> {
73 let mut conn = pool.acquire().await?;
74
75 let token = authorize(
76 &mut conn,
77 Act::Edit,
78 Some(user.id),
79 Res::Course(code_giveaway.course_id),
80 )
81 .await?;
82
83 let code_giveaway = models::code_giveaways::insert(&mut conn, &code_giveaway).await?;
84
85 token.authorized_ok(web::Json(code_giveaway))
86}
87
88#[utoipa::path(
92 get,
93 path = "/{id}",
94 operation_id = "getCodeGiveawayById",
95 tag = "code_giveaways",
96 params(
97 ("id" = Uuid, Path, description = "Code giveaway id")
98 ),
99 responses(
100 (status = 200, description = "Code giveaway", body = CodeGiveaway)
101 )
102)]
103#[instrument(skip(pool))]
104async fn get_code_giveaway_by_id(
105 pool: web::Data<PgPool>,
106 id: web::Path<Uuid>,
107 user: AuthUser,
108) -> ControllerResult<web::Json<CodeGiveaway>> {
109 let mut conn = pool.acquire().await?;
110
111 let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *id).await?;
112
113 let token = authorize(
114 &mut conn,
115 Act::Edit,
116 Some(user.id),
117 Res::Course(code_giveaway.course_id),
118 )
119 .await?;
120
121 token.authorized_ok(web::Json(code_giveaway))
122}
123
124#[utoipa::path(
128 post,
129 path = "/{id}/codes",
130 operation_id = "addCodeGiveawayCodes",
131 tag = "code_giveaways",
132 params(
133 ("id" = Uuid, Path, description = "Code giveaway id")
134 ),
135 request_body = Vec<String>,
136 responses(
137 (status = 200, description = "Added giveaway codes", body = Vec<CodeGiveawayCode>)
138 )
139)]
140#[instrument(skip(pool))]
141async fn add_codes_to_code_giveaway(
142 pool: web::Data<PgPool>,
143 id: web::Path<Uuid>,
144 codes: web::Json<Vec<String>>,
145 user: AuthUser,
146) -> ControllerResult<web::Json<Vec<CodeGiveawayCode>>> {
147 let mut conn = pool.acquire().await?;
148
149 let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *id).await?;
150
151 let token = authorize(
152 &mut conn,
153 Act::Edit,
154 Some(user.id),
155 Res::Course(code_giveaway.course_id),
156 )
157 .await?;
158
159 let codes = models::code_giveaway_codes::insert_many(&mut conn, *id, &codes, user.id).await?;
160
161 token.authorized_ok(web::Json(codes))
162}
163
164#[utoipa::path(
168 get,
169 path = "/{id}/codes",
170 operation_id = "getCodeGiveawayCodes",
171 tag = "code_giveaways",
172 params(
173 ("id" = Uuid, Path, description = "Code giveaway id")
174 ),
175 responses(
176 (status = 200, description = "Giveaway codes", body = Vec<CodeGiveawayCode>)
177 )
178)]
179#[instrument(skip(pool))]
180async fn get_codes_by_code_giveaway_id(
181 pool: web::Data<PgPool>,
182 id: web::Path<Uuid>,
183 user: AuthUser,
184) -> ControllerResult<web::Json<Vec<CodeGiveawayCode>>> {
185 let mut conn = pool.acquire().await?;
186
187 let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *id).await?;
188
189 let token = authorize(
190 &mut conn,
191 Act::Edit,
192 Some(user.id),
193 Res::Course(code_giveaway.course_id),
194 )
195 .await?;
196
197 let codes = models::code_giveaway_codes::get_all_by_code_giveaway_id(&mut conn, *id).await?;
198
199 token.authorized_ok(web::Json(codes))
200}
201
202#[utoipa::path(
206 delete,
207 path = "/{id}/codes/{code_id}",
208 operation_id = "deleteCodeGiveawayCode",
209 tag = "code_giveaways",
210 params(
211 ("id" = Uuid, Path, description = "Code giveaway id"),
212 ("code_id" = Uuid, Path, description = "Giveaway code id")
213 ),
214 responses(
215 (status = 200, description = "Deleted giveaway code")
216 )
217)]
218#[instrument(skip(pool))]
219async fn delete_code_giveaway_code(
220 pool: web::Data<PgPool>,
221 path: web::Path<(Uuid, Uuid)>,
222 user: AuthUser,
223) -> ControllerResult<web::Json<()>> {
224 let (id, code_id) = path.into_inner();
225 let mut conn = pool.acquire().await?;
226
227 let giveaway = models::code_giveaways::get_by_id(&mut conn, id).await?;
228 let code = models::code_giveaway_codes::get_by_id(&mut conn, code_id).await?;
229
230 if id != code.code_giveaway_id {
231 return Err(ControllerError::new(
232 ControllerErrorType::BadRequest,
233 "Code giveaway and code course mismatch",
234 None,
235 ));
236 }
237
238 let token = authorize(
239 &mut conn,
240 Act::Edit,
241 Some(user.id),
242 Res::Course(giveaway.course_id),
243 )
244 .await?;
245
246 models::code_giveaway_codes::delete_by_id(&mut conn, code_id).await?;
247
248 token.authorized_ok(web::Json(()))
249}
250
251#[utoipa::path(
255 get,
256 path = "/{id}/codes/csv",
257 operation_id = "downloadCodeGiveawayCodesCsv",
258 tag = "code_giveaways",
259 params(
260 ("id" = Uuid, Path, description = "Code giveaway id")
261 ),
262 responses(
263 (status = 200, description = "Giveaway codes CSV", content_type = "text/csv", body = String)
264 )
265)]
266#[instrument(skip(pool))]
267async fn get_codes_by_code_giveaway_id_csv(
268 pool: web::Data<PgPool>,
269 id: web::Path<Uuid>,
270 user: AuthUser,
271) -> ControllerResult<HttpResponse> {
272 let mut conn = pool.acquire().await?;
273
274 let code_giveaway = models::code_giveaways::get_by_id(&mut conn, *id).await?;
275 let course = models::courses::get_course(&mut conn, code_giveaway.course_id).await?;
276
277 let token = authorize(
278 &mut conn,
279 Act::Edit,
280 Some(user.id),
281 Res::Course(code_giveaway.course_id),
282 )
283 .await?;
284
285 general_export(
286 pool,
287 &format!(
288 "attachment; filename=\"Giveaway codes {} / {} - {}.csv\"",
289 code_giveaway.name,
290 course.name,
291 Utc::now().format("%Y-%m-%d")
292 ),
293 CodeGiveawayCodesExportOperation {
294 code_giveaway_id: *id,
295 },
296 token,
297 )
298 .await
299}
300
301pub fn _add_routes(cfg: &mut ServiceConfig) {
309 cfg.route(
310 "by-course/{course_id}",
311 web::get().to(get_code_giveaways_by_course),
312 )
313 .route("", web::post().to(create_code_giveaway))
314 .route("{id}", web::get().to(get_code_giveaway_by_id))
315 .route("{id}/codes", web::get().to(get_codes_by_code_giveaway_id))
316 .route(
317 "{id}/codes/csv",
318 web::get().to(get_codes_by_code_giveaway_id_csv),
319 )
320 .route("{id}/codes", web::post().to(add_codes_to_code_giveaway))
321 .route(
322 "{id}/codes/{code_id}",
323 web::delete().to(delete_code_giveaway_code),
324 );
325}