1use crate::model::*;
2use qm_entity::ids::InfraId;
3use sqlx::types::Uuid;
4use sqlx::PgPool;
5use std::sync::Arc;
6
7pub const DEFAULT_TYPE: &str = "none";
8
9const NAME_MAX_LEN: usize = 1024;
10const TY_MAX_LEN: usize = 16;
11const INPUT_SLICE_MAX_SIZE: usize = 1024 * 1024 * 1024;
12
13fn check_max_size(name: &str, v: Option<&str>, max_len: usize) -> anyhow::Result<()> {
14 if let Some(v) = v {
15 if v.len() > max_len {
16 anyhow::bail!("The value of '{name}' name is bigger than {max_len} characters");
17 }
18 }
19 Ok(())
20}
21
22fn check_max_size_input_slice<T>(name: &str, v: &[T]) -> anyhow::Result<()> {
23 let mem_size = std::mem::size_of_val(v);
24 if mem_size > INPUT_SLICE_MAX_SIZE {
25 anyhow::bail!(
26 "The input length of '{name}' is bigger than {} bytes",
27 INPUT_SLICE_MAX_SIZE
28 );
29 }
30 Ok(())
31}
32
33pub async fn create_customer(
34 pool: &PgPool,
35 id: Option<i64>,
36 name: &str,
37 ty: Option<&str>,
38 created_by: &Uuid,
39) -> anyhow::Result<QmCustomer> {
40 check_max_size("Customer name", Some(name), NAME_MAX_LEN)?;
41 check_max_size("Customer ty", ty, TY_MAX_LEN)?;
42 if let Some(id) = id {
43 let rec = sqlx::query!(
44 r#"
45INSERT INTO customers ( id, name, ty, created_by )
46VALUES ( $1, $2, $3, $4 )
47RETURNING
48 id,
49 name,
50 ty,
51 created_by,
52 created_at,
53 updated_by,
54 updated_at
55"#,
56 id,
57 name,
58 ty.unwrap_or(DEFAULT_TYPE),
59 created_by
60 )
61 .fetch_one(pool)
62 .await?;
63 Ok(QmCustomer {
64 id: rec.id.into(),
65 name: Arc::from(rec.name),
66 ty: Arc::from(rec.ty),
67 created_by: rec.created_by,
68 created_at: rec.created_at,
69 updated_by: rec.updated_by,
70 updated_at: rec.updated_at,
71 })
72 } else {
73 let rec = sqlx::query!(
74 r#"
75INSERT INTO customers ( name, ty, created_by )
76VALUES ( $1, $2, $3 )
77RETURNING
78 id,
79 name,
80 ty,
81 created_by,
82 created_at,
83 updated_by,
84 updated_at
85"#,
86 name,
87 ty.unwrap_or(DEFAULT_TYPE),
88 created_by
89 )
90 .fetch_one(pool)
91 .await?;
92
93 Ok(QmCustomer {
94 id: rec.id.into(),
95 name: Arc::from(rec.name),
96 ty: Arc::from(rec.ty),
97 created_by: rec.created_by,
98 created_at: rec.created_at,
99 updated_by: rec.updated_by,
100 updated_at: rec.updated_at,
101 })
102 }
103}
104
105pub async fn update_customer(
106 pool: &PgPool,
107 id: InfraId,
108 name: &str,
109 updated_by: &Uuid,
110) -> anyhow::Result<QmCustomer> {
111 check_max_size("Customer name", Some(name), NAME_MAX_LEN)?;
112 let rec = sqlx::query!(
113 r#"
114UPDATE customers AS v
115SET name = $2, updated_by = $3, updated_at = NOW()
116WHERE v.id = $1
117RETURNING
118 v.id as id,
119 v.name as name,
120 v.ty as ty,
121 v.created_by as created_by,
122 v.created_at as created_at,
123 v.updated_by as updated_by,
124 v.updated_at as updated_at
125"#,
126 id.as_ref(),
127 name,
128 updated_by
129 )
130 .fetch_one(pool)
131 .await?;
132
133 Ok(QmCustomer {
134 id: rec.id.into(),
135 name: Arc::from(rec.name),
136 ty: Arc::from(rec.ty),
137 created_by: rec.created_by,
138 created_at: rec.created_at,
139 updated_by: rec.updated_by,
140 updated_at: rec.updated_at,
141 })
142}
143
144pub async fn remove_customer(pool: &PgPool, id: InfraId) -> anyhow::Result<u64> {
145 Ok(
146 sqlx::query!("DELETE FROM customers WHERE id = $1", id.as_ref())
147 .execute(pool)
148 .await?
149 .rows_affected() as u64,
150 )
151}
152
153pub async fn remove_customers(pool: &PgPool, ids: &[i64]) -> anyhow::Result<u64> {
154 check_max_size_input_slice("Customer ids", ids)?;
155 let result = sqlx::query!(
156 "DELETE FROM customers WHERE id IN (SELECT UNNEST($1::int8[]))",
157 &ids[..] as &[i64]
158 )
159 .execute(pool)
160 .await?
161 .rows_affected() as u64;
162 Ok(result)
163}
164
165pub async fn create_organization(
166 pool: &PgPool,
167 id: Option<i64>,
168 name: &str,
169 ty: Option<&str>,
170 customer_id: InfraId,
171 created_by: &Uuid,
172) -> anyhow::Result<QmOrganization> {
173 check_max_size("Organization name", Some(name), NAME_MAX_LEN)?;
174 check_max_size("Organization ty", ty, TY_MAX_LEN)?;
175 if let Some(id) = id {
176 let rec = sqlx::query!(
177 r#"
178 INSERT INTO organizations ( id, name, ty, customer_id, created_by )
179 VALUES ( $1, $2, $3, $4, $5 )
180 RETURNING
181 id,
182 customer_id,
183 name,
184 ty,
185 created_by,
186 created_at,
187 updated_by,
188 updated_at
189 "#,
190 id,
191 name,
192 ty.unwrap_or(DEFAULT_TYPE),
193 customer_id.as_ref(),
194 created_by
195 )
196 .fetch_one(pool)
197 .await?;
198
199 Ok(QmOrganization {
200 id: rec.id.into(),
201 customer_id: rec.customer_id.into(),
202 name: Arc::from(rec.name),
203 ty: Arc::from(rec.ty),
204 created_by: rec.created_by,
205 created_at: rec.created_at,
206 updated_by: rec.updated_by,
207 updated_at: rec.updated_at,
208 })
209 } else {
210 let rec = sqlx::query!(
211 r#"
212 INSERT INTO organizations ( name, ty, customer_id, created_by )
213 VALUES ( $1, $2, $3, $4 )
214 RETURNING
215 id,
216 customer_id,
217 name,
218 ty,
219 created_by,
220 created_at,
221 updated_by,
222 updated_at
223 "#,
224 name,
225 ty.unwrap_or(DEFAULT_TYPE),
226 customer_id.as_ref(),
227 created_by
228 )
229 .fetch_one(pool)
230 .await?;
231
232 Ok(QmOrganization {
233 id: rec.id.into(),
234 customer_id: rec.customer_id.into(),
235 name: Arc::from(rec.name),
236 ty: Arc::from(rec.ty),
237 created_by: rec.created_by,
238 created_at: rec.created_at,
239 updated_by: rec.updated_by,
240 updated_at: rec.updated_at,
241 })
242 }
243}
244
245pub async fn update_organization(
246 pool: &PgPool,
247 id: InfraId,
248 name: &str,
249 updated_by: &Uuid,
250) -> anyhow::Result<QmOrganization> {
251 let rec = sqlx::query!(
252 r#"
253UPDATE organizations AS v
254SET name = $2, updated_by = $3, updated_at = NOW()
255WHERE v.id = $1
256RETURNING
257 v.id as id,
258 v.customer_id as customer_id,
259 v.name as name,
260 v.ty as ty,
261 v.created_by as created_by,
262 v.created_at as created_at,
263 v.updated_by as updated_by,
264 v.updated_at as updated_at
265"#,
266 id.as_ref(),
267 name,
268 updated_by,
269 )
270 .fetch_one(pool)
271 .await?;
272
273 Ok(QmOrganization {
274 id: rec.id.into(),
275 customer_id: rec.customer_id.into(),
276 name: Arc::from(rec.name),
277 ty: Arc::from(rec.ty),
278 created_by: rec.created_by,
279 created_at: rec.created_at,
280 updated_by: rec.updated_by,
281 updated_at: rec.updated_at,
282 })
283}
284
285pub async fn remove_organization(pool: &PgPool, id: InfraId) -> anyhow::Result<u64> {
286 Ok(
287 sqlx::query!("DELETE FROM organizations WHERE id = $1", id.as_ref())
288 .execute(pool)
289 .await?
290 .rows_affected() as u64,
291 )
292}
293
294pub async fn remove_organizations(pool: &PgPool, ids: &[i64]) -> anyhow::Result<u64> {
295 check_max_size_input_slice("Organization ids", ids)?;
296 let result = sqlx::query!(
297 "DELETE FROM organizations WHERE id IN (SELECT UNNEST($1::int8[]))",
298 &ids[..] as &[i64]
299 )
300 .execute(pool)
301 .await?
302 .rows_affected() as u64;
303 Ok(result)
304}
305
306pub async fn create_institution(
307 pool: &PgPool,
308 id: Option<i64>,
309 name: &str,
310 ty: Option<&str>,
311 customer_id: InfraId,
312 organization_id: InfraId,
313 created_by: &Uuid,
314) -> anyhow::Result<QmInstitution> {
315 check_max_size("Institution name", Some(name), NAME_MAX_LEN)?;
316 check_max_size("Institution ty", ty, TY_MAX_LEN)?;
317 if let Some(id) = id {
318 let rec = sqlx::query!(
319 r#"
320INSERT INTO institutions ( id, name, ty, customer_id, organization_id, created_by )
321VALUES ( $1, $2, $3, $4, $5, $6 )
322RETURNING
323 id,
324 customer_id,
325 organization_id,
326 name,
327 ty,
328 created_by,
329 created_at,
330 updated_by,
331 updated_at
332"#,
333 id,
334 name,
335 ty.unwrap_or(DEFAULT_TYPE),
336 customer_id.as_ref(),
337 organization_id.as_ref(),
338 created_by
339 )
340 .fetch_one(pool)
341 .await?;
342
343 Ok(QmInstitution {
344 id: rec.id.into(),
345 customer_id: rec.customer_id.into(),
346 organization_id: rec.organization_id.into(),
347 name: Arc::from(rec.name),
348 ty: Arc::from(rec.ty),
349 created_by: rec.created_by,
350 created_at: rec.created_at,
351 updated_by: rec.updated_by,
352 updated_at: rec.updated_at,
353 })
354 } else {
355 let rec = sqlx::query!(
356 r#"
357INSERT INTO institutions ( name, ty, customer_id, organization_id, created_by )
358VALUES ( $1, $2, $3, $4, $5 )
359RETURNING
360 id,
361 customer_id,
362 organization_id,
363 name,
364 ty,
365 created_by,
366 created_at,
367 updated_by,
368 updated_at
369"#,
370 name,
371 ty.unwrap_or(DEFAULT_TYPE),
372 customer_id.as_ref(),
373 organization_id.as_ref(),
374 created_by
375 )
376 .fetch_one(pool)
377 .await?;
378
379 Ok(QmInstitution {
380 id: rec.id.into(),
381 customer_id: rec.customer_id.into(),
382 organization_id: rec.organization_id.into(),
383 name: Arc::from(rec.name),
384 ty: Arc::from(rec.ty),
385 created_by: rec.created_by,
386 created_at: rec.created_at,
387 updated_by: rec.updated_by,
388 updated_at: rec.updated_at,
389 })
390 }
391}
392
393pub async fn update_institution(
394 pool: &PgPool,
395 id: InfraId,
396 name: &str,
397 updated_by: &Uuid,
398) -> anyhow::Result<QmInstitution> {
399 check_max_size("Institution name", Some(name), NAME_MAX_LEN)?;
400 let rec = sqlx::query!(
401 r#"
402UPDATE institutions AS v
403SET name = $2, updated_by = $3, updated_at = NOW()
404WHERE v.id = $1
405RETURNING
406 v.id as id,
407 v.customer_id as customer_id,
408 v.organization_id as organization_id,
409 v.name as name,
410 v.ty as ty,
411 v.created_by as created_by,
412 v.created_at as created_at,
413 v.updated_by as updated_by,
414 v.updated_at as updated_at
415"#,
416 id.as_ref(),
417 name,
418 updated_by,
419 )
420 .fetch_one(pool)
421 .await?;
422
423 Ok(QmInstitution {
424 id: rec.id.into(),
425 customer_id: rec.customer_id.into(),
426 organization_id: rec.organization_id.into(),
427 name: Arc::from(rec.name),
428 ty: Arc::from(rec.ty),
429 created_by: rec.created_by,
430 created_at: rec.created_at,
431 updated_by: rec.updated_by,
432 updated_at: rec.updated_at,
433 })
434}
435
436pub async fn remove_institution(pool: &PgPool, id: InfraId) -> anyhow::Result<u64> {
437 Ok(
438 sqlx::query!("DELETE FROM institutions WHERE id = $1", id.as_ref())
439 .execute(pool)
440 .await?
441 .rows_affected() as u64,
442 )
443}
444
445pub async fn remove_institutions(pool: &PgPool, ids: &[i64]) -> anyhow::Result<u64> {
446 check_max_size_input_slice("Institution ids", ids)?;
447 let result = sqlx::query!(
448 "DELETE FROM institutions WHERE id IN (SELECT UNNEST($1::int8[]))",
449 &ids[..] as &[i64]
450 )
451 .execute(pool)
452 .await?
453 .rows_affected() as u64;
454 Ok(result)
455}