async_graphql/dynamic/
resolve.rs

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/// Federation service
21#[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                    // is query root
314                    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}