use super::{
DetectorGroupModel, Error, JsonSnafu, Model, ModelListParams, Schema, SchemaAllowCreate,
SchemaAllowEdit, SchemaOption, SchemaOptionValue, SchemaType, SchemaView, SqlxSnafu,
format_datetime, parse_primitive_datetime,
};
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use sqlx::FromRow;
use sqlx::{Pool, Postgres, QueryBuilder};
use std::collections::HashMap;
use time::PrimitiveDateTime;
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DetectorGroupRole {
Owner = 1,
Admin = 2,
Member = 3,
Viewer = 4,
}
impl DetectorGroupRole {
pub fn to_i16(self) -> i16 {
self as i16
}
pub fn as_str(self) -> &'static str {
match self {
Self::Owner => "owner",
Self::Admin => "admin",
Self::Member => "member",
Self::Viewer => "viewer",
}
}
}
impl std::fmt::Display for DetectorGroupRole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(FromRow)]
struct DetectorGroupUserSchema {
id: i64,
user_id: i64,
group_id: i64,
role: i16,
status: i16,
effective_start_time: PrimitiveDateTime,
effective_end_time: PrimitiveDateTime,
invited_by: i64,
remark: String,
created: PrimitiveDateTime,
modified: PrimitiveDateTime,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct DetectorGroupUser {
pub id: i64,
pub user_id: i64,
pub group_id: i64,
pub role: i16,
pub status: i16,
pub effective_start_time: String,
pub effective_end_time: String,
pub invited_by: i64,
pub remark: String,
pub created: String,
pub modified: String,
}
impl From<DetectorGroupUserSchema> for DetectorGroupUser {
fn from(schema: DetectorGroupUserSchema) -> Self {
Self {
id: schema.id,
user_id: schema.user_id,
group_id: schema.group_id,
role: schema.role,
status: schema.status,
effective_start_time: format_datetime(schema.effective_start_time),
effective_end_time: format_datetime(schema.effective_end_time),
invited_by: schema.invited_by,
remark: schema.remark,
created: format_datetime(schema.created),
modified: format_datetime(schema.modified),
}
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct DetectorGroupUserInsertParams {
pub user_id: u64,
pub group_id: u64,
pub role: i16,
pub status: i16,
pub effective_start_time: String,
pub effective_end_time: String,
pub invited_by: u64,
pub created_by: u64,
pub remark: String,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct DetectorGroupUserUpdateParams {
pub role: Option<i16>,
pub status: Option<i16>,
pub effective_start_time: Option<String>,
pub effective_end_time: Option<String>,
pub invited_by: Option<u64>,
pub remark: Option<String>,
}
pub struct DetectorGroupUserModel {}
impl Model for DetectorGroupUserModel {
type Output = DetectorGroupUser;
fn new() -> Self {
Self {}
}
async fn schema_view(&self, pool: &Pool<Postgres>) -> SchemaView {
let mut group_options = vec![];
if let Ok(groups) = DetectorGroupModel::new().list_enabled(pool).await {
for group in groups {
group_options.push(SchemaOption {
label: group.name,
value: SchemaOptionValue::Integer(group.id),
});
}
group_options.sort_by_key(|option| option.label.clone());
}
SchemaView {
schemas: vec![
Schema::new_id(),
Schema::new_user_search("user_id"),
Schema {
name: "group_id".to_string(),
category: SchemaType::Number,
required: true,
options: Some(group_options),
..Default::default()
},
Schema {
name: "role".to_string(),
category: SchemaType::Number,
options: Some(vec![
SchemaOption {
label: DetectorGroupRole::Owner.to_string(),
value: SchemaOptionValue::Integer(
DetectorGroupRole::Owner.to_i16() as i64
),
},
SchemaOption {
label: DetectorGroupRole::Admin.to_string(),
value: SchemaOptionValue::Integer(
DetectorGroupRole::Admin.to_i16() as i64
),
},
SchemaOption {
label: DetectorGroupRole::Member.to_string(),
value: SchemaOptionValue::Integer(
DetectorGroupRole::Member.to_i16() as i64
),
},
SchemaOption {
label: DetectorGroupRole::Viewer.to_string(),
value: SchemaOptionValue::Integer(
DetectorGroupRole::Viewer.to_i16() as i64
),
},
]),
..Default::default()
},
Schema::new_status(),
Schema::new_effective_start_time(),
Schema::new_effective_end_time(),
Schema::new_remark(),
Schema::new_created(),
],
allow_edit: SchemaAllowEdit {
owner: true,
roles: vec!["*".to_string()],
..Default::default()
},
allow_create: SchemaAllowCreate {
roles: vec!["*".to_string()],
..Default::default()
},
}
}
fn push_filter_conditions<'args>(
&self,
qb: &mut QueryBuilder<'args, Postgres>,
filters: &HashMap<String, String>,
) -> Result<()> {
if let Some(status) = filters.get("status").and_then(|s| s.parse::<i16>().ok()) {
qb.push(" AND status = ");
qb.push_bind(status);
}
if let Some(group_id) = filters.get("group_id").and_then(|s| s.parse::<i64>().ok()) {
qb.push(" AND group_id = ");
qb.push_bind(group_id);
}
Ok(())
}
async fn insert(&self, pool: &Pool<Postgres>, params: serde_json::Value) -> Result<u64> {
let params: DetectorGroupUserInsertParams =
serde_json::from_value(params).context(JsonSnafu)?;
let row: (i64,) = sqlx::query_as(
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"#,
)
.bind(params.user_id as i64)
.bind(params.group_id as i64)
.bind(params.role)
.bind(params.status)
.bind(parse_primitive_datetime(¶ms.effective_start_time)?)
.bind(parse_primitive_datetime(¶ms.effective_end_time)?)
.bind(params.invited_by as i64)
.bind(params.created_by as i64)
.bind(params.remark)
.fetch_one(pool)
.await
.context(SqlxSnafu)?;
Ok(row.0 as u64)
}
async fn get_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<Option<Self::Output>> {
let result = sqlx::query_as::<_, DetectorGroupUserSchema>(
r#"SELECT * FROM detector_group_users WHERE id = $1 AND deleted_at IS NULL"#,
)
.bind(id as i64)
.fetch_optional(pool)
.await
.context(SqlxSnafu)?;
Ok(result.map(|schema| schema.into()))
}
async fn delete_by_id(&self, pool: &Pool<Postgres>, id: u64) -> Result<()> {
sqlx::query(r#"UPDATE detector_group_users SET deleted_at = NOW() WHERE id = $1"#)
.bind(id as i64)
.execute(pool)
.await
.context(SqlxSnafu)?;
Ok(())
}
async fn update_by_id(
&self,
pool: &Pool<Postgres>,
id: u64,
params: serde_json::Value,
) -> Result<()> {
let params: DetectorGroupUserUpdateParams =
serde_json::from_value(params).context(JsonSnafu)?;
let _ = sqlx::query(
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) WHERE id = $7 AND deleted_at IS NULL"#,
)
.bind(params.role)
.bind(params.status)
.bind(params.effective_start_time.as_deref().map(parse_primitive_datetime).transpose()?)
.bind(params.effective_end_time.as_deref().map(parse_primitive_datetime).transpose()?)
.bind(params.invited_by.map(|v| v as i64))
.bind(params.remark)
.bind(id as i64)
.execute(pool)
.await
.context(SqlxSnafu)?;
Ok(())
}
async fn count(&self, pool: &Pool<Postgres>, params: &ModelListParams) -> Result<i64> {
let mut qb = QueryBuilder::new("SELECT COUNT(*) FROM detector_group_users");
self.push_conditions(&mut qb, params)?;
let count = qb
.build_query_scalar::<i64>()
.fetch_one(pool)
.await
.context(SqlxSnafu)?;
Ok(count)
}
async fn list(
&self,
pool: &Pool<Postgres>,
params: &ModelListParams,
) -> Result<Vec<Self::Output>> {
let mut qb = QueryBuilder::new("SELECT * FROM detector_group_users");
self.push_conditions(&mut qb, params)?;
params.push_pagination(&mut qb);
let users = qb
.build_query_as::<DetectorGroupUserSchema>()
.fetch_all(pool)
.await
.context(SqlxSnafu)?;
Ok(users.into_iter().map(|s| s.into()).collect())
}
}