1use sea_orm::entity::prelude::*;
2use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, DeleteResult};
3use strum::{Display, EnumString};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
9#[sea_orm(table_name = "user_module_roles")]
10pub struct Model {
11 #[sea_orm(primary_key, auto_increment = false)]
13 pub user_id: i64,
14
15 #[sea_orm(primary_key, auto_increment = false)]
17 pub module_id: i64,
18
19 pub role: Role,
21}
22
23#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum, Display, EnumString, Deserialize, Serialize)]
26#[serde(rename_all = "snake_case")]
27#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "user_module_role_type")]
28#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
29pub enum Role {
30 #[sea_orm(string_value = "lecturer")]
31 Lecturer,
32
33 #[sea_orm(string_value = "assistant_lecturer")]
34 AssistantLecturer,
35
36 #[sea_orm(string_value = "tutor")]
37 Tutor,
38
39 #[sea_orm(string_value = "student")]
40 Student,
41}
42
43#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
45pub enum Relation {
46 #[sea_orm(
48 belongs_to = "super::user::Entity",
49 from = "Column::UserId",
50 to = "super::user::Column::Id"
51 )]
52 User,
53
54 #[sea_orm(
56 belongs_to = "super::module::Entity",
57 from = "Column::ModuleId",
58 to = "super::module::Column::Id"
59 )]
60 Module,
61}
62
63impl Related<super::module::Entity> for Entity {
64 fn to() -> RelationDef {
65 Relation::Module.def()
66 }
67
68 fn via() -> Option<RelationDef> {
69 None
70 }
71}
72
73impl ActiveModelBehavior for ActiveModel {}
75
76impl Model {
78 pub async fn assign_user_to_module(
80 db: &DatabaseConnection,
81 user_id: i64,
82 module_id: i64,
83 role: Role,
84 ) -> Result<Self, DbErr> {
85 let active = ActiveModel {
86 user_id: Set(user_id),
87 module_id: Set(module_id),
88 role: Set(role),
89 };
90 active.insert(db).await
91 }
92
93 pub async fn remove_user_from_module(
95 db: &DatabaseConnection,
96 user_id: i64,
97 module_id: i64,
98 ) -> Result<DeleteResult, DbErr> {
99 Entity::delete_many()
100 .filter(Column::UserId.eq(user_id))
101 .filter(Column::ModuleId.eq(module_id))
102 .exec(db)
103 .await
104 }
105
106 pub async fn get_all(db: &DatabaseConnection) -> Result<Vec<Self>, DbErr> {
108 Entity::find().all(db).await
109 }
110
111 pub async fn get_users_by_module_role(
113 db: &DatabaseConnection,
114 module_id: i32,
115 role: Role,
116 ) -> Result<Vec<Self>, DbErr> {
117 Entity::find()
118 .filter(Column::ModuleId.eq(module_id))
119 .filter(Column::Role.eq(role))
120 .all(db)
121 .await
122 }
123
124 pub async fn get_modules_by_user_role(
126 db: &DatabaseConnection,
127 user_id: i32,
128 role: Role,
129 ) -> Result<Vec<Self>, DbErr> {
130 Entity::find()
131 .filter(Column::UserId.eq(user_id))
132 .filter(Column::Role.eq(role))
133 .all(db)
134 .await
135 }
136}
137
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::models::{module, user};
143 use crate::test_utils::setup_test_db;
144
145 #[tokio::test]
146 async fn test_assign_and_get_user_module_role() {
147 let db = setup_test_db().await;
148
149 user::ActiveModel {
151 id: Set(1),
152 username: Set("s123456".to_string()),
153 email: Set("[email protected]".to_string()),
154 password_hash: Set("hash".to_string()),
155 admin: Set(false),
156 ..Default::default()
157 }
158 .insert(&db)
159 .await
160 .unwrap();
161
162 module::ActiveModel {
163 id: Set(1),
164 code: Set("COS301".to_string()),
165 year: Set(2025),
166 description: Set(Some("Capstone".to_string())),
167 credits: Set(30),
168 ..Default::default()
169 }.insert(&db).await.unwrap();
170
171 let assigned = Model::assign_user_to_module(&db, 1, 1, Role::Lecturer).await.unwrap();
172 assert_eq!(assigned.user_id, 1);
173 assert_eq!(assigned.module_id, 1);
174 assert_eq!(assigned.role, Role::Lecturer);
175
176 let fetched = Model::get_users_by_module_role(&db, 1, Role::Lecturer).await.unwrap();
177 assert_eq!(fetched.len(), 1);
178 assert_eq!(fetched[0].user_id, 1);
179 }
180
181 #[tokio::test]
182 async fn test_remove_user_module_role() {
183 let db = setup_test_db().await;
184
185 user::ActiveModel {
187 id: Set(2),
188 username: Set("s654321".to_string()),
189 email: Set("[email protected]".to_string()),
190 password_hash: Set("hash".to_string()),
191 admin: Set(false),
192 ..Default::default()
193 }
194 .insert(&db)
195 .await
196 .unwrap();
197
198 module::ActiveModel {
199 id: Set(2),
200 code: Set("COS333".to_string()),
201 year: Set(2025),
202 description: Set(Some("Networks".to_string())),
203 credits: Set(16),
204 ..Default::default()
205 }.insert(&db).await.unwrap();
206
207 Model::assign_user_to_module(&db, 2, 2, Role::Tutor).await.unwrap();
208 let result = Model::remove_user_from_module(&db, 2, 2).await.unwrap();
209 assert_eq!(result.rows_affected, 1);
210 }
211}