1use std::{borrow::Cow, pin::Pin};
2
3use async_graphql_derive::SimpleObject;
4use async_graphql_parser::{Positioned, types::Field};
5use futures_util::{Future, FutureExt, future::BoxFuture};
6use indexmap::IndexMap;
7
8use crate::{
9 Context, ContextSelectionSet, Error, IntrospectionMode, Name, SDLExportOptions, ServerError,
10 ServerResult, Value,
11 dynamic::{
12 FieldFuture, FieldValue, Object, ObjectAccessor, ResolverContext, Schema, Type, TypeRef,
13 field::FieldValueInner,
14 },
15 extensions::ResolveInfo,
16 parser::types::Selection,
17 resolver_utils::create_value_object,
18};
19
20#[derive(SimpleObject)]
22#[graphql(internal, name = "_Service")]
23struct Service {
24 sdl: Option<String>,
25}
26
27type BoxFieldFuture<'a> = Pin<Box<dyn Future<Output = ServerResult<(Name, Value)>> + 'a + Send>>;
28
29pub(crate) async fn resolve_container(
30 schema: &Schema,
31 object: &Object,
32 ctx: &ContextSelectionSet<'_>,
33 parent_value: &FieldValue<'_>,
34 serial: bool,
35) -> ServerResult<Option<Value>> {
36 let mut fields = Vec::new();
37 collect_fields(&mut fields, schema, object, ctx, parent_value)?;
38
39 let res = if !serial {
40 futures_util::future::try_join_all(fields).await?
41 } else {
42 let mut results = Vec::with_capacity(fields.len());
43 for field in fields {
44 results.push(field.await?);
45 }
46 results
47 };
48
49 Ok(Some(create_value_object(res)))
50}
51
52fn collect_typename_field<'a>(
53 fields: &mut Vec<BoxFieldFuture<'a>>,
54 object: &'a Object,
55 field: &'a Positioned<Field>,
56) {
57 fields.push(
58 async move {
59 Ok((
60 field.node.response_key().node.clone(),
61 Value::from(object.name.as_str()),
62 ))
63 }
64 .boxed(),
65 )
66}
67
68fn collect_schema_field<'a>(
69 fields: &mut Vec<BoxFieldFuture<'a>>,
70 ctx: &ContextSelectionSet<'a>,
71 field: &'a Positioned<Field>,
72) {
73 let ctx = ctx.clone();
74 fields.push(
75 async move {
76 let ctx_field = ctx.with_field(field);
77 let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set);
78 ctx_obj.is_for_introspection = true;
79 let visible_types = ctx.schema_env.registry.find_visible_types(&ctx_field);
80 let value = crate::OutputType::resolve(
81 &crate::model::__Schema::new(&ctx.schema_env.registry, &visible_types),
82 &ctx_obj,
83 ctx_field.item,
84 )
85 .await?;
86 Ok((field.node.response_key().node.clone(), value))
87 }
88 .boxed(),
89 );
90}
91
92fn collect_type_field<'a>(
93 fields: &mut Vec<BoxFieldFuture<'a>>,
94 ctx: &ContextSelectionSet<'a>,
95 field: &'a Positioned<Field>,
96) {
97 let ctx = ctx.clone();
98 fields.push(
99 async move {
100 let ctx_field = ctx.with_field(field);
101 let (_, type_name) = ctx_field.param_value::<String>("name", None)?;
102 let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set);
103 ctx_obj.is_for_introspection = true;
104 let visible_types = ctx.schema_env.registry.find_visible_types(&ctx_field);
105 let value = crate::OutputType::resolve(
106 &ctx.schema_env
107 .registry
108 .types
109 .get(&type_name)
110 .filter(|_| visible_types.contains(type_name.as_str()))
111 .map(|ty| {
112 crate::model::__Type::new_simple(
113 &ctx.schema_env.registry,
114 &visible_types,
115 ty,
116 )
117 }),
118 &ctx_obj,
119 ctx_field.item,
120 )
121 .await?;
122 Ok((field.node.response_key().node.clone(), value))
123 }
124 .boxed(),
125 );
126}
127
128fn collect_service_field<'a>(
129 fields: &mut Vec<BoxFieldFuture<'a>>,
130 ctx: &ContextSelectionSet<'a>,
131 field: &'a Positioned<Field>,
132) {
133 let ctx = ctx.clone();
134 fields.push(
135 async move {
136 let ctx_field = ctx.with_field(field);
137 let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set);
138 ctx_obj.is_for_introspection = true;
139
140 let output_type = crate::OutputType::resolve(
141 &Service {
142 sdl: Some(
143 ctx.schema_env
144 .registry
145 .export_sdl(SDLExportOptions::new().federation().compose_directive()),
146 ),
147 },
148 &ctx_obj,
149 ctx_field.item,
150 )
151 .await?;
152
153 Ok((field.node.response_key().node.clone(), output_type))
154 }
155 .boxed(),
156 );
157}
158
159fn collect_entities_field<'a>(
160 fields: &mut Vec<BoxFieldFuture<'a>>,
161 schema: &'a Schema,
162 ctx: &ContextSelectionSet<'a>,
163 parent_value: &'a FieldValue,
164 field: &'a Positioned<Field>,
165) {
166 let ctx = ctx.clone();
167 fields.push(
168 async move {
169 let ctx_field = ctx.with_field(field);
170 let entity_resolver = schema.0.entity_resolver.as_ref().ok_or_else(|| {
171 ctx_field.set_error_path(
172 Error::new("internal: missing entity resolver")
173 .into_server_error(ctx_field.item.pos),
174 )
175 })?;
176 let entity_type = TypeRef::named_list_nn("_Entity");
177
178 let arguments = ObjectAccessor(Cow::Owned(
179 field
180 .node
181 .arguments
182 .iter()
183 .map(|(name, value)| {
184 ctx_field
185 .resolve_input_value(value.clone())
186 .map(|value| (name.node.clone(), value))
187 })
188 .collect::<ServerResult<IndexMap<Name, Value>>>()?,
189 ));
190
191 let field_future = (entity_resolver)(ResolverContext {
192 ctx: &ctx_field,
193 args: arguments,
194 parent_value,
195 });
196
197 let field_value = match field_future {
198 FieldFuture::Future(fut) => {
199 fut.await.map_err(|err| err.into_server_error(field.pos))?
200 }
201 FieldFuture::Value(value) => value,
202 };
203 let value = resolve(schema, &ctx_field, &entity_type, field_value.as_ref())
204 .await?
205 .unwrap_or_default();
206 Ok((field.node.response_key().node.clone(), value))
207 }
208 .boxed(),
209 );
210}
211
212fn collect_field<'a>(
213 fields: &mut Vec<BoxFieldFuture<'a>>,
214 schema: &'a Schema,
215 object: &'a Object,
216 ctx: &ContextSelectionSet<'a>,
217 parent_value: &'a FieldValue,
218 field_def: &'a crate::dynamic::Field,
219 field: &'a Positioned<Field>,
220) {
221 let ctx = ctx.clone();
222 fields.push(
223 async move {
224 let ctx_field = ctx.with_field(field);
225 let arguments = ObjectAccessor(Cow::Owned({
226 let mut args = field
227 .node
228 .arguments
229 .iter()
230 .map(|(name, value)| {
231 ctx_field
232 .resolve_input_value(value.clone())
233 .map(|value| (name.node.clone(), value))
234 })
235 .collect::<ServerResult<IndexMap<Name, Value>>>()?;
236 field_def.arguments.iter().for_each(|(name, arg)| {
237 if let Some(def) = &arg.default_value {
238 if !args.contains_key(name.as_str()) {
239 args.insert(Name::new(name), def.clone());
240 }
241 }
242 });
243 args
244 }));
245
246 let resolve_info = ResolveInfo {
247 path_node: ctx_field.path_node.as_ref().unwrap(),
248 parent_type: &object.name,
249 return_type: &field_def.ty_str,
250 name: &field.node.name.node,
251 alias: field.node.alias.as_ref().map(|alias| &*alias.node),
252 is_for_introspection: ctx_field.is_for_introspection,
253 field: &field.node,
254 };
255 let resolve_fut = async {
256 let field_future = (field_def.resolver_fn)(ResolverContext {
257 ctx: &ctx_field,
258 args: arguments,
259 parent_value,
260 });
261
262 let field_value = match field_future {
263 FieldFuture::Value(field_value) => field_value,
264 FieldFuture::Future(future) => future
265 .await
266 .map_err(|err| err.into_server_error(field.pos))?,
267 };
268
269 let value =
270 resolve(schema, &ctx_field, &field_def.ty, field_value.as_ref()).await?;
271
272 Ok(value)
273 };
274 futures_util::pin_mut!(resolve_fut);
275
276 let res_value = ctx_field
277 .query_env
278 .extensions
279 .resolve(resolve_info, &mut resolve_fut)
280 .await?
281 .unwrap_or_default();
282 Ok((field.node.response_key().node.clone(), res_value))
283 }
284 .boxed(),
285 );
286}
287
288fn collect_fields<'a>(
289 fields: &mut Vec<BoxFieldFuture<'a>>,
290 schema: &'a Schema,
291 object: &'a Object,
292 ctx: &ContextSelectionSet<'a>,
293 parent_value: &'a FieldValue,
294) -> ServerResult<()> {
295 for selection in &ctx.item.node.items {
296 match &selection.node {
297 Selection::Field(field) => {
298 if field.node.name.node == "__typename" {
299 collect_typename_field(fields, object, field);
300 continue;
301 }
302
303 if object.name == schema.0.env.registry.query_type
304 && matches!(
305 ctx.schema_env.registry.introspection_mode,
306 IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly
307 )
308 && matches!(
309 ctx.query_env.introspection_mode,
310 IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly,
311 )
312 {
313 if field.node.name.node == "__schema" {
315 collect_schema_field(fields, ctx, field);
316 continue;
317 } else if field.node.name.node == "__type" {
318 collect_type_field(fields, ctx, field);
319 continue;
320 } else if ctx.schema_env.registry.enable_federation
321 && field.node.name.node == "_service"
322 {
323 collect_service_field(fields, ctx, field);
324 continue;
325 } else if ctx.schema_env.registry.enable_federation
326 && field.node.name.node == "_entities"
327 {
328 collect_entities_field(fields, schema, ctx, parent_value, field);
329 continue;
330 }
331 }
332
333 if ctx.schema_env.registry.introspection_mode
334 == IntrospectionMode::IntrospectionOnly
335 || ctx.query_env.introspection_mode == IntrospectionMode::IntrospectionOnly
336 {
337 fields.push(
338 async move { Ok((field.node.response_key().node.clone(), Value::Null)) }
339 .boxed(),
340 );
341 continue;
342 }
343
344 if let Some(field_def) = object.fields.get(field.node.name.node.as_str()) {
345 collect_field(fields, schema, object, ctx, parent_value, field_def, field);
346 }
347 }
348 selection => {
349 let (type_condition, selection_set) = match selection {
350 Selection::Field(_) => unreachable!(),
351 Selection::FragmentSpread(spread) => {
352 let fragment = ctx.query_env.fragments.get(&spread.node.fragment_name.node);
353 let fragment = match fragment {
354 Some(fragment) => fragment,
355 None => {
356 return Err(ServerError::new(
357 format!(
358 "Unknown fragment \"{}\".",
359 spread.node.fragment_name.node
360 ),
361 Some(spread.pos),
362 ));
363 }
364 };
365 (
366 Some(&fragment.node.type_condition),
367 &fragment.node.selection_set,
368 )
369 }
370 Selection::InlineFragment(fragment) => (
371 fragment.node.type_condition.as_ref(),
372 &fragment.node.selection_set,
373 ),
374 };
375
376 let type_condition =
377 type_condition.map(|condition| condition.node.on.node.as_str());
378 let introspection_type_name = &object.name;
379
380 let type_condition_matched = match type_condition {
381 None => true,
382 Some(type_condition) if type_condition == introspection_type_name => true,
383 Some(type_condition) if object.implements.contains(type_condition) => true,
384 _ => false,
385 };
386 if type_condition_matched {
387 collect_fields(
388 fields,
389 schema,
390 object,
391 &ctx.with_selection_set(selection_set),
392 parent_value,
393 )?;
394 }
395 }
396 }
397 }
398
399 Ok(())
400}
401
402pub(crate) fn resolve<'a>(
403 schema: &'a Schema,
404 ctx: &'a Context<'a>,
405 type_ref: &'a TypeRef,
406 value: Option<&'a FieldValue>,
407) -> BoxFuture<'a, ServerResult<Option<Value>>> {
408 async move {
409 match (type_ref, value) {
410 (TypeRef::Named(type_name), Some(value)) => {
411 resolve_value(schema, ctx, &schema.0.types[type_name.as_ref()], value).await
412 }
413 (TypeRef::Named(_), None) => Ok(None),
414
415 (TypeRef::NonNull(type_ref), Some(value)) => {
416 resolve(schema, ctx, type_ref, Some(value)).await
417 }
418 (TypeRef::NonNull(_), None) => Err(ctx.set_error_path(
419 Error::new("internal: non-null types require a return value")
420 .into_server_error(ctx.item.pos),
421 )),
422
423 (TypeRef::List(type_ref), Some(FieldValue(FieldValueInner::List(values)))) => {
424 resolve_list(schema, ctx, type_ref, values).await
425 }
426 (
427 TypeRef::List(type_ref),
428 Some(FieldValue(FieldValueInner::Value(Value::List(values)))),
429 ) => {
430 let values = values
431 .iter()
432 .cloned()
433 .map(FieldValue::value)
434 .collect::<Vec<_>>();
435 resolve_list(schema, ctx, type_ref, &values).await
436 }
437 (TypeRef::List(_), Some(_)) => Err(ctx.set_error_path(
438 Error::new("internal: expects an array").into_server_error(ctx.item.pos),
439 )),
440 (TypeRef::List(_), None) => Ok(None),
441 }
442 }
443 .boxed()
444}
445
446async fn resolve_list<'a>(
447 schema: &'a Schema,
448 ctx: &'a Context<'a>,
449 type_ref: &'a TypeRef,
450 values: &[FieldValue<'_>],
451) -> ServerResult<Option<Value>> {
452 let mut futures = Vec::with_capacity(values.len());
453 for (idx, value) in values.iter().enumerate() {
454 let ctx_item = ctx.with_index(idx);
455
456 futures.push(async move {
457 let parent_type = format!("[{}]", type_ref);
458 let return_type = type_ref.to_string();
459 let resolve_info = ResolveInfo {
460 path_node: ctx_item.path_node.as_ref().unwrap(),
461 parent_type: &parent_type,
462 return_type: &return_type,
463 name: ctx.item.node.name.node.as_str(),
464 alias: ctx
465 .item
466 .node
467 .alias
468 .as_ref()
469 .map(|alias| alias.node.as_str()),
470 is_for_introspection: ctx_item.is_for_introspection,
471 field: &ctx_item.item.node,
472 };
473
474 let resolve_fut = async { resolve(schema, &ctx_item, type_ref, Some(value)).await };
475 futures_util::pin_mut!(resolve_fut);
476
477 let res_value = ctx_item
478 .query_env
479 .extensions
480 .resolve(resolve_info, &mut resolve_fut)
481 .await?;
482 Ok::<_, ServerError>(res_value.unwrap_or_default())
483 });
484 }
485 let values = futures_util::future::try_join_all(futures).await?;
486 Ok(Some(Value::List(values)))
487}
488
489async fn resolve_value(
490 schema: &Schema,
491 ctx: &Context<'_>,
492 field_type: &Type,
493 value: &FieldValue<'_>,
494) -> ServerResult<Option<Value>> {
495 match (field_type, &value.0) {
496 (Type::Scalar(scalar), FieldValueInner::Value(value)) if scalar.validate(value) => {
497 Ok(Some(value.clone()))
498 }
499 (Type::Scalar(scalar), _) => Err(ctx.set_error_path(
500 Error::new(format!(
501 "internal: invalid value for scalar \"{}\", expected \"FieldValue::Value\"",
502 scalar.name
503 ))
504 .into_server_error(ctx.item.pos),
505 )),
506
507 (Type::Object(object), _) => {
508 resolve_container(
509 schema,
510 object,
511 &ctx.with_selection_set(&ctx.item.node.selection_set),
512 value,
513 true,
514 )
515 .await
516 }
517
518 (Type::InputObject(obj), _) => Err(ctx.set_error_path(
519 Error::new(format!(
520 "internal: cannot use input object \"{}\" as output value",
521 obj.name
522 ))
523 .into_server_error(ctx.item.pos),
524 )),
525
526 (Type::Enum(e), FieldValueInner::Value(Value::Enum(name))) => {
527 if !e.enum_values.contains_key(name.as_str()) {
528 return Err(ctx.set_error_path(
529 Error::new(format!("internal: invalid item for enum \"{}\"", e.name))
530 .into_server_error(ctx.item.pos),
531 ));
532 }
533 Ok(Some(Value::Enum(name.clone())))
534 }
535 (Type::Enum(e), FieldValueInner::Value(Value::String(name))) => {
536 if !e.enum_values.contains_key(name) {
537 return Err(ctx.set_error_path(
538 Error::new(format!("internal: invalid item for enum \"{}\"", e.name))
539 .into_server_error(ctx.item.pos),
540 ));
541 }
542 Ok(Some(Value::Enum(Name::new(name))))
543 }
544 (Type::Enum(e), _) => Err(ctx.set_error_path(
545 Error::new(format!("internal: invalid item for enum \"{}\"", e.name))
546 .into_server_error(ctx.item.pos),
547 )),
548
549 (Type::Interface(interface), FieldValueInner::WithType { value, ty }) => {
550 let is_contains_obj = schema
551 .0
552 .env
553 .registry
554 .types
555 .get(&interface.name)
556 .and_then(|meta_type| {
557 meta_type
558 .possible_types()
559 .map(|possible_types| possible_types.contains(ty.as_ref()))
560 })
561 .unwrap_or_default();
562 if !is_contains_obj {
563 return Err(ctx.set_error_path(
564 Error::new(format!(
565 "internal: object \"{}\" does not implement interface \"{}\"",
566 ty, interface.name,
567 ))
568 .into_server_error(ctx.item.pos),
569 ));
570 }
571
572 let object_type = schema
573 .0
574 .types
575 .get(ty.as_ref())
576 .ok_or_else(|| {
577 ctx.set_error_path(
578 Error::new(format!("internal: object \"{}\" does not registered", ty))
579 .into_server_error(ctx.item.pos),
580 )
581 })?
582 .as_object()
583 .ok_or_else(|| {
584 ctx.set_error_path(
585 Error::new(format!("internal: type \"{}\" is not object", ty))
586 .into_server_error(ctx.item.pos),
587 )
588 })?;
589
590 resolve_container(
591 schema,
592 object_type,
593 &ctx.with_selection_set(&ctx.item.node.selection_set),
594 value,
595 true,
596 )
597 .await
598 }
599 (Type::Interface(interface), _) => Err(ctx.set_error_path(
600 Error::new(format!(
601 "internal: invalid value for interface \"{}\", expected \"FieldValue::WithType\"",
602 interface.name
603 ))
604 .into_server_error(ctx.item.pos),
605 )),
606
607 (Type::Union(union), FieldValueInner::WithType { value, ty }) => {
608 if !union.possible_types.contains(ty.as_ref()) {
609 return Err(ctx.set_error_path(
610 Error::new(format!(
611 "internal: union \"{}\" does not contain object \"{}\"",
612 union.name, ty,
613 ))
614 .into_server_error(ctx.item.pos),
615 ));
616 }
617
618 let object_type = schema
619 .0
620 .types
621 .get(ty.as_ref())
622 .ok_or_else(|| {
623 ctx.set_error_path(
624 Error::new(format!("internal: object \"{}\" does not registered", ty))
625 .into_server_error(ctx.item.pos),
626 )
627 })?
628 .as_object()
629 .ok_or_else(|| {
630 ctx.set_error_path(
631 Error::new(format!("internal: type \"{}\" is not object", ty))
632 .into_server_error(ctx.item.pos),
633 )
634 })?;
635
636 resolve_container(
637 schema,
638 object_type,
639 &ctx.with_selection_set(&ctx.item.node.selection_set),
640 value,
641 true,
642 )
643 .await
644 }
645 (Type::Union(union), _) => Err(ctx.set_error_path(
646 Error::new(format!(
647 "internal: invalid value for union \"{}\", expected \"FieldValue::WithType\"",
648 union.name
649 ))
650 .into_server_error(ctx.item.pos),
651 )),
652 (Type::Subscription(subscription), _) => Err(ctx.set_error_path(
653 Error::new(format!(
654 "internal: cannot use subscription \"{}\" as output value",
655 subscription.name
656 ))
657 .into_server_error(ctx.item.pos),
658 )),
659 (Type::Upload, _) => Err(ctx.set_error_path(
660 Error::new("internal: cannot use upload as output value")
661 .into_server_error(ctx.item.pos),
662 )),
663 }
664}