Skip to main content

apollo_federation/
merge.rs

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        // TODO: Consider an easier transition / interop between MergeFailure and FederationError
76        // TODO: This is most certainly not the right error kind. MergeFailure's
77        // errors need to be in an enum that could be matched on rather than a
78        // str.
79        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            // TODO: Implement JS codebase's name transform (which always generates a valid GraphQL
140            // name and avoids collisions).
141            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        // TODO handle @compose
159
160        // add core features
161        // TODO verify federation versions across subgraphs
162        add_core_feature_link(&mut supergraph);
163        add_core_feature_join(&mut supergraph, &subgraphs_and_enum_values);
164
165        // create stubs
166        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            // TODO merge directives
174
175            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                    // skip built-ins and federation specific types
181                    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            // merge executable directives
235            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            // TODO: validate here and extend `MergeFailure` to propagate validation errors
248            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                    // TODO add info about type and from/to subgraph
269                    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            // TODO mismatch on query types
284        }
285        if subgraph_def.mutation.is_some() {
286            supergraph_def.mutation.clone_from(&subgraph_def.mutation);
287            // TODO mismatch on mutation types
288        }
289        if subgraph_def.subscription.is_some() {
290            supergraph_def
291                .subscription
292                .clone_from(&subgraph_def.subscription);
293            // TODO mismatch on subscription types
294        }
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            // TODO we need to merge those fields LAST so we know whether enum is used as input/output/both as different merge rules will apply
323            // below logic only works for output enums
324            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            // TODO - conflict
354        }
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                        // TODO warning - mismatch on input fields
387                    }
388                    Occupied(mut i) => {
389                        self.add_inaccessible(
390                            directive_names,
391                            &mut i.get_mut().make_mut().directives,
392                            &field.directives,
393                        );
394                        // merge_options(&i.get_mut().description, &field.description);
395                        // TODO check description
396                        // TODO check type
397                        // TODO check default value
398                        // TODO process directives
399                    }
400                }
401            }
402        } else {
403            // TODO conflict on type
404        }
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                        // TODO warning mismatch missing fields
437                        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                        // TODO check description
453                        // TODO check type
454                        // TODO check default value
455                        // TODO process directives
456                    }
457                }
458            }
459        } else {
460            // TODO conflict on type
461        }
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                // IndexSet::insert deduplicates
495                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                // skip federation built-in queries
505                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                        // check description
513                        // check type
514                        // check args
515                        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                // TODO: implement needsJoinField to avoid adding join__field when unnecessary
610                // https://github.com/apollographql/federation/blob/0d8a88585d901dff6844fdce1146a4539dec48df/composition-js/src/merging/merge.ts#L1648
611            }
612        } else if let ExtendedType::Interface(intf) = existing_type {
613            // TODO support interface object
614            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        // TODO merge fields
620    }
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                // IndexSet::insert deduplicates
647                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            // conflict?
688        }
689    }
690
691    // generic so it handles ast::DirectiveList and schema::DirectiveList
692    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
794// TODO handle federation specific types - skip if any of the link/fed spec
795// TODO this info should be coming from other module
796const 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        // skip federation built-in queries
891        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
1021// TODO link spec
1022fn add_core_feature_link(supergraph: &mut Schema) {
1023    // @link(url: "https://specs.apollo.dev/link/v1.0")
1024    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    // scalar Import
1040    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
1056/// directive @link(url: String, as: String, import: [Import], for: link__Purpose) repeatable on SCHEMA
1057fn 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
1096/// enum link__Purpose {
1097///   """
1098///   \`SECURITY\` features provide metadata necessary to securely resolve fields.
1099///   """
1100///   SECURITY
1101///
1102///   """
1103///   \`EXECUTION\` features provide metadata necessary for operation execution.
1104///   """
1105///   EXECUTION
1106/// }
1107fn 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
1140// TODO join spec
1141fn add_core_feature_join(
1142    supergraph: &mut Schema,
1143    subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, Name)>,
1144) {
1145    // @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1146    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    // scalar FieldSet
1165    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
1215/// directive @enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
1216fn 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
1232/// directive @field(
1233///   graph: Graph,
1234///   requires: FieldSet,
1235///   provides: FieldSet,
1236///   type: String,
1237///   external: Boolean,
1238///   override: String,
1239///   usedOverridden: Boolean
1240/// ) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
1241fn 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>)>, // from, label
1317) -> 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
1358/// directive @graph(name: String!, url: String!) on ENUM_VALUE
1359fn 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
1384/// directive @implements(
1385///   graph: Graph!,
1386///   interface: String!
1387/// ) on OBJECT | INTERFACE
1388fn 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
1413/// directive @type(
1414///   graph: Graph!,
1415///   key: FieldSet,
1416///   extension: Boolean! = false,
1417///   resolvable: Boolean = true,
1418///   isInterfaceObject: Boolean = false
1419/// ) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
1420fn 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
1473/// directive @unionMember(graph: join__Graph!, member: String!) repeatable on UNION
1474fn 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
1499/// enum Graph
1500fn 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    // @link(url: "https://specs.apollo.dev/inaccessible/v0.2")
1538    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}