async_graphql/
schema.rs

1use std::{
2    any::{Any, TypeId},
3    collections::{HashMap, HashSet},
4    ops::Deref,
5    sync::Arc,
6};
7
8use async_graphql_parser::types::ExecutableDocument;
9use futures_util::stream::{self, BoxStream, FuturesOrdered, StreamExt};
10
11use crate::{
12    BatchRequest, BatchResponse, CacheControl, ContextBase, EmptyMutation, EmptySubscription,
13    Executor, InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError,
14    ServerResult, SubscriptionType, Variables,
15    context::{Data, QueryEnvInner},
16    custom_directive::CustomDirectiveFactory,
17    extensions::{ExtensionFactory, Extensions},
18    parser::{
19        Positioned, parse_query,
20        types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet},
21    },
22    registry::{Registry, SDLExportOptions},
23    resolver_utils::{resolve_container, resolve_container_serial},
24    subscription::collect_subscription_streams,
25    types::QueryRoot,
26    validation::{ValidationMode, check_rules},
27};
28
29/// Introspection mode
30#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
31pub enum IntrospectionMode {
32    /// Introspection only
33    IntrospectionOnly,
34    /// Enables introspection
35    #[default]
36    Enabled,
37    /// Disables introspection
38    Disabled,
39}
40
41/// Schema builder
42pub struct SchemaBuilder<Query, Mutation, Subscription> {
43    validation_mode: ValidationMode,
44    query: QueryRoot<Query>,
45    mutation: Mutation,
46    subscription: Subscription,
47    registry: Registry,
48    data: Data,
49    complexity: Option<usize>,
50    depth: Option<usize>,
51    recursive_depth: usize,
52    max_directives: Option<usize>,
53    extensions: Vec<Box<dyn ExtensionFactory>>,
54    custom_directives: HashMap<String, Box<dyn CustomDirectiveFactory>>,
55}
56
57impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription> {
58    /// Manually register a input type in the schema.
59    ///
60    /// You can use this function to register schema types that are not directly
61    /// referenced.
62    #[must_use]
63    pub fn register_input_type<T: InputType>(mut self) -> Self {
64        T::create_type_info(&mut self.registry);
65        self
66    }
67
68    /// Manually register a output type in the schema.
69    ///
70    /// You can use this function to register schema types that are not directly
71    /// referenced.
72    #[must_use]
73    pub fn register_output_type<T: OutputType>(mut self) -> Self {
74        T::create_type_info(&mut self.registry);
75        self
76    }
77
78    /// Disable introspection queries.
79    #[must_use]
80    pub fn disable_introspection(mut self) -> Self {
81        self.registry.introspection_mode = IntrospectionMode::Disabled;
82        self
83    }
84
85    /// Only process introspection queries, everything else is processed as an
86    /// error.
87    #[must_use]
88    pub fn introspection_only(mut self) -> Self {
89        self.registry.introspection_mode = IntrospectionMode::IntrospectionOnly;
90        self
91    }
92
93    /// Set the maximum complexity a query can have. By default, there is no
94    /// limit.
95    #[must_use]
96    pub fn limit_complexity(mut self, complexity: usize) -> Self {
97        self.complexity = Some(complexity);
98        self
99    }
100
101    /// Set the maximum depth a query can have. By default, there is no limit.
102    #[must_use]
103    pub fn limit_depth(mut self, depth: usize) -> Self {
104        self.depth = Some(depth);
105        self
106    }
107
108    /// Set the maximum recursive depth a query can have. (default: 32)
109    ///
110    /// If the value is too large, stack overflow may occur, usually `32` is
111    /// enough.
112    #[must_use]
113    pub fn limit_recursive_depth(mut self, depth: usize) -> Self {
114        self.recursive_depth = depth;
115        self
116    }
117
118    /// Set the maximum number of directives on a single field. (default: no
119    /// limit)
120    pub fn limit_directives(mut self, max_directives: usize) -> Self {
121        self.max_directives = Some(max_directives);
122        self
123    }
124
125    /// Add an extension to the schema.
126    ///
127    /// # Examples
128    ///
129    /// ```rust
130    /// use async_graphql::*;
131    ///
132    /// struct Query;
133    ///
134    /// #[Object]
135    /// impl Query {
136    ///     async fn value(&self) -> i32 {
137    ///         100
138    ///     }
139    /// }
140    ///
141    /// let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
142    ///     .extension(extensions::Logger)
143    ///     .finish();
144    /// ```
145    #[must_use]
146    pub fn extension(mut self, extension: impl ExtensionFactory) -> Self {
147        self.extensions.push(Box::new(extension));
148        self
149    }
150
151    /// Add a global data that can be accessed in the `Schema`. You access it
152    /// with `Context::data`.
153    #[must_use]
154    pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
155        self.data.insert(data);
156        self
157    }
158
159    /// Set the validation mode, default is `ValidationMode::Strict`.
160    #[must_use]
161    pub fn validation_mode(mut self, validation_mode: ValidationMode) -> Self {
162        self.validation_mode = validation_mode;
163        self
164    }
165
166    /// Enable federation, which is automatically enabled if the Query has least
167    /// one entity definition.
168    #[must_use]
169    pub fn enable_federation(mut self) -> Self {
170        self.registry.enable_federation = true;
171        self
172    }
173
174    /// Make the Federation SDL include subscriptions.
175    ///
176    /// Note: Not included by default, in order to be compatible with Apollo
177    /// Server.
178    #[must_use]
179    pub fn enable_subscription_in_federation(mut self) -> Self {
180        self.registry.federation_subscription = true;
181        self
182    }
183
184    /// Override the name of the specified input type.
185    #[must_use]
186    pub fn override_input_type_description<T: InputType>(mut self, desc: &'static str) -> Self {
187        self.registry.set_description(&*T::type_name(), desc);
188        self
189    }
190
191    /// Override the name of the specified output type.
192    #[must_use]
193    pub fn override_output_type_description<T: OutputType>(mut self, desc: &'static str) -> Self {
194        self.registry.set_description(&*T::type_name(), desc);
195        self
196    }
197
198    /// Register a custom directive.
199    ///
200    /// # Panics
201    ///
202    /// Panics if the directive with the same name is already registered.
203    #[must_use]
204    pub fn directive<T: CustomDirectiveFactory>(mut self, directive: T) -> Self {
205        let name = directive.name();
206        let instance = Box::new(directive);
207
208        instance.register(&mut self.registry);
209
210        if name == "skip"
211            || name == "include"
212            || self
213                .custom_directives
214                .insert(name.clone().into(), instance)
215                .is_some()
216        {
217            panic!("Directive `{}` already exists", name);
218        }
219
220        self
221    }
222
223    /// Disable field suggestions.
224    #[must_use]
225    pub fn disable_suggestions(mut self) -> Self {
226        self.registry.enable_suggestions = false;
227        self
228    }
229
230    /// Make all fields sorted on introspection queries.
231    pub fn with_sorted_fields(mut self) -> Self {
232        use crate::registry::MetaType;
233        for ty in self.registry.types.values_mut() {
234            match ty {
235                MetaType::Object { fields, .. } | MetaType::Interface { fields, .. } => {
236                    fields.sort_keys();
237                }
238                MetaType::InputObject { input_fields, .. } => {
239                    input_fields.sort_keys();
240                }
241                MetaType::Scalar { .. } | MetaType::Enum { .. } | MetaType::Union { .. } => {
242                    // have no fields
243                }
244            }
245        }
246        self
247    }
248
249    /// Make all enum variants sorted on introspection queries.
250    pub fn with_sorted_enums(mut self) -> Self {
251        use crate::registry::MetaType;
252        for ty in &mut self.registry.types.values_mut() {
253            if let MetaType::Enum { enum_values, .. } = ty {
254                enum_values.sort_keys();
255            }
256        }
257        self
258    }
259
260    /// Consumes this builder and returns a schema.
261    pub fn finish(mut self) -> Schema<Query, Mutation, Subscription> {
262        // federation
263        if self.registry.enable_federation || self.registry.has_entities() {
264            self.registry.create_federation_types();
265        }
266
267        Schema(Arc::new(SchemaInner {
268            validation_mode: self.validation_mode,
269            query: self.query,
270            mutation: self.mutation,
271            subscription: self.subscription,
272            complexity: self.complexity,
273            depth: self.depth,
274            recursive_depth: self.recursive_depth,
275            max_directives: self.max_directives,
276            extensions: self.extensions,
277            env: SchemaEnv(Arc::new(SchemaEnvInner {
278                registry: self.registry,
279                data: self.data,
280                custom_directives: self.custom_directives,
281            })),
282        }))
283    }
284}
285
286#[doc(hidden)]
287pub struct SchemaEnvInner {
288    pub registry: Registry,
289    pub data: Data,
290    pub custom_directives: HashMap<String, Box<dyn CustomDirectiveFactory>>,
291}
292
293#[doc(hidden)]
294#[derive(Clone)]
295pub struct SchemaEnv(pub(crate) Arc<SchemaEnvInner>);
296
297impl Deref for SchemaEnv {
298    type Target = SchemaEnvInner;
299
300    fn deref(&self) -> &Self::Target {
301        &self.0
302    }
303}
304
305#[doc(hidden)]
306pub struct SchemaInner<Query, Mutation, Subscription> {
307    pub(crate) validation_mode: ValidationMode,
308    pub(crate) query: QueryRoot<Query>,
309    pub(crate) mutation: Mutation,
310    pub(crate) subscription: Subscription,
311    pub(crate) complexity: Option<usize>,
312    pub(crate) depth: Option<usize>,
313    pub(crate) recursive_depth: usize,
314    pub(crate) max_directives: Option<usize>,
315    pub(crate) extensions: Vec<Box<dyn ExtensionFactory>>,
316    pub(crate) env: SchemaEnv,
317}
318
319/// GraphQL schema.
320///
321/// Cloning a schema is cheap, so it can be easily shared.
322pub struct Schema<Query, Mutation, Subscription>(
323    pub(crate) Arc<SchemaInner<Query, Mutation, Subscription>>,
324);
325
326impl<Query, Mutation, Subscription> Clone for Schema<Query, Mutation, Subscription> {
327    fn clone(&self) -> Self {
328        Schema(self.0.clone())
329    }
330}
331
332impl<Query, Mutation, Subscription> Default for Schema<Query, Mutation, Subscription>
333where
334    Query: Default + ObjectType + 'static,
335    Mutation: Default + ObjectType + 'static,
336    Subscription: Default + SubscriptionType + 'static,
337{
338    fn default() -> Self {
339        Schema::new(
340            Query::default(),
341            Mutation::default(),
342            Subscription::default(),
343        )
344    }
345}
346
347impl<Query, Mutation, Subscription> Schema<Query, Mutation, Subscription>
348where
349    Query: ObjectType + 'static,
350    Mutation: ObjectType + 'static,
351    Subscription: SubscriptionType + 'static,
352{
353    /// Create a schema builder
354    ///
355    /// The root object for the query and Mutation needs to be specified.
356    /// If there is no mutation, you can use `EmptyMutation`.
357    /// If there is no subscription, you can use `EmptySubscription`.
358    pub fn build(
359        query: Query,
360        mutation: Mutation,
361        subscription: Subscription,
362    ) -> SchemaBuilder<Query, Mutation, Subscription> {
363        Self::build_with_ignore_name_conflicts(query, mutation, subscription, [] as [&str; 0])
364    }
365
366    /// Create a schema builder and specifies a list to ignore type conflict
367    /// detection.
368    ///
369    /// NOTE: It is not recommended to use it unless you know what it does.
370    #[must_use]
371    pub fn build_with_ignore_name_conflicts<I, T>(
372        query: Query,
373        mutation: Mutation,
374        subscription: Subscription,
375        ignore_name_conflicts: I,
376    ) -> SchemaBuilder<Query, Mutation, Subscription>
377    where
378        I: IntoIterator<Item = T>,
379        T: Into<String>,
380    {
381        SchemaBuilder {
382            validation_mode: ValidationMode::Strict,
383            query: QueryRoot { inner: query },
384            mutation,
385            subscription,
386            registry: Self::create_registry(
387                ignore_name_conflicts.into_iter().map(Into::into).collect(),
388            ),
389            data: Default::default(),
390            complexity: None,
391            depth: None,
392            recursive_depth: 32,
393            max_directives: None,
394            extensions: Default::default(),
395            custom_directives: Default::default(),
396        }
397    }
398
399    pub(crate) fn create_registry(ignore_name_conflicts: HashSet<String>) -> Registry {
400        let mut registry = Registry {
401            types: Default::default(),
402            directives: Default::default(),
403            implements: Default::default(),
404            query_type: Query::type_name().to_string(),
405            mutation_type: if Mutation::is_empty() {
406                None
407            } else {
408                Some(Mutation::type_name().to_string())
409            },
410            subscription_type: if Subscription::is_empty() {
411                None
412            } else {
413                Some(Subscription::type_name().to_string())
414            },
415            introspection_mode: IntrospectionMode::Enabled,
416            enable_federation: false,
417            federation_subscription: false,
418            ignore_name_conflicts,
419            enable_suggestions: true,
420        };
421        registry.add_system_types();
422
423        QueryRoot::<Query>::create_type_info(&mut registry);
424        if !Mutation::is_empty() {
425            Mutation::create_type_info(&mut registry);
426        }
427        if !Subscription::is_empty() {
428            Subscription::create_type_info(&mut registry);
429        }
430
431        registry.remove_unused_types();
432        registry
433    }
434
435    /// Create a schema
436    pub fn new(
437        query: Query,
438        mutation: Mutation,
439        subscription: Subscription,
440    ) -> Schema<Query, Mutation, Subscription> {
441        Self::build(query, mutation, subscription).finish()
442    }
443
444    #[inline]
445    #[allow(unused)]
446    pub(crate) fn registry(&self) -> &Registry {
447        &self.0.env.registry
448    }
449
450    /// Returns SDL(Schema Definition Language) of this schema.
451    pub fn sdl(&self) -> String {
452        self.0.env.registry.export_sdl(Default::default())
453    }
454
455    /// Returns SDL(Schema Definition Language) of this schema with options.
456    pub fn sdl_with_options(&self, options: SDLExportOptions) -> String {
457        self.0.env.registry.export_sdl(options)
458    }
459
460    /// Get all names in this schema
461    ///
462    /// Maybe you want to serialize a custom binary protocol. In order to
463    /// minimize message size, a dictionary is usually used to compress type
464    /// names, field names, directive names, and parameter names. This function
465    /// gets all the names, so you can create this dictionary.
466    pub fn names(&self) -> Vec<String> {
467        self.0.env.registry.names()
468    }
469
470    fn create_extensions(&self, session_data: Arc<Data>) -> Extensions {
471        Extensions::new(
472            self.0.extensions.iter().map(|f| f.create()),
473            self.0.env.clone(),
474            session_data,
475        )
476    }
477
478    async fn execute_once(&self, env: QueryEnv, execute_data: Option<&Data>) -> Response {
479        // execute
480        let ctx = ContextBase {
481            path_node: None,
482            is_for_introspection: false,
483            item: &env.operation.node.selection_set,
484            schema_env: &self.0.env,
485            query_env: &env,
486            execute_data,
487        };
488
489        let res = match &env.operation.node.ty {
490            OperationType::Query => resolve_container(&ctx, &self.0.query).await,
491            OperationType::Mutation => {
492                if self.0.env.registry.introspection_mode == IntrospectionMode::IntrospectionOnly
493                    || env.introspection_mode == IntrospectionMode::IntrospectionOnly
494                {
495                    resolve_container_serial(&ctx, &EmptyMutation).await
496                } else {
497                    resolve_container_serial(&ctx, &self.0.mutation).await
498                }
499            }
500            OperationType::Subscription => Err(ServerError::new(
501                "Subscriptions are not supported on this transport.",
502                None,
503            )),
504        };
505
506        let mut resp = match res {
507            Ok(value) => Response::new(value),
508            Err(err) => Response::from_errors(vec![err]),
509        }
510        .http_headers(std::mem::take(&mut *env.http_headers.lock().unwrap()));
511
512        resp.errors
513            .extend(std::mem::take(&mut *env.errors.lock().unwrap()));
514        resp
515    }
516
517    /// Execute a GraphQL query.
518    pub async fn execute(&self, request: impl Into<Request>) -> Response {
519        let request = request.into();
520        let extensions = self.create_extensions(Default::default());
521        let request_fut = {
522            let extensions = extensions.clone();
523            async move {
524                match prepare_request(
525                    extensions,
526                    request,
527                    Default::default(),
528                    &self.0.env.registry,
529                    self.0.validation_mode,
530                    self.0.recursive_depth,
531                    self.0.max_directives,
532                    self.0.complexity,
533                    self.0.depth,
534                )
535                .await
536                {
537                    Ok((env, cache_control)) => {
538                        let f = |execute_data: Option<Data>| {
539                            let env = env.clone();
540                            async move {
541                                self.execute_once(env, execute_data.as_ref())
542                                    .await
543                                    .cache_control(cache_control)
544                            }
545                        };
546                        env.extensions
547                            .execute(env.operation_name.as_deref(), f)
548                            .await
549                    }
550                    Err(errors) => Response::from_errors(errors),
551                }
552            }
553        };
554        futures_util::pin_mut!(request_fut);
555        extensions.request(&mut request_fut).await
556    }
557
558    /// Execute a GraphQL batch query.
559    pub async fn execute_batch(&self, batch_request: BatchRequest) -> BatchResponse {
560        match batch_request {
561            BatchRequest::Single(request) => BatchResponse::Single(self.execute(request).await),
562            BatchRequest::Batch(requests) => BatchResponse::Batch(
563                FuturesOrdered::from_iter(
564                    requests.into_iter().map(|request| self.execute(request)),
565                )
566                .collect()
567                .await,
568            ),
569        }
570    }
571
572    /// Execute a GraphQL subscription with session data.
573    pub fn execute_stream_with_session_data(
574        &self,
575        request: impl Into<Request>,
576        session_data: Arc<Data>,
577    ) -> BoxStream<'static, Response> {
578        let schema = self.clone();
579        let request = request.into();
580        let extensions = self.create_extensions(session_data.clone());
581
582        let stream = futures_util::stream::StreamExt::boxed({
583            let extensions = extensions.clone();
584            let env = self.0.env.clone();
585            asynk_strim::stream_fn(|mut yielder| async move {
586                let (env, cache_control) = match prepare_request(
587                    extensions,
588                    request,
589                    session_data,
590                    &env.registry,
591                    schema.0.validation_mode,
592                    schema.0.recursive_depth,
593                    schema.0.max_directives,
594                    schema.0.complexity,
595                    schema.0.depth,
596                )
597                .await
598                {
599                    Ok(res) => res,
600                    Err(errors) => {
601                        yielder.yield_item(Response::from_errors(errors)).await;
602                        return;
603                    }
604                };
605
606                if env.operation.node.ty != OperationType::Subscription {
607                    let f = |execute_data: Option<Data>| {
608                        let env = env.clone();
609                        let schema = schema.clone();
610                        async move {
611                            schema
612                                .execute_once(env, execute_data.as_ref())
613                                .await
614                                .cache_control(cache_control)
615                        }
616                    };
617                    yielder
618                        .yield_item(
619                            env.extensions
620                                .execute(env.operation_name.as_deref(), f)
621                                .await
622                                .cache_control(cache_control),
623                        )
624                        .await;
625                    return;
626                }
627
628                let ctx = env.create_context(
629                    &schema.0.env,
630                    None,
631                    &env.operation.node.selection_set,
632                    None,
633                );
634
635                let mut streams = Vec::new();
636                let collect_result = if schema.0.env.registry.introspection_mode
637                    == IntrospectionMode::IntrospectionOnly
638                    || env.introspection_mode == IntrospectionMode::IntrospectionOnly
639                {
640                    collect_subscription_streams(&ctx, &EmptySubscription, &mut streams)
641                } else {
642                    collect_subscription_streams(&ctx, &schema.0.subscription, &mut streams)
643                };
644                if let Err(err) = collect_result {
645                    yielder.yield_item(Response::from_errors(vec![err])).await;
646                }
647
648                let mut stream = stream::select_all(streams);
649                while let Some(resp) = stream.next().await {
650                    yielder.yield_item(resp).await;
651                }
652            })
653        });
654        extensions.subscribe(stream)
655    }
656
657    /// Execute a GraphQL subscription.
658    pub fn execute_stream(&self, request: impl Into<Request>) -> BoxStream<'static, Response> {
659        self.execute_stream_with_session_data(request, Default::default())
660    }
661
662    /// Access global data stored in the Schema
663    pub fn data<D: Any + Send + Sync>(&self) -> Option<&D> {
664        self.0
665            .env
666            .data
667            .get(&TypeId::of::<D>())
668            .and_then(|d| d.downcast_ref::<D>())
669    }
670}
671
672#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
673impl<Query, Mutation, Subscription> Executor for Schema<Query, Mutation, Subscription>
674where
675    Query: ObjectType + 'static,
676    Mutation: ObjectType + 'static,
677    Subscription: SubscriptionType + 'static,
678{
679    async fn execute(&self, request: Request) -> Response {
680        Schema::execute(self, request).await
681    }
682
683    fn execute_stream(
684        &self,
685        request: Request,
686        session_data: Option<Arc<Data>>,
687    ) -> BoxStream<'static, Response> {
688        Schema::execute_stream_with_session_data(&self, request, session_data.unwrap_or_default())
689    }
690}
691
692fn check_max_directives(doc: &ExecutableDocument, max_directives: usize) -> ServerResult<()> {
693    fn check_selection_set(
694        doc: &ExecutableDocument,
695        selection_set: &Positioned<SelectionSet>,
696        limit_directives: usize,
697    ) -> ServerResult<()> {
698        for selection in &selection_set.node.items {
699            match &selection.node {
700                Selection::Field(field) => {
701                    if field.node.directives.len() > limit_directives {
702                        return Err(ServerError::new(
703                            format!(
704                                "The number of directives on the field `{}` cannot be greater than `{}`",
705                                field.node.name.node, limit_directives
706                            ),
707                            Some(field.pos),
708                        ));
709                    }
710                    check_selection_set(doc, &field.node.selection_set, limit_directives)?;
711                }
712                Selection::FragmentSpread(fragment_spread) => {
713                    if let Some(fragment) =
714                        doc.fragments.get(&fragment_spread.node.fragment_name.node)
715                    {
716                        check_selection_set(doc, &fragment.node.selection_set, limit_directives)?;
717                    }
718                }
719                Selection::InlineFragment(inline_fragment) => {
720                    check_selection_set(
721                        doc,
722                        &inline_fragment.node.selection_set,
723                        limit_directives,
724                    )?;
725                }
726            }
727        }
728
729        Ok(())
730    }
731
732    for (_, operation) in doc.operations.iter() {
733        check_selection_set(doc, &operation.node.selection_set, max_directives)?;
734    }
735
736    Ok(())
737}
738
739fn check_recursive_depth(doc: &ExecutableDocument, max_depth: usize) -> ServerResult<()> {
740    fn check_selection_set(
741        doc: &ExecutableDocument,
742        selection_set: &Positioned<SelectionSet>,
743        current_depth: usize,
744        max_depth: usize,
745    ) -> ServerResult<()> {
746        if current_depth > max_depth {
747            return Err(ServerError::new(
748                format!(
749                    "The recursion depth of the query cannot be greater than `{}`",
750                    max_depth
751                ),
752                Some(selection_set.pos),
753            ));
754        }
755
756        for selection in &selection_set.node.items {
757            match &selection.node {
758                Selection::Field(field) => {
759                    if !field.node.selection_set.node.items.is_empty() {
760                        check_selection_set(
761                            doc,
762                            &field.node.selection_set,
763                            current_depth + 1,
764                            max_depth,
765                        )?;
766                    }
767                }
768                Selection::FragmentSpread(fragment_spread) => {
769                    if let Some(fragment) =
770                        doc.fragments.get(&fragment_spread.node.fragment_name.node)
771                    {
772                        check_selection_set(
773                            doc,
774                            &fragment.node.selection_set,
775                            current_depth + 1,
776                            max_depth,
777                        )?;
778                    }
779                }
780                Selection::InlineFragment(inline_fragment) => {
781                    check_selection_set(
782                        doc,
783                        &inline_fragment.node.selection_set,
784                        current_depth + 1,
785                        max_depth,
786                    )?;
787                }
788            }
789        }
790
791        Ok(())
792    }
793
794    for (_, operation) in doc.operations.iter() {
795        check_selection_set(doc, &operation.node.selection_set, 0, max_depth)?;
796    }
797
798    Ok(())
799}
800
801fn remove_skipped_selection(selection_set: &mut SelectionSet, variables: &Variables) {
802    fn is_skipped(directives: &[Positioned<Directive>], variables: &Variables) -> bool {
803        for directive in directives {
804            let include = match &*directive.node.name.node {
805                "skip" => false,
806                "include" => true,
807                _ => continue,
808            };
809
810            if let Some(condition_input) = directive.node.get_argument("if") {
811                let value = condition_input
812                    .node
813                    .clone()
814                    .into_const_with(|name| variables.get(&name).cloned().ok_or(()))
815                    .unwrap_or_default();
816                let value: bool = InputType::parse(Some(value)).unwrap_or_default();
817                if include != value {
818                    return true;
819                }
820            }
821        }
822
823        false
824    }
825
826    selection_set
827        .items
828        .retain(|selection| !is_skipped(selection.node.directives(), variables));
829
830    for selection in &mut selection_set.items {
831        selection.node.directives_mut().retain(|directive| {
832            directive.node.name.node != "skip" && directive.node.name.node != "include"
833        });
834    }
835
836    for selection in &mut selection_set.items {
837        match &mut selection.node {
838            Selection::Field(field) => {
839                remove_skipped_selection(&mut field.node.selection_set.node, variables);
840            }
841            Selection::FragmentSpread(_) => {}
842            Selection::InlineFragment(inline_fragment) => {
843                remove_skipped_selection(&mut inline_fragment.node.selection_set.node, variables);
844            }
845        }
846    }
847}
848
849#[allow(clippy::too_many_arguments)]
850pub(crate) async fn prepare_request(
851    mut extensions: Extensions,
852    request: Request,
853    session_data: Arc<Data>,
854    registry: &Registry,
855    validation_mode: ValidationMode,
856    recursive_depth: usize,
857    max_directives: Option<usize>,
858    complexity: Option<usize>,
859    depth: Option<usize>,
860) -> Result<(QueryEnv, CacheControl), Vec<ServerError>> {
861    let mut request = extensions.prepare_request(request).await?;
862    let query_data = Arc::new(std::mem::take(&mut request.data));
863    extensions.attach_query_data(query_data.clone());
864
865    let mut document = {
866        let query = &request.query;
867        let parsed_doc = request.parsed_query.take();
868        let fut_parse = async move {
869            let doc = match parsed_doc {
870                Some(parsed_doc) => parsed_doc,
871                None => parse_query(query)?,
872            };
873            check_recursive_depth(&doc, recursive_depth)?;
874            if let Some(max_directives) = max_directives {
875                check_max_directives(&doc, max_directives)?;
876            }
877            Ok(doc)
878        };
879        futures_util::pin_mut!(fut_parse);
880        extensions
881            .parse_query(query, &request.variables, &mut fut_parse)
882            .await?
883    };
884
885    // check rules
886    let validation_result = {
887        let validation_fut = async {
888            check_rules(
889                registry,
890                &document,
891                Some(&request.variables),
892                validation_mode,
893                complexity,
894                depth,
895            )
896        };
897        futures_util::pin_mut!(validation_fut);
898        extensions.validation(&mut validation_fut).await?
899    };
900
901    let operation = if let Some(operation_name) = &request.operation_name {
902        match document.operations {
903            DocumentOperations::Single(_) => None,
904            DocumentOperations::Multiple(mut operations) => operations
905                .remove(operation_name.as_str())
906                .map(|operation| (Some(operation_name.clone()), operation)),
907        }
908        .ok_or_else(|| {
909            ServerError::new(
910                format!(r#"Unknown operation named "{}""#, operation_name),
911                None,
912            )
913        })
914    } else {
915        match document.operations {
916            DocumentOperations::Single(operation) => Ok((None, operation)),
917            DocumentOperations::Multiple(map) if map.len() == 1 => {
918                let (operation_name, operation) = map.into_iter().next().unwrap();
919                Ok((Some(operation_name.to_string()), operation))
920            }
921            DocumentOperations::Multiple(_) => Err(ServerError::new(
922                "Operation name required in request.",
923                None,
924            )),
925        }
926    };
927
928    let (operation_name, mut operation) = operation.map_err(|err| vec![err])?;
929
930    // remove skipped fields
931    for fragment in document.fragments.values_mut() {
932        remove_skipped_selection(&mut fragment.node.selection_set.node, &request.variables);
933    }
934    remove_skipped_selection(&mut operation.node.selection_set.node, &request.variables);
935
936    let env = QueryEnvInner {
937        extensions,
938        variables: request.variables,
939        operation_name,
940        operation,
941        fragments: document.fragments,
942        uploads: request.uploads,
943        session_data,
944        query_data,
945        http_headers: Default::default(),
946        introspection_mode: request.introspection_mode,
947        errors: Default::default(),
948    };
949    Ok((QueryEnv::new(env), validation_result.cache_control))
950}