1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4use async_graphql::dynamic::{
5 Field, FieldFuture, FieldValue, InputObject, InputValue, Object, Schema,
6 SchemaBuilder as AgSchemaBuilder, TypeRef,
7};
8use async_graphql::extensions::{Extension, ExtensionFactory};
9use mongodb::{Client, Database};
10
11use crate::error::GraphQLError;
12use crate::helpers::serialization::json_to_field_value;
13use crate::resolvers::{mutation, query};
14use crate::schema::definition::{CollectionDef, EnumDef, FieldType, RelationKind, SchemaDefinition};
15use crate::types::scalars;
16
17#[derive(Debug, Clone)]
18pub struct RuntimeConfig {
19 pub max_page_size: usize,
20}
21
22enum Operation {
23 Get,
24 List,
25 Create,
26 Update,
27 Delete,
28}
29struct BoxedExtensionFactory(Box<dyn ExtensionFactory>);
30
31impl ExtensionFactory for BoxedExtensionFactory {
32 fn create(&self) -> Arc<dyn Extension> {
33 self.0.create()
34 }
35}
36
37pub struct SchemaBuilder<'a> {
38 config: &'a RuntimeConfig,
39 definition: &'a SchemaDefinition,
40 registered_enums: HashSet<String>,
41 registered_nested_inputs: HashSet<String>,
42}
43
44impl<'a> SchemaBuilder<'a> {
45 pub fn new(config: &'a RuntimeConfig, definition: &'a SchemaDefinition) -> Self {
46 Self {
47 config,
48 definition,
49 registered_enums: HashSet::new(),
50 registered_nested_inputs: HashSet::new(),
51 }
52 }
53
54 pub async fn build(
55 mut self,
56 client: Client,
57 db: Database,
58 extensions: Option<Vec<Box<dyn ExtensionFactory>>>,
59 ) -> Result<Schema, GraphQLError> {
60 let mut ping_cmd = mongodb::bson::Document::new();
61 ping_cmd.insert("ping", 1);
62 db.run_command(ping_cmd)
63 .await
64 .map_err(|err| GraphQLError::Database(format!("Database ping failed: {}", err)))?;
65
66 let mut builder = Schema::build("Query", Some("Mutation"), None);
67 for extension in extensions.into_iter().flatten() {
68 builder = builder.extension(BoxedExtensionFactory(extension));
69 }
70 builder = scalars::register_all(builder);
71 builder = self.register_page_info(builder);
72 builder = self.register_delete_result(builder);
73 builder = self.register_filter_types(builder);
74
75 let mut query_root = Object::new("Query");
76 let mut mutation_root = Object::new("Mutation");
77
78 for collection in &self.definition.collections {
79 let where_unique = InputObject::new(format!("{}WhereUniqueInput", collection.type_name()))
80 .field(InputValue::new("id", TypeRef::named_nn("ID")));
81 builder = builder.register(where_unique);
82 }
83
84 for collection in &self.definition.collections {
85 (builder, query_root, mutation_root) = self.register_collection(
86 builder, query_root, mutation_root, collection, &client, &db,
87 )?;
88 }
89
90 builder = builder.register(query_root);
91 builder = builder.register(mutation_root);
92
93 let schema = builder
94 .data(client)
95 .data(db)
96 .data(self.config.clone())
97 .data(self.definition.clone())
98 .finish()
99 .map_err(|err| GraphQLError::SchemaBuild(format!("Failed to build schema: {}", err)))?;
100
101 Ok(schema)
102 }
103
104 fn register_collection(
105 &mut self,
106 mut builder: AgSchemaBuilder,
107 mut query_root: Object,
108 mut mutation_root: Object,
109 collection: &CollectionDef,
110 client: &Client,
111 db: &Database,
112 ) -> Result<(AgSchemaBuilder, Object, Object), GraphQLError> {
113 let type_name = collection.type_name();
114
115 for field in &collection.fields {
116 if let Some(enum_def) = &field.r#enum {
117 builder = self.register_enum(builder, enum_def);
118 }
119 }
120
121 let collection_map: HashMap<&str, &CollectionDef> = self
122 .definition
123 .collections
124 .iter()
125 .map(|collection| (collection.collection.as_str(), collection))
126 .collect();
127
128 let (reverse_to_many, reverse_to_one) = self.collect_reverse_relations(collection);
131
132 let mut obj = Object::new(type_name.clone());
133 for field in &collection.fields {
134 if matches!(field.field_type, FieldType::Relation(_)) {
135 continue;
136 }
137 let field_type = scalars::type_ref(&field.field_type);
138 let field_name = field.graphql_name();
139 obj = obj.field(Field::new(
140 field_name.clone(),
141 field_type,
142 move |ctx| {
143 let name = field_name.clone();
144 FieldFuture::new(async move {
145 let value = ctx
146 .parent_value
147 .as_value()
148 .and_then(|parent| extract_nested(parent, &name));
149 match value {
150 Some(val) => Ok(Some(FieldValue::value(val))),
151 None => Ok(None),
152 }
153 })
154 },
155 ));
156 }
157
158 for field in &collection.fields {
159 let relation = match &field.field_type {
160 FieldType::Relation(relation) => relation,
161 _ => continue,
162 };
163 let target_def = match collection_map.get(relation.collection.as_str()) {
164 Some(collection_def) => (*collection_def).clone(),
165 None => continue,
166 };
167 let target_type = target_def.type_name();
168 let db_for_rel = db.clone();
169
170 match relation.kind {
171 RelationKind::OneToMany | RelationKind::OneToOne => {
172 let target_coll_name = relation.collection.clone();
173 let fk_mongo_name = field.name.clone();
174 let target_def_for_closure = target_def.clone();
175 obj = obj.field(Field::new(
176 field.graphql_name(),
177 TypeRef::named(target_type),
178 move |ctx| {
179 let db = db_for_rel.clone();
180 let target_coll = target_coll_name.clone();
181 let target_def = target_def_for_closure.clone();
182 let fk_name = fk_mongo_name.clone();
183 FieldFuture::new(async move {
184 let result = crate::resolvers::query_relation::resolve_forward_to_one(
185 &ctx, &db, &target_coll, &target_def, &fk_name,
186 )
187 .await;
188 resolve_to_field_value(result)
189 })
190 },
191 ));
192 }
193 RelationKind::ManyToMany => {
194 let junction = match &relation.junction {
195 Some(junction_def) => junction_def.clone(),
196 None => continue,
197 };
198 let target_def_for_closure = target_def.clone();
199 obj = obj.field(Field::new(
200 field.graphql_name(),
201 TypeRef::named_nn_list(&target_type),
202 move |ctx| {
203 let db = db_for_rel.clone();
204 let junction = junction.clone();
205 let target_def = target_def_for_closure.clone();
206 FieldFuture::new(async move {
207 let result = crate::resolvers::query_relation::resolve_forward_many_to_many(
208 &ctx, &db, &junction, &target_def,
209 )
210 .await;
211 resolve_to_field_value(result)
212 })
213 },
214 ));
215 }
216 }
217 }
218
219 for (reverse_name, target_coll_name, fk_field) in &reverse_to_many {
220 let target_def = match collection_map.get(target_coll_name.as_str()) {
221 Some(collection_def) => (*collection_def).clone(),
222 None => continue,
223 };
224 let target_type = target_def.type_name();
225 let fk_field_for_closure = fk_field.clone();
226 let target_def_for_closure = target_def.clone();
227 let db_for_rel = db.clone();
228 obj = obj.field(Field::new(
229 reverse_name.clone(),
230 TypeRef::named_nn_list(target_type),
231 move |ctx| {
232 let db = db_for_rel.clone();
233 let target_def = target_def_for_closure.clone();
234 let fk_field = fk_field_for_closure.clone();
235 FieldFuture::new(async move {
236 let result = crate::resolvers::query_relation::resolve_reverse_to_many(
237 &ctx, &db, &target_def, &fk_field,
238 )
239 .await;
240 resolve_to_field_value(result)
241 })
242 },
243 ));
244 }
245
246 for (reverse_name, target_coll_name, fk_field) in &reverse_to_one {
247 let target_def = match collection_map.get(target_coll_name.as_str()) {
248 Some(collection_def) => (*collection_def).clone(),
249 None => continue,
250 };
251 let target_type = target_def.type_name();
252 let fk_field_for_closure = fk_field.clone();
253 let target_def_for_closure = target_def.clone();
254 let db_for_rel = db.clone();
255 obj = obj.field(Field::new(
256 reverse_name.clone(),
257 TypeRef::named(target_type),
258 move |ctx| {
259 let db = db_for_rel.clone();
260 let target_def = target_def_for_closure.clone();
261 let fk_field = fk_field_for_closure.clone();
262 FieldFuture::new(async move {
263 let result = crate::resolvers::query_relation::resolve_reverse_to_one(
264 &ctx, &db, &target_def, &fk_field,
265 )
266 .await;
267 resolve_to_field_value(result)
268 })
269 },
270 ));
271 }
272
273 builder = builder.register(obj);
274
275 for field in &collection.fields {
276 if let FieldType::Relation(_rel) = &field.field_type {
277 builder = self.register_nested_inputs_for_field(builder, collection, field);
278 }
279 }
280
281 for (_, target_coll_name, _) in &reverse_to_many {
282 if let Some(target_def) = collection_map.get(target_coll_name.as_str()) {
283 (builder, _) = self.register_reverse_inputs(
284 builder, collection, target_def, RelationKind::OneToMany,
285 );
286 }
287 }
288 for (_, target_coll_name, _) in &reverse_to_one {
289 if let Some(target_def) = collection_map.get(target_coll_name.as_str()) {
290 (builder, _) = self.register_reverse_inputs(
291 builder, collection, target_def, RelationKind::OneToOne,
292 );
293 }
294 }
295
296 let mut create_input = InputObject::new(format!("{}CreateInput", type_name));
297 create_input = self.build_input_fields(create_input, collection, &collection_map, "Create");
298 create_input = self.add_reverse_fields_to_input(create_input, &reverse_to_many, &reverse_to_one, &collection_map, &type_name, "Create");
299 builder = builder.register(create_input);
300
301 let mut update_input = InputObject::new(format!("{}UpdateInput", type_name));
302 update_input = self.build_input_fields(update_input, collection, &collection_map, "Update");
303 update_input = self.add_reverse_fields_to_input(update_input, &reverse_to_many, &reverse_to_one, &collection_map, &type_name, "Update");
304 builder = builder.register(update_input);
305
306 let mut where_input =
307 InputObject::new(format!("{}WhereInput", type_name))
308 .field(InputValue::new("id", TypeRef::named("IdFilter")));
309 for field in &collection.fields {
310 if field.name == "id" || field.name == "_id" {
311 continue;
312 }
313 if matches!(field.field_type, FieldType::Relation(_)) {
314 continue;
315 }
316 let filter_type = filter_type_name(&field.field_type);
317 where_input =
318 where_input.field(InputValue::new(field.graphql_name(), TypeRef::named(filter_type)));
319 }
320 builder = builder.register(where_input);
321
322 let mut sort_input = InputObject::new(format!("{}SortInput", type_name));
323 for field in &collection.fields {
324 if field.name == "id" || field.name == "_id" {
325 continue;
326 }
327 if matches!(field.field_type, FieldType::Relation(_) | FieldType::Json | FieldType::List(_)) {
328 continue;
329 }
330 sort_input =
331 sort_input.field(InputValue::new(field.graphql_name(), TypeRef::named("SortDirection")));
332 }
333 builder = builder.register(sort_input);
334
335 let object_ref = TypeRef::named(type_name.clone());
336
337 let conn_name = format!("{}Connection", type_name);
338 let conn_obj = Object::new(conn_name.clone())
339 .field(nested_field("edges", TypeRef::named_nn_list(type_name.clone())))
340 .field(nested_field("pageInfo", TypeRef::named_nn("PageInfo")))
341 .field(nested_field("totalCount", TypeRef::named_nn("Int")));
342 builder = builder.register(conn_obj);
343
344 let coll_for_get = collection.clone();
345 let db_for_get = db.clone();
346 query_root = query_root.field(Self::apply_directives(
347 Field::new(collection.singular_name(), object_ref.clone(), move |ctx| {
348 let coll_def = coll_for_get.clone();
349 let db = db_for_get.clone();
350 FieldFuture::new(async move {
351 let result = query::resolve_get(ctx, &coll_def, &db).await;
352 (match result {
353 Ok(Some(value)) => json_to_field_value(value).map(Some),
354 Ok(None) => Ok(None),
355 Err(err) => Err(err),
356 })
357 .map_err(|err| err.into_graphql_error())
358 })
359 })
360 .argument(InputValue::new(
361 "where",
362 TypeRef::named_nn(format!("{}WhereUniqueInput", type_name)),
363 )),
364 &collection.directives,
365 Operation::Get,
366 ));
367
368 let coll_for_list = collection.clone();
369 let db_for_list = db.clone();
370 let page_size = self.config.max_page_size;
371 query_root = query_root.field(Self::apply_directives(
372 Field::new(
373 collection.plural_name(),
374 TypeRef::named_nn(conn_name.clone()),
375 move |ctx| {
376 let coll_def = coll_for_list.clone();
377 let db = db_for_list.clone();
378 FieldFuture::new(async move {
379 let result = query::resolve_list(ctx, &coll_def, &db, page_size).await;
380 (match result {
381 Ok(value) => json_to_field_value(value).map(Some),
382 Err(err) => Err(err),
383 })
384 .map_err(|err| err.into_graphql_error())
385 })
386 },
387 )
388 .argument(InputValue::new("first", TypeRef::named("Int")))
389 .argument(InputValue::new("after", TypeRef::named("String")))
390 .argument(InputValue::new("last", TypeRef::named("Int")))
391 .argument(InputValue::new("before", TypeRef::named("String")))
392 .argument(InputValue::new("where", TypeRef::named(format!("{}WhereInput", type_name))))
393 .argument(InputValue::new("sort", TypeRef::named(format!("{}SortInput", type_name)))),
394 &collection.directives,
395 Operation::List,
396 ));
397
398 let coll_for_create = collection.clone();
399 let client_for_create = client.clone();
400 let db_for_create = db.clone();
401 mutation_root = mutation_root.field(Self::apply_directives(
402 Field::new(
403 format!("create{}", type_name),
404 object_ref.clone(),
405 move |ctx| {
406 let coll_def = coll_for_create.clone();
407 let client = client_for_create.clone();
408 let db = db_for_create.clone();
409 FieldFuture::new(async move {
410 let result = mutation::resolve_create(ctx, &coll_def, &client, &db).await;
411 resolve_to_field_value(result)
412 })
413 },
414 )
415 .argument(InputValue::new(
416 "input",
417 TypeRef::named_nn(format!("{}CreateInput", type_name)),
418 )),
419 &collection.directives,
420 Operation::Create,
421 ));
422
423 let coll_for_update = collection.clone();
424 let client_for_update = client.clone();
425 let db_for_update = db.clone();
426 mutation_root = mutation_root.field(Self::apply_directives(
427 Field::new(
428 format!("update{}", type_name),
429 object_ref.clone(),
430 move |ctx| {
431 let coll_def = coll_for_update.clone();
432 let client = client_for_update.clone();
433 let db = db_for_update.clone();
434 FieldFuture::new(async move {
435 let result = mutation::resolve_update(ctx, &coll_def, &client, &db).await;
436 resolve_to_field_value(result)
437 })
438 },
439 )
440 .argument(InputValue::new(
441 "where",
442 TypeRef::named_nn(format!("{}WhereUniqueInput", type_name)),
443 ))
444 .argument(InputValue::new(
445 "input",
446 TypeRef::named_nn(format!("{}UpdateInput", type_name)),
447 )),
448 &collection.directives,
449 Operation::Update,
450 ));
451
452 let coll_for_delete = collection.clone();
453 let client_for_delete = client.clone();
454 let db_for_delete = db.clone();
455 mutation_root = mutation_root.field(Self::apply_directives(
456 Field::new(
457 format!("delete{}", type_name),
458 TypeRef::named_nn("DeleteResult"),
459 move |ctx| {
460 let coll_def = coll_for_delete.clone();
461 let client = client_for_delete.clone();
462 let db = db_for_delete.clone();
463 FieldFuture::new(async move {
464 let result = mutation::resolve_delete(ctx, &coll_def, &client, &db).await;
465 resolve_to_field_value(result)
466 })
467 },
468 )
469 .argument(InputValue::new(
470 "where",
471 TypeRef::named_nn(format!("{}WhereUniqueInput", type_name)),
472 )),
473 &collection.directives,
474 Operation::Delete,
475 ));
476
477 Ok((builder, query_root, mutation_root))
478 }
479
480 fn apply_directives(
481 field: Field,
482 directive_defs: &crate::schema::definition::FieldDirectives,
483 op: Operation,
484 ) -> Field {
485 let directives = match op {
486 Operation::Get => &directive_defs.get,
487 Operation::List => &directive_defs.list,
488 Operation::Create => &directive_defs.create,
489 Operation::Update => &directive_defs.update,
490 Operation::Delete => &directive_defs.delete,
491 };
492 if directives.is_empty() {
493 return field;
494 }
495 let mut built_field = field;
496 for directive_def in directives {
497 let mut graphql_directive =
498 async_graphql::dynamic::Directive::new(directive_def.name.clone());
499 for (arg_name, arg_value) in &directive_def.args {
500 let graphql_value = async_graphql::Value::from_json(arg_value.clone())
501 .unwrap_or(async_graphql::Value::Null);
502 graphql_directive = graphql_directive.argument(arg_name.as_str(), graphql_value);
503 }
504 built_field = built_field.directive(graphql_directive);
505 }
506 built_field
507 }
508
509 fn collect_reverse_relations(
510 &self,
511 collection: &CollectionDef,
512 ) -> (Vec<(String, String, String)>, Vec<(String, String, String)>) {
513 let mut to_many = Vec::new();
514 let mut to_one = Vec::new();
515 for other in &self.definition.collections {
516 if other.collection == collection.collection {
517 continue;
518 }
519 for field in &other.fields {
520 if let FieldType::Relation(relation) = &field.field_type {
521 if relation.collection != collection.collection {
522 continue;
523 }
524 match relation.kind {
525 RelationKind::OneToMany => {
526 let name = relation
527 .reverse_name
528 .clone()
529 .unwrap_or_else(|| other.plural_name());
530 to_many.push((name, other.collection.clone(), field.name.clone()));
531 }
532 RelationKind::OneToOne => {
533 let name = relation
534 .reverse_name
535 .clone()
536 .unwrap_or_else(|| other.singular_name());
537 to_one.push((name, other.collection.clone(), field.name.clone()));
538 }
539 _ => {}
540 }
541 }
542 }
543 }
544 (to_many, to_one)
545 }
546
547 fn build_input_fields(
549 &self,
550 mut input: InputObject,
551 collection: &CollectionDef,
552 collection_map: &HashMap<&str, &CollectionDef>,
553 mutation: &str,
554 ) -> InputObject {
555 for field in &collection.fields {
556 if field.name == "id" || field.name == "_id" {
557 continue;
558 }
559 if let FieldType::Relation(relation) = &field.field_type {
560 let target_type = collection_map
561 .get(relation.collection.as_str())
562 .map(|collection| collection.type_name())
563 .unwrap_or_else(|| relation.collection.clone());
564 let input_name = Self::relation_nested_input_name(relation.kind, &target_type, &collection.type_name(), mutation);
565 input = input.field(InputValue::new(field.graphql_name(), TypeRef::named(&input_name)));
566 continue;
567 }
568 let field_type = scalars::type_ref(&field.field_type);
569 let input_type = if field.required && mutation == "Create" {
570 TypeRef::named_nn(field_type.type_name())
571 } else {
572 field_type
573 };
574 input = input.field(InputValue::new(field.graphql_name(), input_type));
575 }
576 input
577 }
578
579 fn add_reverse_fields_to_input(
581 &self,
582 mut input: InputObject,
583 reverse_to_many: &[(String, String, String)],
584 reverse_to_one: &[(String, String, String)],
585 collection_map: &HashMap<&str, &CollectionDef>,
586 source_type: &str,
587 mutation: &str,
588 ) -> InputObject {
589 for (reverse_name, target_coll_name, _) in reverse_to_many {
590 if let Some(target_def) = collection_map.get(target_coll_name.as_str()) {
591 let input_name = Self::relation_nested_input_name(
592 RelationKind::ManyToMany, &target_def.type_name(), source_type, mutation,
593 );
594 input = input.field(InputValue::new(reverse_name.clone(), TypeRef::named(&input_name)));
595 }
596 }
597 for (reverse_name, target_coll_name, _) in reverse_to_one {
598 if let Some(target_def) = collection_map.get(target_coll_name.as_str()) {
599 let input_name = Self::relation_nested_input_name(
600 RelationKind::OneToOne, &target_def.type_name(), source_type, mutation,
601 );
602 input = input.field(InputValue::new(reverse_name.clone(), TypeRef::named(&input_name)));
603 }
604 }
605 input
606 }
607
608 fn build_without_fields(
612 &self,
613 mut without_input: InputObject,
614 target_coll: &CollectionDef,
615 source_coll: &CollectionDef,
616 mutation: &str,
617 ) -> InputObject {
618 for field in &target_coll.fields {
619 if field.name == "id" || field.name == "_id" {
620 continue;
621 }
622 if let FieldType::Relation(relation) = &field.field_type {
623 if relation.collection == source_coll.collection {
624 continue;
625 }
626 let target_type = self
627 .definition
628 .collection_by_name(&relation.collection)
629 .map(|collection| collection.type_name())
630 .unwrap_or_else(|| relation.collection.clone());
631 let input_name = Self::relation_nested_input_name(
632 relation.kind, &target_type, &target_coll.type_name(), mutation,
633 );
634 without_input =
635 without_input.field(InputValue::new(field.graphql_name(), TypeRef::named(&input_name)));
636 continue;
637 }
638 let field_type = scalars::type_ref(&field.field_type);
639 let input_type = if field.required && mutation == "Create" {
640 TypeRef::named_nn(field_type.type_name())
641 } else {
642 field_type
643 };
644 without_input =
645 without_input.field(InputValue::new(field.graphql_name(), input_type));
646 }
647 without_input
648 }
649
650 fn ensure_without_type(
653 &mut self,
654 mut builder: AgSchemaBuilder,
655 target_coll: &CollectionDef,
656 source_coll: &CollectionDef,
657 mutation: &str,
658 ) -> (AgSchemaBuilder, String) {
659 let target_type = target_coll.type_name();
660 let source_type = source_coll.type_name();
661 let without_name = match mutation {
662 "Create" => format!("{}CreateWithout{}Input", target_type, source_type),
663 "Update" => format!("{}UpdateWithout{}Input", target_type, source_type),
664 _ => panic!("unknown mutation: {}", mutation),
665 };
666 if !self.registered_nested_inputs.contains(&without_name) {
667 let base = self.build_without_fields(
668 InputObject::new(&without_name), target_coll, source_coll, mutation,
669 );
670 let with_reverse;
671 (builder, with_reverse) = self.add_reverse_fields_to_without(
672 builder, base, target_coll, source_coll,
673 );
674 builder = builder.register(with_reverse);
675 self.registered_nested_inputs.insert(without_name.clone());
676 }
677 (builder, without_name)
678 }
679
680 fn register_nested_inputs_for_field(
681 &mut self,
682 mut builder: AgSchemaBuilder,
683 source_coll: &CollectionDef,
684 field: &crate::schema::definition::FieldDef,
685 ) -> AgSchemaBuilder {
686 let relation = match &field.field_type {
687 FieldType::Relation(relation) => relation,
688 _ => return builder,
689 };
690 let target_def = match self.definition.collection_by_name(&relation.collection) {
691 Some(collection_def) => collection_def,
692 None => return builder,
693 };
694 let target_type = target_def.type_name();
695 let source_type = source_coll.type_name();
696
697 let create_without;
698 (builder, create_without) = self.ensure_without_type(builder, target_def, source_coll, "Create");
699 let update_without;
700 (builder, update_without) = self.ensure_without_type(builder, target_def, source_coll, "Update");
701
702 let is_to_many = matches!(relation.kind, RelationKind::ManyToMany);
703 builder = self.register_one_or_many_inputs(
704 builder, &target_type, &source_type, &create_without, &update_without, is_to_many,
705 );
706
707 builder
708 }
709
710 fn register_one_or_many_inputs(
713 &mut self,
714 mut builder: AgSchemaBuilder,
715 target_type: &str,
716 source_type: &str,
717 create_without_name: &str,
718 update_without_name: &str,
719 is_to_many: bool,
720 ) -> AgSchemaBuilder {
721 let where_unique = format!("{}WhereUniqueInput", target_type);
722 let (cardinality, create_ref, connect_ref, disconnect_delete_ref) = if is_to_many {
723 ("Many", TypeRef::named_nn_list(create_without_name), TypeRef::named_nn_list(&where_unique), TypeRef::named_nn_list(&where_unique))
724 } else {
725 ("One", TypeRef::named(create_without_name), TypeRef::named(&where_unique), TypeRef::named("Boolean"))
726 };
727
728 let create_name = format!("{}CreateNested{}Without{}Input", target_type, cardinality, source_type);
729 if !self.registered_nested_inputs.contains(&create_name) {
730 let input = InputObject::new(&create_name)
731 .field(InputValue::new("create", create_ref.clone()))
732 .field(InputValue::new("connect", connect_ref.clone()));
733 builder = builder.register(input);
734 self.registered_nested_inputs.insert(create_name);
735 }
736
737 let update_ref = if is_to_many {
739 let with_where_name = format!("{}UpdateWithWhereUniqueWithout{}Input", target_type, source_type);
740 if !self.registered_nested_inputs.contains(&with_where_name) {
741 let with_where = InputObject::new(&with_where_name)
742 .field(InputValue::new("where", TypeRef::named_nn(&where_unique)))
743 .field(InputValue::new("data", TypeRef::named_nn(update_without_name)));
744 builder = builder.register(with_where);
745 self.registered_nested_inputs.insert(with_where_name.clone());
746 }
747 TypeRef::named_nn_list(&with_where_name)
748 } else {
749 TypeRef::named(update_without_name)
750 };
751
752 let update_name = format!("{}Update{}Without{}NestedInput", target_type, cardinality, source_type);
753 if !self.registered_nested_inputs.contains(&update_name) {
754 let input = InputObject::new(&update_name)
755 .field(InputValue::new("create", create_ref))
756 .field(InputValue::new("connect", connect_ref))
757 .field(InputValue::new("disconnect", disconnect_delete_ref.clone()))
758 .field(InputValue::new("delete", disconnect_delete_ref))
759 .field(InputValue::new("update", update_ref));
760 builder = builder.register(input);
761 self.registered_nested_inputs.insert(update_name);
762 }
763
764 builder
765 }
766
767 fn register_reverse_inputs(
770 &mut self,
771 mut builder: AgSchemaBuilder,
772 source_coll: &CollectionDef,
773 target_coll: &CollectionDef,
774 kind: RelationKind,
775 ) -> (AgSchemaBuilder, String) {
776 let target_type = target_coll.type_name();
777 let source_type = source_coll.type_name();
778 let is_to_many = matches!(kind, RelationKind::OneToMany);
779
780 let create_without;
781 (builder, create_without) = self.ensure_without_type(builder, target_coll, source_coll, "Create");
782 let update_without;
783 (builder, update_without) = self.ensure_without_type(builder, target_coll, source_coll, "Update");
784
785 builder = self.register_one_or_many_inputs(
786 builder, &target_type, &source_type, &create_without, &update_without, is_to_many,
787 );
788
789 let nested_name = if is_to_many {
790 format!("{}CreateNestedManyWithout{}Input", target_type, source_type)
791 } else {
792 format!("{}CreateNestedOneWithout{}Input", target_type, source_type)
793 };
794
795 (builder, nested_name)
796 }
797
798 fn add_reverse_fields_to_without(
801 &mut self,
802 mut builder: AgSchemaBuilder,
803 mut without_input: InputObject,
804 target_coll: &CollectionDef,
805 source_coll: &CollectionDef,
806 ) -> (AgSchemaBuilder, InputObject) {
807 for other_coll in &self.definition.collections {
808 if other_coll.collection == target_coll.collection {
809 continue;
810 }
811 for other_field in &other_coll.fields {
812 let relation = match &other_field.field_type {
813 FieldType::Relation(relation) if relation.collection == target_coll.collection => relation,
814 _ => continue,
815 };
816 if other_coll.collection == source_coll.collection {
819 continue;
820 }
821 let (reverse_name, kind) = match relation.kind {
822 RelationKind::OneToMany => (
823 relation.reverse_name.clone().unwrap_or_else(|| other_coll.plural_name()),
824 RelationKind::OneToMany,
825 ),
826 RelationKind::OneToOne => (
827 relation.reverse_name.clone().unwrap_or_else(|| other_coll.singular_name()),
828 RelationKind::OneToOne,
829 ),
830 _ => continue,
831 };
832 let type_name;
833 (builder, type_name) = self.register_reverse_inputs(
834 builder, target_coll, other_coll, kind,
835 );
836 without_input = without_input.field(InputValue::new(
837 reverse_name,
838 TypeRef::named(&type_name),
839 ));
840 }
841 }
842 (builder, without_input)
843 }
844
845 fn relation_nested_input_name(kind: RelationKind, target_type: &str, source_type: &str, mutation: &str) -> String {
846 let cardinality = if matches!(kind, RelationKind::ManyToMany) { "Many" } else { "One" };
847 match mutation {
848 "Create" => format!("{}CreateNested{}Without{}Input", target_type, cardinality, source_type),
849 "Update" => format!("{}Update{}Without{}NestedInput", target_type, cardinality, source_type),
850 _ => panic!("unknown mutation: {}", mutation),
851 }
852 }
853
854 fn register_enum(
855 &mut self,
856 mut builder: AgSchemaBuilder,
857 enum_def: &EnumDef,
858 ) -> AgSchemaBuilder {
859 if self.registered_enums.contains(&enum_def.name) {
860 return builder;
861 }
862 let items: Vec<async_graphql::dynamic::EnumItem> = enum_def
863 .values
864 .iter()
865 .map(|value| async_graphql::dynamic::EnumItem::new(value.clone()))
866 .collect();
867 builder = builder.register(async_graphql::dynamic::Enum::new(
868 enum_def.name.clone(),
869 ).items(items));
870 self.registered_enums.insert(enum_def.name.clone());
871 builder
872 }
873
874 fn register_page_info(&self, builder: AgSchemaBuilder) -> AgSchemaBuilder {
875 let fields = [
876 ("hasNextPage", "Boolean", false),
877 ("hasPreviousPage", "Boolean", false),
878 ("startCursor", "String", false),
879 ("endCursor", "String", false),
880 ];
881 let mut page_info = Object::new("PageInfo");
882 for (field_name, type_name, non_null) in fields {
883 let type_ref = if non_null { TypeRef::named_nn(type_name) } else { TypeRef::named(type_name) };
884 page_info = page_info.field(nested_field(field_name, type_ref));
885 }
886 builder.register(page_info)
887 }
888
889 fn register_delete_result(&self, builder: AgSchemaBuilder) -> AgSchemaBuilder {
890 let mut delete_result = Object::new("DeleteResult");
891 delete_result = delete_result
892 .field(nested_field("success", TypeRef::named_nn("Boolean")))
893 .field(nested_field("deletedId", TypeRef::named_nn("ID")));
894 builder.register(delete_result)
895 }
896
897 fn register_filter_types(&self, builder: AgSchemaBuilder) -> AgSchemaBuilder {
898 builder
899 .register(
900 async_graphql::dynamic::Enum::new("SortDirection")
901 .item(async_graphql::dynamic::EnumItem::new("ASC"))
902 .item(async_graphql::dynamic::EnumItem::new("DESC")),
903 )
904 .register(InputObject::new("IdFilter")
905 .field(InputValue::new("eq", TypeRef::named("ID")))
906 .field(InputValue::new("ne", TypeRef::named("ID"))))
907 .register(InputObject::new("StringFilter")
908 .field(InputValue::new("eq", TypeRef::named("String")))
909 .field(InputValue::new("ne", TypeRef::named("String")))
910 .field(InputValue::new("contains", TypeRef::named("String")))
911 .field(InputValue::new("startsWith", TypeRef::named("String")))
912 .field(InputValue::new("endsWith", TypeRef::named("String"))))
913 .register(InputObject::new("IntFilter")
914 .field(InputValue::new("eq", TypeRef::named("Int")))
915 .field(InputValue::new("ne", TypeRef::named("Int")))
916 .field(InputValue::new("gt", TypeRef::named("Int")))
917 .field(InputValue::new("gte", TypeRef::named("Int")))
918 .field(InputValue::new("lt", TypeRef::named("Int")))
919 .field(InputValue::new("lte", TypeRef::named("Int"))))
920 .register(InputObject::new("FloatFilter")
921 .field(InputValue::new("eq", TypeRef::named("Float")))
922 .field(InputValue::new("ne", TypeRef::named("Float")))
923 .field(InputValue::new("gt", TypeRef::named("Float")))
924 .field(InputValue::new("gte", TypeRef::named("Float")))
925 .field(InputValue::new("lt", TypeRef::named("Float")))
926 .field(InputValue::new("lte", TypeRef::named("Float"))))
927 .register(InputObject::new("BooleanFilter")
928 .field(InputValue::new("eq", TypeRef::named("Boolean"))))
929 .register(InputObject::new("DateTimeFilter")
930 .field(InputValue::new("eq", TypeRef::named("DateTime")))
931 .field(InputValue::new("ne", TypeRef::named("DateTime")))
932 .field(InputValue::new("gt", TypeRef::named("DateTime")))
933 .field(InputValue::new("gte", TypeRef::named("DateTime")))
934 .field(InputValue::new("lt", TypeRef::named("DateTime")))
935 .field(InputValue::new("lte", TypeRef::named("DateTime"))))
936 }
937}
938
939fn extract_nested(parent: &async_graphql::Value, field_name: &str) -> Option<async_graphql::Value> {
940 match parent {
941 async_graphql::Value::Object(map) => map.get(field_name).cloned(),
942 _ => None,
943 }
944}
945
946fn filter_type_name(field_type: &FieldType) -> &'static str {
947 match field_type {
948 FieldType::ID => "IdFilter",
949 FieldType::String => "StringFilter",
950 FieldType::Int => "IntFilter",
951 FieldType::Float => "FloatFilter",
952 FieldType::Boolean => "BooleanFilter",
953 FieldType::DateTime => "DateTimeFilter",
954 FieldType::Json => "StringFilter",
955 FieldType::List(_) => "StringFilter",
956 FieldType::Relation(_) => "IdFilter",
957 }
958}
959
960fn nested_field(name: &str, type_ref: TypeRef) -> Field {
961 let field_name = name.to_string();
962 Field::new(name, type_ref, move |ctx| {
963 let name = field_name.clone();
964 FieldFuture::new(async move {
965 let value = ctx
966 .parent_value
967 .as_value()
968 .and_then(|parent| extract_nested(parent, &name));
969 match value {
970 Some(val) => Ok(Some(FieldValue::value(val))),
971 None => Ok(None),
972 }
973 })
974 })
975}
976
977fn resolve_to_field_value(
978 result: Result<Option<serde_json::Value>, GraphQLError>,
979) -> Result<Option<FieldValue<'static>>, async_graphql::Error> {
980 match result {
981 Ok(Some(value)) => json_to_field_value(value).map(Some),
982 Ok(None) => Ok(None),
983 Err(err) => Err(err),
984 }
985 .map_err(|err| err.into_graphql_error())
986}