airlab_lib/model/
member.rs

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}