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#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
32pub enum IntrospectionMode {
33 IntrospectionOnly,
35 #[default]
37 Enabled,
38 Disabled,
40}
41
42pub 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 #[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 #[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 #[must_use]
81 pub fn disable_introspection(mut self) -> Self {
82 self.registry.introspection_mode = IntrospectionMode::Disabled;
83 self
84 }
85
86 #[must_use]
89 pub fn introspection_only(mut self) -> Self {
90 self.registry.introspection_mode = IntrospectionMode::IntrospectionOnly;
91 self
92 }
93
94 #[must_use]
97 pub fn limit_complexity(mut self, complexity: usize) -> Self {
98 self.complexity = Some(complexity);
99 self
100 }
101
102 #[must_use]
104 pub fn limit_depth(mut self, depth: usize) -> Self {
105 self.depth = Some(depth);
106 self
107 }
108
109 #[must_use]
114 pub fn limit_recursive_depth(mut self, depth: usize) -> Self {
115 self.recursive_depth = depth;
116 self
117 }
118
119 pub fn limit_directives(mut self, max_directives: usize) -> Self {
122 self.max_directives = Some(max_directives);
123 self
124 }
125
126 #[must_use]
147 pub fn extension(mut self, extension: impl ExtensionFactory) -> Self {
148 self.extensions.push(Box::new(extension));
149 self
150 }
151
152 #[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 #[must_use]
162 pub fn validation_mode(mut self, validation_mode: ValidationMode) -> Self {
163 self.validation_mode = validation_mode;
164 self
165 }
166
167 #[must_use]
170 pub fn enable_federation(mut self) -> Self {
171 self.registry.enable_federation = true;
172 self
173 }
174
175 #[must_use]
180 pub fn enable_subscription_in_federation(mut self) -> Self {
181 self.registry.federation_subscription = true;
182 self
183 }
184
185 #[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 #[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 #[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 #[must_use]
226 pub fn disable_suggestions(mut self) -> Self {
227 self.registry.enable_suggestions = false;
228 self
229 }
230
231 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 }
245 }
246 }
247 self
248 }
249
250 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 pub fn finish(mut self) -> Schema<Query, Mutation, Subscription> {
263 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
320pub 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 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 #[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 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 pub fn sdl(&self) -> String {
453 self.0.env.registry.export_sdl(Default::default())
454 }
455
456 pub fn sdl_with_options(&self, options: SDLExportOptions) -> String {
458 self.0.env.registry.export_sdl(options)
459 }
460
461 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 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 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 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 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 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 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 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}