async_graphql/types/
query_root.rs1use std::borrow::Cow;
2
3use crate::{
4 Any, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError,
5 ServerResult, SimpleObject, Value,
6 model::{__Schema, __Type},
7 parser::types::Field,
8 registry::{self, SDLExportOptions},
9 resolver_utils::{ContainerType, resolve_container},
10 schema::IntrospectionMode,
11};
12
13#[derive(SimpleObject)]
15#[graphql(internal, name = "_Service")]
16struct Service {
17 sdl: Option<String>,
18}
19
20pub(crate) struct QueryRoot<T> {
21 pub(crate) inner: T,
22}
23
24#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
25impl<T: ObjectType> ContainerType for QueryRoot<T> {
26 async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
27 if matches!(
28 ctx.schema_env.registry.introspection_mode,
29 IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly
30 ) && matches!(
31 ctx.query_env.introspection_mode,
32 IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly,
33 ) {
34 if ctx.item.node.name.node == "__schema" {
35 let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
36 ctx_obj.is_for_introspection = true;
37 let visible_types = ctx.schema_env.registry.find_visible_types(ctx);
38 return OutputType::resolve(
39 &__Schema::new(&ctx.schema_env.registry, &visible_types),
40 &ctx_obj,
41 ctx.item,
42 )
43 .await
44 .map(Some);
45 } else if ctx.item.node.name.node == "__type" {
46 let (_, type_name) = ctx.param_value::<String>("name", None)?;
47 let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
48 ctx_obj.is_for_introspection = true;
49 let visible_types = ctx.schema_env.registry.find_visible_types(ctx);
50 return OutputType::resolve(
51 &ctx.schema_env
52 .registry
53 .types
54 .get(&type_name)
55 .filter(|_| visible_types.contains(type_name.as_str()))
56 .map(|ty| __Type::new_simple(&ctx.schema_env.registry, &visible_types, ty)),
57 &ctx_obj,
58 ctx.item,
59 )
60 .await
61 .map(Some);
62 }
63 }
64
65 if ctx.schema_env.registry.introspection_mode == IntrospectionMode::IntrospectionOnly
66 || ctx.query_env.introspection_mode == IntrospectionMode::IntrospectionOnly
67 {
68 return Ok(None);
69 }
70
71 if ctx.schema_env.registry.enable_federation || ctx.schema_env.registry.has_entities() {
72 if ctx.item.node.name.node == "_entities" {
73 let (_, representations) = ctx.param_value::<Vec<Any>>("representations", None)?;
74 let res = futures_util::future::try_join_all(representations.iter().map(
75 |item| async move {
76 self.inner.find_entity(ctx, &item.0).await?.ok_or_else(|| {
77 ServerError::new("Entity not found.", Some(ctx.item.pos))
78 })
79 },
80 ))
81 .await?;
82 return Ok(Some(Value::List(res)));
83 } else if ctx.item.node.name.node == "_service" {
84 let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
85 ctx_obj.is_for_introspection = true;
86 return OutputType::resolve(
87 &Service {
88 sdl: Some(
89 ctx.schema_env.registry.export_sdl(
90 SDLExportOptions::new().federation().compose_directive(),
91 ),
92 ),
93 },
94 &ctx_obj,
95 ctx.item,
96 )
97 .await
98 .map(Some);
99 }
100 }
101
102 self.inner.resolve_field(ctx).await
103 }
104}
105
106#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
107impl<T: ObjectType> OutputType for QueryRoot<T> {
108 fn type_name() -> Cow<'static, str> {
109 T::type_name()
110 }
111
112 fn create_type_info(registry: &mut registry::Registry) -> String {
113 let root = T::create_type_info(registry);
114
115 if matches!(
116 registry.introspection_mode,
117 IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly
118 ) {
119 registry.create_introspection_types();
120 }
121
122 root
123 }
124
125 async fn resolve(
126 &self,
127 ctx: &ContextSelectionSet<'_>,
128 _field: &Positioned<Field>,
129 ) -> ServerResult<Value> {
130 resolve_container(ctx, self).await
131 }
132}
133
134impl<T: ObjectType> ObjectType for QueryRoot<T> {}