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