headless_lms_server/controllers/main_frontend/
proposed_edits.rs

1use std::sync::Arc;
2
3use models::proposed_page_edits::{self, EditProposalInfo, PageProposal, ProposalCount};
4
5use crate::{
6    domain::{
7        models_requests::{self, JwtKey},
8        request_id::RequestId,
9    },
10    prelude::*,
11};
12
13#[derive(Debug, Deserialize)]
14#[cfg_attr(feature = "ts_rs", derive(TS))]
15pub struct GetEditProposalsQuery {
16    pending: bool,
17    #[serde(flatten)]
18    pagination: Pagination,
19}
20
21/**
22GET `/api/v0/main-frontend/proposed-edits/course/:id?pending=true` - Returns feedback for the given course.
23*/
24#[instrument(skip(pool))]
25pub async fn get_edit_proposals(
26    course_id: web::Path<Uuid>,
27    pool: web::Data<PgPool>,
28    query: web::Query<GetEditProposalsQuery>,
29    user: AuthUser,
30) -> ControllerResult<web::Json<Vec<PageProposal>>> {
31    let mut conn = pool.acquire().await?;
32
33    let feedback = proposed_page_edits::get_proposals_for_course(
34        &mut conn,
35        *course_id,
36        query.pending,
37        query.pagination,
38    )
39    .await?;
40
41    let token = authorize(
42        &mut conn,
43        Act::Teach,
44        Some(user.id),
45        Res::Course(*course_id),
46    )
47    .await?;
48    token.authorized_ok(web::Json(feedback))
49}
50
51/**
52GET `/api/v0/main-frontend/proposed-edits/course/:id/count` - Returns the amount of feedback for the given course.
53*/
54#[instrument(skip(pool))]
55pub async fn get_edit_proposal_count(
56    course_id: web::Path<Uuid>,
57    pool: web::Data<PgPool>,
58    user: AuthUser,
59) -> ControllerResult<web::Json<ProposalCount>> {
60    let mut conn = pool.acquire().await?;
61
62    let edit_proposal_count =
63        proposed_page_edits::get_proposal_count_for_course(&mut conn, *course_id).await?;
64
65    let token = authorize(
66        &mut conn,
67        Act::Teach,
68        Some(user.id),
69        Res::Course(*course_id),
70    )
71    .await?;
72    token.authorized_ok(web::Json(edit_proposal_count))
73}
74
75/**
76POST `/api/v0/main-frontend/proposed-edits/process-edit-proposal` - Processes the given edit proposal.
77*/
78#[instrument(skip(pool, app_conf))]
79pub async fn process_edit_proposal(
80    request_id: RequestId,
81    proposal: web::Json<EditProposalInfo>,
82    app_conf: web::Data<ApplicationConfiguration>,
83    user: AuthUser,
84    pool: web::Data<PgPool>,
85    jwt_key: web::Data<JwtKey>,
86) -> ControllerResult<HttpResponse> {
87    let mut conn = pool.acquire().await?;
88    let proposal = proposal.into_inner();
89
90    proposed_page_edits::process_proposal(
91        &mut conn,
92        proposal.page_id,
93        proposal.page_proposal_id,
94        proposal.block_proposals,
95        user.id,
96        models_requests::make_spec_fetcher(
97            app_conf.base_url.clone(),
98            request_id.0,
99            Arc::clone(&jwt_key),
100        ),
101        models_requests::fetch_service_info,
102    )
103    .await?;
104
105    let token = authorize(
106        &mut conn,
107        Act::Edit,
108        Some(user.id),
109        Res::Page(proposal.page_id),
110    )
111    .await?;
112    token.authorized_ok(HttpResponse::Ok().finish())
113}
114
115/**
116Add a route for each controller in this module.
117
118The name starts with an underline in order to appear before other functions in the module documentation.
119
120We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
121*/
122pub fn _add_routes(cfg: &mut ServiceConfig) {
123    cfg.route("/course/{course_id}", web::get().to(get_edit_proposals))
124        .route(
125            "/course/{course_id}/count",
126            web::get().to(get_edit_proposal_count),
127        )
128        .route(
129            "/process-edit-proposal",
130            web::post().to(process_edit_proposal),
131        );
132}