async_graphql/types/
query_root.rs

1use 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/// Federation service
14#[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> {}