Skip to main content

headless_lms_models/
pending_roles.rs

1use crate::{
2    prelude::*,
3    roles::{RoleDomain, RoleInfo, UserRole},
4};
5use utoipa::ToSchema;
6
7#[derive(Debug, Serialize, ToSchema)]
8
9pub struct PendingRole {
10    pub id: Uuid,
11    pub created_at: DateTime<Utc>,
12    pub updated_at: DateTime<Utc>,
13    pub deleted_at: Option<DateTime<Utc>>,
14    pub user_email: String,
15    pub role: UserRole,
16    pub course_id: Option<Uuid>,
17    pub course_instance_id: Option<Uuid>,
18    pub expires_at: DateTime<Utc>,
19}
20
21pub async fn insert(
22    conn: &mut PgConnection,
23    pkey_policy: PKeyPolicy<Uuid>,
24    role_info: RoleInfo,
25) -> ModelResult<Uuid> {
26    match role_info.domain {
27        crate::roles::RoleDomain::Global
28        | crate::roles::RoleDomain::Organization(_)
29        | crate::roles::RoleDomain::Exam(_) => {
30            return Err(ModelError::new(
31                ModelErrorType::InvalidRequest,
32                "Cannot use a pending role for a role this broad".to_string(),
33                None,
34            ));
35        }
36
37        crate::roles::RoleDomain::Course(_) | crate::roles::RoleDomain::CourseInstance(_) => (),
38    };
39
40    match role_info.role {
41        UserRole::Admin | UserRole::Teacher => {
42            return Err(ModelError::new(
43                ModelErrorType::InvalidRequest,
44                "Cannot use a pending role with this much power".to_string(),
45                None,
46            ));
47        }
48        _ => (),
49    }
50
51    let course_id = match role_info.domain {
52        crate::roles::RoleDomain::Course(id) => Some(id),
53        _ => None,
54    };
55
56    let course_instance_id = match role_info.domain {
57        crate::roles::RoleDomain::CourseInstance(id) => Some(id),
58        _ => None,
59    };
60
61    let id = sqlx::query!(
62        r#"
63INSERT INTO pending_roles (
64    id,
65    user_email,
66    role,
67    course_id,
68    course_instance_id
69  )
70VALUES ($1, $2, $3, $4, $5)
71RETURNING *;
72        "#,
73        pkey_policy.into_uuid(),
74        role_info.email,
75        role_info.role as UserRole,
76        course_id,
77        course_instance_id
78    )
79    .fetch_one(conn)
80    .await?
81    .id;
82    Ok(id)
83}
84
85pub async fn get_all(conn: &mut PgConnection, domain: RoleDomain) -> ModelResult<Vec<PendingRole>> {
86    let res = match domain {
87        RoleDomain::Global | RoleDomain::Organization(_) | RoleDomain::Exam(_) => {
88            return Ok(Vec::new());
89        }
90        RoleDomain::Course(course_id) => {
91            sqlx::query_as!(
92                PendingRole,
93                r#"
94SELECT * FROM pending_roles
95WHERE course_id = $1
96AND deleted_at IS NULL
97AND expires_at > NOW()
98          "#,
99                course_id
100            )
101            .fetch_all(&mut *conn)
102            .await?
103        }
104        RoleDomain::CourseInstance(course_instance_id) => {
105            sqlx::query_as!(
106                PendingRole,
107                r#"
108SELECT * FROM pending_roles
109WHERE course_instance_id = $1
110AND deleted_at IS NULL
111AND expires_at > NOW()
112        "#,
113                course_instance_id
114            )
115            .fetch_all(&mut *conn)
116            .await?
117        }
118    };
119    Ok(res)
120}