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