1use crate::ctx::Ctx;
2use crate::model::ModelManager;
3use crate::model::Result;
4use crate::model::base::{self, DbBmc};
5use modql::field::Fields;
6use modql::filter::{FilterNodes, ListOptions, OpValsInt64};
7use serde::{Deserialize, Serialize};
8use sqlx::FromRow;
9
10impl MemberBmc {
11 #[must_use]
12 pub fn get_create_sql(drop_table: bool) -> String {
13 let table = Self::TABLE;
14 format!(
15 r##"{}
16create table if not exists "{table}" (
17 id serial primary key,
18 group_id integer NOT NULL,
19 user_id integer NOT NULL,
20 role smallint DEFAULT 0 NOT NULL,
21 all_panels boolean DEFAULT false NOT NULL,
22 activation_key character varying,
23 is_active boolean DEFAULT false NOT NULL,
24 created_at timestamp with time zone DEFAULT now() NOT NULL,
25 updated_at timestamp with time zone DEFAULT now() NOT NULL
26);
27ALTER TABLE ONLY member
28 ADD CONSTRAINT "UQ_member_group_id_and_user_id" UNIQUE (group_id, user_id);
29CREATE INDEX "IDX_member_activation_key" ON member USING btree (activation_key);
30CREATE INDEX "IDX_member_group_id" ON member USING btree (group_id);
31CREATE INDEX "IDX_member_is_active" ON member USING btree (is_active);
32CREATE INDEX "IDX_member_user_id" ON member USING btree (user_id);
33
34 "##,
35 if drop_table {
36 format!("drop table if exists {table};")
37 } else {
38 String::new()
39 }
40 )
41 }
42}
43
44#[derive(Debug, Clone, Fields, FromRow, Serialize, Default, Deserialize)]
45pub struct Member {
46 pub id: i32,
47
48 #[serde(rename = "groupId")]
49 pub group_id: i32,
50 #[serde(rename = "userId")]
51 pub user_id: i32,
52 pub role: i16,
53 #[serde(rename = "allPanels")]
54 pub all_panels: bool,
55 #[serde(rename = "activationKey")]
56 pub activation_key: Option<String>,
57 #[serde(rename = "isActive")]
58 pub is_active: bool,
59 #[serde(rename = "createdAt")]
60 pub created_at: chrono::DateTime<chrono::Utc>,
61 #[serde(rename = "updatedAt")]
62 pub updated_at: chrono::DateTime<chrono::Utc>,
63}
64
65#[derive(Fields, Deserialize, Debug)]
66pub struct MemberForCreate {
67 #[serde(rename = "groupId")]
68 pub group_id: i32,
69 #[serde(rename = "userId")]
70 pub user_id: i32,
71 pub role: i16,
72 #[serde(rename = "activationKey")]
73 pub activation_key: Option<String>,
74 #[serde(rename = "allPanels")]
75 pub all_panels: bool,
76 #[serde(rename = "isActive")]
77 pub is_active: bool,
78}
79
80#[derive(Fields, Default, Deserialize, Debug)]
81pub struct MemberForUpdate {
82 pub role: Option<i16>,
83 #[serde(rename = "activationKey")]
84 pub activation_key: Option<String>,
85 #[serde(rename = "allPanels")]
86 pub all_panels: bool,
87 #[serde(rename = "isActive")]
88 pub is_active: bool,
89}
90
91#[derive(FilterNodes, Deserialize, Default, Debug)]
92pub struct MemberFilter {
93 id: Option<OpValsInt64>,
94 user_id: Option<OpValsInt64>,
95 group_id: Option<OpValsInt64>,
96}
97
98pub struct MemberBmc;
99
100impl DbBmc for MemberBmc {
101 const TABLE: &'static str = "member";
102}
103
104impl MemberBmc {
105 pub async fn create(ctx: &Ctx, mm: &ModelManager, member_c: MemberForCreate) -> Result<i32> {
106 base::create::<Self, _>(ctx, mm, member_c).await
107 }
108 pub async fn create_full(ctx: &Ctx, mm: &ModelManager, member_c: Member) -> Result<i32> {
109 base::create::<Self, _>(ctx, mm, member_c).await
110 }
111
112 pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<Member> {
113 base::get::<Self, _>(ctx, mm, id).await
114 }
115
116 pub async fn list(
117 ctx: &Ctx,
118 mm: &ModelManager,
119 filters: Option<Vec<MemberFilter>>,
120 list_options: Option<ListOptions>,
121 ) -> Result<Vec<Member>> {
122 base::list::<Self, _, _>(ctx, mm, filters, list_options).await
123 }
124
125 pub async fn update(
126 ctx: &Ctx,
127 mm: &ModelManager,
128 id: i32,
129 member_u: MemberForUpdate,
130 ) -> Result<()> {
131 base::update::<Self, _>(ctx, mm, id, member_u).await
132 }
133
134 pub async fn delete(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<()> {
135 base::delete::<Self>(ctx, mm, id).await
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::_dev_utils;
143 use anyhow::Result;
144 use serde_json::json;
145
146 #[ignore]
147 #[tokio::test]
148 async fn test_member_create_ok() -> Result<()> {
149 let mm = ModelManager::new().await?;
150 let ctx = Ctx::root_ctx();
151 let fx_user_id = 1;
152 let fx_group_id = 1;
153
154 let member_c = MemberForCreate {
155 group_id: fx_group_id,
156 user_id: fx_user_id,
157 activation_key: None,
158 all_panels: false,
159 is_active: false,
160 role: 100,
161 };
162 let id = MemberBmc::create(&ctx, &mm, member_c).await?;
163
164 let member = MemberBmc::get(&ctx, &mm, id).await?;
165 assert_eq!(member.user_id, fx_user_id);
166
167 MemberBmc::delete(&ctx, &mm, id).await?;
168
169 Ok(())
170 }
171
172 #[allow(dead_code)]
173 fn get_seed() -> Vec<(i32, i32)> {
174 vec![(1000, 1000)]
175 }
176
177 #[ignore]
178 #[tokio::test]
179 async fn test_member_list_by_filter_ok() -> Result<()> {
180 let mm = ModelManager::new().await?;
181 let ctx = Ctx::root_ctx();
182 let fx_names = get_seed();
183 _dev_utils::seed_members(&ctx, &mm, &fx_names).await?;
184
185 let filters: Vec<MemberFilter> = serde_json::from_value(json!([
186 {
187 "user_id": 1001
188 }
189 ]))?;
190 let members = MemberBmc::list(&ctx, &mm, Some(filters), None).await?;
191 println!("MEMBERS {members:?}");
192
193 assert_eq!(members.len(), 3);
194 assert!(members[0].id == 1);
195 Ok(())
196 }
197}