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}