1use super::DetectorGroupModel;
16use serde::{Deserialize, Serialize};
17use snafu::ResultExt;
18use sqlx::FromRow;
19use sqlx::{Pool, Postgres, QueryBuilder};
20use std::collections::HashMap;
21use tibba_model::{
22 Error, JsonSnafu, Model, ModelListParams, Schema, SchemaAllowCreate, SchemaAllowEdit,
23 SchemaOption, SchemaOptionValue, SchemaType, SchemaView, SqlxSnafu, format_datetime,
24 parse_primitive_datetime,
25};
26use time::PrimitiveDateTime;
27
28type Result<T> = std::result::Result<T, Error>;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum DetectorGroupRole {
33 Owner = 1,
34 Admin = 2,
35 Member = 3,
36 Viewer = 4,
37}
38
39impl DetectorGroupRole {
40 pub fn to_i16(self) -> i16 {
51 self as i16
52 }
53
54 pub fn as_str(self) -> &'static str {
55 match self {
56 Self::Owner => "owner",
57 Self::Admin => "admin",
58 Self::Member => "member",
59 Self::Viewer => "viewer",
60 }
61 }
62}
63
64impl std::fmt::Display for DetectorGroupRole {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{}", self.as_str())
67 }
68}
69
70#[derive(FromRow)]
71struct DetectorGroupUserSchema {
72 id: i64,
73 user_id: i64,
74 group_id: i64,
75 role: i16,
76 status: i16,
77 effective_start_time: PrimitiveDateTime,
78 effective_end_time: PrimitiveDateTime,
79 invited_by: i64,
80 remark: String,
81 created: PrimitiveDateTime,
82 modified: PrimitiveDateTime,
83}
84
85#[derive(Deserialize, Serialize, Clone, Debug)]
86pub struct DetectorGroupUser {
87 pub id: i64,
88 pub user_id: i64,
89 pub group_id: i64,
90 pub role: i16,
91 pub status: i16,
92 pub effective_start_time: String,
93 pub effective_end_time: String,
94 pub invited_by: i64,
95 pub remark: String,
96 pub created: String,
97 pub modified: String,
98}
99
100impl From<DetectorGroupUserSchema> for DetectorGroupUser {
101 fn from(schema: DetectorGroupUserSchema) -> Self {
102 Self {
103 id: schema.id,
104 user_id: schema.user_id,
105 group_id: schema.group_id,
106 role: schema.role,
107 status: schema.status,
108 effective_start_time: format_datetime(schema.effective_start_time),
109 effective_end_time: format_datetime(schema.effective_end_time),
110 invited_by: schema.invited_by,
111 remark: schema.remark,
112 created: format_datetime(schema.created),
113 modified: format_datetime(schema.modified),
114 }
115 }
116}
117
118#[derive(Deserialize, Serialize, Clone, Debug)]
119pub struct DetectorGroupUserInsertParams {
120 pub user_id: u64,
121 pub group_id: u64,
122 pub role: i16,
123 pub status: i16,
124 pub effective_start_time: String,
125 pub effective_end_time: String,
126 pub invited_by: u64,
127 pub created_by: u64,
128 pub remark: String,
129}
130
131#[derive(Deserialize, Serialize, Clone, Debug)]
132pub struct DetectorGroupUserUpdateParams {
133 pub role: Option<i16>,
134 pub status: Option<i16>,
135 pub effective_start_time: Option<String>,
136 pub effective_end_time: Option<String>,
137 pub invited_by: Option<u64>,
138 pub remark: Option<String>,
139}
140
141pub struct DetectorGroupUserModel {}
142
143impl Model for DetectorGroupUserModel {
144 type Output = DetectorGroupUser;
145 fn new() -> Self {
146 Self {}
147 }
148 async fn schema_view(&self, pool: &Pool<Postgres>) -> SchemaView {
149 let mut group_options = vec![];
150 if let Ok(groups) = DetectorGroupModel::new().list_enabled(pool).await {
151 for group in groups {
152 group_options.push(SchemaOption {
153 label: group.name,
154 value: SchemaOptionValue::Integer(group.id),
155 });
156 }
157 group_options.sort_by_key(|option| option.label.clone());
158 }
159 SchemaView {
160 schemas: vec![
161 Schema::new_id(),
162 Schema::new_user_search("user_id"),
163 Schema {
164 name: "group_id".to_string(),
165 category: SchemaType::Number,
166 required: true,
167 options: Some(group_options),
168 ..Default::default()
169 },
170 Schema {
171 name: "role".to_string(),
172 category: SchemaType::Number,
173 options: Some(vec![
174 SchemaOption {
175 label: DetectorGroupRole::Owner.to_string(),
176 value: SchemaOptionValue::Integer(
177 DetectorGroupRole::Owner.to_i16() as i64
178 ),
179 },
180 SchemaOption {
181 label: DetectorGroupRole::Admin.to_string(),
182 value: SchemaOptionValue::Integer(
183 DetectorGroupRole::Admin.to_i16() as i64
184 ),
185 },
186 SchemaOption {
187 label: DetectorGroupRole::Member.to_string(),
188 value: SchemaOptionValue::Integer(
189 DetectorGroupRole::Member.to_i16() as i64
190 ),
191 },
192 SchemaOption {
193 label: DetectorGroupRole::Viewer.to_string(),
194 value: SchemaOptionValue::Integer(
195 DetectorGroupRole::Viewer.to_i16() as i64
196 ),
197 },
198 ]),
199 ..Default::default()
200 },
201 Schema::new_status(),
202 Schema::new_effective_start_time(),
203 Schema::new_effective_end_time(),
204 Schema::new_remark(),
205 Schema::new_created(),
206 ],
207 allow_edit: SchemaAllowEdit {
208 owner: true,
209 roles: vec!["*".to_string()],
210 ..Default::default()
211 },
212 allow_create: SchemaAllowCreate {
213 roles: vec!["*".to_string()],
214 ..Default::default()
215 },
216 }
217 }
218
219 fn push_filter_conditions<'args>(
220 &self,
221 qb: &mut QueryBuilder<'args, Postgres>,
222 filters: &HashMap<String, String>,
223 ) -> Result<()> {
224 if let Some(status) = filters.get("status").and_then(|s| s.parse::<i16>().ok()) {
225 qb.push(" AND status = ");
226 qb.push_bind(status);
227 }
228 if let Some(group_id) = filters.get("group_id").and_then(|s| s.parse::<i64>().ok()) {
229 qb.push(" AND group_id = ");
230 qb.push_bind(group_id);
231 }
232 Ok(())
233 }
234
235 async fn insert(&self, pool: &Pool<Postgres>, mut params: serde_json::Value) -> Result<u64> {
236 if let Some(obj) = params.as_object_mut() {
239 if !obj.contains_key("invited_by") {
240 if let Some(created_by) = obj.get("created_by").cloned() {
241 obj.insert("invited_by".to_string(), created_by);
242 }
243 }
244 if let Some(id_str) = obj.get("user_id").and_then(|v| v.as_str()) {
245 if let Ok(id) = id_str.parse::<u64>() {
246 obj.insert("user_id".to_string(), id.into());
247 }
248 }
249 }
250 let params: DetectorGroupUserInsertParams =
251 serde_json::from_value(params).context(JsonSnafu)?;
252 let row: (i64,) = sqlx::query_as(
253 r#"INSERT INTO detector_group_users (user_id, group_id, role, status, effective_start_time, effective_end_time, invited_by, created_by, remark) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id"#,
254 )
255 .bind(params.user_id as i64)
256 .bind(params.group_id as i64)
257 .bind(params.role)
258 .bind(params.status)
259 .bind(parse_primitive_datetime(¶ms.effective_start_time)?)
260 .bind(parse_primitive_datetime(¶ms.effective_end_time)?)
261 .bind(params.invited_by as i64)
262 .bind(params.created_by as i64)
263 .bind(params.remark)
264 .fetch_one(pool)
265 .await
266 .context(SqlxSnafu)?;
267
268 Ok(row.0 as u64)
269 }
270
271 async fn get_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<Option<Self::Output>> {
272 let result = sqlx::query_as::<_, DetectorGroupUserSchema>(
273 r#"SELECT * FROM detector_group_users WHERE id = $1 AND deleted_at IS NULL"#,
274 )
275 .bind(id as i64)
276 .fetch_optional(pool)
277 .await
278 .context(SqlxSnafu)?;
279
280 Ok(result.map(|schema| schema.into()))
281 }
282
283 async fn delete_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<()> {
284 sqlx::query(
285 r#"UPDATE detector_group_users SET deleted_at = NOW(), modified = NOW() WHERE id = $1"#,
286 )
287 .bind(id as i64)
288 .execute(pool)
289 .await
290 .context(SqlxSnafu)?;
291
292 Ok(())
293 }
294
295 async fn update_by_id(
296 &self,
297 pool: &Pool<Postgres>,
298 id: u64,
299 params: serde_json::Value,
300 ) -> Result<()> {
301 let params: DetectorGroupUserUpdateParams =
302 serde_json::from_value(params).context(JsonSnafu)?;
303
304 let _ = sqlx::query(
305 r#"UPDATE detector_group_users SET role = COALESCE($1, role), status = COALESCE($2, status), effective_start_time = COALESCE($3, effective_start_time), effective_end_time = COALESCE($4, effective_end_time), invited_by = COALESCE($5, invited_by), remark = COALESCE($6, remark), modified = NOW() WHERE id = $7 AND deleted_at IS NULL"#,
306 )
307 .bind(params.role)
308 .bind(params.status)
309 .bind(params.effective_start_time.as_deref().map(parse_primitive_datetime).transpose()?)
310 .bind(params.effective_end_time.as_deref().map(parse_primitive_datetime).transpose()?)
311 .bind(params.invited_by.map(|v| v as i64))
312 .bind(params.remark)
313 .bind(id as i64)
314 .execute(pool)
315 .await
316 .context(SqlxSnafu)?;
317
318 Ok(())
319 }
320
321 async fn count(&self, pool: &Pool<Postgres>, params: &ModelListParams) -> Result<i64> {
322 let mut qb = QueryBuilder::new("SELECT COUNT(*) FROM detector_group_users");
323 self.push_conditions(&mut qb, params)?;
324 let count = qb
325 .build_query_scalar::<i64>()
326 .fetch_one(pool)
327 .await
328 .context(SqlxSnafu)?;
329 Ok(count)
330 }
331
332 async fn list(
333 &self,
334 pool: &Pool<Postgres>,
335 params: &ModelListParams,
336 ) -> Result<Vec<Self::Output>> {
337 let mut qb = QueryBuilder::new("SELECT * FROM detector_group_users");
338 self.push_conditions(&mut qb, params)?;
339 params.push_pagination(&mut qb);
340 let users = qb
341 .build_query_as::<DetectorGroupUserSchema>()
342 .fetch_all(pool)
343 .await
344 .context(SqlxSnafu)?;
345 Ok(users.into_iter().map(|s| s.into()).collect())
346 }
347}