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 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 ) -> impl Stream<Item = Response> + Send + Unpin + 'static {
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(
646 &self,
647 request: impl Into<Request>,
648 ) -> impl Stream<Item = Response> + Send + Unpin {
649 self.execute_stream_with_session_data(request, Default::default())
650 }
651}
652
653#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
654impl<Query, Mutation, Subscription> Executor for Schema<Query, Mutation, Subscription>
655where
656 Query: ObjectType + 'static,
657 Mutation: ObjectType + 'static,
658 Subscription: SubscriptionType + 'static,
659{
660 async fn execute(&self, request: Request) -> Response {
661 Schema::execute(self, request).await
662 }
663
664 fn execute_stream(
665 &self,
666 request: Request,
667 session_data: Option<Arc<Data>>,
668 ) -> BoxStream<'static, Response> {
669 Schema::execute_stream_with_session_data(&self, request, session_data.unwrap_or_default())
670 .boxed()
671 }
672}
673
674fn check_max_directives(doc: &ExecutableDocument, max_directives: usize) -> ServerResult<()> {
675 fn check_selection_set(
676 doc: &ExecutableDocument,
677 selection_set: &Positioned<SelectionSet>,
678 limit_directives: usize,
679 ) -> ServerResult<()> {
680 for selection in &selection_set.node.items {
681 match &selection.node {
682 Selection::Field(field) => {
683 if field.node.directives.len() > limit_directives {
684 return Err(ServerError::new(
685 format!(
686 "The number of directives on the field `{}` cannot be greater than `{}`",
687 field.node.name.node, limit_directives
688 ),
689 Some(field.pos),
690 ));
691 }
692 check_selection_set(doc, &field.node.selection_set, limit_directives)?;
693 }
694 Selection::FragmentSpread(fragment_spread) => {
695 if let Some(fragment) =
696 doc.fragments.get(&fragment_spread.node.fragment_name.node)
697 {
698 check_selection_set(doc, &fragment.node.selection_set, limit_directives)?;
699 }
700 }
701 Selection::InlineFragment(inline_fragment) => {
702 check_selection_set(
703 doc,
704 &inline_fragment.node.selection_set,
705 limit_directives,
706 )?;
707 }
708 }
709 }
710
711 Ok(())
712 }
713
714 for (_, operation) in doc.operations.iter() {
715 check_selection_set(doc, &operation.node.selection_set, max_directives)?;
716 }
717
718 Ok(())
719}
720
721fn check_recursive_depth(doc: &ExecutableDocument, max_depth: usize) -> ServerResult<()> {
722 fn check_selection_set(
723 doc: &ExecutableDocument,
724 selection_set: &Positioned<SelectionSet>,
725 current_depth: usize,
726 max_depth: usize,
727 ) -> ServerResult<()> {
728 if current_depth > max_depth {
729 return Err(ServerError::new(
730 format!(
731 "The recursion depth of the query cannot be greater than `{}`",
732 max_depth
733 ),
734 Some(selection_set.pos),
735 ));
736 }
737
738 for selection in &selection_set.node.items {
739 match &selection.node {
740 Selection::Field(field) => {
741 if !field.node.selection_set.node.items.is_empty() {
742 check_selection_set(
743 doc,
744 &field.node.selection_set,
745 current_depth + 1,
746 max_depth,
747 )?;
748 }
749 }
750 Selection::FragmentSpread(fragment_spread) => {
751 if let Some(fragment) =
752 doc.fragments.get(&fragment_spread.node.fragment_name.node)
753 {
754 check_selection_set(
755 doc,
756 &fragment.node.selection_set,
757 current_depth + 1,
758 max_depth,
759 )?;
760 }
761 }
762 Selection::InlineFragment(inline_fragment) => {
763 check_selection_set(
764 doc,
765 &inline_fragment.node.selection_set,
766 current_depth + 1,
767 max_depth,
768 )?;
769 }
770 }
771 }
772
773 Ok(())
774 }
775
776 for (_, operation) in doc.operations.iter() {
777 check_selection_set(doc, &operation.node.selection_set, 0, max_depth)?;
778 }
779
780 Ok(())
781}
782
783fn remove_skipped_selection(selection_set: &mut SelectionSet, variables: &Variables) {
784 fn is_skipped(directives: &[Positioned<Directive>], variables: &Variables) -> bool {
785 for directive in directives {
786 let include = match &*directive.node.name.node {
787 "skip" => false,
788 "include" => true,
789 _ => continue,
790 };
791
792 if let Some(condition_input) = directive.node.get_argument("if") {
793 let value = condition_input
794 .node
795 .clone()
796 .into_const_with(|name| variables.get(&name).cloned().ok_or(()))
797 .unwrap_or_default();
798 let value: bool = InputType::parse(Some(value)).unwrap_or_default();
799 if include != value {
800 return true;
801 }
802 }
803 }
804
805 false
806 }
807
808 selection_set
809 .items
810 .retain(|selection| !is_skipped(selection.node.directives(), variables));
811
812 for selection in &mut selection_set.items {
813 selection.node.directives_mut().retain(|directive| {
814 directive.node.name.node != "skip" && directive.node.name.node != "include"
815 });
816 }
817
818 for selection in &mut selection_set.items {
819 match &mut selection.node {
820 Selection::Field(field) => {
821 remove_skipped_selection(&mut field.node.selection_set.node, variables);
822 }
823 Selection::FragmentSpread(_) => {}
824 Selection::InlineFragment(inline_fragment) => {
825 remove_skipped_selection(&mut inline_fragment.node.selection_set.node, variables);
826 }
827 }
828 }
829}
830
831#[allow(clippy::too_many_arguments)]
832pub(crate) async fn prepare_request(
833 mut extensions: Extensions,
834 request: Request,
835 session_data: Arc<Data>,
836 registry: &Registry,
837 validation_mode: ValidationMode,
838 recursive_depth: usize,
839 max_directives: Option<usize>,
840 complexity: Option<usize>,
841 depth: Option<usize>,
842) -> Result<(QueryEnv, CacheControl), Vec<ServerError>> {
843 let mut request = extensions.prepare_request(request).await?;
844 let query_data = Arc::new(std::mem::take(&mut request.data));
845 extensions.attach_query_data(query_data.clone());
846
847 let mut document = {
848 let query = &request.query;
849 let parsed_doc = request.parsed_query.take();
850 let fut_parse = async move {
851 let doc = match parsed_doc {
852 Some(parsed_doc) => parsed_doc,
853 None => parse_query(query)?,
854 };
855 check_recursive_depth(&doc, recursive_depth)?;
856 if let Some(max_directives) = max_directives {
857 check_max_directives(&doc, max_directives)?;
858 }
859 Ok(doc)
860 };
861 futures_util::pin_mut!(fut_parse);
862 extensions
863 .parse_query(query, &request.variables, &mut fut_parse)
864 .await?
865 };
866
867 let validation_result = {
869 let validation_fut = async {
870 check_rules(
871 registry,
872 &document,
873 Some(&request.variables),
874 validation_mode,
875 complexity,
876 depth,
877 )
878 };
879 futures_util::pin_mut!(validation_fut);
880 extensions.validation(&mut validation_fut).await?
881 };
882
883 let operation = if let Some(operation_name) = &request.operation_name {
884 match document.operations {
885 DocumentOperations::Single(_) => None,
886 DocumentOperations::Multiple(mut operations) => operations
887 .remove(operation_name.as_str())
888 .map(|operation| (Some(operation_name.clone()), operation)),
889 }
890 .ok_or_else(|| {
891 ServerError::new(
892 format!(r#"Unknown operation named "{}""#, operation_name),
893 None,
894 )
895 })
896 } else {
897 match document.operations {
898 DocumentOperations::Single(operation) => Ok((None, operation)),
899 DocumentOperations::Multiple(map) if map.len() == 1 => {
900 let (operation_name, operation) = map.into_iter().next().unwrap();
901 Ok((Some(operation_name.to_string()), operation))
902 }
903 DocumentOperations::Multiple(_) => Err(ServerError::new(
904 "Operation name required in request.",
905 None,
906 )),
907 }
908 };
909
910 let (operation_name, mut operation) = operation.map_err(|err| vec![err])?;
911
912 for fragment in document.fragments.values_mut() {
914 remove_skipped_selection(&mut fragment.node.selection_set.node, &request.variables);
915 }
916 remove_skipped_selection(&mut operation.node.selection_set.node, &request.variables);
917
918 let env = QueryEnvInner {
919 extensions,
920 variables: request.variables,
921 operation_name,
922 operation,
923 fragments: document.fragments,
924 uploads: request.uploads,
925 session_data,
926 query_data,
927 http_headers: Default::default(),
928 introspection_mode: request.introspection_mode,
929 errors: Default::default(),
930 };
931 Ok((QueryEnv::new(env), validation_result.cache_control))
932}