qm_customer/
mutation.rs

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}