1use apollo_compiler::ast::Directives;
2use apollo_compiler::ast::{
3 Argument, Directive, DirectiveDefinition, DirectiveLocation, EnumValueDefinition,
4 FieldDefinition, NamedType, Type, Value,
5};
6use apollo_compiler::schema::{
7 Component, EnumType, ExtendedType, InputObjectType, InputValueDefinition, InterfaceType, Name,
8 ObjectType, ScalarType, UnionType,
9};
10use apollo_compiler::{Node, NodeStr, Schema};
11use apollo_subgraph::Subgraph;
12use indexmap::map::Entry::{Occupied, Vacant};
13use indexmap::map::Iter;
14use indexmap::{IndexMap, IndexSet};
15use std::collections::HashSet;
16use std::iter;
17
18type MergeWarning = &'static str;
19type MergeError = &'static str;
20
21struct Merger {
22 errors: Vec<MergeError>,
23 composition_hints: Vec<MergeWarning>,
24}
25
26pub struct MergeSuccess {
27 pub schema: Schema,
28 pub composition_hints: Vec<MergeWarning>,
29}
30
31pub struct MergeFailure {
32 pub schema: Option<Schema>,
33 pub errors: Vec<MergeError>,
34 pub composition_hints: Vec<MergeWarning>,
35}
36
37pub fn merge_subgraphs(subgraphs: Vec<&Subgraph>) -> Result<MergeSuccess, MergeFailure> {
38 let mut merger = Merger::new();
39 merger.merge(subgraphs)
40}
41
42impl Merger {
43 fn new() -> Self {
44 Merger {
45 composition_hints: Vec::new(),
46 errors: Vec::new(),
47 }
48 }
49 fn merge(&mut self, subgraphs: Vec<&Subgraph>) -> Result<MergeSuccess, MergeFailure> {
50 let mut subgraphs = subgraphs.clone();
51 subgraphs.sort_by(|s1, s2| s1.name.cmp(&s2.name));
52
53 let mut supergraph = Schema::new();
54 add_core_feature_link(&mut supergraph);
59 add_core_feature_join(&mut supergraph, &subgraphs);
60
61 for subgraph in &subgraphs {
63 let subgraph_name = subgraph.name.to_uppercase().clone();
64 self.merge_schema(&mut supergraph, subgraph);
65 for (key, value) in &subgraph.schema.types {
68 if value.is_built_in() || !is_mergeable_type(key) {
69 continue;
71 }
72
73 match value {
74 ExtendedType::Enum(value) => self.merge_enum_type(
75 &mut supergraph.types,
76 &subgraph_name,
77 key.clone(),
78 value,
79 ),
80 ExtendedType::InputObject(value) => self.merge_input_object_type(
81 &mut supergraph.types,
82 &subgraph_name,
83 key.clone(),
84 value,
85 ),
86 ExtendedType::Interface(value) => self.merge_interface_type(
87 &mut supergraph.types,
88 &subgraph_name,
89 key.clone(),
90 value,
91 ),
92 ExtendedType::Object(value) => self.merge_object_type(
93 &mut supergraph.types,
94 &subgraph_name,
95 key.clone(),
96 value,
97 ),
98 ExtendedType::Union(value) => self.merge_union_type(
99 &mut supergraph.types,
100 &subgraph_name,
101 key.clone(),
102 value,
103 ),
104 ExtendedType::Scalar(_value) => {
105 }
107 }
108 }
109
110 for (_, directive) in subgraph.schema.directive_definitions.iter() {
112 if is_executable_directive(directive) {
113 merge_directive(&mut supergraph.directive_definitions, directive);
114 }
115 }
116 }
117
118 if self.errors.is_empty() {
119 Ok(MergeSuccess {
120 schema: supergraph,
121 composition_hints: self.composition_hints.to_owned(),
122 })
123 } else {
124 Err(MergeFailure {
125 schema: Some(supergraph),
126 composition_hints: self.composition_hints.to_owned(),
127 errors: self.errors.to_owned(),
128 })
129 }
130 }
131
132 fn merge_descriptions<T: Eq + Clone>(&mut self, merged: &mut Option<T>, new: &Option<T>) {
133 match (&mut *merged, new) {
134 (_, None) => {}
135 (None, Some(_)) => *merged = new.clone(),
136 (Some(a), Some(b)) => {
137 if a != b {
138 self.composition_hints.push("conflicting descriptions");
140 }
141 }
142 }
143 }
144
145 fn merge_schema(&mut self, supergraph_schema: &mut Schema, subgraph: &Subgraph) {
146 let supergraph_def = &mut supergraph_schema.schema_definition.make_mut();
147 let subgraph_def = &subgraph.schema.schema_definition;
148 self.merge_descriptions(&mut supergraph_def.description, &subgraph_def.description);
149
150 if subgraph_def.query.is_some() {
151 supergraph_def.query = subgraph_def.query.clone();
152 }
154 if subgraph_def.mutation.is_some() {
155 supergraph_def.mutation = subgraph_def.mutation.clone();
156 }
158 if subgraph_def.subscription.is_some() {
159 supergraph_def.subscription = subgraph_def.subscription.clone();
160 }
162 }
163
164 fn merge_enum_type(
165 &mut self,
166 types: &mut IndexMap<NamedType, ExtendedType>,
167 subgraph_name: &str,
168 enum_name: NamedType,
169 enum_type: &Node<EnumType>,
170 ) {
171 let existing_type = types.entry(enum_name).or_insert(copy_enum_type(enum_type));
172 if let ExtendedType::Enum(e) = existing_type {
173 let join_type_directives =
174 join_type_applied_directive(subgraph_name, iter::empty(), false);
175 e.make_mut().directives.extend(join_type_directives);
176
177 self.merge_descriptions(&mut e.make_mut().description, &enum_type.description);
178
179 for (enum_value_name, enum_value) in enum_type.values.iter() {
182 let ev = e
183 .make_mut()
184 .values
185 .entry(enum_value_name.clone())
186 .or_insert(Component::new(EnumValueDefinition {
187 value: enum_value.value.clone(),
188 description: None,
189 directives: Default::default(),
190 }));
191 self.merge_descriptions(&mut ev.make_mut().description, &enum_value.description);
192 ev.make_mut().directives.push(Node::new(Directive {
193 name: Name::new("join__enumValue"),
194 arguments: vec![
195 (Node::new(Argument {
196 name: Name::new("graph"),
197 value: Node::new(Value::Enum(Name::new(subgraph_name))),
198 })),
199 ],
200 }));
201 }
202 } else {
203 }
205 }
206
207 fn merge_input_object_type(
208 &mut self,
209 types: &mut IndexMap<NamedType, ExtendedType>,
210 subgraph_name: &str,
211 input_object_name: NamedType,
212 input_object: &Node<InputObjectType>,
213 ) {
214 let existing_type = types
215 .entry(input_object_name)
216 .or_insert(copy_input_object_type(input_object));
217 if let ExtendedType::InputObject(obj) = existing_type {
218 let join_type_directives =
219 join_type_applied_directive(subgraph_name, iter::empty(), false);
220 let mutable_object = obj.make_mut();
221 mutable_object.directives.extend(join_type_directives);
222
223 for (field_name, _field) in input_object.fields.iter() {
224 let existing_field = mutable_object.fields.entry(field_name.clone());
225 match existing_field {
226 Vacant(_i) => {
227 }
229 Occupied(_i) => {
230 }
236 }
237 }
238 } else {
239 }
241 }
242
243 fn merge_interface_type(
244 &mut self,
245 types: &mut IndexMap<NamedType, ExtendedType>,
246 subgraph_name: &str,
247 interface_name: NamedType,
248 interface: &Node<InterfaceType>,
249 ) {
250 let existing_type = types
251 .entry(interface_name.clone())
252 .or_insert(copy_interface_type(interface));
253 if let ExtendedType::Interface(intf) = existing_type {
254 let key_directives = interface.directives.get_all("key");
255 let join_type_directives =
256 join_type_applied_directive(subgraph_name, key_directives, false);
257 let mutable_intf = intf.make_mut();
258 mutable_intf.directives.extend(join_type_directives);
259
260 for (field_name, field) in interface.fields.iter() {
261 let existing_field = mutable_intf.fields.entry(field_name.clone());
262 match existing_field {
263 Vacant(i) => {
264 i.insert(Component::new(FieldDefinition {
266 name: field.name.clone(),
267 description: field.description.clone(),
268 arguments: vec![],
269 ty: field.ty.clone(),
270 directives: Default::default(),
271 }));
272 }
273 Occupied(_i) => {
274 }
279 }
280 }
281 } else {
282 }
284 }
285
286 fn merge_object_type(
287 &mut self,
288 types: &mut IndexMap<NamedType, ExtendedType>,
289 subgraph_name: &str,
290 object_name: NamedType,
291 object: &Node<ObjectType>,
292 ) {
293 let is_interface_object = object.directives.has("interfaceObject");
294 let existing_type = types
295 .entry(object_name.clone())
296 .or_insert(copy_object_type_stub(object, is_interface_object));
297 if let ExtendedType::Object(obj) = existing_type {
298 let key_fields: HashSet<&str> = parse_keys(object.directives.get_all("key"));
299 let is_join_field = !key_fields.is_empty() || object_name.eq("Query");
300 let key_directives = object.directives.get_all("key");
301 let join_type_directives =
302 join_type_applied_directive(subgraph_name, key_directives, false);
303 let mutable_object = obj.make_mut();
304 mutable_object.directives.extend(join_type_directives);
305 self.merge_descriptions(&mut mutable_object.description, &object.description);
306 object.implements_interfaces.iter().for_each(|intf_name| {
307 mutable_object
309 .implements_interfaces
310 .insert(intf_name.clone());
311 let join_implements_directive = join_type_implements(subgraph_name, intf_name);
312 mutable_object.directives.push(join_implements_directive);
313 });
314
315 for (field_name, field) in object.fields.iter() {
316 if field_name.eq(&Name::new("_service")) || field_name.eq(&Name::new("_entities")) {
318 continue;
319 }
320
321 let existing_field = mutable_object.fields.entry(field_name.clone());
322 let supergraph_field = match existing_field {
323 Occupied(f) => {
324 f.into_mut()
328 }
329 Vacant(f) => f.insert(Component::new(FieldDefinition {
330 name: field.name.clone(),
331 description: field.description.clone(),
332 arguments: vec![],
333 directives: Default::default(),
334 ty: field.ty.clone(),
335 })),
336 };
337 self.merge_descriptions(
338 &mut supergraph_field.make_mut().description,
339 &field.description,
340 );
341 let mut existing_args = supergraph_field.arguments.iter();
342 for arg in field.arguments.iter() {
343 if let Some(_existing_arg) = &existing_args.find(|a| a.name.eq(&arg.name)) {
344 } else {
345 }
347 }
348
349 if is_join_field {
350 let is_key_field = key_fields.contains(field_name.as_str());
351 if !is_key_field {
352 let requires_directive_option =
353 Option::and_then(field.directives.get_all("requires").next(), |p| {
354 let requires_fields =
355 directive_string_arg_value(p, "fields").unwrap();
356 Some(requires_fields.as_str())
357 });
358 let provides_directive_option =
359 Option::and_then(field.directives.get_all("provides").next(), |p| {
360 let provides_fields =
361 directive_string_arg_value(p, "fields").unwrap();
362 Some(provides_fields.as_str())
363 });
364 let external_field = field.directives.get_all("external").next().is_some();
365 let join_field_directive = join_field_applied_directive(
366 subgraph_name,
367 requires_directive_option,
368 provides_directive_option,
369 external_field,
370 );
371
372 supergraph_field
373 .make_mut()
374 .directives
375 .push(Node::new(join_field_directive));
376 }
377 }
378 }
379 } else if let ExtendedType::Interface(intf) = existing_type {
380 let key_directives = object.directives.get_all("key");
382 let join_type_directives =
383 join_type_applied_directive(subgraph_name, key_directives, true);
384 intf.make_mut().directives.extend(join_type_directives);
385 };
386 }
388
389 fn merge_union_type(
390 &mut self,
391 types: &mut IndexMap<NamedType, ExtendedType>,
392 subgraph_name: &str,
393 union_name: NamedType,
394 union: &Node<UnionType>,
395 ) {
396 let existing_type = types
397 .entry(union_name.clone())
398 .or_insert(copy_union_type(&union_name, union.description.clone()));
399 if let ExtendedType::Union(u) = existing_type {
400 let join_type_directives =
401 join_type_applied_directive(subgraph_name, iter::empty(), false);
402 u.make_mut().directives.extend(join_type_directives);
403
404 for union_member in union.members.iter() {
405 u.make_mut().members.insert(union_member.clone());
407 u.make_mut().directives.push(Component::new(Directive {
408 name: Name::new("join__unionMember"),
409 arguments: vec![
410 Node::new(Argument {
411 name: Name::new("graph"),
412 value: Node::new(Value::Enum(Name::new(subgraph_name))),
413 }),
414 Node::new(Argument {
415 name: Name::new("member"),
416 value: Node::new(Value::String(Name::new(union_member))),
417 }),
418 ],
419 }));
420 }
421 }
422 }
423}
424
425const EXECUTABLE_DIRECTIVE_LOCATIONS: [DirectiveLocation; 8] = [
426 DirectiveLocation::Query,
427 DirectiveLocation::Mutation,
428 DirectiveLocation::Subscription,
429 DirectiveLocation::Field,
430 DirectiveLocation::FragmentDefinition,
431 DirectiveLocation::FragmentSpread,
432 DirectiveLocation::InlineFragment,
433 DirectiveLocation::VariableDefinition,
434];
435fn is_executable_directive(directive: &Node<DirectiveDefinition>) -> bool {
436 directive
437 .locations
438 .iter()
439 .any(|loc| EXECUTABLE_DIRECTIVE_LOCATIONS.contains(loc))
440}
441
442const FEDERATION_TYPES: [&str; 4] = ["_Any", "_Entity", "_Service", "@key"];
445fn is_mergeable_type(type_name: &str) -> bool {
446 if type_name.starts_with("federation__") || type_name.starts_with("link__") {
447 return false;
448 }
449 !FEDERATION_TYPES.contains(&type_name)
450}
451
452fn copy_enum_type(enum_type: &Node<EnumType>) -> ExtendedType {
453 ExtendedType::Enum(Node::new(EnumType {
454 description: enum_type.description.clone(),
455 directives: Default::default(),
456 values: IndexMap::new(),
457 }))
458}
459
460fn copy_input_object_type(input_object: &Node<InputObjectType>) -> ExtendedType {
461 let mut new_input_object = InputObjectType {
462 description: input_object.description.clone(),
463 directives: Default::default(),
464 fields: IndexMap::new(),
465 };
466
467 for (field_name, input_field) in input_object.fields.iter() {
468 new_input_object.fields.insert(
469 field_name.clone(),
470 Component::new(InputValueDefinition {
471 name: input_field.name.clone(),
472 description: input_field.description.clone(),
473 directives: Default::default(),
474 ty: input_field.ty.clone(),
475 default_value: input_field.default_value.clone(),
476 }),
477 );
478 }
479
480 ExtendedType::InputObject(Node::new(new_input_object))
481}
482
483fn copy_interface_type(interface: &Node<InterfaceType>) -> ExtendedType {
484 let new_interface = InterfaceType {
485 description: interface.description.clone(),
486 directives: Default::default(),
487 fields: copy_fields(interface.fields.iter()),
488 implements_interfaces: interface.implements_interfaces.clone(),
489 };
490 ExtendedType::Interface(Node::new(new_interface))
491}
492
493fn copy_object_type_stub(object: &Node<ObjectType>, is_interface_object: bool) -> ExtendedType {
494 if is_interface_object {
495 let new_interface = InterfaceType {
496 description: object.description.clone(),
497 directives: Default::default(),
498 fields: copy_fields(object.fields.iter()),
499 implements_interfaces: object.implements_interfaces.clone(),
500 };
501 ExtendedType::Interface(Node::new(new_interface))
502 } else {
503 let new_object = ObjectType {
504 description: object.description.clone(),
505 directives: Default::default(),
506 fields: copy_fields(object.fields.iter()),
507 implements_interfaces: object.implements_interfaces.clone(),
508 };
509 ExtendedType::Object(Node::new(new_object))
510 }
511}
512
513fn copy_fields(
514 fields_to_copy: Iter<Name, Component<FieldDefinition>>,
515) -> IndexMap<Name, Component<FieldDefinition>> {
516 let mut new_fields: IndexMap<Name, Component<FieldDefinition>> = IndexMap::new();
517 for (field_name, field) in fields_to_copy {
518 if field_name.eq(&Name::new("_service")) || field_name.eq(&Name::new("_entities")) {
520 continue;
521 }
522 let args: Vec<Node<InputValueDefinition>> = field
523 .arguments
524 .iter()
525 .map(|a| {
526 Node::new(InputValueDefinition {
527 name: a.name.clone(),
528 description: a.description.clone(),
529 directives: Default::default(),
530 ty: a.ty.clone(),
531 default_value: a.default_value.clone(),
532 })
533 })
534 .collect();
535 let new_field = Component::new(FieldDefinition {
536 name: field.name.clone(),
537 description: field.description.clone(),
538 directives: Default::default(),
539 arguments: args,
540 ty: field.ty.clone(),
541 });
542
543 new_fields.insert(field_name.clone(), new_field);
544 }
545 new_fields
546}
547
548fn copy_union_type(_name: &NamedType, description: Option<NodeStr>) -> ExtendedType {
549 ExtendedType::Union(Node::new(UnionType {
550 description,
551 directives: Default::default(),
552 members: IndexSet::new(),
553 }))
554}
555
556fn join_type_applied_directive<'a>(
557 subgraph_name: &str,
558 key_directives: impl Iterator<Item = &'a Component<Directive>> + Sized,
559 is_interface_object: bool,
560) -> Vec<Component<Directive>> {
561 let mut join_type_directive = Directive {
562 name: Name::new("join__type"),
563 arguments: vec![Node::new(Argument {
564 name: Name::new("graph"),
565 value: Node::new(Value::Enum(Name::new(subgraph_name))),
566 })],
567 };
568 if is_interface_object {
569 join_type_directive.arguments.push(Node::new(Argument {
570 name: Name::new("isInterfaceObject"),
571 value: Node::new(Value::Boolean(is_interface_object)),
572 }));
573 }
574
575 let mut result = vec![];
576 for key_directive in key_directives {
577 let mut join_type_directive_with_key = join_type_directive.clone();
578 let field_set = directive_string_arg_value(key_directive, "fields").unwrap();
579 join_type_directive_with_key
580 .arguments
581 .push(Node::new(Argument {
582 name: Name::new("key"),
583 value: Node::new(Value::String(NodeStr::new(field_set.as_str()))),
584 }));
585
586 let resolvable = directive_bool_arg_value(key_directive, "resolvable").unwrap_or(&true);
587 if !resolvable {
588 join_type_directive_with_key
589 .arguments
590 .push(Node::new(Argument {
591 name: Name::new("resolvable"),
592 value: Node::new(Value::Boolean(false)),
593 }));
594 }
595 result.push(join_type_directive_with_key)
596 }
597 if result.is_empty() {
598 result.push(join_type_directive)
599 }
600 result
601 .into_iter()
602 .map(Component::new)
603 .collect::<Vec<Component<Directive>>>()
604}
605
606fn join_type_implements(subgraph_name: &str, intf_name: &str) -> Component<Directive> {
607 Component::new(Directive {
608 name: Name::new("join__implements"),
609 arguments: vec![
610 Node::new(Argument {
611 name: Name::new("graph"),
612 value: Node::new(Value::String(NodeStr::new(subgraph_name))),
613 }),
614 Node::new(Argument {
615 name: Name::new("interface"),
616 value: Node::new(Value::String(NodeStr::new(intf_name))),
617 }),
618 ],
619 })
620}
621
622fn directive_arg_value<'a>(directive: &'a Directive, arg_name: &'static str) -> Option<&'a Value> {
623 directive
624 .arguments
625 .iter()
626 .find(|arg| arg.name == arg_name)
627 .map(|arg| arg.value.as_ref())
628}
629
630fn directive_string_arg_value<'a>(
631 directive: &'a Directive,
632 arg_name: &'static str,
633) -> Option<&'a NodeStr> {
634 match directive_arg_value(directive, arg_name) {
635 Some(Value::String(value)) => Some(value),
636 _ => None,
637 }
638}
639
640fn directive_bool_arg_value<'a>(
641 directive: &'a Directive,
642 arg_name: &'static str,
643) -> Option<&'a bool> {
644 match directive_arg_value(directive, arg_name) {
645 Some(Value::Boolean(value)) => Some(value),
646 _ => None,
647 }
648}
649
650fn add_core_feature_link(supergraph: &mut Schema) {
652 supergraph
654 .schema_definition
655 .make_mut()
656 .directives
657 .push(Component::new(Directive {
658 name: Name::new("link"),
659 arguments: vec![Node::new(Argument {
660 name: Name::new("url"),
661 value: Node::new(Value::String(NodeStr::new(
662 "https://specs.apollo.dev/link/v1.0",
663 ))),
664 })],
665 }));
666
667 let (name, link_purpose_enum) = link_purpose_enum_type();
668 supergraph.types.insert(name, link_purpose_enum.into());
669
670 let link_import_scalar = ExtendedType::Scalar(Node::new(ScalarType {
672 directives: Default::default(),
673 description: None,
674 }));
675 supergraph
676 .types
677 .insert("link__Import".into(), link_import_scalar);
678
679 let link_directive_definition = link_directive_definition();
680 supergraph
681 .directive_definitions
682 .insert(NamedType::new("link"), Node::new(link_directive_definition));
683}
684
685fn link_directive_definition() -> DirectiveDefinition {
687 DirectiveDefinition {
688 name: Name::new("link"),
689 description: None,
690 arguments: vec![
691 Node::new(InputValueDefinition {
692 name: Name::new("url"),
693 description: None,
694 directives: Default::default(),
695 ty: Type::new_named("String").into(),
696 default_value: None,
697 }),
698 Node::new(InputValueDefinition {
699 name: Name::new("as"),
700 description: None,
701 directives: Default::default(),
702 ty: Type::new_named("String").into(),
703 default_value: None,
704 }),
705 Node::new(InputValueDefinition {
706 name: Name::new("for"),
707 description: None,
708 directives: Default::default(),
709 ty: Type::new_named("link__Purpose").into(),
710 default_value: None,
711 }),
712 Node::new(InputValueDefinition {
713 name: Name::new("import"),
714 description: None,
715 directives: Default::default(),
716 ty: Type::new_named("link__Import").list().into(),
717 default_value: None,
718 }),
719 ],
720 locations: vec![DirectiveLocation::Schema],
721 repeatable: true,
722 }
723}
724
725fn link_purpose_enum_type() -> (Name, EnumType) {
737 let mut link_purpose_enum = EnumType {
738 description: None,
739 directives: Default::default(),
740 values: IndexMap::new(),
741 };
742 let link_purpose_security_value = EnumValueDefinition {
743 description: Some(NodeStr::new(
744 r"SECURITY features provide metadata necessary to securely resolve fields.",
745 )),
746 directives: Default::default(),
747 value: Name::new("SECURITY"),
748 };
749 let link_purpose_execution_value = EnumValueDefinition {
750 description: Some(NodeStr::new(
751 r"EXECUTION features provide metadata necessary for operation execution.",
752 )),
753 directives: Default::default(),
754 value: Name::new("EXECUTION"),
755 };
756 link_purpose_enum.values.insert(
757 link_purpose_security_value.value.clone(),
758 Component::new(link_purpose_security_value),
759 );
760 link_purpose_enum.values.insert(
761 link_purpose_execution_value.value.clone(),
762 Component::new(link_purpose_execution_value),
763 );
764 (Name::new("link__Purpose"), link_purpose_enum)
765}
766
767fn add_core_feature_join(supergraph: &mut Schema, subgraphs: &Vec<&Subgraph>) {
769 supergraph
771 .schema_definition
772 .make_mut()
773 .directives
774 .push(Component::new(Directive {
775 name: Name::new("link"),
776 arguments: vec![
777 Node::new(Argument {
778 name: Name::new("url"),
779 value: Node::new(Value::String(NodeStr::new(
780 "https://specs.apollo.dev/join/v0.3",
781 ))),
782 }),
783 Node::new(Argument {
784 name: Name::new("for"),
785 value: Node::new(Value::Enum(NodeStr::new("EXECUTION"))),
786 }),
787 ],
788 }));
789
790 let join_field_set_scalar = ExtendedType::Scalar(Node::new(ScalarType {
792 directives: Default::default(),
793 description: None,
794 }));
795 supergraph
796 .types
797 .insert("join__FieldSet".into(), join_field_set_scalar);
798
799 let join_graph_directive_definition = join_graph_directive_definition();
800 supergraph.directive_definitions.insert(
801 join_graph_directive_definition.name.clone(),
802 Node::new(join_graph_directive_definition),
803 );
804
805 let join_type_directive_definition = join_type_directive_definition();
806 supergraph.directive_definitions.insert(
807 join_type_directive_definition.name.clone(),
808 Node::new(join_type_directive_definition),
809 );
810
811 let join_field_directive_definition = join_field_directive_definition();
812 supergraph.directive_definitions.insert(
813 join_field_directive_definition.name.clone(),
814 Node::new(join_field_directive_definition),
815 );
816
817 let join_implements_directive_definition = join_implements_directive_definition();
818 supergraph.directive_definitions.insert(
819 join_implements_directive_definition.name.clone(),
820 Node::new(join_implements_directive_definition),
821 );
822
823 let join_union_member_directive_definition = join_union_member_directive_definition();
824 supergraph.directive_definitions.insert(
825 join_union_member_directive_definition.name.clone(),
826 Node::new(join_union_member_directive_definition),
827 );
828
829 let join_enum_value_directive_definition = join_enum_value_directive_definition();
830 supergraph.directive_definitions.insert(
831 join_enum_value_directive_definition.name.clone(),
832 Node::new(join_enum_value_directive_definition),
833 );
834
835 let (name, join_graph_enum_type) = join_graph_enum_type(subgraphs);
836 supergraph.types.insert(name, join_graph_enum_type.into());
837}
838
839fn join_enum_value_directive_definition() -> DirectiveDefinition {
841 DirectiveDefinition {
842 name: Name::new("join__enumValue"),
843 description: None,
844 arguments: vec![Node::new(InputValueDefinition {
845 name: Name::new("graph"),
846 description: None,
847 directives: Default::default(),
848 ty: Type::new_named("join__Graph").non_null().into(),
849 default_value: None,
850 })],
851 locations: vec![DirectiveLocation::EnumValue],
852 repeatable: true,
853 }
854}
855
856fn join_field_directive_definition() -> DirectiveDefinition {
866 DirectiveDefinition {
867 name: Name::new("join__field"),
868 description: None,
869 arguments: vec![
870 Node::new(InputValueDefinition {
871 name: Name::new("graph"),
872 description: None,
873 directives: Default::default(),
874 ty: Type::new_named("join__Graph").into(),
875 default_value: None,
876 }),
877 Node::new(InputValueDefinition {
878 name: Name::new("requires"),
879 description: None,
880 directives: Default::default(),
881 ty: Type::new_named("join__FieldSet").into(),
882 default_value: None,
883 }),
884 Node::new(InputValueDefinition {
885 name: Name::new("provides"),
886 description: None,
887 directives: Default::default(),
888 ty: Type::new_named("join__FieldSet").into(),
889 default_value: None,
890 }),
891 Node::new(InputValueDefinition {
892 name: Name::new("type"),
893 description: None,
894 directives: Default::default(),
895 ty: Type::new_named("String").into(),
896 default_value: None,
897 }),
898 Node::new(InputValueDefinition {
899 name: Name::new("external"),
900 description: None,
901 directives: Default::default(),
902 ty: Type::new_named("Boolean").into(),
903 default_value: None,
904 }),
905 Node::new(InputValueDefinition {
906 name: Name::new("override"),
907 description: None,
908 directives: Default::default(),
909 ty: Type::new_named("String").into(),
910 default_value: None,
911 }),
912 Node::new(InputValueDefinition {
913 name: Name::new("usedOverridden"),
914 description: None,
915 directives: Default::default(),
916 ty: Type::new_named("Boolean").into(),
917 default_value: None,
918 }),
919 ],
920 locations: vec![
921 DirectiveLocation::FieldDefinition,
922 DirectiveLocation::InputFieldDefinition,
923 ],
924 repeatable: true,
925 }
926}
927
928fn join_field_applied_directive(
929 subgraph_name: &str,
930 requires: Option<&str>,
931 provides: Option<&str>,
932 external: bool,
933) -> Directive {
934 let mut join_field_directive = Directive {
935 name: Name::new("join__field"),
936 arguments: vec![Node::new(Argument {
937 name: Name::new("graph"),
938 value: Node::new(Value::Enum(Name::new(subgraph_name))),
939 })],
940 };
941 if let Some(required_fields) = requires {
942 join_field_directive.arguments.push(Node::new(Argument {
943 name: Name::new("requires"),
944 value: Node::new(Value::String(Name::new(required_fields))),
945 }));
946 }
947 if let Some(provided_fields) = provides {
948 join_field_directive.arguments.push(Node::new(Argument {
949 name: Name::new("provides"),
950 value: Node::new(Value::String(Name::new(provided_fields))),
951 }));
952 }
953 if external {
954 join_field_directive.arguments.push(Node::new(Argument {
955 name: Name::new("external"),
956 value: Node::new(Value::Boolean(external)),
957 }));
958 }
959 join_field_directive
960}
961
962fn join_graph_directive_definition() -> DirectiveDefinition {
964 DirectiveDefinition {
965 name: Name::new("join__graph"),
966 description: None,
967 arguments: vec![
968 Node::new(InputValueDefinition {
969 name: Name::new("name"),
970 description: None,
971 directives: Default::default(),
972 ty: Type::new_named("String").non_null().into(),
973 default_value: None,
974 }),
975 Node::new(InputValueDefinition {
976 name: Name::new("url"),
977 description: None,
978 directives: Default::default(),
979 ty: Type::new_named("String").non_null().into(),
980 default_value: None,
981 }),
982 ],
983 locations: vec![DirectiveLocation::EnumValue],
984 repeatable: false,
985 }
986}
987
988fn join_implements_directive_definition() -> DirectiveDefinition {
993 DirectiveDefinition {
994 name: Name::new("join__implements"),
995 description: None,
996 arguments: vec![
997 Node::new(InputValueDefinition {
998 name: Name::new("graph"),
999 description: None,
1000 directives: Default::default(),
1001 ty: Type::new_named("join__Graph").non_null().into(),
1002 default_value: None,
1003 }),
1004 Node::new(InputValueDefinition {
1005 name: Name::new("interface"),
1006 description: None,
1007 directives: Default::default(),
1008 ty: Type::new_named("String").non_null().into(),
1009 default_value: None,
1010 }),
1011 ],
1012 locations: vec![DirectiveLocation::Interface, DirectiveLocation::Object],
1013 repeatable: true,
1014 }
1015}
1016
1017fn join_type_directive_definition() -> DirectiveDefinition {
1025 DirectiveDefinition {
1026 name: Name::new("join__type"),
1027 description: None,
1028 arguments: vec![
1029 Node::new(InputValueDefinition {
1030 name: Name::new("graph"),
1031 description: None,
1032 directives: Default::default(),
1033 ty: Type::new_named("join__Graph").non_null().into(),
1034 default_value: None,
1035 }),
1036 Node::new(InputValueDefinition {
1037 name: Name::new("key"),
1038 description: None,
1039 directives: Default::default(),
1040 ty: Type::new_named("join__FieldSet").into(),
1041 default_value: None,
1042 }),
1043 Node::new(InputValueDefinition {
1044 name: Name::new("extension"),
1045 description: None,
1046 directives: Default::default(),
1047 ty: Type::new_named("Boolean").non_null().into(),
1048 default_value: Some(Node::new(Value::Boolean(false))),
1049 }),
1050 Node::new(InputValueDefinition {
1051 name: Name::new("resolvable"),
1052 description: None,
1053 directives: Default::default(),
1054 ty: Type::new_named("Boolean").non_null().into(),
1055 default_value: Some(Node::new(Value::Boolean(true))),
1056 }),
1057 Node::new(InputValueDefinition {
1058 name: Name::new("isInterfaceObject"),
1059 description: None,
1060 directives: Default::default(),
1061 ty: Type::new_named("Boolean").non_null().into(),
1062 default_value: Some(Node::new(Value::Boolean(false))),
1063 }),
1064 ],
1065 locations: vec![
1066 DirectiveLocation::Enum,
1067 DirectiveLocation::InputObject,
1068 DirectiveLocation::Interface,
1069 DirectiveLocation::Object,
1070 DirectiveLocation::Scalar,
1071 DirectiveLocation::Union,
1072 ],
1073 repeatable: true,
1074 }
1075}
1076
1077fn join_union_member_directive_definition() -> DirectiveDefinition {
1079 DirectiveDefinition {
1080 name: Name::new("join__unionMember"),
1081 description: None,
1082 arguments: vec![
1083 Node::new(InputValueDefinition {
1084 name: Name::new("graph"),
1085 description: None,
1086 directives: Default::default(),
1087 ty: Type::new_named("join__Graph").non_null().into(),
1088 default_value: None,
1089 }),
1090 Node::new(InputValueDefinition {
1091 name: Name::new("member"),
1092 description: None,
1093 directives: Default::default(),
1094 ty: Type::new_named("String").non_null().into(),
1095 default_value: None,
1096 }),
1097 ],
1098 locations: vec![DirectiveLocation::Union],
1099 repeatable: true,
1100 }
1101}
1102
1103fn join_graph_enum_type(subgraphs: &Vec<&Subgraph>) -> (Name, EnumType) {
1105 let mut join_graph_enum_type = EnumType {
1106 description: None,
1107 directives: Default::default(),
1108 values: IndexMap::new(),
1109 };
1110 for s in subgraphs {
1111 let join_graph_applied_directive = Directive {
1112 name: Name::new("join__graph"),
1113 arguments: vec![
1114 (Node::new(Argument {
1115 name: Name::new("name"),
1116 value: Node::new(Value::String(NodeStr::new(s.name.as_str()))),
1117 })),
1118 (Node::new(Argument {
1119 name: Name::new("url"),
1120 value: Node::new(Value::String(NodeStr::new(s.url.as_str()))),
1121 })),
1122 ],
1123 };
1124 let graph = EnumValueDefinition {
1125 description: None,
1126 directives: Directives(vec![Node::new(join_graph_applied_directive)]),
1127 value: Name::new(s.name.to_uppercase().as_str()),
1128 };
1129 join_graph_enum_type
1130 .values
1131 .insert(graph.value.clone(), Component::new(graph));
1132 }
1133 (Name::new("join__Graph"), join_graph_enum_type)
1134}
1135
1136fn parse_keys<'a>(
1137 directives: impl Iterator<Item = &'a Component<Directive>> + Sized,
1138) -> HashSet<&'a str> {
1139 HashSet::from_iter(
1140 directives
1141 .flat_map(|k| {
1142 let field_set = directive_string_arg_value(k, "fields").unwrap();
1143 field_set.split_whitespace()
1144 })
1145 .collect::<Vec<&str>>(),
1146 )
1147}
1148
1149fn merge_directive(
1150 supergraph_directives: &mut IndexMap<Name, Node<DirectiveDefinition>>,
1151 directive: &Node<DirectiveDefinition>,
1152) {
1153 if !supergraph_directives.contains_key(&directive.name.clone()) {
1154 supergraph_directives.insert(directive.name.clone(), directive.clone());
1155 }
1156}