headless_lms_models/
exercise_repositories.rs1use crate::prelude::*;
2use secrecy::{ExposeSecret, SecretString};
3use utoipa::ToSchema;
4
5#[derive(Debug, Serialize, sqlx::Type, ToSchema)]
6#[sqlx(type_name = "exercise_repository_status", rename_all = "kebab-case")]
7pub enum ExerciseRepositoryStatus {
8 Pending,
9 Success,
10 Failure,
11}
12
13#[derive(Debug, Serialize, ToSchema)]
14
15pub struct ExerciseRepository {
16 pub id: Uuid,
17 pub url: String,
18 pub course_id: Option<Uuid>,
19 pub exam_id: Option<Uuid>,
20 pub status: ExerciseRepositoryStatus,
21 pub error_message: Option<String>,
22}
23
24pub async fn get(conn: &mut PgConnection, id: Uuid) -> ModelResult<ExerciseRepository> {
25 let res = sqlx::query_as!(
26 ExerciseRepository,
27 r#"
28SELECT id,
29 url,
30 course_id,
31 exam_id,
32 status,
33 error_message
34FROM exercise_repositories
35WHERE id = $1
36 AND deleted_at IS NULL
37"#,
38 id
39 )
40 .fetch_one(conn)
41 .await?;
42 Ok(res)
43}
44
45pub async fn new(
46 conn: &mut PgConnection,
47 id: Uuid,
48 course_or_exam_id: CourseOrExamId,
49 url: &str,
50 public_key: Option<&str>,
51 deploy_key: Option<&SecretString>,
52) -> ModelResult<()> {
53 let (course_id, exam_id) = course_or_exam_id.to_course_and_exam_ids();
54 sqlx::query!(
55 "
56INSERT INTO exercise_repositories (id, course_id, exam_id, url, public_key, deploy_key)
57VALUES ($1, $2, $3, $4, $5, $6)
58",
59 id,
60 course_id,
61 exam_id,
62 url,
63 public_key,
64 deploy_key.map(|k| k.expose_secret())
66 )
67 .execute(conn)
68 .await?;
69 Ok(())
70}
71
72pub async fn mark_success(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
73 sqlx::query!(
74 "
75UPDATE exercise_repositories
76SET status = 'success'
77WHERE id = $1
78",
79 id
80 )
81 .execute(conn)
82 .await?;
83 Ok(())
84}
85
86pub async fn mark_failure(
87 conn: &mut PgConnection,
88 id: Uuid,
89 error_message: &str,
90) -> ModelResult<()> {
91 sqlx::query!(
92 "
93UPDATE exercise_repositories
94SET status = 'failure',
95 error_message = $2
96WHERE id = $1
97",
98 id,
99 error_message
100 )
101 .execute(conn)
102 .await?;
103 Ok(())
104}
105
106pub async fn delete(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
107 sqlx::query!(
108 "
109UPDATE exercise_repositories
110SET deleted_at = now()
111WHERE id = $1
112AND deleted_at IS NULL
113",
114 id
115 )
116 .execute(conn)
117 .await?;
118 Ok(())
119}
120
121pub async fn get_for_course_or_exam(
122 conn: &mut PgConnection,
123 id: CourseOrExamId,
124) -> ModelResult<Vec<ExerciseRepository>> {
125 let (course_id, exam_id) = id.to_course_and_exam_ids();
126 let res = sqlx::query_as!(
127 ExerciseRepository,
128 r#"
129SELECT id,
130 url,
131 course_id,
132 exam_id,
133 status,
134 error_message
135FROM exercise_repositories
136WHERE (
137 course_id = $1
138 OR exam_id = $2
139 )
140 AND deleted_at IS NULL
141"#,
142 course_id,
143 exam_id
144 )
145 .fetch_all(conn)
146 .await?;
147 Ok(res)
148}
149
150#[derive(Debug, Deserialize, ToSchema)]
151pub struct ExerciseRepositoryUpdate {
152 pub url: String,
153}
154
155pub async fn update(
156 conn: &mut PgConnection,
157 id: Uuid,
158 update: &ExerciseRepositoryUpdate,
159) -> ModelResult<()> {
160 sqlx::query!(
161 "
162UPDATE exercise_repositories SET url = $1 WHERE id = $2
163",
164 update.url,
165 id
166 )
167 .execute(conn)
168 .await?;
169 Ok(())
170}