async-graphql/src/resolver.rs

169 lines
6.2 KiB
Rust
Raw Normal View History

2020-03-25 03:39:28 +00:00
use crate::base::BoxFieldFuture;
use crate::extensions::{ErrorLogger, Extension, ResolveInfo};
use crate::parser::query::{Selection, TypeCondition};
use crate::{ContextSelectionSet, Error, ObjectType, QueryError, Result};
2020-03-25 03:39:28 +00:00
use futures::{future, TryFutureExt};
2020-03-06 15:58:43 +00:00
2020-03-25 03:39:28 +00:00
#[allow(missing_docs)]
pub async fn do_resolve<'a, T: ObjectType + Send + Sync>(
2020-03-06 15:58:43 +00:00
ctx: &'a ContextSelectionSet<'a>,
2020-03-25 03:39:28 +00:00
root: &'a T,
) -> Result<serde_json::Value> {
let mut futures = Vec::new();
2020-03-26 10:30:29 +00:00
collect_fields(ctx, root, &mut futures)?;
2020-03-25 03:39:28 +00:00
let res = futures::future::try_join_all(futures).await?;
2020-05-27 07:23:53 +00:00
let mut map = serde_json::Map::new();
for (name, value) in res {
2020-06-02 04:02:14 +00:00
if let serde_json::Value::Object(b) = value {
if let Some(serde_json::Value::Object(a)) = map.get_mut(&name) {
2020-05-27 07:23:53 +00:00
a.extend(b);
2020-06-02 04:02:14 +00:00
} else {
map.insert(name, b.into());
2020-05-27 07:23:53 +00:00
}
2020-06-02 04:02:14 +00:00
} else {
map.insert(name, value);
2020-05-27 07:23:53 +00:00
}
}
2020-03-25 03:39:28 +00:00
Ok(map.into())
2020-03-06 15:58:43 +00:00
}
2020-03-25 03:39:28 +00:00
#[allow(missing_docs)]
pub fn collect_fields<'a, T: ObjectType + Send + Sync>(
2020-03-26 10:30:29 +00:00
ctx: &ContextSelectionSet<'a>,
2020-03-25 03:39:28 +00:00
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(),
},
});
2020-03-25 03:39:28 +00:00
}
2020-03-24 10:54:22 +00:00
2020-03-25 03:39:28 +00:00
for selection in &ctx.item.items {
match &selection.node {
2020-03-25 03:39:28 +00:00
Selection::Field(field) => {
if ctx.is_skip(&field.directives)? {
continue;
}
2020-03-06 15:58:43 +00:00
if field.name.node == "__typename" {
2020-03-25 03:39:28 +00:00
// Get the typename
2020-03-26 10:30:29 +00:00
let ctx_field = ctx.with_field(field);
let field_name = ctx_field.result_name().to_string();
2020-03-25 03:39:28 +00:00
futures.push(Box::pin(
future::ok::<serde_json::Value, Error>(
root.introspection_type_name().to_string().into(),
)
.map_ok(move |value| (field_name, value)),
2020-03-25 03:39:28 +00:00
));
continue;
}
2020-03-07 02:39:55 +00:00
2020-03-25 03:39:28 +00:00
futures.push(Box::pin({
2020-03-26 10:30:29 +00:00
let ctx = ctx.clone();
2020-03-25 03:39:28 +00:00
async move {
2020-03-26 10:30:29 +00:00
let ctx_field = ctx.with_field(field);
let field_name = ctx_field.result_name().to_string();
2020-03-26 03:34:28 +00:00
2020-05-22 03:58:49 +00:00
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(),
},
});
}
},
};
2020-03-26 03:34:28 +00:00
ctx_field
.query_env
.extensions
.lock()
.resolve_start(&resolve_info);
2020-03-26 03:34:28 +00:00
let res = root
.resolve_field(&ctx_field)
.map_ok(move |value| (field_name, value))
.await
.log_error(&ctx_field.query_env.extensions)?;
2020-03-26 03:34:28 +00:00
ctx_field
.query_env
.extensions
.lock()
.resolve_end(&resolve_info);
2020-03-26 03:34:28 +00:00
Ok(res)
2020-03-06 15:58:43 +00:00
}
2020-03-25 03:39:28 +00:00
}))
}
Selection::FragmentSpread(fragment_spread) => {
if ctx.is_skip(&fragment_spread.directives)? {
continue;
}
2020-03-07 02:39:55 +00:00
if let Some(fragment) = ctx
.query_env
.document
.fragments()
2020-05-16 13:14:26 +00:00
.get(fragment_spread.fragment_name.as_str())
{
2020-03-26 03:34:28 +00:00
collect_fields(
2020-03-26 10:30:29 +00:00
&ctx.with_selection_set(&fragment.selection_set),
2020-03-26 03:34:28 +00:00
root,
futures,
)?;
2020-03-25 03:39:28 +00:00
} else {
return Err(Error::Query {
pos: fragment_spread.position(),
path: None,
err: QueryError::UnknownFragment {
name: fragment_spread.fragment_name.to_string(),
},
});
2020-03-06 15:58:43 +00:00
}
}
2020-03-25 03:39:28 +00:00
Selection::InlineFragment(inline_fragment) => {
if ctx.is_skip(&inline_fragment.directives)? {
continue;
}
2020-03-06 15:58:43 +00:00
if let Some(TypeCondition::On(name)) = inline_fragment.type_condition.as_deref() {
2020-03-25 03:39:28 +00:00
root.collect_inline_fields(
name,
2020-03-26 10:30:29 +00:00
&ctx.with_selection_set(&inline_fragment.selection_set),
2020-03-25 03:39:28 +00:00
futures,
)?;
2020-03-31 07:09:28 +00:00
} else {
collect_fields(
&ctx.with_selection_set(&inline_fragment.selection_set),
root,
futures,
)?;
2020-03-25 03:39:28 +00:00
}
}
}
2020-03-06 15:58:43 +00:00
}
2020-03-07 02:39:55 +00:00
Ok(())
2020-03-06 15:58:43 +00:00
}