seaography/inputs/
entity_input.rs1use 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
8pub struct EntityInputConfig {
10 pub insert_suffix: String,
12 pub insert_skips: Vec<String>,
14 pub update_suffix: String,
16 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
31pub struct EntityInputBuilder {
33 pub context: &'static BuilderContext,
34}
35
36impl EntityInputBuilder {
37 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 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 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 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 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}