async_graphql/
schema.rs

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