seaography/inputs/
entity_input.rs

1use std::collections::BTreeMap;
2
3use async_graphql::dynamic::{InputObject, InputValue, ObjectAccessor};
4use sea_orm::{ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, PrimaryKeyTrait};
5
6use crate::{BuilderContext, EntityObjectBuilder, SeaResult, TypesMapHelper};
7
8/// The configuration structure of EntityInputBuilder
9pub struct EntityInputConfig {
10    /// suffix that is appended on insert input objects
11    pub insert_suffix: String,
12    /// names of "{entity}.{column}" you want to skip the insert input to be generated
13    pub insert_skips: Vec<String>,
14    /// suffix that is appended on update input objects
15    pub update_suffix: String,
16    /// names of "{entity}.{column}" you want to skip the update input to be generated
17    pub update_skips: Vec<String>,
18}
19
20impl std::default::Default for EntityInputConfig {
21    fn default() -> Self {
22        EntityInputConfig {
23            insert_suffix: "InsertInput".into(),
24            insert_skips: Vec::new(),
25            update_suffix: "UpdateInput".into(),
26            update_skips: Vec::new(),
27        }
28    }
29}
30
31/// Used to create the entity create/update input object
32pub struct EntityInputBuilder {
33    pub context: &'static BuilderContext,
34}
35
36impl EntityInputBuilder {
37    /// used to get SeaORM entity insert input object name
38    pub fn insert_type_name<T>(&self) -> String
39    where
40        T: EntityTrait,
41        <T as EntityTrait>::Model: Sync,
42    {
43        let entity_object_builder = EntityObjectBuilder {
44            context: self.context,
45        };
46        let object_name = entity_object_builder.type_name::<T>();
47        format!("{}{}", object_name, self.context.entity_input.insert_suffix)
48    }
49
50    /// used to get SeaORM entity update input object name
51    pub fn update_type_name<T>(&self) -> String
52    where
53        T: EntityTrait,
54        <T as EntityTrait>::Model: Sync,
55    {
56        let entity_object_builder = EntityObjectBuilder {
57            context: self.context,
58        };
59        let object_name = entity_object_builder.type_name::<T>();
60        format!("{}{}", object_name, self.context.entity_input.update_suffix)
61    }
62
63    /// used to produce the SeaORM entity input object
64    fn input_object<T>(&self, is_insert: bool) -> InputObject
65    where
66        T: EntityTrait,
67        <T as EntityTrait>::Model: Sync,
68    {
69        let name = if is_insert {
70            self.insert_type_name::<T>()
71        } else {
72            self.update_type_name::<T>()
73        };
74
75        let entity_object_builder = EntityObjectBuilder {
76            context: self.context,
77        };
78        let types_map_helper = TypesMapHelper {
79            context: self.context,
80        };
81
82        T::Column::iter().fold(InputObject::new(name), |object, column| {
83            let column_name = entity_object_builder.column_name::<T>(&column);
84
85            let full_name = format!("{}.{}", entity_object_builder.type_name::<T>(), column_name);
86
87            let skip = if is_insert {
88                self.context.entity_input.insert_skips.contains(&full_name)
89            } else {
90                self.context.entity_input.update_skips.contains(&full_name)
91            };
92
93            if skip {
94                return object;
95            }
96
97            let column_def = column.def();
98            let enum_type_name = column.enum_type_name();
99
100            let auto_increment = match <T::PrimaryKey as PrimaryKeyToColumn>::from_column(column) {
101                Some(_) => T::PrimaryKey::auto_increment(),
102                None => false,
103            };
104            let has_default_expr = column_def.get_column_default().is_some();
105            let is_insert_not_nullable =
106                is_insert && !(column_def.is_null() || auto_increment || has_default_expr);
107
108            let graphql_type = match types_map_helper.sea_orm_column_type_to_graphql_type(
109                column_def.get_column_type(),
110                is_insert_not_nullable,
111                enum_type_name,
112            ) {
113                Some(type_name) => type_name,
114                None => return object,
115            };
116
117            object.field(InputValue::new(column_name, graphql_type))
118        })
119    }
120
121    /// used to produce the SeaORM entity insert input object
122    pub fn insert_input_object<T>(&self) -> InputObject
123    where
124        T: EntityTrait,
125        <T as EntityTrait>::Model: Sync,
126    {
127        self.input_object::<T>(true)
128    }
129
130    /// used to produce the SeaORM entity update input object
131    pub fn update_input_object<T>(&self) -> InputObject
132    where
133        T: EntityTrait,
134        <T as EntityTrait>::Model: Sync,
135    {
136        self.input_object::<T>(false)
137    }
138
139    pub fn parse_object<T>(
140        &self,
141        object: &ObjectAccessor,
142    ) -> SeaResult<BTreeMap<String, sea_orm::Value>>
143    where
144        T: EntityTrait,
145        <T as EntityTrait>::Model: Sync,
146    {
147        let entity_object_builder = EntityObjectBuilder {
148            context: self.context,
149        };
150        let types_map_helper = TypesMapHelper {
151            context: self.context,
152        };
153
154        let mut map = BTreeMap::<String, sea_orm::Value>::new();
155
156        for column in T::Column::iter() {
157            let column_name = entity_object_builder.column_name::<T>(&column);
158
159            let value = match object.get(&column_name) {
160                Some(value) => value,
161                None => continue,
162            };
163
164            let result =
165                types_map_helper.async_graphql_value_to_sea_orm_value::<T>(&column, &value)?;
166
167            map.insert(column_name, result);
168        }
169
170        Ok(map)
171    }
172}