airlab_lib/model/
protein.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, OpValsString};
7use serde::{Deserialize, Serialize};
8use sqlx::FromRow;
9
10impl ProteinBmc {
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  created_by integer NOT NULL,
20  name character varying NOT NULL,
21  description character varying,
22  meta jsonb,
23  created_at timestamp with time zone DEFAULT now() NOT NULL
24);
25CREATE INDEX "IDX_protein_created_by" ON protein USING btree (created_by);
26CREATE INDEX "IDX_protein_group_id" ON protein USING btree (group_id);
27CREATE INDEX "IDX_protein_name" ON protein USING btree (name);
28        "##,
29            if drop_table {
30                format!("drop table if exists {table};")
31            } else {
32                String::new()
33            }
34        )
35    }
36}
37
38#[derive(Debug, Clone, Fields, FromRow, Serialize, Deserialize, Default)]
39pub struct Protein {
40    pub id: i32,
41    #[serde(rename = "groupId")]
42    pub group_id: i32,
43
44    #[serde(rename = "createdBy")]
45    pub created_by: i32,
46    pub name: String,
47    pub description: Option<String>,
48    pub meta: Option<serde_json::Value>,
49    #[serde(rename = "createdAt")]
50    pub created_at: chrono::DateTime<chrono::Utc>, //String
51}
52
53#[derive(Fields, Deserialize, Clone)]
54pub struct ProteinForCreate {
55    pub name: String,
56    pub description: Option<String>,
57    pub group_id: i32,
58    pub created_by: i32,
59}
60
61#[derive(Fields, Default, Deserialize)]
62pub struct ProteinForUpdate {
63    pub name: Option<String>,
64    pub description: Option<String>,
65}
66
67#[derive(FilterNodes, Deserialize, Default, Debug)]
68pub struct ProteinFilter {
69    id: Option<OpValsInt64>,
70    group_id: Option<OpValsInt64>,
71    name: Option<OpValsString>,
72    description: Option<OpValsString>,
73}
74
75pub struct ProteinBmc;
76
77impl DbBmc for ProteinBmc {
78    const TABLE: &'static str = "protein";
79}
80
81impl ProteinBmc {
82    pub async fn create(ctx: &Ctx, mm: &ModelManager, protein_c: ProteinForCreate) -> Result<i32> {
83        base::create::<Self, _>(ctx, mm, protein_c).await
84    }
85    pub async fn create_full(ctx: &Ctx, mm: &ModelManager, protein_c: Protein) -> Result<i32> {
86        base::create::<Self, _>(ctx, mm, protein_c).await
87    }
88
89    pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<Protein> {
90        base::get::<Self, _>(ctx, mm, id).await
91    }
92
93    pub async fn list(
94        ctx: &Ctx,
95        mm: &ModelManager,
96        filters: Option<Vec<ProteinFilter>>,
97        list_options: Option<ListOptions>,
98    ) -> Result<Vec<Protein>> {
99        base::list::<Self, _, _>(ctx, mm, filters, list_options).await
100    }
101
102    pub async fn update(
103        ctx: &Ctx,
104        mm: &ModelManager,
105        id: i32,
106        protein_u: ProteinForUpdate,
107    ) -> Result<()> {
108        base::update::<Self, _>(ctx, mm, id, protein_u).await
109    }
110
111    pub async fn delete(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<()> {
112        base::delete::<Self>(ctx, mm, id).await
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::_dev_utils;
120    use crate::model::Error;
121    use anyhow::Result;
122    use serde_json::json;
123
124    #[ignore]
125    #[tokio::test]
126    async fn test_protein_create_ok() -> Result<()> {
127        let ctx = Ctx::root_ctx();
128        let mm = ModelManager::new().await?;
129        let fx_name = "test_create_ok name";
130
131        let protein_c = ProteinForCreate {
132            name: fx_name.to_string(),
133            description: Some(fx_name.to_string()),
134            group_id: 1,
135            created_by: 261,
136        };
137        let id = ProteinBmc::create(&ctx, &mm, protein_c).await?;
138
139        let protein = ProteinBmc::get(&ctx, &mm, id).await?;
140        assert_eq!(protein.name, fx_name);
141
142        ProteinBmc::delete(&ctx, &mm, id).await?;
143
144        Ok(())
145    }
146
147    #[ignore]
148    #[tokio::test]
149    async fn test_protein_get_err_not_found() -> Result<()> {
150        let mm = ModelManager::new().await?;
151        let ctx = Ctx::root_ctx();
152        let fx_id = 100;
153
154        let res = ProteinBmc::get(&ctx, &mm, fx_id).await;
155
156        assert!(
157            matches!(
158                res,
159                Err(Error::EntityNotFound {
160                    entity: "protein",
161                    id: 100
162                })
163            ),
164            "EntityNotFound not matching"
165        );
166
167        Ok(())
168    }
169
170    #[ignore]
171    #[tokio::test]
172    async fn test_protein_list_all_ok() -> Result<()> {
173        let mm = ModelManager::new().await?;
174        let ctx = Ctx::root_ctx();
175        let tname = "test_protein_list_all_ok";
176        let seeds = _dev_utils::get_protein_seed(tname);
177        _dev_utils::seed_proteins(&ctx, &mm, &seeds).await?;
178
179        let proteins = ProteinBmc::list(&ctx, &mm, None, None).await?;
180
181        let proteins: Vec<Protein> = proteins
182            .into_iter()
183            .filter(|t| t.name.starts_with("test_list_all_ok-protein"))
184            .collect();
185        assert_eq!(proteins.len(), 4, "number of seeded proteins.");
186
187        if false {
188            for protein in proteins.iter() {
189                ProteinBmc::delete(&ctx, &mm, protein.id).await?;
190            }
191        }
192
193        Ok(())
194    }
195
196    #[ignore]
197    #[tokio::test]
198    async fn test_protein_list_by_filter_ok() -> Result<()> {
199        let mm = ModelManager::new().await?;
200        let ctx = Ctx::root_ctx();
201        let tname = "test_protein_list_by_filter_ok";
202        let seeds = _dev_utils::get_protein_seed(tname);
203        _dev_utils::seed_proteins(&ctx, &mm, &seeds).await?;
204
205        let filters: Vec<ProteinFilter> = serde_json::from_value(json!([
206            {
207                "name": {
208                    "$endsWith": ".a",
209                    "$containsAny": ["01", "02"]
210                }
211            },
212            {
213                "name": {"$contains": "03"}
214            }
215        ]))?;
216        let list_options = serde_json::from_value(json!({
217            "order_bys": "!id"
218        }))?;
219        let proteins = ProteinBmc::list(&ctx, &mm, Some(filters), Some(list_options)).await?;
220
221        assert_eq!(proteins.len(), 3);
222        assert!(proteins[0].name.ends_with("03"));
223        assert!(proteins[1].name.ends_with("02.a"));
224        assert!(proteins[2].name.ends_with("01.a"));
225
226        if false {
227            let proteins = ProteinBmc::list(
228                &ctx,
229                &mm,
230                Some(serde_json::from_value(json!([{
231                    "name": {"$startsWith": "test_list_by_filter_ok"}
232                }]))?),
233                None,
234            )
235            .await?;
236            assert_eq!(proteins.len(), 5);
237            for protein in proteins.iter() {
238                ProteinBmc::delete(&ctx, &mm, protein.id).await?;
239            }
240        }
241
242        Ok(())
243    }
244
245    #[ignore]
246    #[tokio::test]
247    async fn test_protein_update_ok() -> Result<()> {
248        let mm = ModelManager::new().await?;
249        let ctx = Ctx::root_ctx();
250        let tname = "test_protein_list_by_filter_ok";
251        let seeds = _dev_utils::get_protein_seed(tname);
252        let fx_protein = _dev_utils::seed_proteins(&ctx, &mm, &seeds)
253            .await?
254            .remove(0);
255
256        ProteinBmc::update(
257            &ctx,
258            &mm,
259            fx_protein.id,
260            ProteinForUpdate {
261                name: Some(tname.to_string()),
262                ..Default::default()
263            },
264        )
265        .await?;
266
267        let protein = ProteinBmc::get(&ctx, &mm, fx_protein.id).await?;
268        assert_eq!(protein.name, tname);
269
270        Ok(())
271    }
272
273    #[ignore]
274    #[tokio::test]
275    async fn test_protein_delete_err_not_found() -> Result<()> {
276        let mm = ModelManager::new().await?;
277        let ctx = Ctx::root_ctx();
278        let fx_id = 100;
279
280        let res = ProteinBmc::delete(&ctx, &mm, fx_id).await;
281
282        assert!(
283            matches!(
284                res,
285                Err(Error::EntityNotFound {
286                    entity: "protein",
287                    id: 100
288                })
289            ),
290            "EntityNotFound not matching"
291        );
292
293        Ok(())
294    }
295}