1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::base::BoxFieldFuture;
use crate::extensions::{Extension, ResolveInfo};
use crate::parser::query::{Selection, TypeCondition};
use crate::{ContextSelectionSet, Error, ObjectType, QueryError, Result};
use futures::{future, TryFutureExt};

#[allow(missing_docs)]
pub async fn do_resolve<'a, T: ObjectType + Send + Sync>(
    ctx: &'a ContextSelectionSet<'a>,
    root: &'a T,
) -> Result<serde_json::Value> {
    let mut futures = Vec::new();
    collect_fields(ctx, root, &mut futures)?;
    let res = futures::future::try_join_all(futures).await?;
    let mut map = serde_json::Map::new();
    for (name, value) in res {
        match (map.remove(&name), value) {
            (Some(serde_json::Value::Object(mut a)), serde_json::Value::Object(b)) => {
                a.extend(b);
                map.insert(name, a.into());
            }
            (_, b) => {
                map.insert(name, b);
            }
        }
    }
    Ok(map.into())
}

#[allow(missing_docs)]
pub fn collect_fields<'a, T: ObjectType + Send + Sync>(
    ctx: &ContextSelectionSet<'a>,
    root: &'a T,
    futures: &mut Vec<BoxFieldFuture<'a>>,
) -> Result<()> {
    if ctx.items.is_empty() {
        return Err(Error::Query {
            pos: ctx.position(),
            path: None,
            err: QueryError::MustHaveSubFields {
                object: T::type_name().to_string(),
            },
        });
    }

    for selection in &ctx.item.items {
        match &selection.node {
            Selection::Field(field) => {
                if ctx.is_skip(&field.directives)? {
                    continue;
                }

                if field.name.node == "__typename" {
                    // Get the typename
                    let ctx_field = ctx.with_field(field);
                    let field_name = ctx_field.result_name().to_string();
                    futures.push(Box::pin(
                        future::ok::<serde_json::Value, Error>(
                            root.introspection_type_name().to_string().into(),
                        )
                        .map_ok(move |value| (field_name, value)),
                    ));
                    continue;
                }

                futures.push(Box::pin({
                    let ctx = ctx.clone();
                    async move {
                        let ctx_field = ctx.with_field(field);
                        let field_name = ctx_field.result_name().to_string();

                        let resolve_info = ResolveInfo {
                            resolve_id: ctx_field.resolve_id,
                            path_node: ctx_field.path_node.as_ref().unwrap(),
                            parent_type: &T::type_name(),
                            return_type: match ctx_field
                                .schema_env
                                .registry
                                .types
                                .get(T::type_name().as_ref())
                                .and_then(|ty| ty.field_by_name(field.name.as_str()))
                                .map(|field| &field.ty)
                            {
                                Some(ty) => &ty,
                                None => {
                                    return Err(Error::Query {
                                        pos: field.position(),
                                        path: None,
                                        err: QueryError::FieldNotFound {
                                            field_name: field.name.to_string(),
                                            object: T::type_name().to_string(),
                                        },
                                    });
                                }
                            },
                        };

                        ctx_field.query_env.extensions.resolve_start(&resolve_info);

                        let res = ctx_field.query_env.extensions.log_error(
                            root.resolve_field(&ctx_field)
                                .map_ok(move |value| (field_name, value))
                                .await,
                        )?;

                        ctx_field.query_env.extensions.resolve_end(&resolve_info);
                        Ok(res)
                    }
                }))
            }
            Selection::FragmentSpread(fragment_spread) => {
                if ctx.is_skip(&fragment_spread.directives)? {
                    continue;
                }

                if let Some(fragment) = ctx
                    .query_env
                    .document
                    .fragments()
                    .get(fragment_spread.fragment_name.as_str())
                {
                    collect_fields(
                        &ctx.with_selection_set(&fragment.selection_set),
                        root,
                        futures,
                    )?;
                } else {
                    return Err(Error::Query {
                        pos: fragment_spread.position(),
                        path: None,
                        err: QueryError::UnknownFragment {
                            name: fragment_spread.fragment_name.to_string(),
                        },
                    });
                }
            }
            Selection::InlineFragment(inline_fragment) => {
                if ctx.is_skip(&inline_fragment.directives)? {
                    continue;
                }

                if let Some(TypeCondition::On(name)) = inline_fragment.type_condition.as_deref() {
                    root.collect_inline_fields(
                        name,
                        &ctx.with_selection_set(&inline_fragment.selection_set),
                        futures,
                    )?;
                } else {
                    collect_fields(
                        &ctx.with_selection_set(&inline_fragment.selection_set),
                        root,
                        futures,
                    )?;
                }
            }
        }
    }

    Ok(())
}