headless_lms_server/controllers/main_frontend/
exercise_services.rs

1//! Controllers for requests starting with `/api/v0/main-frontend/exercise-services/`.
2
3use headless_lms_models::{ModelError, exercise_service_info::fetch_and_upsert_service_info};
4use models::exercise_services::{ExerciseService, ExerciseServiceNewOrUpdate};
5
6use crate::{domain::models_requests, prelude::*};
7
8/**
9DELETE `/api/v0/main-frontend/exercise-services/:id`
10*/
11#[instrument(skip(pool))]
12async fn delete_exercise_service(
13    exercise_service_id: web::Path<Uuid>,
14    pool: web::Data<PgPool>,
15    user: AuthUser,
16) -> ControllerResult<web::Json<ExerciseService>> {
17    let mut conn = pool.acquire().await?;
18    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::ExerciseService).await?;
19
20    let deleted =
21        models::exercise_services::delete_exercise_service(&mut conn, *exercise_service_id).await?;
22
23    token.authorized_ok(web::Json(deleted))
24}
25
26#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
27#[cfg_attr(feature = "ts_rs", derive(TS))]
28pub struct ExerciseServiceWithError {
29    exercise_service: ExerciseService,
30    service_info_error: Option<String>,
31}
32
33/**
34POST `/api/v0/main-frontend/exercise-services`
35*/
36#[instrument(skip(pool))]
37async fn add_exercise_service(
38    pool: web::Data<PgPool>,
39    user: AuthUser,
40    payload: web::Json<ExerciseServiceNewOrUpdate>,
41) -> ControllerResult<web::Json<ExerciseServiceWithError>> {
42    let mut conn = pool.acquire().await?;
43    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::ExerciseService).await?;
44
45    let exercise_service = payload.0;
46    let exercise_service =
47        models::exercise_services::insert_exercise_service(&mut conn, &exercise_service).await?;
48
49    let exercise_service_with_error =
50        try_to_get_exercise_service_info(&mut conn, exercise_service).await?;
51    token.authorized_ok(web::Json(exercise_service_with_error))
52}
53
54/**
55GET `/api/v0/main-frontend/exercise-services/:id`
56*/
57#[instrument(skip(pool))]
58async fn get_exercise_service_by_id(
59    exercise_service_id: web::Path<Uuid>,
60    pool: web::Data<PgPool>,
61    user: AuthUser,
62) -> ControllerResult<web::Json<ExerciseService>> {
63    let mut conn = pool.acquire().await?;
64    let exercise_service =
65        models::exercise_services::get_exercise_service(&mut conn, *exercise_service_id).await?;
66
67    let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::ExerciseService).await?;
68    token.authorized_ok(web::Json(exercise_service))
69}
70
71/**
72GET `/api/v0/main-frontend/exercise-services`
73*/
74#[instrument(skip(pool))]
75async fn get_exercise_services(
76    pool: web::Data<PgPool>,
77    user: AuthUser,
78) -> ControllerResult<web::Json<Vec<ExerciseService>>> {
79    let mut conn = pool.acquire().await?;
80    let exercise_services = models::exercise_services::get_exercise_services(&mut conn).await?;
81
82    let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::ExerciseService).await?;
83    token.authorized_ok(web::Json(exercise_services))
84}
85
86/**
87PUT `/api/v0/main-frontend/exercise-services/:id`
88*/
89#[instrument(skip(pool))]
90async fn update_exercise_service(
91    payload: web::Json<ExerciseServiceNewOrUpdate>,
92    exercise_service_id: web::Path<Uuid>,
93    pool: web::Data<PgPool>,
94    user: AuthUser,
95) -> ControllerResult<web::Json<ExerciseServiceWithError>> {
96    let mut conn = pool.acquire().await?;
97    let token = authorize(&mut conn, Act::Edit, Some(user.id), Res::ExerciseService).await?;
98
99    let updated_exercise_service = payload.0;
100    let updated_service = models::exercise_services::update_exercise_service(
101        &mut conn,
102        *exercise_service_id,
103        &updated_exercise_service,
104    )
105    .await?;
106
107    let exercise_service_with_error =
108        try_to_get_exercise_service_info(&mut conn, updated_service).await?;
109    token.authorized_ok(web::Json(exercise_service_with_error))
110}
111
112async fn try_to_get_exercise_service_info(
113    conn: &mut PgConnection,
114    exercise_service: ExerciseService,
115) -> Result<ExerciseServiceWithError, ModelError> {
116    // try to get exercise service info
117    let service_info_error = if let Err(err) = fetch_and_upsert_service_info(
118        conn,
119        &exercise_service,
120        models_requests::fetch_service_info_fast,
121    )
122    .await
123    {
124        tracing::warn!(
125            "Failed to get exercise service info for new exercise service {}: {err}",
126            exercise_service.name
127        );
128        Some(err.to_string())
129    } else {
130        None
131    };
132
133    let with_error = ExerciseServiceWithError {
134        exercise_service,
135        service_info_error,
136    };
137    Ok(with_error)
138}
139
140/**
141Add a route for each controller in this module.
142
143The name starts with an underline in order to appear before other functions in the module documentation.
144
145We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
146*/
147pub fn _add_routes(cfg: &mut ServiceConfig) {
148    cfg.route("/", web::post().to(add_exercise_service))
149        .route("/", web::get().to(get_exercise_services))
150        .route(
151            "/{exercise_service_id}",
152            web::delete().to(delete_exercise_service),
153        )
154        .route(
155            "/{exercise_service_id}",
156            web::put().to(update_exercise_service),
157        )
158        .route(
159            "/{exercise_service_id}",
160            web::get().to(get_exercise_service_by_id),
161        );
162}