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::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            crate::compat::coerce_schema_default_values(&mut supergraph);
268
269            // TODO: validate here and extend `MergeFailure` to propagate validation errors
270            let supergraph = Valid::assume_valid(supergraph);
271            Ok(MergeSuccess {
272                schema: supergraph,
273                composition_hints: self.composition_hints.to_owned(),
274            })
275        } else {
276            Err(MergeFailure {
277                schema: Some(Box::new(supergraph)),
278                composition_hints: self.composition_hints.to_owned(),
279                errors: self.errors.to_owned(),
280            })
281        }
282    }
283
284    fn add_interface_object_fields(
285        &mut self,
286        types: &mut IndexMap<NamedType, ExtendedType>,
287        implementers_map: HashMap<Name, Implementers>,
288    ) -> Result<(), MergeFailure> {
289        for interface_object_name in self.interface_objects.iter() {
290            let Some(ExtendedType::Interface(intf_def)) = types.get(interface_object_name) else {
291                return Err(MergeFailure {
292                    schema: None,
293                    composition_hints: self.composition_hints.to_owned(),
294                    errors: vec![format!("Interface {} not found", interface_object_name)],
295                });
296            };
297            let fields = intf_def.fields.clone();
298
299            if let Some(implementers) = implementers_map.get(interface_object_name) {
300                for implementer in implementers.iter() {
301                    types.entry(implementer.clone()).and_modify(|f| {
302                        if let ExtendedType::Object(obj) = f {
303                            let obj = obj.make_mut();
304                            for (field_name, field_def) in fields.iter() {
305                                let mut field_def = field_def.clone();
306                                let field_def = field_def.make_mut();
307                                field_def.directives = field_def
308                                    .directives
309                                    .iter()
310                                    .filter(|d| d.name != name!("join__field"))
311                                    .cloned()
312                                    .collect();
313                                field_def.directives.push(Node::new(Directive {
314                                    name: name!("join__field"),
315                                    arguments: vec![],
316                                }));
317
318                                obj.fields
319                                    .entry(field_name.clone())
320                                    .or_insert(field_def.clone().into());
321                            }
322                        }
323                    });
324                }
325            };
326        }
327
328        Ok(())
329    }
330
331    fn merge_descriptions<T: Eq + Clone>(&mut self, merged: &mut Option<T>, new: &Option<T>) {
332        match (&mut *merged, new) {
333            (_, None) => {}
334            (None, Some(_)) => merged.clone_from(new),
335            (Some(a), Some(b)) => {
336                if a != b {
337                    // TODO add info about type and from/to subgraph
338                    self.composition_hints
339                        .push(String::from("conflicting descriptions"));
340                }
341            }
342        }
343    }
344
345    fn merge_schema(&mut self, supergraph_schema: &mut Schema, subgraph: &ValidFederationSubgraph) {
346        let supergraph_def = &mut supergraph_schema.schema_definition.make_mut();
347        let subgraph_def = &subgraph.schema.schema().schema_definition;
348        self.merge_descriptions(&mut supergraph_def.description, &subgraph_def.description);
349
350        if subgraph_def.query.is_some() {
351            supergraph_def.query.clone_from(&subgraph_def.query);
352            // TODO mismatch on query types
353        }
354        if subgraph_def.mutation.is_some() {
355            supergraph_def.mutation.clone_from(&subgraph_def.mutation);
356            // TODO mismatch on mutation types
357        }
358        if subgraph_def.subscription.is_some() {
359            supergraph_def
360                .subscription
361                .clone_from(&subgraph_def.subscription);
362            // TODO mismatch on subscription types
363        }
364    }
365
366    fn merge_enum_type(
367        &mut self,
368        types: &mut IndexMap<NamedType, ExtendedType>,
369        metadata: &DirectiveNames,
370        subgraph_name: &EnumValue,
371        enum_name: NamedType,
372        enum_type: &Node<EnumType>,
373    ) {
374        let existing_type = types
375            .entry(enum_name.clone())
376            .or_insert(copy_enum_type(enum_name, enum_type));
377
378        if let ExtendedType::Enum(e) = existing_type {
379            let join_type_directives =
380                join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
381            e.make_mut().directives.extend(join_type_directives);
382
383            self.add_inaccessible(
384                metadata,
385                &mut e.make_mut().directives,
386                &enum_type.directives,
387            );
388
389            self.merge_descriptions(&mut e.make_mut().description, &enum_type.description);
390
391            // 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
392            // below logic only works for output enums
393            for (enum_value_name, enum_value) in enum_type.values.iter() {
394                let ev = e
395                    .make_mut()
396                    .values
397                    .entry(enum_value_name.clone())
398                    .or_insert(Component::new(EnumValueDefinition {
399                        value: enum_value.value.clone(),
400                        description: None,
401                        directives: Default::default(),
402                    }));
403                self.merge_descriptions(&mut ev.make_mut().description, &enum_value.description);
404
405                self.add_inaccessible(
406                    metadata,
407                    &mut ev.make_mut().directives,
408                    &enum_value.directives,
409                );
410
411                ev.make_mut().directives.push(Node::new(Directive {
412                    name: name!("join__enumValue"),
413                    arguments: vec![
414                        (Node::new(Argument {
415                            name: name!("graph"),
416                            value: Node::new(Value::Enum(subgraph_name.to_name())),
417                        })),
418                    ],
419                }));
420            }
421        } else {
422            // TODO - conflict
423        }
424    }
425
426    fn merge_input_object_type(
427        &mut self,
428        types: &mut IndexMap<NamedType, ExtendedType>,
429        directive_names: &DirectiveNames,
430        subgraph_name: &EnumValue,
431        input_object_name: NamedType,
432        input_object: &Node<InputObjectType>,
433    ) {
434        let existing_type = types
435            .entry(input_object_name.clone())
436            .or_insert(copy_input_object_type(input_object_name, input_object));
437
438        if let ExtendedType::InputObject(obj) = existing_type {
439            let join_type_directives =
440                join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
441            let mutable_object = obj.make_mut();
442            mutable_object.directives.extend(join_type_directives);
443
444            self.add_inaccessible(
445                directive_names,
446                &mut mutable_object.directives,
447                &input_object.directives,
448            );
449
450            for (field_name, field) in input_object.fields.iter() {
451                let existing_field = mutable_object.fields.entry(field_name.clone());
452
453                let supergraph_field = match existing_field {
454                    Vacant(i) => i.insert(Component::new(InputValueDefinition {
455                        name: field.name.clone(),
456                        description: field.description.clone(),
457                        ty: field.ty.clone(),
458                        default_value: field.default_value.clone(),
459                        directives: Default::default(),
460                    })),
461                    Occupied(i) => {
462                        i.into_mut()
463                        // merge_options(&i.get_mut().description, &field.description);
464                        // TODO check description
465                        // TODO check type
466                        // TODO check default value
467                        // TODO process directives
468                    }
469                };
470
471                self.add_inaccessible(
472                    directive_names,
473                    &mut supergraph_field.make_mut().directives,
474                    &field.directives,
475                );
476
477                let join_field_directive = join_field_applied_directive(
478                    subgraph_name,
479                    None,
480                    None,
481                    false,
482                    None,
483                    Some(&field.ty),
484                );
485                supergraph_field
486                    .make_mut()
487                    .directives
488                    .push(Node::new(join_field_directive));
489            }
490        } else {
491            // TODO conflict on type
492        }
493    }
494
495    fn merge_interface_type(
496        &mut self,
497        types: &mut IndexMap<NamedType, ExtendedType>,
498        directive_names: &DirectiveNames,
499        subgraph_name: &EnumValue,
500        interface_name: NamedType,
501        interface: &Node<InterfaceType>,
502    ) {
503        let existing_type = types
504            .entry(interface_name.clone())
505            .or_insert(copy_interface_type(interface_name, interface));
506
507        if let ExtendedType::Interface(intf) = existing_type {
508            let key_directives = interface.directives.get_all(&directive_names.key);
509            let join_type_directives =
510                join_type_applied_directive(subgraph_name.clone(), key_directives, false);
511            let mutable_intf = intf.make_mut();
512            mutable_intf.directives.extend(join_type_directives);
513
514            self.add_inaccessible(
515                directive_names,
516                &mut mutable_intf.directives,
517                &interface.directives,
518            );
519
520            interface
521                .implements_interfaces
522                .iter()
523                .for_each(|intf_name| {
524                    // IndexSet::insert deduplicates
525                    mutable_intf.implements_interfaces.insert(intf_name.clone());
526                    let join_implements_directive =
527                        join_implements_applied_directive(subgraph_name.clone(), intf_name);
528                    mutable_intf.directives.push(join_implements_directive);
529                });
530
531            for (field_name, field) in interface.fields.iter() {
532                let existing_field = mutable_intf.fields.entry(field_name.clone());
533                let supergraph_field = match existing_field {
534                    Occupied(f) => {
535                        f.into_mut()
536                        // TODO check description
537                        // TODO check type
538                        // TODO check default value
539                        // TODO process directives
540                    }
541                    Vacant(f) => {
542                        // TODO warning mismatch missing fields
543                        f.insert(Component::new(FieldDefinition {
544                            name: field.name.clone(),
545                            description: field.description.clone(),
546                            arguments: vec![],
547                            ty: field.ty.clone(),
548                            directives: Default::default(),
549                        }))
550                    }
551                };
552
553                fields::merge_arguments(
554                    field.arguments.iter(),
555                    &mut supergraph_field.make_mut().arguments,
556                    self,
557                    directive_names,
558                );
559                self.merge_descriptions(
560                    &mut supergraph_field.make_mut().description,
561                    &field.description,
562                );
563
564                self.add_inaccessible(
565                    directive_names,
566                    &mut supergraph_field.make_mut().directives,
567                    &field.directives,
568                );
569
570                let join_field_directive = join_field_applied_directive(
571                    subgraph_name,
572                    None,
573                    None,
574                    false,
575                    None,
576                    Some(&field.ty),
577                );
578
579                supergraph_field
580                    .make_mut()
581                    .directives
582                    .push(Node::new(join_field_directive));
583            }
584        } else {
585            // TODO conflict on type
586        }
587    }
588
589    fn merge_object_type(
590        &mut self,
591        types: &mut IndexMap<NamedType, ExtendedType>,
592        directive_names: &DirectiveNames,
593        subgraph_name: &EnumValue,
594        object_name: NamedType,
595        object: &Node<ObjectType>,
596    ) {
597        let is_interface_object = object.directives.has(&directive_names.interface_object);
598        let existing_type = types
599            .entry(object_name.clone())
600            .or_insert(copy_object_type_stub(
601                object_name,
602                object,
603                is_interface_object,
604            ));
605
606        if let ExtendedType::Object(obj) = existing_type {
607            let key_directives = object.directives.get_all(&directive_names.key);
608            let join_type_directives =
609                join_type_applied_directive(subgraph_name.clone(), key_directives, false);
610            let mutable_object = obj.make_mut();
611            mutable_object.directives.extend(join_type_directives);
612            self.merge_descriptions(&mut mutable_object.description, &object.description);
613            self.add_inaccessible(
614                directive_names,
615                &mut mutable_object.directives,
616                &object.directives,
617            );
618            object.implements_interfaces.iter().for_each(|intf_name| {
619                // IndexSet::insert deduplicates
620                mutable_object
621                    .implements_interfaces
622                    .insert(intf_name.clone());
623                let join_implements_directive =
624                    join_implements_applied_directive(subgraph_name.clone(), intf_name);
625                mutable_object.directives.push(join_implements_directive);
626            });
627
628            for (field_name, field) in object.fields.iter() {
629                // skip federation built-in queries
630                if field_name == "_service" || field_name == "_entities" {
631                    continue;
632                }
633
634                let existing_field = mutable_object.fields.entry(field_name.clone());
635                let supergraph_field = match existing_field {
636                    Occupied(f) => {
637                        // check description
638                        // check type
639                        // check args
640                        f.into_mut()
641                    }
642                    Vacant(f) => f.insert(Component::new(FieldDefinition {
643                        name: field.name.clone(),
644                        description: field.description.clone(),
645                        arguments: vec![],
646                        directives: Default::default(),
647                        ty: field.ty.clone(),
648                    })),
649                };
650                self.merge_descriptions(
651                    &mut supergraph_field.make_mut().description,
652                    &field.description,
653                );
654
655                self.add_inaccessible(
656                    directive_names,
657                    &mut supergraph_field.make_mut().directives,
658                    &field.directives,
659                );
660
661                fields::merge_arguments(
662                    field.arguments.iter(),
663                    &mut supergraph_field.make_mut().arguments,
664                    self,
665                    directive_names,
666                );
667
668                let requires_directive_option = field
669                    .directives
670                    .get_all(&directive_names.requires)
671                    .next()
672                    .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
673
674                let provides_directive_option = field
675                    .directives
676                    .get_all(&directive_names.provides)
677                    .next()
678                    .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
679
680                let overrides_directive_option = field
681                    .directives
682                    .get_all(&directive_names.r#override)
683                    .next()
684                    .and_then(|p| {
685                        let overrides_from =
686                            directive_string_arg_value(p, &FEDERATION_FROM_ARGUMENT_NAME);
687                        let overrides_label =
688                            directive_string_arg_value(p, &FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME);
689                        overrides_from.map(|from| (from, overrides_label))
690                    });
691
692                let external_field = field
693                    .directives
694                    .get_all(&directive_names.external)
695                    .next()
696                    .is_some();
697
698                let join_field_directive = join_field_applied_directive(
699                    subgraph_name,
700                    requires_directive_option,
701                    provides_directive_option,
702                    external_field,
703                    overrides_directive_option,
704                    Some(&field.ty),
705                );
706
707                supergraph_field
708                    .make_mut()
709                    .directives
710                    .push(Node::new(join_field_directive));
711
712                // TODO: implement needsJoinField to avoid adding join__field when unnecessary
713                // https://github.com/apollographql/federation/blob/0d8a88585d901dff6844fdce1146a4539dec48df/composition-js/src/merging/merge.ts#L1648
714            }
715        } else if let ExtendedType::Interface(intf) = existing_type {
716            self.interface_objects.insert(intf.name.clone());
717
718            let key_directives = object.directives.get_all(&directive_names.key);
719            let join_type_directives =
720                join_type_applied_directive(subgraph_name.clone(), key_directives, true);
721            let mutable_object = intf.make_mut();
722            mutable_object.directives.extend(join_type_directives);
723            self.merge_descriptions(&mut mutable_object.description, &object.description);
724            self.add_inaccessible(
725                directive_names,
726                &mut mutable_object.directives,
727                &object.directives,
728            );
729
730            for (field_name, field) in object.fields.iter() {
731                // skip federation built-in queries
732                if field_name == "_service" || field_name == "_entities" {
733                    continue;
734                }
735
736                let existing_field = mutable_object.fields.entry(field_name.clone());
737                let supergraph_field = match existing_field {
738                    Occupied(f) => {
739                        // check description
740                        // check type
741                        // check args
742                        f.into_mut()
743                    }
744                    Vacant(f) => f.insert(Component::new(FieldDefinition {
745                        name: field.name.clone(),
746                        description: field.description.clone(),
747                        arguments: vec![],
748                        directives: Default::default(),
749                        ty: field.ty.clone(),
750                    })),
751                };
752                self.merge_descriptions(
753                    &mut supergraph_field.make_mut().description,
754                    &field.description,
755                );
756
757                self.add_inaccessible(
758                    directive_names,
759                    &mut supergraph_field.make_mut().directives,
760                    &field.directives,
761                );
762
763                fields::merge_arguments(
764                    field.arguments.iter(),
765                    &mut supergraph_field.make_mut().arguments,
766                    self,
767                    directive_names,
768                );
769                let requires_directive_option = field
770                    .directives
771                    .get_all(&directive_names.requires)
772                    .next()
773                    .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
774
775                let provides_directive_option = field
776                    .directives
777                    .get_all(&directive_names.provides)
778                    .next()
779                    .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
780
781                let overrides_directive_option = field
782                    .directives
783                    .get_all(&directive_names.r#override)
784                    .next()
785                    .and_then(|p| {
786                        let overrides_from =
787                            directive_string_arg_value(p, &FEDERATION_FROM_ARGUMENT_NAME);
788                        let overrides_label =
789                            directive_string_arg_value(p, &FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME);
790                        overrides_from.map(|from| (from, overrides_label))
791                    });
792
793                let external_field = field
794                    .directives
795                    .get_all(&directive_names.external)
796                    .next()
797                    .is_some();
798
799                let join_field_directive = join_field_applied_directive(
800                    subgraph_name,
801                    requires_directive_option,
802                    provides_directive_option,
803                    external_field,
804                    overrides_directive_option,
805                    Some(&field.ty),
806                );
807
808                supergraph_field
809                    .make_mut()
810                    .directives
811                    .push(Node::new(join_field_directive));
812
813                // TODO: implement needsJoinField to avoid adding join__field when unnecessary
814                // https://github.com/apollographql/federation/blob/0d8a88585d901dff6844fdce1146a4539dec48df/composition-js/src/merging/merge.ts#L1648
815            }
816        };
817        // TODO merge fields
818    }
819
820    fn merge_union_type(
821        &mut self,
822        types: &mut IndexMap<NamedType, ExtendedType>,
823        directive_names: &DirectiveNames,
824        subgraph_name: &EnumValue,
825        union_name: NamedType,
826        union: &Node<UnionType>,
827    ) {
828        let existing_type = types
829            .entry(union_name.clone())
830            .or_insert(copy_union_type(union_name, union.description.clone()));
831
832        if let ExtendedType::Union(u) = existing_type {
833            let join_type_directives =
834                join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
835            u.make_mut().directives.extend(join_type_directives);
836            self.add_inaccessible(
837                directive_names,
838                &mut u.make_mut().directives,
839                &union.directives,
840            );
841
842            for union_member in union.members.iter() {
843                // IndexSet::insert deduplicates
844                u.make_mut().members.insert(union_member.clone());
845                u.make_mut().directives.push(Component::new(Directive {
846                    name: name!("join__unionMember"),
847                    arguments: vec![
848                        Node::new(Argument {
849                            name: name!("graph"),
850                            value: Node::new(Value::Enum(subgraph_name.to_name())),
851                        }),
852                        Node::new(Argument {
853                            name: name!("member"),
854                            value: union_member.as_str().into(),
855                        }),
856                    ],
857                }));
858            }
859        }
860    }
861
862    fn merge_scalar_type(
863        &mut self,
864        types: &mut IndexMap<Name, ExtendedType>,
865        directive_names: &DirectiveNames,
866        subgraph_name: EnumValue,
867        scalar_name: NamedType,
868        ty: &Node<ScalarType>,
869    ) {
870        let existing_type = types
871            .entry(scalar_name.clone())
872            .or_insert(copy_scalar_type(scalar_name, ty));
873
874        if let ExtendedType::Scalar(s) = existing_type {
875            let join_type_directives =
876                join_type_applied_directive(subgraph_name, iter::empty(), false);
877            s.make_mut().directives.extend(join_type_directives);
878            self.add_inaccessible(
879                directive_names,
880                &mut s.make_mut().directives,
881                &ty.directives,
882            );
883        } else {
884            // conflict?
885        }
886    }
887
888    // generic so it handles ast::DirectiveList and schema::DirectiveList
889    fn add_inaccessible<I>(
890        &mut self,
891        directive_names: &DirectiveNames,
892        new_directives: &mut Vec<I>,
893        original_directives: &[I],
894    ) where
895        I: AsRef<Directive> + From<Directive> + Clone,
896    {
897        if original_directives
898            .iter()
899            .any(|d| d.as_ref().name == directive_names.inaccessible)
900            && !new_directives
901                .iter()
902                .any(|d| d.as_ref().name == INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC)
903        {
904            self.needs_inaccessible = true;
905
906            new_directives.push(
907                Directive {
908                    name: INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
909                    arguments: vec![],
910                }
911                .into(),
912            );
913        }
914    }
915}
916
917struct DirectiveNames {
918    key: Name,
919    requires: Name,
920    provides: Name,
921    external: Name,
922    interface_object: Name,
923    r#override: Name,
924    inaccessible: Name,
925}
926
927impl DirectiveNames {
928    fn for_metadata(metadata: &Option<&LinksMetadata>) -> Self {
929        let federation_identity =
930            metadata.and_then(|m| m.by_identity.get(&Identity::federation_identity()));
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;