async_graphql/resolver_utils/
container.rs

1use std::{future::Future, pin::Pin, sync::Arc};
2
3use futures_util::{FutureExt, StreamExt as _, stream::FuturesUnordered};
4use indexmap::IndexMap;
5
6use crate::{
7    Context, ContextBase, ContextSelectionSet, Error, Name, OutputType, ServerError, ServerResult,
8    Value, extensions::ResolveInfo, parser::types::Selection,
9};
10
11/// Represents a GraphQL container object.
12///
13/// This helper trait allows the type to call `resolve_container` on itself in
14/// its `OutputType::resolve` implementation.
15#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
16pub trait ContainerType: OutputType {
17    /// This function returns true of type `EmptyMutation` only.
18    #[doc(hidden)]
19    fn is_empty() -> bool {
20        false
21    }
22
23    /// Resolves a field value and outputs it as a json value
24    /// `async_graphql::Value`.
25    ///
26    /// If the field was not found returns None.
27    #[cfg(feature = "boxed-trait")]
28    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>>;
29
30    /// Resolves a field value and outputs it as a json value
31    /// `async_graphql::Value`.
32    ///
33    /// If the field was not found returns None.
34    #[cfg(not(feature = "boxed-trait"))]
35    fn resolve_field(
36        &self,
37        ctx: &Context<'_>,
38    ) -> impl Future<Output = ServerResult<Option<Value>>> + Send;
39
40    /// Collect all the fields of the container that are queried in the
41    /// selection set.
42    ///
43    /// Objects do not have to override this, but interfaces and unions must
44    /// call it on their internal type.
45    fn collect_all_fields<'a>(
46        &'a self,
47        ctx: &ContextSelectionSet<'a>,
48        fields: &mut Fields<'a>,
49    ) -> ServerResult<()>
50    where
51        Self: Send + Sync,
52    {
53        fields.add_set(ctx, self)
54    }
55
56    /// Find the GraphQL entity with the given name from the parameter.
57    ///
58    /// Objects should override this in case they are the query root.
59    #[cfg(feature = "boxed-trait")]
60    async fn find_entity(&self, _: &Context<'_>, _: &Value) -> ServerResult<Option<Value>> {
61        Ok(None)
62    }
63
64    /// Find the GraphQL entity with the given name from the parameter.
65    ///
66    /// Objects should override this in case they are the query root.
67    #[cfg(not(feature = "boxed-trait"))]
68    fn find_entity(
69        &self,
70        _: &Context<'_>,
71        _params: &Value,
72    ) -> impl Future<Output = ServerResult<Option<Value>>> + Send {
73        async { Ok(None) }
74    }
75}
76
77#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
78impl<T: ContainerType + ?Sized> ContainerType for &T {
79    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
80        T::resolve_field(*self, ctx).await
81    }
82
83    async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
84        T::find_entity(*self, ctx, params).await
85    }
86}
87
88#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
89impl<T: ContainerType + ?Sized> ContainerType for Arc<T> {
90    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
91        T::resolve_field(self, ctx).await
92    }
93
94    async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
95        T::find_entity(self, ctx, params).await
96    }
97}
98
99#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
100impl<T: ContainerType + ?Sized> ContainerType for Box<T> {
101    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
102        T::resolve_field(self, ctx).await
103    }
104
105    async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
106        T::find_entity(self, ctx, params).await
107    }
108}
109
110#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
111impl<T: ContainerType, E: Into<Error> + Send + Sync + Clone> ContainerType for Result<T, E> {
112    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
113        match self {
114            Ok(value) => T::resolve_field(value, ctx).await,
115            Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
116        }
117    }
118
119    async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
120        match self {
121            Ok(value) => T::find_entity(value, ctx, params).await,
122            Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
123        }
124    }
125}
126
127/// Resolve an container by executing each of the fields concurrently.
128pub async fn resolve_container<'a, T: ContainerType + ?Sized>(
129    ctx: &ContextSelectionSet<'a>,
130    root: &'a T,
131) -> ServerResult<Value> {
132    resolve_container_inner(ctx, root, true).await
133}
134
135/// Resolve an container by executing each of the fields serially.
136pub async fn resolve_container_serial<'a, T: ContainerType + ?Sized>(
137    ctx: &ContextSelectionSet<'a>,
138    root: &'a T,
139) -> ServerResult<Value> {
140    resolve_container_inner(ctx, root, false).await
141}
142
143fn insert_value(target: &mut IndexMap<Name, Value>, name: Name, value: Value) {
144    if let Some(prev_value) = target.get_mut(&name) {
145        if let Value::Object(target_map) = prev_value {
146            if let Value::Object(obj) = value {
147                for (key, value) in obj.into_iter() {
148                    insert_value(target_map, key, value);
149                }
150            }
151        } else if let Value::List(target_list) = prev_value
152            && let Value::List(list) = value
153        {
154            for (idx, value) in list.into_iter().enumerate() {
155                if let Some(Value::Object(target_map)) = target_list.get_mut(idx)
156                    && let Value::Object(obj) = value
157                {
158                    for (key, value) in obj.into_iter() {
159                        insert_value(target_map, key, value);
160                    }
161                }
162            }
163        }
164    } else {
165        target.insert(name, value);
166    }
167}
168
169async fn resolve_container_inner<'a, T: ContainerType + ?Sized>(
170    ctx: &ContextSelectionSet<'a>,
171    root: &'a T,
172    parallel: bool,
173) -> ServerResult<Value> {
174    let mut fields = Fields(Vec::new());
175    fields.add_set(ctx, root)?;
176
177    Ok(do_resolve_container(ctx, parallel, fields.0).await)
178}
179
180pub(crate) async fn do_resolve_container<'a>(
181    ctx: &ContextSelectionSet<'a>,
182    parallel: bool,
183    futures: Vec<BoxFieldFuture<'a>>,
184) -> Value {
185    let mut results = IndexMap::new();
186    let mut handle = |res| match res {
187        Ok((name, value)) => insert_value(&mut results, name, value),
188        Err(e) => ctx.add_error(e),
189    };
190
191    if parallel {
192        let mut set = FuturesUnordered::new();
193        set.extend(futures);
194        while let Some(field) = set.next().await {
195            handle(field);
196        }
197    } else {
198        for fut in futures {
199            handle(fut.await);
200        }
201    }
202
203    if results.is_empty() {
204        Value::Null
205    } else {
206        Value::Object(results)
207    }
208}
209
210type BoxFieldFuture<'a> = Pin<Box<dyn Future<Output = ServerResult<(Name, Value)>> + 'a + Send>>;
211
212/// A set of fields on an container that are being selected.
213pub struct Fields<'a>(Vec<BoxFieldFuture<'a>>);
214
215impl<'a> Fields<'a> {
216    /// Add another set of fields to this set of fields using the given
217    /// container.
218    pub fn add_set<T: ContainerType + ?Sized>(
219        &mut self,
220        ctx: &ContextSelectionSet<'a>,
221        root: &'a T,
222    ) -> ServerResult<()> {
223        for selection in &ctx.item.node.items {
224            match &selection.node {
225                Selection::Field(field) => {
226                    if field.node.name.node == "__typename" {
227                        // Get the typename
228                        let ctx_field = ctx.with_field(field);
229                        let field_name = ctx_field.item.node.response_key().node.clone();
230                        let typename = root.introspection_type_name().into_owned();
231
232                        self.0.push(Box::pin(async move {
233                            Ok((field_name, Value::String(typename)))
234                        }));
235                        continue;
236                    }
237
238                    let resolve_fut = Box::pin({
239                        let ctx = ctx.clone();
240                        async move {
241                            let ctx_field = ctx.with_field(field);
242                            let field_name = ctx_field.item.node.response_key().node.clone();
243                            let extensions = &ctx.query_env.extensions;
244
245                            if extensions.is_empty() && field.node.directives.is_empty() {
246                                Ok((
247                                    field_name,
248                                    root.resolve_field(&ctx_field).await?.unwrap_or_default(),
249                                ))
250                            } else {
251                                let type_name = T::type_name();
252                                let resolve_info = ResolveInfo {
253                                    path_node: ctx_field.path_node.as_ref().unwrap(),
254                                    parent_type: &type_name,
255                                    return_type: match ctx_field
256                                        .schema_env
257                                        .registry
258                                        .types
259                                        .get(type_name.as_ref())
260                                        .and_then(|ty| {
261                                            ty.field_by_name(field.node.name.node.as_str())
262                                        })
263                                        .map(|field| &field.ty)
264                                    {
265                                        Some(ty) => &ty,
266                                        None => {
267                                            return Err(ServerError::new(
268                                                format!(
269                                                    r#"Cannot query field "{}" on type "{}"."#,
270                                                    field_name, type_name
271                                                ),
272                                                Some(ctx_field.item.pos),
273                                            ));
274                                        }
275                                    },
276                                    name: field.node.name.node.as_str(),
277                                    alias: field
278                                        .node
279                                        .alias
280                                        .as_ref()
281                                        .map(|alias| alias.node.as_str()),
282                                    is_for_introspection: ctx_field.is_for_introspection,
283                                    field: &field.node,
284                                };
285
286                                let resolve_fut = root.resolve_field(&ctx_field);
287
288                                if field.node.directives.is_empty() {
289                                    futures_util::pin_mut!(resolve_fut);
290                                    Ok((
291                                        field_name,
292                                        extensions
293                                            .resolve(resolve_info, &mut resolve_fut)
294                                            .await?
295                                            .unwrap_or_default(),
296                                    ))
297                                } else {
298                                    let mut resolve_fut = resolve_fut.boxed();
299
300                                    for directive in &field.node.directives {
301                                        if let Some(directive_factory) = ctx
302                                            .schema_env
303                                            .custom_directives
304                                            .get(directive.node.name.node.as_str())
305                                        {
306                                            let ctx_directive = ContextBase {
307                                                path_node: ctx_field.path_node,
308                                                is_for_introspection: false,
309                                                item: directive,
310                                                schema_env: ctx_field.schema_env,
311                                                query_env: ctx_field.query_env,
312                                                execute_data: ctx_field.execute_data,
313                                            };
314                                            let directive_instance = directive_factory
315                                                .create(&ctx_directive, &directive.node)?;
316                                            resolve_fut = Box::pin({
317                                                let ctx_field = ctx_field.clone();
318                                                async move {
319                                                    directive_instance
320                                                        .resolve_field(&ctx_field, &mut resolve_fut)
321                                                        .await
322                                                }
323                                            });
324                                        }
325                                    }
326
327                                    Ok((
328                                        field_name,
329                                        extensions
330                                            .resolve(resolve_info, &mut resolve_fut)
331                                            .await?
332                                            .unwrap_or_default(),
333                                    ))
334                                }
335                            }
336                        }
337                    });
338
339                    self.0.push(resolve_fut);
340                }
341                selection => {
342                    let (type_condition, selection_set) = match selection {
343                        Selection::Field(_) => unreachable!(),
344                        Selection::FragmentSpread(spread) => {
345                            let fragment =
346                                ctx.query_env.fragments.get(&spread.node.fragment_name.node);
347                            let fragment = match fragment {
348                                Some(fragment) => fragment,
349                                None => {
350                                    return Err(ServerError::new(
351                                        format!(
352                                            r#"Unknown fragment "{}"."#,
353                                            spread.node.fragment_name.node
354                                        ),
355                                        Some(spread.pos),
356                                    ));
357                                }
358                            };
359                            (
360                                Some(&fragment.node.type_condition),
361                                &fragment.node.selection_set,
362                            )
363                        }
364                        Selection::InlineFragment(fragment) => (
365                            fragment.node.type_condition.as_ref(),
366                            &fragment.node.selection_set,
367                        ),
368                    };
369                    let type_condition =
370                        type_condition.map(|condition| condition.node.on.node.as_str());
371
372                    let introspection_type_name = root.introspection_type_name();
373
374                    let applies_concrete_object = type_condition.is_some_and(|condition| {
375                        introspection_type_name == condition
376                            || ctx
377                                .schema_env
378                                .registry
379                                .implements
380                                .get(&*introspection_type_name)
381                                .is_some_and(|interfaces| interfaces.contains(condition))
382                    });
383                    if applies_concrete_object {
384                        root.collect_all_fields(&ctx.with_selection_set(selection_set), self)?;
385                    } else if type_condition.is_none_or(|condition| T::type_name() == condition) {
386                        // The fragment applies to an interface type.
387                        self.add_set(&ctx.with_selection_set(selection_set), root)?;
388                    }
389                }
390            }
391        }
392        Ok(())
393    }
394}