headless_lms_server/controllers/cms/
pages.rs1use models::{
4 CourseOrExamId,
5 page_history::HistoryChangeReason,
6 pages::{
7 CmsPageUpdate, ContentManagementPage, PageInfo, PageNavigationInformation, PageUpdateArgs,
8 PageVisibility,
9 },
10};
11
12use crate::{
13 domain::{
14 models_requests::{self, JwtKey},
15 request_id::RequestId,
16 },
17 prelude::*,
18};
19use utoipa::OpenApi;
20
21#[derive(OpenApi)]
22#[openapi(paths(
23 get_page,
24 get_page_info,
25 update_page,
26 get_page_navigation,
27 get_exercises_with_submissions
28))]
29pub(crate) struct CmsPagesApiDoc;
30
31#[instrument(skip(pool))]
37#[utoipa::path(
38 get,
39 path = "/{page_id}",
40 operation_id = "getCmsPage",
41 tag = "cms_pages",
42 params(
43 ("page_id" = Uuid, Path, description = "Page id")
44 ),
45 responses(
46 (status = 200, description = "CMS page with exercises and peer review data", body = ContentManagementPage)
47 )
48)]
49async fn get_page(
50 page_id: web::Path<Uuid>,
51 pool: web::Data<PgPool>,
52 user: AuthUser,
53) -> ControllerResult<web::Json<ContentManagementPage>> {
54 let mut conn = pool.acquire().await?;
55 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
56
57 let cms_page = models::pages::get_page_with_exercises(&mut conn, *page_id).await?;
58 token.authorized_ok(web::Json(cms_page))
59}
60
61#[utoipa::path(
67 get,
68 path = "/{page_id}/info",
69 operation_id = "getCmsPageInfo",
70 tag = "cms_pages",
71 params(
72 ("page_id" = Uuid, Path, description = "Page id")
73 ),
74 responses(
75 (status = 200, description = "Page info", body = PageInfo)
76 )
77)]
78async fn get_page_info(
79 page_id: web::Path<Uuid>,
80 pool: web::Data<PgPool>,
81 user: AuthUser,
82) -> ControllerResult<web::Json<PageInfo>> {
83 let mut conn = pool.acquire().await?;
84 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
85
86 let cms_page_info = models::pages::get_page_info(&mut conn, *page_id).await?;
87 token.authorized_ok(web::Json(cms_page_info))
88}
89
90#[instrument(skip(pool, app_conf))]
115#[utoipa::path(
116 put,
117 path = "/{page_id}",
118 operation_id = "updateCmsPage",
119 tag = "cms_pages",
120 params(
121 ("page_id" = Uuid, Path, description = "Page id")
122 ),
123 request_body = CmsPageUpdate,
124 responses(
125 (status = 200, description = "Updated CMS page", body = ContentManagementPage)
126 )
127)]
128async fn update_page(
129 request_id: RequestId,
130 payload: web::Json<CmsPageUpdate>,
131 page_id: web::Path<Uuid>,
132 pool: web::Data<PgPool>,
133 jwt_key: web::Data<JwtKey>,
134 app_conf: web::Data<ApplicationConfiguration>,
135 user: AuthUser,
136) -> ControllerResult<web::Json<ContentManagementPage>> {
137 let mut conn = pool.acquire().await?;
138 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
139
140 let cms_page_update = payload.0;
141 let course_or_exam_id = models::pages::get_course_and_exam_id(&mut conn, *page_id).await?;
142 let is_exam_page = matches!(course_or_exam_id, CourseOrExamId::Exam(_));
143 let (expected_course_id, expected_exam_id) = match course_or_exam_id {
144 CourseOrExamId::Course(course_id) => (Some(course_id), None),
145 CourseOrExamId::Exam(exam_id) => (None, Some(exam_id)),
146 };
147 let saved = models::pages::update_by_id_in_parent_context(
148 &mut conn,
149 PageUpdateArgs {
150 page_id: *page_id,
151 author: user.id,
152 cms_page_update,
153 retain_ids: false,
154 history_change_reason: HistoryChangeReason::PageSaved,
155 is_exam_page,
156 },
157 expected_course_id,
158 expected_exam_id,
159 models_requests::make_spec_fetcher(
160 app_conf.base_url.clone(),
161 request_id.0,
162 jwt_key.into_inner(),
163 ),
164 models_requests::fetch_service_info,
165 )
166 .await?;
167 token.authorized_ok(web::Json(saved))
168}
169
170#[instrument(skip(pool))]
177#[utoipa::path(
178 post,
179 path = "/{page_id}/exercises-with-submissions",
180 operation_id = "getExercisesWithSubmissions",
181 tag = "cms_pages",
182 params(
183 ("page_id" = Uuid, Path, description = "Page id")
184 ),
185 request_body = Vec<Uuid>,
186 responses(
187 (status = 200, description = "Exercise ids, among the given ones, that have submissions", body = Vec<Uuid>)
188 )
189)]
190async fn get_exercises_with_submissions(
191 exercise_ids: web::Json<Vec<Uuid>>,
192 page_id: web::Path<Uuid>,
193 pool: web::Data<PgPool>,
194 user: AuthUser,
195) -> ControllerResult<web::Json<Vec<Uuid>>> {
196 let mut conn = pool.acquire().await?;
197 let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::Page(*page_id)).await?;
198
199 let exercise_ids_with_submissions =
200 models::exercise_slide_submissions::get_exercise_ids_with_any_submissions(
201 &mut conn,
202 *page_id,
203 &exercise_ids.0,
204 )
205 .await?;
206
207 token.authorized_ok(web::Json(exercise_ids_with_submissions))
208}
209
210#[instrument(skip(pool))]
214#[utoipa::path(
215 get,
216 path = "/{page_id}/page-navigation",
217 operation_id = "getCmsPageNavigation",
218 tag = "cms_pages",
219 params(
220 ("page_id" = Uuid, Path, description = "Page id")
221 ),
222 responses(
223 (status = 200, description = "Page navigation", body = PageNavigationInformation)
224 )
225)]
226async fn get_page_navigation(
227 page_id: web::Path<Uuid>,
228 pool: web::Data<PgPool>,
229) -> ControllerResult<web::Json<PageNavigationInformation>> {
230 let mut conn = pool.acquire().await?;
231 let token = skip_authorize();
232 let res =
233 models::pages::get_page_navigation_data(&mut conn, *page_id, PageVisibility::Any).await?;
234
235 token.authorized_ok(web::Json(res))
236}
237pub fn _add_routes(cfg: &mut ServiceConfig) {
245 cfg.route("/{page_id}", web::get().to(get_page))
246 .route("/{page_id}/info", web::get().to(get_page_info))
247 .route(
248 "/{page_id}/page-navigation",
249 web::get().to(get_page_navigation),
250 )
251 .route("/{page_id}", web::put().to(update_page))
252 .route(
253 "/{page_id}/exercises-with-submissions",
254 web::post().to(get_exercises_with_submissions),
255 );
256}