juniper/types/
async_await.rs

1use std::future;
2
3use auto_enums::enum_derive;
4
5use crate::{
6    ast::Selection,
7    executor::{ExecutionResult, Executor},
8    parser::Spanning,
9    value::{DefaultScalarValue, Object, ScalarValue, Value},
10};
11
12use crate::BoxFuture;
13
14use super::base::{Arguments, GraphQLType, GraphQLValue, is_excluded, merge_key_into};
15
16/// Extension of [`GraphQLValue`] trait with asynchronous queries/mutations resolvers.
17///
18/// Convenience macros related to asynchronous queries/mutations expand into an implementation of
19/// this trait and [`GraphQLValue`] for the given type.
20pub trait GraphQLValueAsync<S = DefaultScalarValue>: GraphQLValue<S> + Sync
21where
22    Self::TypeInfo: Sync,
23    Self::Context: Sync,
24    S: ScalarValue + Send + Sync,
25{
26    /// Resolves the value of a single field on this [`GraphQLValueAsync`].
27    ///
28    /// The `arguments` object contains all the specified arguments, with default values being
29    /// substituted for the ones not provided by the query.
30    ///
31    /// The `executor` can be used to drive selections into sub-[objects][3].
32    ///
33    /// # Panics
34    ///
35    /// The default implementation panics.
36    ///
37    /// [3]: https://spec.graphql.org/October2021#sec-Objects
38    fn resolve_field_async<'a>(
39        &'a self,
40        _info: &'a Self::TypeInfo,
41        _field_name: &'a str,
42        _arguments: &'a Arguments<S>,
43        _executor: &'a Executor<Self::Context, S>,
44    ) -> BoxFuture<'a, ExecutionResult<S>> {
45        panic!(
46            "GraphQLValueAsync::resolve_field_async() must be implemented by objects and \
47             interfaces",
48        );
49    }
50
51    /// Resolves this [`GraphQLValueAsync`] (being an [interface][1] or an [union][2]) into a
52    /// concrete downstream [object][3] type.
53    ///
54    /// Tries to resolve this [`GraphQLValueAsync`] into the provided `type_name`. If the type
55    /// matches, then passes the instance along to [`Executor::resolve`].
56    ///
57    /// # Panics
58    ///
59    /// The default implementation panics.
60    ///
61    /// [1]: https://spec.graphql.org/October2021#sec-Interfaces
62    /// [2]: https://spec.graphql.org/October2021#sec-Unions
63    /// [3]: https://spec.graphql.org/October2021#sec-Objects
64    fn resolve_into_type_async<'a>(
65        &'a self,
66        info: &'a Self::TypeInfo,
67        type_name: &str,
68        selection_set: Option<&'a [Selection<'a, S>]>,
69        executor: &'a Executor<'a, 'a, Self::Context, S>,
70    ) -> BoxFuture<'a, ExecutionResult<S>> {
71        if self.type_name(info).unwrap() == type_name {
72            self.resolve_async(info, selection_set, executor)
73        } else {
74            panic!(
75                "GraphQLValueAsync::resolve_into_type_async() must be implemented by unions and \
76                 interfaces",
77            );
78        }
79    }
80
81    /// Resolves the provided `selection_set` against this [`GraphQLValueAsync`].
82    ///
83    /// For non-[object][3] types, the `selection_set` will be [`None`] and the value should simply
84    /// be returned.
85    ///
86    /// For [objects][3], all fields in the `selection_set` should be resolved. The default
87    /// implementation uses [`GraphQLValueAsync::resolve_field_async`] to resolve all fields,
88    /// including those through a fragment expansion.
89    ///
90    /// Since the [GraphQL spec specifies][0] that errors during field processing should result in
91    /// a null-value, this might return `Ok(Null)` in case of a failure. Errors are recorded
92    /// internally.
93    ///
94    /// # Panics
95    ///
96    /// The default implementation panics, if `selection_set` is [`None`].
97    ///
98    /// [0]: https://spec.graphql.org/October2021#sec-Handling-Field-Errors
99    /// [3]: https://spec.graphql.org/October2021#sec-Objects
100    fn resolve_async<'a>(
101        &'a self,
102        info: &'a Self::TypeInfo,
103        selection_set: Option<&'a [Selection<S>]>,
104        executor: &'a Executor<Self::Context, S>,
105    ) -> BoxFuture<'a, ExecutionResult<S>> {
106        if let Some(sel) = selection_set {
107            Box::pin(async move {
108                Ok(resolve_selection_set_into_async(self, info, sel, executor).await)
109            })
110        } else {
111            panic!(
112                "GraphQLValueAsync::resolve_async() must be implemented by non-object output types",
113            );
114        }
115    }
116}
117
118/// Extension of [`GraphQLType`] trait with asynchronous queries/mutations resolvers.
119///
120/// It's automatically implemented for [`GraphQLValueAsync`] and [`GraphQLType`] implementers, so
121/// doesn't require manual or code-generated implementation.
122pub trait GraphQLTypeAsync<S = DefaultScalarValue>: GraphQLValueAsync<S> + GraphQLType<S>
123where
124    Self::Context: Sync,
125    Self::TypeInfo: Sync,
126    S: ScalarValue + Send + Sync,
127{
128}
129
130impl<S, T> GraphQLTypeAsync<S> for T
131where
132    T: GraphQLValueAsync<S> + GraphQLType<S> + ?Sized,
133    T::Context: Sync,
134    T::TypeInfo: Sync,
135    S: ScalarValue + Send + Sync,
136{
137}
138
139// Wrapper function around resolve_selection_set_into_async_recursive.
140// This wrapper is necessary because async fns can not be recursive.
141fn resolve_selection_set_into_async<'a, 'e, T, S>(
142    instance: &'a T,
143    info: &'a T::TypeInfo,
144    selection_set: &'e [Selection<'e, S>],
145    executor: &'e Executor<'e, 'e, T::Context, S>,
146) -> BoxFuture<'a, Value<S>>
147where
148    T: GraphQLValueAsync<S> + ?Sized,
149    T::TypeInfo: Sync,
150    T::Context: Sync,
151    S: ScalarValue + Send + Sync,
152    'e: 'a,
153{
154    Box::pin(resolve_selection_set_into_async_recursive(
155        instance,
156        info,
157        selection_set,
158        executor,
159    ))
160}
161
162#[derive(Debug)]
163struct AsyncField<S> {
164    name: String,
165    value: Option<Value<S>>,
166}
167
168#[derive(Debug)]
169enum AsyncValue<S> {
170    Field(AsyncField<S>),
171    Nested(Value<S>),
172}
173
174pub(crate) async fn resolve_selection_set_into_async_recursive<'a, T, S>(
175    instance: &'a T,
176    info: &'a T::TypeInfo,
177    selection_set: &'a [Selection<'a, S>],
178    executor: &'a Executor<'a, 'a, T::Context, S>,
179) -> Value<S>
180where
181    T: GraphQLValueAsync<S> + ?Sized,
182    T::TypeInfo: Sync,
183    T::Context: Sync,
184    S: ScalarValue + Send + Sync,
185{
186    use futures::stream::{FuturesOrdered, StreamExt as _};
187
188    #[enum_derive(Future)]
189    enum AsyncValueFuture<F1, F2, FS, IF1, IF2> {
190        Field1(F1),
191        Field2(F2),
192        FragmentSpread(FS),
193        InlineFragment1(IF1),
194        InlineFragment2(IF2),
195    }
196
197    let mut object = Object::with_capacity(selection_set.len());
198
199    let mut async_values = FuturesOrdered::<AsyncValueFuture<_, _, _, _, _>>::new();
200
201    let meta_type = executor
202        .schema()
203        .concrete_type_by_name(
204            instance
205                .type_name(info)
206                .expect("Resolving named type's selection set"),
207        )
208        .expect("Type not found in schema");
209
210    for selection in selection_set {
211        match *selection {
212            Selection::Field(Spanning {
213                item: ref f,
214                ref span,
215            }) => {
216                if is_excluded(&f.directives, executor.variables()) {
217                    continue;
218                }
219
220                let response_name = f.alias.as_ref().unwrap_or(&f.name).item;
221
222                if f.name.item == "__typename" {
223                    object.add_field(
224                        response_name,
225                        Value::Scalar(instance.concrete_type_name(executor.context(), info).into()),
226                    );
227                    continue;
228                }
229
230                let meta_field = meta_type.field_by_name(f.name.item).unwrap_or_else(|| {
231                    panic!(
232                        "Field {} not found on type {:?}",
233                        f.name.item,
234                        meta_type.name(),
235                    )
236                });
237
238                let exec_vars = executor.variables();
239
240                let sub_exec = executor.field_sub_executor(
241                    response_name,
242                    f.name.item,
243                    span.start,
244                    f.selection_set.as_ref().map(|v| &v[..]),
245                );
246                let args = Arguments::new(
247                    f.arguments.as_ref().map(|m| {
248                        m.item
249                            .iter()
250                            .filter_map(|(k, v)| {
251                                let val = v.item.clone().into_const(exec_vars)?;
252                                Some((k.item, Spanning::new(v.span, val)))
253                            })
254                            .collect()
255                    }),
256                    &meta_field.arguments,
257                );
258
259                let pos = span.start;
260                let is_non_null = meta_field.field_type.is_non_null();
261
262                let response_name = response_name.to_string();
263                async_values.push_back(AsyncValueFuture::Field1(async move {
264                    // TODO: implement custom future type instead of
265                    //       two-level boxing.
266                    let res = instance
267                        .resolve_field_async(info, f.name.item, &args, &sub_exec)
268                        .await;
269
270                    let value = match res {
271                        Ok(Value::Null) if is_non_null => None,
272                        Ok(v) => Some(v),
273                        Err(e) => {
274                            sub_exec.push_error_at(e, pos);
275
276                            if is_non_null {
277                                None
278                            } else {
279                                Some(Value::null())
280                            }
281                        }
282                    };
283                    AsyncValue::Field(AsyncField {
284                        name: response_name,
285                        value,
286                    })
287                }));
288            }
289
290            Selection::FragmentSpread(Spanning {
291                item: ref spread,
292                ref span,
293            }) => {
294                if is_excluded(&spread.directives, executor.variables()) {
295                    continue;
296                }
297
298                let fragment = &executor
299                    .fragment_by_name(spread.name.item)
300                    .expect("Fragment could not be found");
301
302                let sub_exec = executor.type_sub_executor(
303                    Some(fragment.type_condition.item),
304                    Some(&fragment.selection_set[..]),
305                );
306
307                let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
308                let type_name = instance.type_name(info);
309                if executor
310                    .schema()
311                    .is_named_subtype(&concrete_type_name, fragment.type_condition.item)
312                    || Some(fragment.type_condition.item) == type_name.as_deref()
313                {
314                    let sub_result = instance
315                        .resolve_into_type_async(
316                            info,
317                            &concrete_type_name,
318                            Some(&fragment.selection_set[..]),
319                            &sub_exec,
320                        )
321                        .await;
322
323                    if let Ok(Value::Object(obj)) = sub_result {
324                        for (k, v) in obj {
325                            async_values.push_back(AsyncValueFuture::FragmentSpread(
326                                future::ready(AsyncValue::Field(AsyncField {
327                                    name: k,
328                                    value: Some(v),
329                                })),
330                            ));
331                        }
332                    } else {
333                        if let Err(e) = sub_result {
334                            sub_exec.push_error_at(e, span.start);
335                        }
336                        // NOTE: Executing a fragment cannot really result in anything other
337                        //       than `Value::Object`, because it represents a set of fields.
338                        //       So, if an error happens or a `Value::Null` is returned, it's an
339                        //       indication that the fragment execution failed somewhere and,
340                        //       because of non-`null` types involved, its error should be
341                        //       propagated to the parent field, which is done here by returning
342                        //       a `Value::Null`.
343                        async_values.push_back(AsyncValueFuture::Field2(future::ready(
344                            AsyncValue::Field(AsyncField {
345                                name: String::new(), // doesn't matter here
346                                value: None,
347                            }),
348                        )));
349                    }
350                }
351            }
352
353            Selection::InlineFragment(Spanning {
354                item: ref fragment,
355                ref span,
356            }) => {
357                if is_excluded(&fragment.directives, executor.variables()) {
358                    continue;
359                }
360
361                let sub_exec = executor.type_sub_executor(
362                    fragment.type_condition.as_ref().map(|c| c.item),
363                    Some(&fragment.selection_set[..]),
364                );
365
366                if let Some(ref type_condition) = fragment.type_condition {
367                    // Check whether the type matches the type condition.
368                    let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
369                    if executor
370                        .schema()
371                        .is_named_subtype(&concrete_type_name, type_condition.item)
372                    {
373                        let sub_result = instance
374                            .resolve_into_type_async(
375                                info,
376                                &concrete_type_name,
377                                Some(&fragment.selection_set[..]),
378                                &sub_exec,
379                            )
380                            .await;
381
382                        if let Ok(Value::Object(obj)) = sub_result {
383                            for (k, v) in obj {
384                                async_values.push_back(AsyncValueFuture::InlineFragment1(
385                                    future::ready(AsyncValue::Field(AsyncField {
386                                        name: k,
387                                        value: Some(v),
388                                    })),
389                                ));
390                            }
391                        } else {
392                            if let Err(e) = sub_result {
393                                sub_exec.push_error_at(e, span.start);
394                            }
395                            // NOTE: Executing a fragment cannot really result in anything other
396                            //       than `Value::Object`, because it represents a set of fields.
397                            //       So, if an error happens or a `Value::Null` is returned, it's an
398                            //       indication that the fragment execution failed somewhere and,
399                            //       because of non-`null` types involved, its error should be
400                            //       propagated to the parent field, which is done here by returning
401                            //       a `Value::Null`.
402                            async_values.push_back(AsyncValueFuture::Field2(future::ready(
403                                AsyncValue::Field(AsyncField {
404                                    name: String::new(), // doesn't matter here
405                                    value: None,
406                                }),
407                            )));
408                        }
409                    }
410                } else {
411                    async_values.push_back(AsyncValueFuture::InlineFragment2(async move {
412                        let value = resolve_selection_set_into_async(
413                            instance,
414                            info,
415                            &fragment.selection_set[..],
416                            &sub_exec,
417                        )
418                        .await;
419                        AsyncValue::Nested(value)
420                    }));
421                }
422            }
423        }
424    }
425
426    while let Some(item) = async_values.next().await {
427        match item {
428            AsyncValue::Field(AsyncField { name, value }) => {
429                if let Some(value) = value {
430                    merge_key_into(&mut object, &name, value);
431                } else {
432                    return Value::null();
433                }
434            }
435            AsyncValue::Nested(obj) => match obj {
436                v @ Value::Null => {
437                    return v;
438                }
439                Value::Object(obj) => {
440                    for (k, v) in obj {
441                        merge_key_into(&mut object, &k, v);
442                    }
443                }
444                _ => unreachable!(),
445            },
446        }
447    }
448
449    Value::Object(object)
450}