1use std::{
2 any::{Any, TypeId},
3 collections::{HashMap, HashSet},
4 ops::Deref,
5 sync::Arc,
6};
7
8use async_graphql_parser::types::ExecutableDocument;
9use futures_util::stream::{self, BoxStream, FuturesOrdered, StreamExt};
10
11use crate::{
12 BatchRequest, BatchResponse, CacheControl, ContextBase, EmptyMutation, EmptySubscription,
13 Executor, InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError,
14 ServerResult, SubscriptionType, Variables,
15 context::{Data, QueryEnvInner},
16 custom_directive::CustomDirectiveFactory,
17 extensions::{ExtensionFactory, Extensions},
18 parser::{
19 Positioned, parse_query,
20 types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet},
21 },
22 registry::{Registry, SDLExportOptions},
23 resolver_utils::{resolve_container, resolve_container_serial},
24 subscription::collect_subscription_streams,
25 types::QueryRoot,
26 validation::{ValidationMode, check_rules},
27};
28
29#[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 asynk_strim::stream_fn(|mut yielder| async move {
586 let (env, cache_control) = match prepare_request(
587 extensions,
588 request,
589 session_data,
590 &env.registry,
591 schema.0.validation_mode,
592 schema.0.recursive_depth,
593 schema.0.max_directives,
594 schema.0.complexity,
595 schema.0.depth,
596 )
597 .await
598 {
599 Ok(res) => res,
600 Err(errors) => {
601 yielder.yield_item(Response::from_errors(errors)).await;
602 return;
603 }
604 };
605
606 if env.operation.node.ty != OperationType::Subscription {
607 let f = |execute_data: Option<Data>| {
608 let env = env.clone();
609 let schema = schema.clone();
610 async move {
611 schema
612 .execute_once(env, execute_data.as_ref())
613 .await
614 .cache_control(cache_control)
615 }
616 };
617 yielder
618 .yield_item(
619 env.extensions
620 .execute(env.operation_name.as_deref(), f)
621 .await
622 .cache_control(cache_control),
623 )
624 .await;
625 return;
626 }
627
628 let ctx = env.create_context(
629 &schema.0.env,
630 None,
631 &env.operation.node.selection_set,
632 None,
633 );
634
635 let mut streams = Vec::new();
636 let collect_result = if schema.0.env.registry.introspection_mode
637 == IntrospectionMode::IntrospectionOnly
638 || env.introspection_mode == IntrospectionMode::IntrospectionOnly
639 {
640 collect_subscription_streams(&ctx, &EmptySubscription, &mut streams)
641 } else {
642 collect_subscription_streams(&ctx, &schema.0.subscription, &mut streams)
643 };
644 if let Err(err) = collect_result {
645 yielder.yield_item(Response::from_errors(vec![err])).await;
646 }
647
648 let mut stream = stream::select_all(streams);
649 while let Some(resp) = stream.next().await {
650 yielder.yield_item(resp).await;
651 }
652 })
653 });
654 extensions.subscribe(stream)
655 }
656
657 pub fn execute_stream(&self, request: impl Into<Request>) -> BoxStream<'static, Response> {
659 self.execute_stream_with_session_data(request, Default::default())
660 }
661
662 pub fn data<D: Any + Send + Sync>(&self) -> Option<&D> {
664 self.0
665 .env
666 .data
667 .get(&TypeId::of::<D>())
668 .and_then(|d| d.downcast_ref::<D>())
669 }
670}
671
672#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
673impl<Query, Mutation, Subscription> Executor for Schema<Query, Mutation, Subscription>
674where
675 Query: ObjectType + 'static,
676 Mutation: ObjectType + 'static,
677 Subscription: SubscriptionType + 'static,
678{
679 async fn execute(&self, request: Request) -> Response {
680 Schema::execute(self, request).await
681 }
682
683 fn execute_stream(
684 &self,
685 request: Request,
686 session_data: Option<Arc<Data>>,
687 ) -> BoxStream<'static, Response> {
688 Schema::execute_stream_with_session_data(&self, request, session_data.unwrap_or_default())
689 }
690}
691
692fn check_max_directives(doc: &ExecutableDocument, max_directives: usize) -> ServerResult<()> {
693 fn check_selection_set(
694 doc: &ExecutableDocument,
695 selection_set: &Positioned<SelectionSet>,
696 limit_directives: usize,
697 ) -> ServerResult<()> {
698 for selection in &selection_set.node.items {
699 match &selection.node {
700 Selection::Field(field) => {
701 if field.node.directives.len() > limit_directives {
702 return Err(ServerError::new(
703 format!(
704 "The number of directives on the field `{}` cannot be greater than `{}`",
705 field.node.name.node, limit_directives
706 ),
707 Some(field.pos),
708 ));
709 }
710 check_selection_set(doc, &field.node.selection_set, limit_directives)?;
711 }
712 Selection::FragmentSpread(fragment_spread) => {
713 if let Some(fragment) =
714 doc.fragments.get(&fragment_spread.node.fragment_name.node)
715 {
716 check_selection_set(doc, &fragment.node.selection_set, limit_directives)?;
717 }
718 }
719 Selection::InlineFragment(inline_fragment) => {
720 check_selection_set(
721 doc,
722 &inline_fragment.node.selection_set,
723 limit_directives,
724 )?;
725 }
726 }
727 }
728
729 Ok(())
730 }
731
732 for (_, operation) in doc.operations.iter() {
733 check_selection_set(doc, &operation.node.selection_set, max_directives)?;
734 }
735
736 Ok(())
737}
738
739fn check_recursive_depth(doc: &ExecutableDocument, max_depth: usize) -> ServerResult<()> {
740 fn check_selection_set(
741 doc: &ExecutableDocument,
742 selection_set: &Positioned<SelectionSet>,
743 current_depth: usize,
744 max_depth: usize,
745 ) -> ServerResult<()> {
746 if current_depth > max_depth {
747 return Err(ServerError::new(
748 format!(
749 "The recursion depth of the query cannot be greater than `{}`",
750 max_depth
751 ),
752 Some(selection_set.pos),
753 ));
754 }
755
756 for selection in &selection_set.node.items {
757 match &selection.node {
758 Selection::Field(field) => {
759 if !field.node.selection_set.node.items.is_empty() {
760 check_selection_set(
761 doc,
762 &field.node.selection_set,
763 current_depth + 1,
764 max_depth,
765 )?;
766 }
767 }
768 Selection::FragmentSpread(fragment_spread) => {
769 if let Some(fragment) =
770 doc.fragments.get(&fragment_spread.node.fragment_name.node)
771 {
772 check_selection_set(
773 doc,
774 &fragment.node.selection_set,
775 current_depth + 1,
776 max_depth,
777 )?;
778 }
779 }
780 Selection::InlineFragment(inline_fragment) => {
781 check_selection_set(
782 doc,
783 &inline_fragment.node.selection_set,
784 current_depth + 1,
785 max_depth,
786 )?;
787 }
788 }
789 }
790
791 Ok(())
792 }
793
794 for (_, operation) in doc.operations.iter() {
795 check_selection_set(doc, &operation.node.selection_set, 0, max_depth)?;
796 }
797
798 Ok(())
799}
800
801fn remove_skipped_selection(selection_set: &mut SelectionSet, variables: &Variables) {
802 fn is_skipped(directives: &[Positioned<Directive>], variables: &Variables) -> bool {
803 for directive in directives {
804 let include = match &*directive.node.name.node {
805 "skip" => false,
806 "include" => true,
807 _ => continue,
808 };
809
810 if let Some(condition_input) = directive.node.get_argument("if") {
811 let value = condition_input
812 .node
813 .clone()
814 .into_const_with(|name| variables.get(&name).cloned().ok_or(()))
815 .unwrap_or_default();
816 let value: bool = InputType::parse(Some(value)).unwrap_or_default();
817 if include != value {
818 return true;
819 }
820 }
821 }
822
823 false
824 }
825
826 selection_set
827 .items
828 .retain(|selection| !is_skipped(selection.node.directives(), variables));
829
830 for selection in &mut selection_set.items {
831 selection.node.directives_mut().retain(|directive| {
832 directive.node.name.node != "skip" && directive.node.name.node != "include"
833 });
834 }
835
836 for selection in &mut selection_set.items {
837 match &mut selection.node {
838 Selection::Field(field) => {
839 remove_skipped_selection(&mut field.node.selection_set.node, variables);
840 }
841 Selection::FragmentSpread(_) => {}
842 Selection::InlineFragment(inline_fragment) => {
843 remove_skipped_selection(&mut inline_fragment.node.selection_set.node, variables);
844 }
845 }
846 }
847}
848
849#[allow(clippy::too_many_arguments)]
850pub(crate) async fn prepare_request(
851 mut extensions: Extensions,
852 request: Request,
853 session_data: Arc<Data>,
854 registry: &Registry,
855 validation_mode: ValidationMode,
856 recursive_depth: usize,
857 max_directives: Option<usize>,
858 complexity: Option<usize>,
859 depth: Option<usize>,
860) -> Result<(QueryEnv, CacheControl), Vec<ServerError>> {
861 let mut request = extensions.prepare_request(request).await?;
862 let query_data = Arc::new(std::mem::take(&mut request.data));
863 extensions.attach_query_data(query_data.clone());
864
865 let mut document = {
866 let query = &request.query;
867 let parsed_doc = request.parsed_query.take();
868 let fut_parse = async move {
869 let doc = match parsed_doc {
870 Some(parsed_doc) => parsed_doc,
871 None => parse_query(query)?,
872 };
873 check_recursive_depth(&doc, recursive_depth)?;
874 if let Some(max_directives) = max_directives {
875 check_max_directives(&doc, max_directives)?;
876 }
877 Ok(doc)
878 };
879 futures_util::pin_mut!(fut_parse);
880 extensions
881 .parse_query(query, &request.variables, &mut fut_parse)
882 .await?
883 };
884
885 let validation_result = {
887 let validation_fut = async {
888 check_rules(
889 registry,
890 &document,
891 Some(&request.variables),
892 validation_mode,
893 complexity,
894 depth,
895 )
896 };
897 futures_util::pin_mut!(validation_fut);
898 extensions.validation(&mut validation_fut).await?
899 };
900
901 let operation = if let Some(operation_name) = &request.operation_name {
902 match document.operations {
903 DocumentOperations::Single(_) => None,
904 DocumentOperations::Multiple(mut operations) => operations
905 .remove(operation_name.as_str())
906 .map(|operation| (Some(operation_name.clone()), operation)),
907 }
908 .ok_or_else(|| {
909 ServerError::new(
910 format!(r#"Unknown operation named "{}""#, operation_name),
911 None,
912 )
913 })
914 } else {
915 match document.operations {
916 DocumentOperations::Single(operation) => Ok((None, operation)),
917 DocumentOperations::Multiple(map) if map.len() == 1 => {
918 let (operation_name, operation) = map.into_iter().next().unwrap();
919 Ok((Some(operation_name.to_string()), operation))
920 }
921 DocumentOperations::Multiple(_) => Err(ServerError::new(
922 "Operation name required in request.",
923 None,
924 )),
925 }
926 };
927
928 let (operation_name, mut operation) = operation.map_err(|err| vec![err])?;
929
930 for fragment in document.fragments.values_mut() {
932 remove_skipped_selection(&mut fragment.node.selection_set.node, &request.variables);
933 }
934 remove_skipped_selection(&mut operation.node.selection_set.node, &request.variables);
935
936 let env = QueryEnvInner {
937 extensions,
938 variables: request.variables,
939 operation_name,
940 operation,
941 fragments: document.fragments,
942 uploads: request.uploads,
943 session_data,
944 query_data,
945 http_headers: Default::default(),
946 introspection_mode: request.introspection_mode,
947 errors: Default::default(),
948 };
949 Ok((QueryEnv::new(env), validation_result.cache_control))
950}