1use std::fmt::Debug;
2use std::fmt::Formatter;
3use std::iter;
4use std::sync::Arc;
5
6use apollo_compiler::Name;
7use apollo_compiler::Node;
8use apollo_compiler::Schema;
9use apollo_compiler::ast::Argument;
10use apollo_compiler::ast::Directive;
11use apollo_compiler::ast::DirectiveDefinition;
12use apollo_compiler::ast::DirectiveList;
13use apollo_compiler::ast::DirectiveLocation;
14use apollo_compiler::ast::EnumValueDefinition;
15use apollo_compiler::ast::FieldDefinition;
16use apollo_compiler::ast::NamedType;
17use apollo_compiler::ast::Value;
18use apollo_compiler::collections::IndexMap;
19use apollo_compiler::collections::IndexSet;
20use apollo_compiler::name;
21use apollo_compiler::schema::Component;
22use apollo_compiler::schema::EnumType;
23use apollo_compiler::schema::ExtendedType;
24use apollo_compiler::schema::InputObjectType;
25use apollo_compiler::schema::InputValueDefinition;
26use apollo_compiler::schema::InterfaceType;
27use apollo_compiler::schema::ObjectType;
28use apollo_compiler::schema::ScalarType;
29use apollo_compiler::schema::UnionType;
30use apollo_compiler::ty;
31use apollo_compiler::validation::Valid;
32use indexmap::map::Entry::Occupied;
33use indexmap::map::Entry::Vacant;
34use indexmap::map::Iter;
35use itertools::Itertools;
36
37use crate::ValidFederationSubgraph;
38use crate::ValidFederationSubgraphs;
39use crate::error::FederationError;
40use crate::link::LinksMetadata;
41use crate::link::federation_spec_definition::FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC;
42use crate::link::federation_spec_definition::FEDERATION_FIELDS_ARGUMENT_NAME;
43use crate::link::federation_spec_definition::FEDERATION_FROM_ARGUMENT_NAME;
44use crate::link::federation_spec_definition::FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC;
45use crate::link::federation_spec_definition::FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC;
46use crate::link::federation_spec_definition::FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC;
47use crate::link::federation_spec_definition::FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME;
48use crate::link::federation_spec_definition::FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC;
49use crate::link::federation_spec_definition::FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC;
50use crate::link::inaccessible_spec_definition::INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC;
51use crate::link::inaccessible_spec_definition::InaccessibleSpecDefinition;
52use crate::link::join_spec_definition::JOIN_OVERRIDE_LABEL_ARGUMENT_NAME;
53use crate::link::spec::Identity;
54use crate::link::spec::Version;
55use crate::link::spec_definition::SpecDefinition;
56use crate::schema::ValidFederationSchema;
57use crate::subgraph::ValidSubgraph;
58
59type MergeWarning = String;
60type MergeError = String;
61
62struct Merger {
63 errors: Vec<MergeError>,
64 composition_hints: Vec<MergeWarning>,
65 needs_inaccessible: bool,
66}
67
68pub struct MergeSuccess {
69 pub schema: Valid<Schema>,
70 pub composition_hints: Vec<MergeWarning>,
71}
72
73impl From<FederationError> for MergeFailure {
74 fn from(err: FederationError) -> Self {
75 MergeFailure {
80 schema: None,
81 errors: vec![err.to_string()],
82 composition_hints: vec![],
83 }
84 }
85}
86
87pub struct MergeFailure {
88 pub schema: Option<Box<Schema>>,
89 pub errors: Vec<MergeError>,
90 pub composition_hints: Vec<MergeWarning>,
91}
92
93impl Debug for MergeFailure {
94 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
95 f.debug_struct("MergeFailure")
96 .field("errors", &self.errors)
97 .field("composition_hints", &self.composition_hints)
98 .finish()
99 }
100}
101
102pub fn merge_subgraphs(subgraphs: Vec<&ValidSubgraph>) -> Result<MergeSuccess, MergeFailure> {
103 let mut merger = Merger::new();
104 let mut federation_subgraphs = ValidFederationSubgraphs::new();
105 for subgraph in subgraphs {
106 federation_subgraphs.add(ValidFederationSubgraph {
107 name: subgraph.name.clone(),
108 url: subgraph.url.clone(),
109 schema: ValidFederationSchema::new(subgraph.schema.clone())?,
110 })?;
111 }
112 merger.merge(federation_subgraphs)
113}
114
115pub fn merge_federation_subgraphs(
116 subgraphs: ValidFederationSubgraphs,
117) -> Result<MergeSuccess, MergeFailure> {
118 let mut merger = Merger::new();
119 merger.merge(subgraphs)
120}
121
122impl Merger {
123 fn new() -> Self {
124 Merger {
125 composition_hints: Vec::new(),
126 errors: Vec::new(),
127 needs_inaccessible: false,
128 }
129 }
130
131 fn merge(&mut self, subgraphs: ValidFederationSubgraphs) -> Result<MergeSuccess, MergeFailure> {
132 let mut subgraphs = subgraphs
133 .into_iter()
134 .map(|(_, subgraph)| subgraph)
135 .collect_vec();
136 subgraphs.sort_by(|s1, s2| s1.name.cmp(&s2.name));
137 let mut subgraphs_and_enum_values: Vec<(&ValidFederationSubgraph, Name)> = Vec::new();
138 for subgraph in &subgraphs {
139 if let Ok(subgraph_name) = Name::new(&subgraph.name.to_uppercase()) {
142 subgraphs_and_enum_values.push((subgraph, subgraph_name));
143 } else {
144 self.errors.push(String::from(
145 "Subgraph name couldn't be transformed into valid GraphQL name",
146 ));
147 }
148 }
149 if !self.errors.is_empty() {
150 return Err(MergeFailure {
151 schema: None,
152 composition_hints: self.composition_hints.to_owned(),
153 errors: self.errors.to_owned(),
154 });
155 }
156
157 let mut supergraph = Schema::new();
158 add_core_feature_link(&mut supergraph);
163 add_core_feature_join(&mut supergraph, &subgraphs_and_enum_values);
164
165 for (subgraph, subgraph_name) in &subgraphs_and_enum_values {
167 let sources = Arc::make_mut(&mut supergraph.sources);
168 for (key, source) in subgraph.schema.schema().sources.iter() {
169 sources.entry(*key).or_insert_with(|| source.clone());
170 }
171
172 self.merge_schema(&mut supergraph, subgraph);
173 let metadata = subgraph.schema.metadata();
176 let relevant_directives = DirectiveNames::for_metadata(&metadata);
177
178 for (type_name, ty) in &subgraph.schema.schema().types {
179 if ty.is_built_in() || !is_mergeable_type(type_name) {
180 continue;
182 }
183
184 match ty {
185 ExtendedType::Enum(value) => self.merge_enum_type(
186 &mut supergraph.types,
187 &relevant_directives,
188 subgraph_name.clone(),
189 type_name.clone(),
190 value,
191 ),
192 ExtendedType::InputObject(value) => self.merge_input_object_type(
193 &mut supergraph.types,
194 &relevant_directives,
195 subgraph_name.clone(),
196 type_name.clone(),
197 value,
198 ),
199 ExtendedType::Interface(value) => self.merge_interface_type(
200 &mut supergraph.types,
201 &relevant_directives,
202 subgraph_name.clone(),
203 type_name.clone(),
204 value,
205 ),
206 ExtendedType::Object(value) => self.merge_object_type(
207 &mut supergraph.types,
208 &relevant_directives,
209 subgraph_name.clone(),
210 type_name.clone(),
211 value,
212 ),
213 ExtendedType::Union(value) => self.merge_union_type(
214 &mut supergraph.types,
215 &relevant_directives,
216 subgraph_name.clone(),
217 type_name.clone(),
218 value,
219 ),
220 ExtendedType::Scalar(value) => {
221 if !value.is_built_in() {
222 self.merge_scalar_type(
223 &mut supergraph.types,
224 &relevant_directives,
225 subgraph_name.clone(),
226 type_name.clone(),
227 value,
228 );
229 }
230 }
231 }
232 }
233
234 for (_, directive) in subgraph.schema.schema().directive_definitions.iter() {
236 if is_executable_directive(directive) {
237 merge_directive(&mut supergraph.directive_definitions, directive);
238 }
239 }
240 }
241
242 if self.needs_inaccessible {
243 add_core_feature_inaccessible(&mut supergraph);
244 }
245
246 if self.errors.is_empty() {
247 let supergraph = Valid::assume_valid(supergraph);
249 Ok(MergeSuccess {
250 schema: supergraph,
251 composition_hints: self.composition_hints.to_owned(),
252 })
253 } else {
254 Err(MergeFailure {
255 schema: Some(Box::new(supergraph)),
256 composition_hints: self.composition_hints.to_owned(),
257 errors: self.errors.to_owned(),
258 })
259 }
260 }
261
262 fn merge_descriptions<T: Eq + Clone>(&mut self, merged: &mut Option<T>, new: &Option<T>) {
263 match (&mut *merged, new) {
264 (_, None) => {}
265 (None, Some(_)) => merged.clone_from(new),
266 (Some(a), Some(b)) => {
267 if a != b {
268 self.composition_hints
270 .push(String::from("conflicting descriptions"));
271 }
272 }
273 }
274 }
275
276 fn merge_schema(&mut self, supergraph_schema: &mut Schema, subgraph: &ValidFederationSubgraph) {
277 let supergraph_def = &mut supergraph_schema.schema_definition.make_mut();
278 let subgraph_def = &subgraph.schema.schema().schema_definition;
279 self.merge_descriptions(&mut supergraph_def.description, &subgraph_def.description);
280
281 if subgraph_def.query.is_some() {
282 supergraph_def.query.clone_from(&subgraph_def.query);
283 }
285 if subgraph_def.mutation.is_some() {
286 supergraph_def.mutation.clone_from(&subgraph_def.mutation);
287 }
289 if subgraph_def.subscription.is_some() {
290 supergraph_def
291 .subscription
292 .clone_from(&subgraph_def.subscription);
293 }
295 }
296
297 fn merge_enum_type(
298 &mut self,
299 types: &mut IndexMap<NamedType, ExtendedType>,
300 metadata: &DirectiveNames,
301 subgraph_name: Name,
302 enum_name: NamedType,
303 enum_type: &Node<EnumType>,
304 ) {
305 let existing_type = types
306 .entry(enum_name.clone())
307 .or_insert(copy_enum_type(enum_name, enum_type));
308
309 if let ExtendedType::Enum(e) = existing_type {
310 let join_type_directives =
311 join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
312 e.make_mut().directives.extend(join_type_directives);
313
314 self.add_inaccessible(
315 metadata,
316 &mut e.make_mut().directives,
317 &enum_type.directives,
318 );
319
320 self.merge_descriptions(&mut e.make_mut().description, &enum_type.description);
321
322 for (enum_value_name, enum_value) in enum_type.values.iter() {
325 let ev = e
326 .make_mut()
327 .values
328 .entry(enum_value_name.clone())
329 .or_insert(Component::new(EnumValueDefinition {
330 value: enum_value.value.clone(),
331 description: None,
332 directives: Default::default(),
333 }));
334 self.merge_descriptions(&mut ev.make_mut().description, &enum_value.description);
335
336 self.add_inaccessible(
337 metadata,
338 &mut ev.make_mut().directives,
339 &enum_value.directives,
340 );
341
342 ev.make_mut().directives.push(Node::new(Directive {
343 name: name!("join__enumValue"),
344 arguments: vec![
345 (Node::new(Argument {
346 name: name!("graph"),
347 value: Node::new(Value::Enum(subgraph_name.clone())),
348 })),
349 ],
350 }));
351 }
352 } else {
353 }
355 }
356
357 fn merge_input_object_type(
358 &mut self,
359 types: &mut IndexMap<NamedType, ExtendedType>,
360 directive_names: &DirectiveNames,
361 subgraph_name: Name,
362 input_object_name: NamedType,
363 input_object: &Node<InputObjectType>,
364 ) {
365 let existing_type = types
366 .entry(input_object_name.clone())
367 .or_insert(copy_input_object_type(input_object_name, input_object));
368
369 if let ExtendedType::InputObject(obj) = existing_type {
370 let join_type_directives =
371 join_type_applied_directive(subgraph_name, iter::empty(), false);
372 let mutable_object = obj.make_mut();
373 mutable_object.directives.extend(join_type_directives);
374
375 self.add_inaccessible(
376 directive_names,
377 &mut mutable_object.directives,
378 &input_object.directives,
379 );
380
381 for (field_name, field) in input_object.fields.iter() {
382 let existing_field = mutable_object.fields.entry(field_name.clone());
383
384 match existing_field {
385 Vacant(_i) => {
386 }
388 Occupied(mut i) => {
389 self.add_inaccessible(
390 directive_names,
391 &mut i.get_mut().make_mut().directives,
392 &field.directives,
393 );
394 }
400 }
401 }
402 } else {
403 }
405 }
406
407 fn merge_interface_type(
408 &mut self,
409 types: &mut IndexMap<NamedType, ExtendedType>,
410 directive_names: &DirectiveNames,
411 subgraph_name: Name,
412 interface_name: NamedType,
413 interface: &Node<InterfaceType>,
414 ) {
415 let existing_type = types
416 .entry(interface_name.clone())
417 .or_insert(copy_interface_type(interface_name, interface));
418
419 if let ExtendedType::Interface(intf) = existing_type {
420 let key_directives = interface.directives.get_all(&directive_names.key);
421 let join_type_directives =
422 join_type_applied_directive(subgraph_name, key_directives, false);
423 let mutable_intf = intf.make_mut();
424 mutable_intf.directives.extend(join_type_directives);
425
426 self.add_inaccessible(
427 directive_names,
428 &mut mutable_intf.directives,
429 &interface.directives,
430 );
431
432 for (field_name, field) in interface.fields.iter() {
433 let existing_field = mutable_intf.fields.entry(field_name.clone());
434 match existing_field {
435 Vacant(i) => {
436 let f = i.insert(Component::new(FieldDefinition {
438 name: field.name.clone(),
439 description: field.description.clone(),
440 arguments: vec![],
441 ty: field.ty.clone(),
442 directives: Default::default(),
443 }));
444
445 self.add_inaccessible(
446 directive_names,
447 &mut f.make_mut().directives,
448 &field.directives,
449 );
450 }
451 Occupied(_i) => {
452 }
457 }
458 }
459 } else {
460 }
462 }
463
464 fn merge_object_type(
465 &mut self,
466 types: &mut IndexMap<NamedType, ExtendedType>,
467 directive_names: &DirectiveNames,
468 subgraph_name: Name,
469 object_name: NamedType,
470 object: &Node<ObjectType>,
471 ) {
472 let is_interface_object = object.directives.has(&directive_names.interface_object);
473 let existing_type = types
474 .entry(object_name.clone())
475 .or_insert(copy_object_type_stub(
476 object_name.clone(),
477 object,
478 is_interface_object,
479 ));
480
481 if let ExtendedType::Object(obj) = existing_type {
482 let key_directives = object.directives.get_all(&directive_names.key);
483 let join_type_directives =
484 join_type_applied_directive(subgraph_name.clone(), key_directives, false);
485 let mutable_object = obj.make_mut();
486 mutable_object.directives.extend(join_type_directives);
487 self.merge_descriptions(&mut mutable_object.description, &object.description);
488 self.add_inaccessible(
489 directive_names,
490 &mut mutable_object.directives,
491 &object.directives,
492 );
493 object.implements_interfaces.iter().for_each(|intf_name| {
494 mutable_object
496 .implements_interfaces
497 .insert(intf_name.clone());
498 let join_implements_directive =
499 join_implements_applied_directive(subgraph_name.clone(), intf_name);
500 mutable_object.directives.push(join_implements_directive);
501 });
502
503 for (field_name, field) in object.fields.iter() {
504 if field_name == "_service" || field_name == "_entities" {
506 continue;
507 }
508
509 let existing_field = mutable_object.fields.entry(field_name.clone());
510 let supergraph_field = match existing_field {
511 Occupied(f) => {
512 f.into_mut()
516 }
517 Vacant(f) => f.insert(Component::new(FieldDefinition {
518 name: field.name.clone(),
519 description: field.description.clone(),
520 arguments: vec![],
521 directives: Default::default(),
522 ty: field.ty.clone(),
523 })),
524 };
525 self.merge_descriptions(
526 &mut supergraph_field.make_mut().description,
527 &field.description,
528 );
529
530 self.add_inaccessible(
531 directive_names,
532 &mut supergraph_field.make_mut().directives,
533 &field.directives,
534 );
535
536 for arg in field.arguments.iter() {
537 let arguments_to_merge = &mut supergraph_field.make_mut().arguments;
538 let argument_to_merge = arguments_to_merge
539 .iter_mut()
540 .find_map(|a| (a.name == arg.name).then(|| a.make_mut()));
541
542 if let Some(argument) = argument_to_merge {
543 self.add_inaccessible(
544 directive_names,
545 &mut argument.directives,
546 &arg.directives,
547 );
548 } else {
549 let mut argument = InputValueDefinition {
550 name: arg.name.clone(),
551 description: arg.description.clone(),
552 directives: Default::default(),
553 ty: arg.ty.clone(),
554 default_value: arg.default_value.clone(),
555 };
556
557 self.add_inaccessible(
558 directive_names,
559 &mut argument.directives,
560 &arg.directives,
561 );
562 arguments_to_merge.push(argument.into());
563 };
564 }
565
566 let requires_directive_option = field
567 .directives
568 .get_all(&directive_names.requires)
569 .next()
570 .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
571
572 let provides_directive_option = field
573 .directives
574 .get_all(&directive_names.provides)
575 .next()
576 .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
577
578 let overrides_directive_option = field
579 .directives
580 .get_all(&directive_names.r#override)
581 .next()
582 .and_then(|p| {
583 let overrides_from =
584 directive_string_arg_value(p, &FEDERATION_FROM_ARGUMENT_NAME);
585 let overrides_label =
586 directive_string_arg_value(p, &FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME);
587 overrides_from.map(|from| (from, overrides_label))
588 });
589
590 let external_field = field
591 .directives
592 .get_all(&directive_names.external)
593 .next()
594 .is_some();
595
596 let join_field_directive = join_field_applied_directive(
597 subgraph_name.clone(),
598 requires_directive_option,
599 provides_directive_option,
600 external_field,
601 overrides_directive_option,
602 );
603
604 supergraph_field
605 .make_mut()
606 .directives
607 .push(Node::new(join_field_directive));
608
609 }
612 } else if let ExtendedType::Interface(intf) = existing_type {
613 let key_directives = object.directives.get_all(&directive_names.key);
615 let join_type_directives =
616 join_type_applied_directive(subgraph_name, key_directives, true);
617 intf.make_mut().directives.extend(join_type_directives);
618 };
619 }
621
622 fn merge_union_type(
623 &mut self,
624 types: &mut IndexMap<NamedType, ExtendedType>,
625 directive_names: &DirectiveNames,
626 subgraph_name: Name,
627 union_name: NamedType,
628 union: &Node<UnionType>,
629 ) {
630 let existing_type = types.entry(union_name.clone()).or_insert(copy_union_type(
631 union_name.clone(),
632 union.description.clone(),
633 ));
634
635 if let ExtendedType::Union(u) = existing_type {
636 let join_type_directives =
637 join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
638 u.make_mut().directives.extend(join_type_directives);
639 self.add_inaccessible(
640 directive_names,
641 &mut u.make_mut().directives,
642 &union.directives,
643 );
644
645 for union_member in union.members.iter() {
646 u.make_mut().members.insert(union_member.clone());
648 u.make_mut().directives.push(Component::new(Directive {
649 name: name!("join__unionMember"),
650 arguments: vec![
651 Node::new(Argument {
652 name: name!("graph"),
653 value: Node::new(Value::Enum(subgraph_name.clone())),
654 }),
655 Node::new(Argument {
656 name: name!("member"),
657 value: union_member.as_str().into(),
658 }),
659 ],
660 }));
661 }
662 }
663 }
664
665 fn merge_scalar_type(
666 &mut self,
667 types: &mut IndexMap<Name, ExtendedType>,
668 directive_names: &DirectiveNames,
669 subgraph_name: Name,
670 scalar_name: NamedType,
671 ty: &Node<ScalarType>,
672 ) {
673 let existing_type = types
674 .entry(scalar_name.clone())
675 .or_insert(copy_scalar_type(scalar_name, ty));
676
677 if let ExtendedType::Scalar(s) = existing_type {
678 let join_type_directives =
679 join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
680 s.make_mut().directives.extend(join_type_directives);
681 self.add_inaccessible(
682 directive_names,
683 &mut s.make_mut().directives,
684 &ty.directives,
685 );
686 } else {
687 }
689 }
690
691 fn add_inaccessible<I>(
693 &mut self,
694 directive_names: &DirectiveNames,
695 new_directives: &mut Vec<I>,
696 original_directives: &[I],
697 ) where
698 I: AsRef<Directive> + From<Directive> + Clone,
699 {
700 if original_directives
701 .iter()
702 .any(|d| d.as_ref().name == directive_names.inaccessible)
703 && !new_directives
704 .iter()
705 .any(|d| d.as_ref().name == INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC)
706 {
707 self.needs_inaccessible = true;
708
709 new_directives.push(
710 Directive {
711 name: INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
712 arguments: vec![],
713 }
714 .into(),
715 );
716 }
717 }
718}
719
720struct DirectiveNames {
721 key: Name,
722 requires: Name,
723 provides: Name,
724 external: Name,
725 interface_object: Name,
726 r#override: Name,
727 inaccessible: Name,
728}
729
730impl DirectiveNames {
731 fn for_metadata(metadata: &Option<&LinksMetadata>) -> Self {
732 let federation_identity =
733 metadata.and_then(|m| m.by_identity.get(&Identity::federation_identity()));
734
735 let key = federation_identity
736 .map(|link| link.directive_name_in_schema(&FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC))
737 .unwrap_or(FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC);
738
739 let requires = federation_identity
740 .map(|link| link.directive_name_in_schema(&FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC))
741 .unwrap_or(FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC);
742
743 let provides = federation_identity
744 .map(|link| link.directive_name_in_schema(&FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC))
745 .unwrap_or(FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC);
746
747 let external = federation_identity
748 .map(|link| link.directive_name_in_schema(&FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC))
749 .unwrap_or(FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC);
750
751 let interface_object = federation_identity
752 .map(|link| {
753 link.directive_name_in_schema(&FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC)
754 })
755 .unwrap_or(FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC);
756
757 let r#override = federation_identity
758 .map(|link| link.directive_name_in_schema(&FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC))
759 .unwrap_or(FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC);
760
761 let inaccessible = federation_identity
762 .map(|link| link.directive_name_in_schema(&INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC))
763 .unwrap_or(INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC);
764
765 Self {
766 key,
767 requires,
768 provides,
769 external,
770 interface_object,
771 r#override,
772 inaccessible,
773 }
774 }
775}
776
777const EXECUTABLE_DIRECTIVE_LOCATIONS: [DirectiveLocation; 8] = [
778 DirectiveLocation::Query,
779 DirectiveLocation::Mutation,
780 DirectiveLocation::Subscription,
781 DirectiveLocation::Field,
782 DirectiveLocation::FragmentDefinition,
783 DirectiveLocation::FragmentSpread,
784 DirectiveLocation::InlineFragment,
785 DirectiveLocation::VariableDefinition,
786];
787fn is_executable_directive(directive: &Node<DirectiveDefinition>) -> bool {
788 directive
789 .locations
790 .iter()
791 .any(|loc| EXECUTABLE_DIRECTIVE_LOCATIONS.contains(loc))
792}
793
794const FEDERATION_TYPES: [&str; 4] = ["_Any", "_Entity", "_Service", "@key"];
797fn is_mergeable_type(type_name: &str) -> bool {
798 if type_name.starts_with("federation__") || type_name.starts_with("link__") {
799 return false;
800 }
801 !FEDERATION_TYPES.contains(&type_name)
802}
803
804fn copy_scalar_type(scalar_name: Name, scalar_type: &Node<ScalarType>) -> ExtendedType {
805 ExtendedType::Scalar(Node::new(ScalarType {
806 description: scalar_type.description.clone(),
807 name: scalar_name,
808 directives: Default::default(),
809 }))
810}
811
812fn copy_enum_type(enum_name: Name, enum_type: &Node<EnumType>) -> ExtendedType {
813 ExtendedType::Enum(Node::new(EnumType {
814 description: enum_type.description.clone(),
815 name: enum_name,
816 directives: Default::default(),
817 values: IndexMap::default(),
818 }))
819}
820
821fn copy_input_object_type(
822 input_object_name: Name,
823 input_object: &Node<InputObjectType>,
824) -> ExtendedType {
825 let mut new_input_object = InputObjectType {
826 description: input_object.description.clone(),
827 name: input_object_name,
828 directives: Default::default(),
829 fields: IndexMap::default(),
830 };
831
832 for (field_name, input_field) in input_object.fields.iter() {
833 new_input_object.fields.insert(
834 field_name.clone(),
835 Component::new(InputValueDefinition {
836 name: input_field.name.clone(),
837 description: input_field.description.clone(),
838 directives: Default::default(),
839 ty: input_field.ty.clone(),
840 default_value: input_field.default_value.clone(),
841 }),
842 );
843 }
844
845 ExtendedType::InputObject(Node::new(new_input_object))
846}
847
848fn copy_interface_type(interface_name: Name, interface: &Node<InterfaceType>) -> ExtendedType {
849 let new_interface = InterfaceType {
850 description: interface.description.clone(),
851 name: interface_name,
852 directives: Default::default(),
853 fields: copy_fields(interface.fields.iter()),
854 implements_interfaces: interface.implements_interfaces.clone(),
855 };
856 ExtendedType::Interface(Node::new(new_interface))
857}
858
859fn copy_object_type_stub(
860 object_name: Name,
861 object: &Node<ObjectType>,
862 is_interface_object: bool,
863) -> ExtendedType {
864 if is_interface_object {
865 let new_interface = InterfaceType {
866 description: object.description.clone(),
867 name: object_name,
868 directives: Default::default(),
869 fields: copy_fields(object.fields.iter()),
870 implements_interfaces: object.implements_interfaces.clone(),
871 };
872 ExtendedType::Interface(Node::new(new_interface))
873 } else {
874 let new_object = ObjectType {
875 description: object.description.clone(),
876 name: object_name,
877 directives: Default::default(),
878 fields: copy_fields(object.fields.iter()),
879 implements_interfaces: object.implements_interfaces.clone(),
880 };
881 ExtendedType::Object(Node::new(new_object))
882 }
883}
884
885fn copy_fields(
886 fields_to_copy: Iter<Name, Component<FieldDefinition>>,
887) -> IndexMap<Name, Component<FieldDefinition>> {
888 let mut new_fields: IndexMap<Name, Component<FieldDefinition>> = IndexMap::default();
889 for (field_name, field) in fields_to_copy {
890 if field_name == "_service" || field_name == "_entities" {
892 continue;
893 }
894 let args: Vec<Node<InputValueDefinition>> = field
895 .arguments
896 .iter()
897 .map(|a| {
898 Node::new(InputValueDefinition {
899 name: a.name.clone(),
900 description: a.description.clone(),
901 directives: Default::default(),
902 ty: a.ty.clone(),
903 default_value: a.default_value.clone(),
904 })
905 })
906 .collect();
907 let new_field = Component::new(FieldDefinition {
908 name: field.name.clone(),
909 description: field.description.clone(),
910 directives: Default::default(),
911 arguments: args,
912 ty: field.ty.clone(),
913 });
914
915 new_fields.insert(field_name.clone(), new_field);
916 }
917 new_fields
918}
919
920fn copy_union_type(union_name: Name, description: Option<Node<str>>) -> ExtendedType {
921 ExtendedType::Union(Node::new(UnionType {
922 description,
923 name: union_name,
924 directives: Default::default(),
925 members: IndexSet::default(),
926 }))
927}
928
929fn join_type_applied_directive<'a>(
930 subgraph_name: Name,
931 key_directives: impl Iterator<Item = &'a Component<Directive>> + Sized,
932 is_interface_object: bool,
933) -> Vec<Component<Directive>> {
934 let mut join_type_directive = Directive {
935 name: name!("join__type"),
936 arguments: vec![Node::new(Argument {
937 name: name!("graph"),
938 value: Node::new(Value::Enum(subgraph_name)),
939 })],
940 };
941 if is_interface_object {
942 join_type_directive.arguments.push(Node::new(Argument {
943 name: name!("isInterfaceObject"),
944 value: Node::new(Value::Boolean(is_interface_object)),
945 }));
946 }
947
948 let mut result = vec![];
949 for key_directive in key_directives {
950 let mut join_type_directive_with_key = join_type_directive.clone();
951 let field_set = directive_string_arg_value(key_directive, &name!("fields")).unwrap();
952 join_type_directive_with_key
953 .arguments
954 .push(Node::new(Argument {
955 name: name!("key"),
956 value: field_set.into(),
957 }));
958
959 let resolvable =
960 directive_bool_arg_value(key_directive, &name!("resolvable")).unwrap_or(&true);
961 if !resolvable {
962 join_type_directive_with_key
963 .arguments
964 .push(Node::new(Argument {
965 name: name!("resolvable"),
966 value: Node::new(Value::Boolean(false)),
967 }));
968 }
969 result.push(join_type_directive_with_key)
970 }
971 if result.is_empty() {
972 result.push(join_type_directive)
973 }
974 result
975 .into_iter()
976 .map(Component::new)
977 .collect::<Vec<Component<Directive>>>()
978}
979
980fn join_implements_applied_directive(
981 subgraph_name: Name,
982 intf_name: &Name,
983) -> Component<Directive> {
984 Component::new(Directive {
985 name: name!("join__implements"),
986 arguments: vec![
987 Node::new(Argument {
988 name: name!("graph"),
989 value: Node::new(Value::Enum(subgraph_name)),
990 }),
991 Node::new(Argument {
992 name: name!("interface"),
993 value: intf_name.as_str().into(),
994 }),
995 ],
996 })
997}
998
999fn directive_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a Value> {
1000 directive
1001 .arguments
1002 .iter()
1003 .find(|arg| arg.name == *arg_name)
1004 .map(|arg| arg.value.as_ref())
1005}
1006
1007fn directive_string_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a str> {
1008 match directive_arg_value(directive, arg_name) {
1009 Some(Value::String(value)) => Some(value),
1010 _ => None,
1011 }
1012}
1013
1014fn directive_bool_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a bool> {
1015 match directive_arg_value(directive, arg_name) {
1016 Some(Value::Boolean(value)) => Some(value),
1017 _ => None,
1018 }
1019}
1020
1021fn add_core_feature_link(supergraph: &mut Schema) {
1023 supergraph
1025 .schema_definition
1026 .make_mut()
1027 .directives
1028 .push(Component::new(Directive {
1029 name: name!("link"),
1030 arguments: vec![Node::new(Argument {
1031 name: name!("url"),
1032 value: Node::new("https://specs.apollo.dev/link/v1.0".into()),
1033 })],
1034 }));
1035
1036 let (name, link_purpose_enum) = link_purpose_enum_type();
1037 supergraph.types.insert(name, link_purpose_enum.into());
1038
1039 let link_import_name = name!("link__Import");
1041 let link_import_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1042 directives: Default::default(),
1043 name: link_import_name.clone(),
1044 description: None,
1045 }));
1046 supergraph
1047 .types
1048 .insert(link_import_name, link_import_scalar);
1049
1050 let link_directive_definition = link_directive_definition();
1051 supergraph
1052 .directive_definitions
1053 .insert(name!("link"), Node::new(link_directive_definition));
1054}
1055
1056fn link_directive_definition() -> DirectiveDefinition {
1058 DirectiveDefinition {
1059 name: name!("link"),
1060 description: None,
1061 arguments: vec![
1062 Node::new(InputValueDefinition {
1063 name: name!("url"),
1064 description: None,
1065 directives: Default::default(),
1066 ty: ty!(String).into(),
1067 default_value: None,
1068 }),
1069 Node::new(InputValueDefinition {
1070 name: name!("as"),
1071 description: None,
1072 directives: Default::default(),
1073 ty: ty!(String).into(),
1074 default_value: None,
1075 }),
1076 Node::new(InputValueDefinition {
1077 name: name!("for"),
1078 description: None,
1079 directives: Default::default(),
1080 ty: ty!(link__Purpose).into(),
1081 default_value: None,
1082 }),
1083 Node::new(InputValueDefinition {
1084 name: name!("import"),
1085 description: None,
1086 directives: Default::default(),
1087 ty: ty!([link__Import]).into(),
1088 default_value: None,
1089 }),
1090 ],
1091 locations: vec![DirectiveLocation::Schema],
1092 repeatable: true,
1093 }
1094}
1095
1096fn link_purpose_enum_type() -> (Name, EnumType) {
1108 let link_purpose_name = name!("link__Purpose");
1109 let mut link_purpose_enum = EnumType {
1110 description: None,
1111 name: link_purpose_name.clone(),
1112 directives: Default::default(),
1113 values: IndexMap::default(),
1114 };
1115 let link_purpose_security_value = EnumValueDefinition {
1116 description: Some(
1117 r"SECURITY features provide metadata necessary to securely resolve fields.".into(),
1118 ),
1119 directives: Default::default(),
1120 value: name!("SECURITY"),
1121 };
1122 let link_purpose_execution_value = EnumValueDefinition {
1123 description: Some(
1124 r"EXECUTION features provide metadata necessary for operation execution.".into(),
1125 ),
1126 directives: Default::default(),
1127 value: name!("EXECUTION"),
1128 };
1129 link_purpose_enum.values.insert(
1130 link_purpose_security_value.value.clone(),
1131 Component::new(link_purpose_security_value),
1132 );
1133 link_purpose_enum.values.insert(
1134 link_purpose_execution_value.value.clone(),
1135 Component::new(link_purpose_execution_value),
1136 );
1137 (link_purpose_name, link_purpose_enum)
1138}
1139
1140fn add_core_feature_join(
1142 supergraph: &mut Schema,
1143 subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, Name)>,
1144) {
1145 supergraph
1147 .schema_definition
1148 .make_mut()
1149 .directives
1150 .push(Component::new(Directive {
1151 name: name!("link"),
1152 arguments: vec![
1153 Node::new(Argument {
1154 name: name!("url"),
1155 value: "https://specs.apollo.dev/join/v0.3".into(),
1156 }),
1157 Node::new(Argument {
1158 name: name!("for"),
1159 value: Node::new(Value::Enum(name!("EXECUTION"))),
1160 }),
1161 ],
1162 }));
1163
1164 let join_field_set_name = name!("join__FieldSet");
1166 let join_field_set_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1167 directives: Default::default(),
1168 name: join_field_set_name.clone(),
1169 description: None,
1170 }));
1171 supergraph
1172 .types
1173 .insert(join_field_set_name, join_field_set_scalar);
1174
1175 let join_graph_directive_definition = join_graph_directive_definition();
1176 supergraph.directive_definitions.insert(
1177 join_graph_directive_definition.name.clone(),
1178 Node::new(join_graph_directive_definition),
1179 );
1180
1181 let join_type_directive_definition = join_type_directive_definition();
1182 supergraph.directive_definitions.insert(
1183 join_type_directive_definition.name.clone(),
1184 Node::new(join_type_directive_definition),
1185 );
1186
1187 let join_field_directive_definition = join_field_directive_definition();
1188 supergraph.directive_definitions.insert(
1189 join_field_directive_definition.name.clone(),
1190 Node::new(join_field_directive_definition),
1191 );
1192
1193 let join_implements_directive_definition = join_implements_directive_definition();
1194 supergraph.directive_definitions.insert(
1195 join_implements_directive_definition.name.clone(),
1196 Node::new(join_implements_directive_definition),
1197 );
1198
1199 let join_union_member_directive_definition = join_union_member_directive_definition();
1200 supergraph.directive_definitions.insert(
1201 join_union_member_directive_definition.name.clone(),
1202 Node::new(join_union_member_directive_definition),
1203 );
1204
1205 let join_enum_value_directive_definition = join_enum_value_directive_definition();
1206 supergraph.directive_definitions.insert(
1207 join_enum_value_directive_definition.name.clone(),
1208 Node::new(join_enum_value_directive_definition),
1209 );
1210
1211 let (name, join_graph_enum_type) = join_graph_enum_type(subgraphs_and_enum_values);
1212 supergraph.types.insert(name, join_graph_enum_type.into());
1213}
1214
1215fn join_enum_value_directive_definition() -> DirectiveDefinition {
1217 DirectiveDefinition {
1218 name: name!("join__enumValue"),
1219 description: None,
1220 arguments: vec![Node::new(InputValueDefinition {
1221 name: name!("graph"),
1222 description: None,
1223 directives: Default::default(),
1224 ty: ty!(join__Graph!).into(),
1225 default_value: None,
1226 })],
1227 locations: vec![DirectiveLocation::EnumValue],
1228 repeatable: true,
1229 }
1230}
1231
1232fn join_field_directive_definition() -> DirectiveDefinition {
1242 DirectiveDefinition {
1243 name: name!("join__field"),
1244 description: None,
1245 arguments: vec![
1246 Node::new(InputValueDefinition {
1247 name: name!("graph"),
1248 description: None,
1249 directives: Default::default(),
1250 ty: ty!(join__Graph).into(),
1251 default_value: None,
1252 }),
1253 Node::new(InputValueDefinition {
1254 name: name!("requires"),
1255 description: None,
1256 directives: Default::default(),
1257 ty: ty!(join__FieldSet).into(),
1258 default_value: None,
1259 }),
1260 Node::new(InputValueDefinition {
1261 name: name!("provides"),
1262 description: None,
1263 directives: Default::default(),
1264 ty: ty!(join__FieldSet).into(),
1265 default_value: None,
1266 }),
1267 Node::new(InputValueDefinition {
1268 name: name!("type"),
1269 description: None,
1270 directives: Default::default(),
1271 ty: ty!(String).into(),
1272 default_value: None,
1273 }),
1274 Node::new(InputValueDefinition {
1275 name: name!("external"),
1276 description: None,
1277 directives: Default::default(),
1278 ty: ty!(Boolean).into(),
1279 default_value: None,
1280 }),
1281 Node::new(InputValueDefinition {
1282 name: name!("override"),
1283 description: None,
1284 directives: Default::default(),
1285 ty: ty!(String).into(),
1286 default_value: None,
1287 }),
1288 Node::new(InputValueDefinition {
1289 name: JOIN_OVERRIDE_LABEL_ARGUMENT_NAME,
1290 description: None,
1291 directives: Default::default(),
1292 ty: ty!(String).into(),
1293 default_value: None,
1294 }),
1295 Node::new(InputValueDefinition {
1296 name: name!("usedOverridden"),
1297 description: None,
1298 directives: Default::default(),
1299 ty: ty!(Boolean).into(),
1300 default_value: None,
1301 }),
1302 ],
1303 locations: vec![
1304 DirectiveLocation::FieldDefinition,
1305 DirectiveLocation::InputFieldDefinition,
1306 ],
1307 repeatable: true,
1308 }
1309}
1310
1311fn join_field_applied_directive(
1312 subgraph_name: Name,
1313 requires: Option<&str>,
1314 provides: Option<&str>,
1315 external: bool,
1316 overrides: Option<(&str, Option<&str>)>, ) -> Directive {
1318 let mut join_field_directive = Directive {
1319 name: name!("join__field"),
1320 arguments: vec![Node::new(Argument {
1321 name: name!("graph"),
1322 value: Node::new(Value::Enum(subgraph_name)),
1323 })],
1324 };
1325 if let Some(required_fields) = requires {
1326 join_field_directive.arguments.push(Node::new(Argument {
1327 name: name!("requires"),
1328 value: required_fields.into(),
1329 }));
1330 }
1331 if let Some(provided_fields) = provides {
1332 join_field_directive.arguments.push(Node::new(Argument {
1333 name: name!("provides"),
1334 value: provided_fields.into(),
1335 }));
1336 }
1337 if external {
1338 join_field_directive.arguments.push(Node::new(Argument {
1339 name: name!("external"),
1340 value: external.into(),
1341 }));
1342 }
1343 if let Some((from, label)) = overrides {
1344 join_field_directive.arguments.push(Node::new(Argument {
1345 name: name!("override"),
1346 value: Node::new(Value::String(from.to_string())),
1347 }));
1348 if let Some(label) = label {
1349 join_field_directive.arguments.push(Node::new(Argument {
1350 name: name!("overrideLabel"),
1351 value: Node::new(Value::String(label.to_string())),
1352 }));
1353 }
1354 }
1355 join_field_directive
1356}
1357
1358fn join_graph_directive_definition() -> DirectiveDefinition {
1360 DirectiveDefinition {
1361 name: name!("join__graph"),
1362 description: None,
1363 arguments: vec![
1364 Node::new(InputValueDefinition {
1365 name: name!("name"),
1366 description: None,
1367 directives: Default::default(),
1368 ty: ty!(String!).into(),
1369 default_value: None,
1370 }),
1371 Node::new(InputValueDefinition {
1372 name: name!("url"),
1373 description: None,
1374 directives: Default::default(),
1375 ty: ty!(String!).into(),
1376 default_value: None,
1377 }),
1378 ],
1379 locations: vec![DirectiveLocation::EnumValue],
1380 repeatable: false,
1381 }
1382}
1383
1384fn join_implements_directive_definition() -> DirectiveDefinition {
1389 DirectiveDefinition {
1390 name: name!("join__implements"),
1391 description: None,
1392 arguments: vec![
1393 Node::new(InputValueDefinition {
1394 name: name!("graph"),
1395 description: None,
1396 directives: Default::default(),
1397 ty: ty!(join__Graph!).into(),
1398 default_value: None,
1399 }),
1400 Node::new(InputValueDefinition {
1401 name: name!("interface"),
1402 description: None,
1403 directives: Default::default(),
1404 ty: ty!(String!).into(),
1405 default_value: None,
1406 }),
1407 ],
1408 locations: vec![DirectiveLocation::Interface, DirectiveLocation::Object],
1409 repeatable: true,
1410 }
1411}
1412
1413fn join_type_directive_definition() -> DirectiveDefinition {
1421 DirectiveDefinition {
1422 name: name!("join__type"),
1423 description: None,
1424 arguments: vec![
1425 Node::new(InputValueDefinition {
1426 name: name!("graph"),
1427 description: None,
1428 directives: Default::default(),
1429 ty: ty!(join__Graph!).into(),
1430 default_value: None,
1431 }),
1432 Node::new(InputValueDefinition {
1433 name: name!("key"),
1434 description: None,
1435 directives: Default::default(),
1436 ty: ty!(join__FieldSet).into(),
1437 default_value: None,
1438 }),
1439 Node::new(InputValueDefinition {
1440 name: name!("extension"),
1441 description: None,
1442 directives: Default::default(),
1443 ty: ty!(Boolean!).into(),
1444 default_value: Some(Node::new(Value::Boolean(false))),
1445 }),
1446 Node::new(InputValueDefinition {
1447 name: name!("resolvable"),
1448 description: None,
1449 directives: Default::default(),
1450 ty: ty!(Boolean!).into(),
1451 default_value: Some(Node::new(Value::Boolean(true))),
1452 }),
1453 Node::new(InputValueDefinition {
1454 name: name!("isInterfaceObject"),
1455 description: None,
1456 directives: Default::default(),
1457 ty: ty!(Boolean!).into(),
1458 default_value: Some(Node::new(Value::Boolean(false))),
1459 }),
1460 ],
1461 locations: vec![
1462 DirectiveLocation::Enum,
1463 DirectiveLocation::InputObject,
1464 DirectiveLocation::Interface,
1465 DirectiveLocation::Object,
1466 DirectiveLocation::Scalar,
1467 DirectiveLocation::Union,
1468 ],
1469 repeatable: true,
1470 }
1471}
1472
1473fn join_union_member_directive_definition() -> DirectiveDefinition {
1475 DirectiveDefinition {
1476 name: name!("join__unionMember"),
1477 description: None,
1478 arguments: vec![
1479 Node::new(InputValueDefinition {
1480 name: name!("graph"),
1481 description: None,
1482 directives: Default::default(),
1483 ty: ty!(join__Graph!).into(),
1484 default_value: None,
1485 }),
1486 Node::new(InputValueDefinition {
1487 name: name!("member"),
1488 description: None,
1489 directives: Default::default(),
1490 ty: ty!(String!).into(),
1491 default_value: None,
1492 }),
1493 ],
1494 locations: vec![DirectiveLocation::Union],
1495 repeatable: true,
1496 }
1497}
1498
1499fn join_graph_enum_type(
1501 subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, Name)>,
1502) -> (Name, EnumType) {
1503 let join_graph_enum_name = name!("join__Graph");
1504 let mut join_graph_enum_type = EnumType {
1505 description: None,
1506 name: join_graph_enum_name.clone(),
1507 directives: Default::default(),
1508 values: IndexMap::default(),
1509 };
1510 for (s, subgraph_name) in subgraphs_and_enum_values {
1511 let join_graph_applied_directive = Directive {
1512 name: name!("join__graph"),
1513 arguments: vec![
1514 (Node::new(Argument {
1515 name: name!("name"),
1516 value: s.name.as_str().into(),
1517 })),
1518 (Node::new(Argument {
1519 name: name!("url"),
1520 value: s.url.as_str().into(),
1521 })),
1522 ],
1523 };
1524 let graph = EnumValueDefinition {
1525 description: None,
1526 directives: DirectiveList(vec![Node::new(join_graph_applied_directive)]),
1527 value: subgraph_name.clone(),
1528 };
1529 join_graph_enum_type
1530 .values
1531 .insert(graph.value.clone(), Component::new(graph));
1532 }
1533 (join_graph_enum_name, join_graph_enum_type)
1534}
1535
1536fn add_core_feature_inaccessible(supergraph: &mut Schema) {
1537 let spec = InaccessibleSpecDefinition::new(Version { major: 0, minor: 2 });
1539
1540 supergraph
1541 .schema_definition
1542 .make_mut()
1543 .directives
1544 .push(Component::new(Directive {
1545 name: name!("link"),
1546 arguments: vec![
1547 Node::new(Argument {
1548 name: name!("url"),
1549 value: spec.to_string().into(),
1550 }),
1551 Node::new(Argument {
1552 name: name!("for"),
1553 value: Node::new(Value::Enum(name!("SECURITY"))),
1554 }),
1555 ],
1556 }));
1557
1558 supergraph.directive_definitions.insert(
1559 INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
1560 Node::new(DirectiveDefinition {
1561 name: INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
1562 description: None,
1563 arguments: vec![],
1564 locations: vec![
1565 DirectiveLocation::FieldDefinition,
1566 DirectiveLocation::Object,
1567 DirectiveLocation::Interface,
1568 DirectiveLocation::Union,
1569 DirectiveLocation::ArgumentDefinition,
1570 DirectiveLocation::Scalar,
1571 DirectiveLocation::Enum,
1572 DirectiveLocation::EnumValue,
1573 DirectiveLocation::InputObject,
1574 DirectiveLocation::InputFieldDefinition,
1575 ],
1576 repeatable: false,
1577 }),
1578 );
1579}
1580
1581fn merge_directive(
1582 supergraph_directives: &mut IndexMap<Name, Node<DirectiveDefinition>>,
1583 directive: &Node<DirectiveDefinition>,
1584) {
1585 if !supergraph_directives.contains_key(&directive.name.clone()) {
1586 supergraph_directives.insert(directive.name.clone(), directive.clone());
1587 }
1588}
1589
1590#[cfg(test)]
1591mod tests {
1592 use apollo_compiler::Schema;
1593 use insta::assert_snapshot;
1594
1595 use crate::ValidFederationSubgraph;
1596 use crate::ValidFederationSubgraphs;
1597 use crate::merge::merge_federation_subgraphs;
1598 use crate::schema::ValidFederationSchema;
1599
1600 #[test]
1601 fn test_steel_thread() {
1602 let one_sdl =
1603 include_str!("./sources/connect/expand/merge/connector_Query_users_0.graphql");
1604 let two_sdl = include_str!("./sources/connect/expand/merge/connector_Query_user_0.graphql");
1605 let three_sdl = include_str!("./sources/connect/expand/merge/connector_User_d_1.graphql");
1606 let graphql_sdl = include_str!("./sources/connect/expand/merge/graphql.graphql");
1607
1608 let mut subgraphs = ValidFederationSubgraphs::new();
1609 subgraphs
1610 .add(ValidFederationSubgraph {
1611 name: "connector_Query_users_0".to_string(),
1612 url: "".to_string(),
1613 schema: ValidFederationSchema::new(
1614 Schema::parse_and_validate(one_sdl, "./connector_Query_users_0.graphql")
1615 .unwrap(),
1616 )
1617 .unwrap(),
1618 })
1619 .unwrap();
1620 subgraphs
1621 .add(ValidFederationSubgraph {
1622 name: "connector_Query_user_0".to_string(),
1623 url: "".to_string(),
1624 schema: ValidFederationSchema::new(
1625 Schema::parse_and_validate(two_sdl, "./connector_Query_user_0.graphql")
1626 .unwrap(),
1627 )
1628 .unwrap(),
1629 })
1630 .unwrap();
1631 subgraphs
1632 .add(ValidFederationSubgraph {
1633 name: "connector_User_d_1".to_string(),
1634 url: "".to_string(),
1635 schema: ValidFederationSchema::new(
1636 Schema::parse_and_validate(three_sdl, "./connector_User_d_1.graphql").unwrap(),
1637 )
1638 .unwrap(),
1639 })
1640 .unwrap();
1641 subgraphs
1642 .add(ValidFederationSubgraph {
1643 name: "graphql".to_string(),
1644 url: "".to_string(),
1645 schema: ValidFederationSchema::new(
1646 Schema::parse_and_validate(graphql_sdl, "./graphql.graphql").unwrap(),
1647 )
1648 .unwrap(),
1649 })
1650 .unwrap();
1651
1652 let result = merge_federation_subgraphs(subgraphs).unwrap();
1653
1654 let schema = result.schema.into_inner();
1655 let validation = schema.clone().validate();
1656 assert!(validation.is_ok(), "{:?}", validation);
1657
1658 assert_snapshot!(schema.serialize());
1659 }
1660
1661 #[test]
1662 fn test_basic() {
1663 let one_sdl = include_str!("./sources/connect/expand/merge/basic_1.graphql");
1664 let two_sdl = include_str!("./sources/connect/expand/merge/basic_2.graphql");
1665
1666 let mut subgraphs = ValidFederationSubgraphs::new();
1667 subgraphs
1668 .add(ValidFederationSubgraph {
1669 name: "basic_1".to_string(),
1670 url: "".to_string(),
1671 schema: ValidFederationSchema::new(
1672 Schema::parse_and_validate(one_sdl, "./basic_1.graphql").unwrap(),
1673 )
1674 .unwrap(),
1675 })
1676 .unwrap();
1677 subgraphs
1678 .add(ValidFederationSubgraph {
1679 name: "basic_2".to_string(),
1680 url: "".to_string(),
1681 schema: ValidFederationSchema::new(
1682 Schema::parse_and_validate(two_sdl, "./basic_2.graphql").unwrap(),
1683 )
1684 .unwrap(),
1685 })
1686 .unwrap();
1687
1688 let result = merge_federation_subgraphs(subgraphs).unwrap();
1689
1690 let schema = result.schema.into_inner();
1691 let validation = schema.clone().validate();
1692 assert!(validation.is_ok(), "{:?}", validation);
1693
1694 assert_snapshot!(schema.serialize());
1695 }
1696
1697 #[test]
1698 fn test_inaccessible() {
1699 let one_sdl = include_str!("./sources/connect/expand/merge/inaccessible.graphql");
1700 let two_sdl = include_str!("./sources/connect/expand/merge/inaccessible_2.graphql");
1701
1702 let mut subgraphs = ValidFederationSubgraphs::new();
1703 subgraphs
1704 .add(ValidFederationSubgraph {
1705 name: "inaccessible".to_string(),
1706 url: "".to_string(),
1707 schema: ValidFederationSchema::new(
1708 Schema::parse_and_validate(one_sdl, "./inaccessible.graphql").unwrap(),
1709 )
1710 .unwrap(),
1711 })
1712 .unwrap();
1713 subgraphs
1714 .add(ValidFederationSubgraph {
1715 name: "inaccessible_2".to_string(),
1716 url: "".to_string(),
1717 schema: ValidFederationSchema::new(
1718 Schema::parse_and_validate(two_sdl, "./inaccessible_2.graphql").unwrap(),
1719 )
1720 .unwrap(),
1721 })
1722 .unwrap();
1723
1724 let result = merge_federation_subgraphs(subgraphs).unwrap();
1725
1726 let schema = result.schema.into_inner();
1727 let validation = schema.clone().validate();
1728 assert!(validation.is_ok(), "{:?}", validation);
1729
1730 assert_snapshot!(schema.serialize());
1731 }
1732}