Skip to main content

apollo_federation/
merge.rs

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