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, 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#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
31pub enum IntrospectionMode {
32 IntrospectionOnly,
34 #[default]
36 Enabled,
37 Disabled,
39}
40
41pub 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 #[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 #[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 #[must_use]
80 pub fn disable_introspection(mut self) -> Self {
81 self.registry.introspection_mode = IntrospectionMode::Disabled;
82 self
83 }
84
85 #[must_use]
88 pub fn introspection_only(mut self) -> Self {
89 self.registry.introspection_mode = IntrospectionMode::IntrospectionOnly;
90 self
91 }
92
93 #[must_use]
96 pub fn limit_complexity(mut self, complexity: usize) -> Self {
97 self.complexity = Some(complexity);
98 self
99 }
100
101 #[must_use]
103 pub fn limit_depth(mut self, depth: usize) -> Self {
104 self.depth = Some(depth);
105 self
106 }
107
108 #[must_use]
113 pub fn limit_recursive_depth(mut self, depth: usize) -> Self {
114 self.recursive_depth = depth;
115 self
116 }
117
118 pub fn limit_directives(mut self, max_directives: usize) -> Self {
121 self.max_directives = Some(max_directives);
122 self
123 }
124
125 #[must_use]
146 pub fn extension(mut self, extension: impl ExtensionFactory) -> Self {
147 self.extensions.push(Box::new(extension));
148 self
149 }
150
151 #[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 #[must_use]
161 pub fn validation_mode(mut self, validation_mode: ValidationMode) -> Self {
162 self.validation_mode = validation_mode;
163 self
164 }
165
166 #[must_use]
169 pub fn enable_federation(mut self) -> Self {
170 self.registry.enable_federation = true;
171 self
172 }
173
174 #[must_use]
179 pub fn enable_subscription_in_federation(mut self) -> Self {
180 self.registry.federation_subscription = true;
181 self
182 }
183
184 #[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 #[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 #[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 #[must_use]
225 pub fn disable_suggestions(mut self) -> Self {
226 self.registry.enable_suggestions = false;
227 self
228 }
229
230 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 }
244 }
245 }
246 self
247 }
248
249 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 pub fn finish(mut self) -> Schema<Query, Mutation, Subscription> {
262 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
319pub 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 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 #[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 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 pub fn sdl(&self) -> String {
452 self.0.env.registry.export_sdl(Default::default())
453 }
454
455 pub fn sdl_with_options(&self, options: SDLExportOptions) -> String {
457 self.0.env.registry.export_sdl(options)
458 }
459
460 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 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 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 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 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 async_stream::stream! {
586 let (env, cache_control) = match prepare_request(
587 extensions, request, session_data, &env.registry,
588 schema.0.validation_mode, schema.0.recursive_depth,
589 schema.0.max_directives, schema.0.complexity, schema.0.depth
590 ).await {
591 Ok(res) => res,
592 Err(errors) => {
593 yield Response::from_errors(errors);
594 return;
595 }
596 };
597
598 if env.operation.node.ty != OperationType::Subscription {
599 let f = |execute_data: Option<Data>| {
600 let env = env.clone();
601 let schema = schema.clone();
602 async move {
603 schema.execute_once(env, execute_data.as_ref())
604 .await
605 .cache_control(cache_control)
606 }
607 };
608 yield env.extensions
609 .execute(env.operation_name.as_deref(), f)
610 .await
611 .cache_control(cache_control);
612 return;
613 }
614
615 let ctx = env.create_context(
616 &schema.0.env,
617 None,
618 &env.operation.node.selection_set,
619 None,
620 );
621
622 let mut streams = Vec::new();
623 let collect_result = if schema.0.env.registry.introspection_mode
624 == IntrospectionMode::IntrospectionOnly
625 || env.introspection_mode == IntrospectionMode::IntrospectionOnly
626 {
627 collect_subscription_streams(&ctx, &EmptySubscription, &mut streams)
628 } else {
629 collect_subscription_streams(&ctx, &schema.0.subscription, &mut streams)
630 };
631 if let Err(err) = collect_result {
632 yield Response::from_errors(vec![err]);
633 }
634
635 let mut stream = stream::select_all(streams);
636 while let Some(resp) = stream.next().await {
637 yield resp;
638 }
639 }
640 });
641 extensions.subscribe(stream)
642 }
643
644 pub fn execute_stream(&self, request: impl Into<Request>) -> BoxStream<'static, Response> {
646 self.execute_stream_with_session_data(request, Default::default())
647 }
648}
649
650#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
651impl<Query, Mutation, Subscription> Executor for Schema<Query, Mutation, Subscription>
652where
653 Query: ObjectType + 'static,
654 Mutation: ObjectType + 'static,
655 Subscription: SubscriptionType + 'static,
656{
657 async fn execute(&self, request: Request) -> Response {
658 Schema::execute(self, request).await
659 }
660
661 fn execute_stream(
662 &self,
663 request: Request,
664 session_data: Option<Arc<Data>>,
665 ) -> BoxStream<'static, Response> {
666 Schema::execute_stream_with_session_data(&self, request, session_data.unwrap_or_default())
667 }
668}
669
670fn check_max_directives(doc: &ExecutableDocument, max_directives: usize) -> ServerResult<()> {
671 fn check_selection_set(
672 doc: &ExecutableDocument,
673 selection_set: &Positioned<SelectionSet>,
674 limit_directives: usize,
675 ) -> ServerResult<()> {
676 for selection in &selection_set.node.items {
677 match &selection.node {
678 Selection::Field(field) => {
679 if field.node.directives.len() > limit_directives {
680 return Err(ServerError::new(
681 format!(
682 "The number of directives on the field `{}` cannot be greater than `{}`",
683 field.node.name.node, limit_directives
684 ),
685 Some(field.pos),
686 ));
687 }
688 check_selection_set(doc, &field.node.selection_set, limit_directives)?;
689 }
690 Selection::FragmentSpread(fragment_spread) => {
691 if let Some(fragment) =
692 doc.fragments.get(&fragment_spread.node.fragment_name.node)
693 {
694 check_selection_set(doc, &fragment.node.selection_set, limit_directives)?;
695 }
696 }
697 Selection::InlineFragment(inline_fragment) => {
698 check_selection_set(
699 doc,
700 &inline_fragment.node.selection_set,
701 limit_directives,
702 )?;
703 }
704 }
705 }
706
707 Ok(())
708 }
709
710 for (_, operation) in doc.operations.iter() {
711 check_selection_set(doc, &operation.node.selection_set, max_directives)?;
712 }
713
714 Ok(())
715}
716
717fn check_recursive_depth(doc: &ExecutableDocument, max_depth: usize) -> ServerResult<()> {
718 fn check_selection_set(
719 doc: &ExecutableDocument,
720 selection_set: &Positioned<SelectionSet>,
721 current_depth: usize,
722 max_depth: usize,
723 ) -> ServerResult<()> {
724 if current_depth > max_depth {
725 return Err(ServerError::new(
726 format!(
727 "The recursion depth of the query cannot be greater than `{}`",
728 max_depth
729 ),
730 Some(selection_set.pos),
731 ));
732 }
733
734 for selection in &selection_set.node.items {
735 match &selection.node {
736 Selection::Field(field) => {
737 if !field.node.selection_set.node.items.is_empty() {
738 check_selection_set(
739 doc,
740 &field.node.selection_set,
741 current_depth + 1,
742 max_depth,
743 )?;
744 }
745 }
746 Selection::FragmentSpread(fragment_spread) => {
747 if let Some(fragment) =
748 doc.fragments.get(&fragment_spread.node.fragment_name.node)
749 {
750 check_selection_set(
751 doc,
752 &fragment.node.selection_set,
753 current_depth + 1,
754 max_depth,
755 )?;
756 }
757 }
758 Selection::InlineFragment(inline_fragment) => {
759 check_selection_set(
760 doc,
761 &inline_fragment.node.selection_set,
762 current_depth + 1,
763 max_depth,
764 )?;
765 }
766 }
767 }
768
769 Ok(())
770 }
771
772 for (_, operation) in doc.operations.iter() {
773 check_selection_set(doc, &operation.node.selection_set, 0, max_depth)?;
774 }
775
776 Ok(())
777}
778
779fn remove_skipped_selection(selection_set: &mut SelectionSet, variables: &Variables) {
780 fn is_skipped(directives: &[Positioned<Directive>], variables: &Variables) -> bool {
781 for directive in directives {
782 let include = match &*directive.node.name.node {
783 "skip" => false,
784 "include" => true,
785 _ => continue,
786 };
787
788 if let Some(condition_input) = directive.node.get_argument("if") {
789 let value = condition_input
790 .node
791 .clone()
792 .into_const_with(|name| variables.get(&name).cloned().ok_or(()))
793 .unwrap_or_default();
794 let value: bool = InputType::parse(Some(value)).unwrap_or_default();
795 if include != value {
796 return true;
797 }
798 }
799 }
800
801 false
802 }
803
804 selection_set
805 .items
806 .retain(|selection| !is_skipped(selection.node.directives(), variables));
807
808 for selection in &mut selection_set.items {
809 selection.node.directives_mut().retain(|directive| {
810 directive.node.name.node != "skip" && directive.node.name.node != "include"
811 });
812 }
813
814 for selection in &mut selection_set.items {
815 match &mut selection.node {
816 Selection::Field(field) => {
817 remove_skipped_selection(&mut field.node.selection_set.node, variables);
818 }
819 Selection::FragmentSpread(_) => {}
820 Selection::InlineFragment(inline_fragment) => {
821 remove_skipped_selection(&mut inline_fragment.node.selection_set.node, variables);
822 }
823 }
824 }
825}
826
827#[allow(clippy::too_many_arguments)]
828pub(crate) async fn prepare_request(
829 mut extensions: Extensions,
830 request: Request,
831 session_data: Arc<Data>,
832 registry: &Registry,
833 validation_mode: ValidationMode,
834 recursive_depth: usize,
835 max_directives: Option<usize>,
836 complexity: Option<usize>,
837 depth: Option<usize>,
838) -> Result<(QueryEnv, CacheControl), Vec<ServerError>> {
839 let mut request = extensions.prepare_request(request).await?;
840 let query_data = Arc::new(std::mem::take(&mut request.data));
841 extensions.attach_query_data(query_data.clone());
842
843 let mut document = {
844 let query = &request.query;
845 let parsed_doc = request.parsed_query.take();
846 let fut_parse = async move {
847 let doc = match parsed_doc {
848 Some(parsed_doc) => parsed_doc,
849 None => parse_query(query)?,
850 };
851 check_recursive_depth(&doc, recursive_depth)?;
852 if let Some(max_directives) = max_directives {
853 check_max_directives(&doc, max_directives)?;
854 }
855 Ok(doc)
856 };
857 futures_util::pin_mut!(fut_parse);
858 extensions
859 .parse_query(query, &request.variables, &mut fut_parse)
860 .await?
861 };
862
863 let validation_result = {
865 let validation_fut = async {
866 check_rules(
867 registry,
868 &document,
869 Some(&request.variables),
870 validation_mode,
871 complexity,
872 depth,
873 )
874 };
875 futures_util::pin_mut!(validation_fut);
876 extensions.validation(&mut validation_fut).await?
877 };
878
879 let operation = if let Some(operation_name) = &request.operation_name {
880 match document.operations {
881 DocumentOperations::Single(_) => None,
882 DocumentOperations::Multiple(mut operations) => operations
883 .remove(operation_name.as_str())
884 .map(|operation| (Some(operation_name.clone()), operation)),
885 }
886 .ok_or_else(|| {
887 ServerError::new(
888 format!(r#"Unknown operation named "{}""#, operation_name),
889 None,
890 )
891 })
892 } else {
893 match document.operations {
894 DocumentOperations::Single(operation) => Ok((None, operation)),
895 DocumentOperations::Multiple(map) if map.len() == 1 => {
896 let (operation_name, operation) = map.into_iter().next().unwrap();
897 Ok((Some(operation_name.to_string()), operation))
898 }
899 DocumentOperations::Multiple(_) => Err(ServerError::new(
900 "Operation name required in request.",
901 None,
902 )),
903 }
904 };
905
906 let (operation_name, mut operation) = operation.map_err(|err| vec![err])?;
907
908 for fragment in document.fragments.values_mut() {
910 remove_skipped_selection(&mut fragment.node.selection_set.node, &request.variables);
911 }
912 remove_skipped_selection(&mut operation.node.selection_set.node, &request.variables);
913
914 let env = QueryEnvInner {
915 extensions,
916 variables: request.variables,
917 operation_name,
918 operation,
919 fragments: document.fragments,
920 uploads: request.uploads,
921 session_data,
922 query_data,
923 http_headers: Default::default(),
924 introspection_mode: request.introspection_mode,
925 errors: Default::default(),
926 };
927 Ok((QueryEnv::new(env), validation_result.cache_control))
928}