Skip to main content

cms_for_rust/operations/
insert_one.rs

1use std::ops::Not;
2
3use axum::{
4    body::Body,
5    extract::State,
6    http::{Response, StatusCode},
7    response::IntoResponse,
8    Json,
9};
10use case::CaseExt;
11use inventory::Collect;
12use queries_for_sqlx::{execute_no_cache::ExecuteNoCache, InitStatement};
13use queries_for_sqlx::{
14    impl_into_mut_arguments_prelude::Type, prelude::stmt,
15    IntoMutArguments, SupportNamedBind, SupportReturning,
16};
17use serde::{Deserialize, Serialize};
18use serde_json::{json, Map, Value};
19use sqlx::Row;
20use sqlx::{
21    ColumnIndex, Database, Decode, Encode, Executor, Pool,
22};
23use tracing::warn;
24
25use crate::{
26    axum_router::{error, MyError},
27    entities::Entity,
28    relations::get_relation,
29};
30
31#[derive(Debug, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct InsertInput<E, D, R> {
34    #[serde(skip)]
35    pub entity: E,
36    pub data: D,
37    pub attributes: bool,
38    pub relations: R,
39}
40
41pub async fn insert_one<S, T>(
42    pool: State<Pool<S>>,
43    Json(input): Json<InsertInput<(), T, Map<String, Value>>>,
44) -> Result<Response<Body>, MyError>
45where
46    S: Database,
47    T: Entity<S>
48        + Serialize
49        + Send
50        + for<'d> Deserialize<'d>
51        + Clone
52        + for<'q> IntoMutArguments<'q, S>,
53    S: SupportNamedBind + SupportReturning,
54    for<'c> &'c mut S::Connection: Executor<'c, Database = S>,
55    for<'r> &'r str: ColumnIndex<S::Row>,
56    i64: Type<S> + for<'d> Decode<'d, S> + for<'d> Encode<'d, S>,
57    crate::relations::Submitable<S>: Collect,
58{
59    if input.attributes.not() {
60        panic!(
61            "not returning attributes is not supported for now"
62        );
63    }
64
65    let mut rels = match get_relation::<S>(
66        T::table_name().to_snake().as_str(),
67        "from_insert_one",
68        input.relations,
69    ) {
70        Ok(e) => e,
71        Err(e) => {
72            return Err(error(StatusCode::BAD_REQUEST).for_dev(
73                format!(
74                "following relations ({}) are not related to {}",
75                e.join(", "),
76                T::table_name()
77            ),
78            ));
79        }
80    };
81
82    let mut st = stmt::InsertStOne::init(T::table_name());
83
84    input.data.clone().insert_st(&mut st);
85
86    for rel in rels.iter_mut() {
87        rel.on_insert(&mut st);
88    }
89
90    // I will rely on cloning for now the input to output instead of populating for now
91    let res = st
92        .returning(vec!["id"])
93        .fetch_one(&pool.0, |r| {
94            let id: i64 = r.get("id");
95            warn!("refactor: from_row and take should not exist in insert_one");
96            Ok(id)
97        })
98        .await
99        .unwrap();
100
101    for rel in rels.iter_mut() {
102        rel.sub_op2(pool.0.clone(), res).await;
103    }
104
105    let mut relation = serde_json::Map::default();
106    for rel in rels.iter_mut() {
107        warn!("refactor: take2 should not take any id on insert_one");
108        if let Some(value) = rel.take2(Default::default()) {
109            relation.entry(rel.schema_key()).or_insert(value);
110        }
111    }
112
113    Ok((
114        StatusCode::CREATED,
115        Json(json!({
116            "id": res,
117            "attributes": input.data,
118            "relations": relation,
119        })),
120    )
121        .into_response())
122}
123
124#[cfg(test)]
125mod test_insert_one {
126    fn main() {
127        panic!("test not implemented");
128    }
129}