Skip to main content

apollo_federation/
merge.rs

1mod fields;
2
3use std::fmt::Debug;
4use std::fmt::Formatter;
5use std::iter;
6use std::sync::Arc;
7
8use apollo_compiler::Name;
9use apollo_compiler::Node;
10use apollo_compiler::Schema;
11use apollo_compiler::ast::Argument;
12use apollo_compiler::ast::Directive;
13use apollo_compiler::ast::DirectiveDefinition;
14use apollo_compiler::ast::DirectiveList;
15use apollo_compiler::ast::DirectiveLocation;
16use apollo_compiler::ast::EnumValueDefinition;
17use apollo_compiler::ast::FieldDefinition;
18use apollo_compiler::ast::NamedType;
19use apollo_compiler::ast::Type;
20use apollo_compiler::ast::Value;
21use apollo_compiler::collections::HashMap;
22use apollo_compiler::collections::IndexMap;
23use apollo_compiler::collections::IndexSet;
24use apollo_compiler::name;
25use apollo_compiler::schema::Component;
26use apollo_compiler::schema::EnumType;
27use apollo_compiler::schema::ExtendedType;
28use apollo_compiler::schema::Implementers;
29use apollo_compiler::schema::InputObjectType;
30use apollo_compiler::schema::InputValueDefinition;
31use apollo_compiler::schema::InterfaceType;
32use apollo_compiler::schema::ObjectType;
33use apollo_compiler::schema::ScalarType;
34use apollo_compiler::schema::UnionType;
35use apollo_compiler::ty;
36use apollo_compiler::validation::Valid;
37use indexmap::map::Entry::Occupied;
38use indexmap::map::Entry::Vacant;
39use indexmap::map::Iter;
40use itertools::Itertools;
41
42use crate::ValidFederationSubgraph;
43use crate::ValidFederationSubgraphs;
44use crate::error::FederationError;
45use crate::link::federation_spec_definition::FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC;
46use crate::link::federation_spec_definition::FEDERATION_FIELDS_ARGUMENT_NAME;
47use crate::link::federation_spec_definition::FEDERATION_FROM_ARGUMENT_NAME;
48use crate::link::federation_spec_definition::FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC;
49use crate::link::federation_spec_definition::FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC;
50use crate::link::federation_spec_definition::FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC;
51use crate::link::federation_spec_definition::FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME;
52use crate::link::federation_spec_definition::FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC;
53use crate::link::federation_spec_definition::FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC;
54use crate::link::inaccessible_spec_definition::INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC;
55use crate::link::inaccessible_spec_definition::InaccessibleSpecDefinition;
56use crate::link::join_spec_definition::EnumValue;
57use crate::link::join_spec_definition::JOIN_OVERRIDE_LABEL_ARGUMENT_NAME;
58use crate::link::metadata::LinksMetadata;
59use crate::link::spec::Identity;
60use crate::link::spec::Version;
61use crate::link::spec_definition::SpecDefinition;
62use crate::schema::ValidFederationSchema;
63use crate::subgraph::ValidSubgraph;
64
65type MergeWarning = String;
66type MergeError = String;
67
68struct Merger {
69    errors: Vec<MergeError>,
70    composition_hints: Vec<MergeWarning>,
71    needs_inaccessible: bool,
72    interface_objects: IndexSet<Name>,
73}
74
75pub struct MergeSuccess {
76    pub schema: Valid<Schema>,
77    pub composition_hints: Vec<MergeWarning>,
78}
79
80impl From<FederationError> for MergeFailure {
81    fn from(err: FederationError) -> Self {
82        // TODO: Consider an easier transition / interop between MergeFailure and FederationError
83        // TODO: This is most certainly not the right error kind. MergeFailure's
84        // errors need to be in an enum that could be matched on rather than a
85        // str.
86        MergeFailure {
87            schema: None,
88            errors: vec![err.to_string()],
89            composition_hints: vec![],
90        }
91    }
92}
93
94pub struct MergeFailure {
95    pub schema: Option<Box<Schema>>,
96    pub errors: Vec<MergeError>,
97    pub composition_hints: Vec<MergeWarning>,
98}
99
100impl Debug for MergeFailure {
101    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
102        f.debug_struct("MergeFailure")
103            .field("errors", &self.errors)
104            .field("composition_hints", &self.composition_hints)
105            .finish()
106    }
107}
108
109pub fn merge_subgraphs(subgraphs: Vec<&ValidSubgraph>) -> Result<MergeSuccess, MergeFailure> {
110    let mut merger = Merger::new();
111    let mut federation_subgraphs = ValidFederationSubgraphs::new();
112    for subgraph in subgraphs {
113        federation_subgraphs.add(ValidFederationSubgraph {
114            name: subgraph.name.clone(),
115            url: subgraph.url.clone(),
116            schema: ValidFederationSchema::new(subgraph.schema.clone())?,
117        })?;
118    }
119    merger.merge(federation_subgraphs)
120}
121
122pub fn merge_federation_subgraphs(
123    subgraphs: ValidFederationSubgraphs,
124) -> Result<MergeSuccess, MergeFailure> {
125    let mut merger = Merger::new();
126    merger.merge(subgraphs)
127}
128
129impl Merger {
130    fn new() -> Self {
131        Merger {
132            composition_hints: Vec::new(),
133            errors: Vec::new(),
134            needs_inaccessible: false,
135            interface_objects: IndexSet::default(),
136        }
137    }
138
139    fn merge(&mut self, subgraphs: ValidFederationSubgraphs) -> Result<MergeSuccess, MergeFailure> {
140        let mut subgraphs = subgraphs
141            .into_iter()
142            .map(|(_, subgraph)| subgraph)
143            .collect_vec();
144        subgraphs.sort_by(|s1, s2| s1.name.cmp(&s2.name));
145        let mut subgraphs_and_enum_values = Vec::new();
146        let mut enum_values = IndexSet::default();
147        for subgraph in &subgraphs {
148            let enum_value = match EnumValue::new(&subgraph.name) {
149                Ok(enum_value) => enum_value,
150                Err(err) => {
151                    self.errors.push(err);
152                    continue;
153                }
154            };
155
156            // Ensure that enum values are unique after normalizing them
157            let enum_value = if enum_values.contains(&enum_value.to_string()) {
158                EnumValue::new(&format!("{}_{}", subgraph.name, enum_values.len()))
159                    .expect("adding a suffix always works")
160            } else {
161                enum_value
162            };
163            enum_values.insert(enum_value.to_string());
164            subgraphs_and_enum_values.push((subgraph, enum_value))
165        }
166        if !self.errors.is_empty() {
167            return Err(MergeFailure {
168                schema: None,
169                composition_hints: self.composition_hints.to_owned(),
170                errors: self.errors.to_owned(),
171            });
172        }
173
174        let mut supergraph = Schema::new();
175        // TODO handle @compose
176
177        // add core features
178        // TODO verify federation versions across subgraphs
179        add_core_feature_link(&mut supergraph);
180        add_core_feature_join(&mut supergraph, &subgraphs_and_enum_values);
181
182        // create stubs
183        for (subgraph, subgraph_name) in &subgraphs_and_enum_values {
184            let sources = Arc::make_mut(&mut supergraph.sources);
185            for (key, source) in subgraph.schema.schema().sources.iter() {
186                sources.entry(*key).or_insert_with(|| source.clone());
187            }
188
189            self.merge_schema(&mut supergraph, subgraph);
190            // TODO merge directives
191
192            let metadata = subgraph.schema.metadata();
193            let relevant_directives = DirectiveNames::for_metadata(&metadata);
194
195            for (type_name, ty) in &subgraph.schema.schema().types {
196                if ty.is_built_in() || !is_mergeable_type(type_name) {
197                    // skip built-ins and federation specific types
198                    continue;
199                }
200
201                match ty {
202                    ExtendedType::Enum(value) => self.merge_enum_type(
203                        &mut supergraph.types,
204                        &relevant_directives,
205                        subgraph_name,
206                        type_name.clone(),
207                        value,
208                    ),
209                    ExtendedType::InputObject(value) => self.merge_input_object_type(
210                        &mut supergraph.types,
211                        &relevant_directives,
212                        subgraph_name,
213                        type_name.clone(),
214                        value,
215                    ),
216                    ExtendedType::Interface(value) => self.merge_interface_type(
217                        &mut supergraph.types,
218                        &relevant_directives,
219                        subgraph_name,
220                        type_name.clone(),
221                        value,
222                    ),
223                    ExtendedType::Object(value) => self.merge_object_type(
224                        &mut supergraph.types,
225                        &relevant_directives,
226                        subgraph_name,
227                        type_name.clone(),
228                        value,
229                    ),
230                    ExtendedType::Union(value) => self.merge_union_type(
231                        &mut supergraph.types,
232                        &relevant_directives,
233                        subgraph_name,
234                        type_name.clone(),
235                        value,
236                    ),
237                    ExtendedType::Scalar(value) => {
238                        if !value.is_built_in() {
239                            self.merge_scalar_type(
240                                &mut supergraph.types,
241                                &relevant_directives,
242                                subgraph_name.clone(),
243                                type_name.clone(),
244                                value,
245                            );
246                        }
247                    }
248                }
249            }
250
251            // merge executable directives
252            for (_, directive) in subgraph.schema.schema().directive_definitions.iter() {
253                if is_executable_directive(directive) {
254                    merge_directive(&mut supergraph.directive_definitions, directive);
255                }
256            }
257        }
258
259        let implementers_map = supergraph.implementers_map();
260        self.add_interface_object_fields(&mut supergraph.types, implementers_map)?;
261
262        if self.needs_inaccessible {
263            add_core_feature_inaccessible(&mut supergraph);
264        }
265
266        if self.errors.is_empty() {
267            crate::compat::coerce_schema_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.for_identity(&Identity::federation_identity()));
931        let federation_identity = federation_identity.as_ref();
932
933        let key = federation_identity
934            .map(|link| link.directive_name_in_schema(&FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC))
935            .unwrap_or(FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC);
936
937        let requires = federation_identity
938            .map(|link| link.directive_name_in_schema(&FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC))
939            .unwrap_or(FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC);
940
941        let provides = federation_identity
942            .map(|link| link.directive_name_in_schema(&FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC))
943            .unwrap_or(FEDERATION_PROVIDES_DIRECTIVE_NAME_IN_SPEC);
944
945        let external = federation_identity
946            .map(|link| link.directive_name_in_schema(&FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC))
947            .unwrap_or(FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC);
948
949        let interface_object = federation_identity
950            .map(|link| {
951                link.directive_name_in_schema(&FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC)
952            })
953            .unwrap_or(FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC);
954
955        let r#override = federation_identity
956            .map(|link| link.directive_name_in_schema(&FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC))
957            .unwrap_or(FEDERATION_OVERRIDE_DIRECTIVE_NAME_IN_SPEC);
958
959        let inaccessible = federation_identity
960            .map(|link| link.directive_name_in_schema(&INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC))
961            .unwrap_or(INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC);
962
963        Self {
964            key,
965            requires,
966            provides,
967            external,
968            interface_object,
969            r#override,
970            inaccessible,
971        }
972    }
973}
974
975const EXECUTABLE_DIRECTIVE_LOCATIONS: [DirectiveLocation; 8] = [
976    DirectiveLocation::Query,
977    DirectiveLocation::Mutation,
978    DirectiveLocation::Subscription,
979    DirectiveLocation::Field,
980    DirectiveLocation::FragmentDefinition,
981    DirectiveLocation::FragmentSpread,
982    DirectiveLocation::InlineFragment,
983    DirectiveLocation::VariableDefinition,
984];
985fn is_executable_directive(directive: &Node<DirectiveDefinition>) -> bool {
986    directive
987        .locations
988        .iter()
989        .any(|loc| EXECUTABLE_DIRECTIVE_LOCATIONS.contains(loc))
990}
991
992// TODO handle federation specific types - skip if any of the link/fed spec
993// TODO this info should be coming from other module
994const FEDERATION_TYPES: [&str; 4] = ["_Any", "_Entity", "_Service", "@key"];
995fn is_mergeable_type(type_name: &str) -> bool {
996    if type_name.starts_with("federation__") || type_name.starts_with("link__") {
997        return false;
998    }
999    !FEDERATION_TYPES.contains(&type_name)
1000}
1001
1002fn copy_scalar_type(scalar_name: Name, scalar_type: &Node<ScalarType>) -> ExtendedType {
1003    ExtendedType::Scalar(Node::new(ScalarType {
1004        description: scalar_type.description.clone(),
1005        name: scalar_name,
1006        directives: Default::default(),
1007    }))
1008}
1009
1010fn copy_enum_type(enum_name: Name, enum_type: &Node<EnumType>) -> ExtendedType {
1011    ExtendedType::Enum(Node::new(EnumType {
1012        description: enum_type.description.clone(),
1013        name: enum_name,
1014        directives: Default::default(),
1015        values: IndexMap::default(),
1016    }))
1017}
1018
1019fn copy_input_object_type(
1020    input_object_name: Name,
1021    input_object: &Node<InputObjectType>,
1022) -> ExtendedType {
1023    let mut new_input_object = InputObjectType {
1024        description: input_object.description.clone(),
1025        name: input_object_name,
1026        directives: Default::default(),
1027        fields: IndexMap::default(),
1028    };
1029
1030    for (field_name, input_field) in input_object.fields.iter() {
1031        new_input_object.fields.insert(
1032            field_name.clone(),
1033            Component::new(InputValueDefinition {
1034                name: input_field.name.clone(),
1035                description: input_field.description.clone(),
1036                directives: Default::default(),
1037                ty: input_field.ty.clone(),
1038                default_value: input_field.default_value.clone(),
1039            }),
1040        );
1041    }
1042
1043    ExtendedType::InputObject(Node::new(new_input_object))
1044}
1045
1046fn copy_interface_type(interface_name: Name, interface: &Node<InterfaceType>) -> ExtendedType {
1047    let new_interface = InterfaceType {
1048        description: interface.description.clone(),
1049        name: interface_name,
1050        directives: Default::default(),
1051        fields: copy_fields(interface.fields.iter()),
1052        implements_interfaces: interface.implements_interfaces.clone(),
1053    };
1054    ExtendedType::Interface(Node::new(new_interface))
1055}
1056
1057fn copy_object_type_stub(
1058    object_name: Name,
1059    object: &Node<ObjectType>,
1060    is_interface_object: bool,
1061) -> ExtendedType {
1062    if is_interface_object {
1063        let new_interface = InterfaceType {
1064            description: object.description.clone(),
1065            name: object_name,
1066            directives: Default::default(),
1067            fields: copy_fields(object.fields.iter()),
1068            implements_interfaces: object.implements_interfaces.clone(),
1069        };
1070        ExtendedType::Interface(Node::new(new_interface))
1071    } else {
1072        let new_object = ObjectType {
1073            description: object.description.clone(),
1074            name: object_name,
1075            directives: Default::default(),
1076            fields: copy_fields(object.fields.iter()),
1077            implements_interfaces: object.implements_interfaces.clone(),
1078        };
1079        ExtendedType::Object(Node::new(new_object))
1080    }
1081}
1082
1083fn copy_fields(
1084    fields_to_copy: Iter<Name, Component<FieldDefinition>>,
1085) -> IndexMap<Name, Component<FieldDefinition>> {
1086    let mut new_fields: IndexMap<Name, Component<FieldDefinition>> = IndexMap::default();
1087    for (field_name, field) in fields_to_copy {
1088        // skip federation built-in queries
1089        if field_name == "_service" || field_name == "_entities" {
1090            continue;
1091        }
1092        let args: Vec<Node<InputValueDefinition>> = field
1093            .arguments
1094            .iter()
1095            .map(|a| {
1096                Node::new(InputValueDefinition {
1097                    name: a.name.clone(),
1098                    description: a.description.clone(),
1099                    directives: Default::default(),
1100                    ty: a.ty.clone(),
1101                    default_value: a.default_value.clone(),
1102                })
1103            })
1104            .collect();
1105        let new_field = Component::new(FieldDefinition {
1106            name: field.name.clone(),
1107            description: field.description.clone(),
1108            directives: Default::default(),
1109            arguments: args,
1110            ty: field.ty.clone(),
1111        });
1112
1113        new_fields.insert(field_name.clone(), new_field);
1114    }
1115    new_fields
1116}
1117
1118fn copy_union_type(union_name: Name, description: Option<Node<str>>) -> ExtendedType {
1119    ExtendedType::Union(Node::new(UnionType {
1120        description,
1121        name: union_name,
1122        directives: Default::default(),
1123        members: IndexSet::default(),
1124    }))
1125}
1126
1127fn join_type_applied_directive<'a>(
1128    subgraph_name: EnumValue,
1129    key_directives: impl Iterator<Item = &'a Component<Directive>> + Sized,
1130    is_interface_object: bool,
1131) -> Vec<Component<Directive>> {
1132    let mut join_type_directive = Directive {
1133        name: name!("join__type"),
1134        arguments: vec![Node::new(Argument {
1135            name: name!("graph"),
1136            value: Node::new(Value::Enum(subgraph_name.into())),
1137        })],
1138    };
1139    if is_interface_object {
1140        join_type_directive.arguments.push(Node::new(Argument {
1141            name: name!("isInterfaceObject"),
1142            value: Node::new(Value::Boolean(is_interface_object)),
1143        }));
1144    }
1145
1146    let mut result = vec![];
1147    for key_directive in key_directives {
1148        let mut join_type_directive_with_key = join_type_directive.clone();
1149        let field_set = directive_string_arg_value(key_directive, &name!("fields")).unwrap();
1150        join_type_directive_with_key
1151            .arguments
1152            .push(Node::new(Argument {
1153                name: name!("key"),
1154                value: field_set.into(),
1155            }));
1156
1157        let resolvable =
1158            directive_bool_arg_value(key_directive, &name!("resolvable")).unwrap_or(&true);
1159        if !resolvable {
1160            join_type_directive_with_key
1161                .arguments
1162                .push(Node::new(Argument {
1163                    name: name!("resolvable"),
1164                    value: Node::new(Value::Boolean(false)),
1165                }));
1166        }
1167        result.push(join_type_directive_with_key)
1168    }
1169    if result.is_empty() {
1170        result.push(join_type_directive)
1171    }
1172    result
1173        .into_iter()
1174        .map(Component::new)
1175        .collect::<Vec<Component<Directive>>>()
1176}
1177
1178fn join_implements_applied_directive(
1179    subgraph_name: EnumValue,
1180    intf_name: &Name,
1181) -> Component<Directive> {
1182    Component::new(Directive {
1183        name: name!("join__implements"),
1184        arguments: vec![
1185            Node::new(Argument {
1186                name: name!("graph"),
1187                value: Node::new(Value::Enum(subgraph_name.into())),
1188            }),
1189            Node::new(Argument {
1190                name: name!("interface"),
1191                value: intf_name.as_str().into(),
1192            }),
1193        ],
1194    })
1195}
1196
1197fn directive_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a Value> {
1198    directive
1199        .arguments
1200        .iter()
1201        .find(|arg| arg.name == *arg_name)
1202        .map(|arg| arg.value.as_ref())
1203}
1204
1205fn directive_string_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a str> {
1206    match directive_arg_value(directive, arg_name) {
1207        Some(Value::String(value)) => Some(value),
1208        _ => None,
1209    }
1210}
1211
1212fn directive_bool_arg_value<'a>(directive: &'a Directive, arg_name: &Name) -> Option<&'a bool> {
1213    match directive_arg_value(directive, arg_name) {
1214        Some(Value::Boolean(value)) => Some(value),
1215        _ => None,
1216    }
1217}
1218
1219// TODO link spec
1220fn add_core_feature_link(supergraph: &mut Schema) {
1221    // @link(url: "https://specs.apollo.dev/link/v1.0")
1222    supergraph
1223        .schema_definition
1224        .make_mut()
1225        .directives
1226        .push(Component::new(Directive {
1227            name: name!("link"),
1228            arguments: vec![Node::new(Argument {
1229                name: name!("url"),
1230                value: Node::new("https://specs.apollo.dev/link/v1.0".into()),
1231            })],
1232        }));
1233
1234    let (name, link_purpose_enum) = link_purpose_enum_type();
1235    supergraph.types.insert(name, link_purpose_enum.into());
1236
1237    // scalar Import
1238    let link_import_name = name!("link__Import");
1239    let link_import_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1240        directives: Default::default(),
1241        name: link_import_name.clone(),
1242        description: None,
1243    }));
1244    supergraph
1245        .types
1246        .insert(link_import_name, link_import_scalar);
1247
1248    let link_directive_definition = link_directive_definition();
1249    supergraph
1250        .directive_definitions
1251        .insert(name!("link"), Node::new(link_directive_definition));
1252}
1253
1254/// directive @link(url: String, as: String, import: [Import], for: link__Purpose) repeatable on SCHEMA
1255fn link_directive_definition() -> DirectiveDefinition {
1256    DirectiveDefinition {
1257        name: name!("link"),
1258        description: None,
1259        arguments: vec![
1260            Node::new(InputValueDefinition {
1261                name: name!("url"),
1262                description: None,
1263                directives: Default::default(),
1264                ty: ty!(String).into(),
1265                default_value: None,
1266            }),
1267            Node::new(InputValueDefinition {
1268                name: name!("as"),
1269                description: None,
1270                directives: Default::default(),
1271                ty: ty!(String).into(),
1272                default_value: None,
1273            }),
1274            Node::new(InputValueDefinition {
1275                name: name!("for"),
1276                description: None,
1277                directives: Default::default(),
1278                ty: ty!(link__Purpose).into(),
1279                default_value: None,
1280            }),
1281            Node::new(InputValueDefinition {
1282                name: name!("import"),
1283                description: None,
1284                directives: Default::default(),
1285                ty: ty!([link__Import]).into(),
1286                default_value: None,
1287            }),
1288        ],
1289        locations: vec![DirectiveLocation::Schema],
1290        repeatable: true,
1291    }
1292}
1293
1294/// enum link__Purpose {
1295///   """
1296///   \`SECURITY\` features provide metadata necessary to securely resolve fields.
1297///   """
1298///   SECURITY
1299///
1300///   """
1301///   \`EXECUTION\` features provide metadata necessary for operation execution.
1302///   """
1303///   EXECUTION
1304/// }
1305fn link_purpose_enum_type() -> (Name, EnumType) {
1306    let link_purpose_name = name!("link__Purpose");
1307    let mut link_purpose_enum = EnumType {
1308        description: None,
1309        name: link_purpose_name.clone(),
1310        directives: Default::default(),
1311        values: IndexMap::default(),
1312    };
1313    let link_purpose_security_value = EnumValueDefinition {
1314        description: Some(
1315            r"SECURITY features provide metadata necessary to securely resolve fields.".into(),
1316        ),
1317        directives: Default::default(),
1318        value: name!("SECURITY"),
1319    };
1320    let link_purpose_execution_value = EnumValueDefinition {
1321        description: Some(
1322            r"EXECUTION features provide metadata necessary for operation execution.".into(),
1323        ),
1324        directives: Default::default(),
1325        value: name!("EXECUTION"),
1326    };
1327    link_purpose_enum.values.insert(
1328        link_purpose_security_value.value.clone(),
1329        Component::new(link_purpose_security_value),
1330    );
1331    link_purpose_enum.values.insert(
1332        link_purpose_execution_value.value.clone(),
1333        Component::new(link_purpose_execution_value),
1334    );
1335    (link_purpose_name, link_purpose_enum)
1336}
1337
1338// TODO join spec
1339fn add_core_feature_join(
1340    supergraph: &mut Schema,
1341    subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, EnumValue)>,
1342) {
1343    // @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION)
1344    supergraph
1345        .schema_definition
1346        .make_mut()
1347        .directives
1348        .push(Component::new(Directive {
1349            name: name!("link"),
1350            arguments: vec![
1351                Node::new(Argument {
1352                    name: name!("url"),
1353                    value: "https://specs.apollo.dev/join/v0.5".into(),
1354                }),
1355                Node::new(Argument {
1356                    name: name!("for"),
1357                    value: Node::new(Value::Enum(name!("EXECUTION"))),
1358                }),
1359            ],
1360        }));
1361
1362    // scalar FieldSet
1363    let join_field_set_name = name!("join__FieldSet");
1364    let join_field_set_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1365        directives: Default::default(),
1366        name: join_field_set_name.clone(),
1367        description: None,
1368    }));
1369    supergraph
1370        .types
1371        .insert(join_field_set_name, join_field_set_scalar);
1372
1373    // scalar join__FieldValue
1374    let join_field_value_name = name!("join__FieldValue");
1375    let join_field_value_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1376        directives: Default::default(),
1377        name: join_field_value_name.clone(),
1378        description: None,
1379    }));
1380    supergraph
1381        .types
1382        .insert(join_field_value_name, join_field_value_scalar);
1383
1384    // input join__ContextArgument {
1385    //   name: String!
1386    //   type: String!
1387    //   context: String!
1388    //   selection: join__FieldValue!
1389    // }
1390    let join_context_argument_name = name!("join__ContextArgument");
1391    let join_context_argument_input = ExtendedType::InputObject(Node::new(InputObjectType {
1392        description: None,
1393        name: join_context_argument_name.clone(),
1394        directives: Default::default(),
1395        fields: vec![
1396            (
1397                name!("name"),
1398                Component::new(InputValueDefinition {
1399                    name: name!("name"),
1400                    description: None,
1401                    directives: Default::default(),
1402                    ty: ty!(String!).into(),
1403                    default_value: None,
1404                }),
1405            ),
1406            (
1407                name!("type"),
1408                Component::new(InputValueDefinition {
1409                    name: name!("type"),
1410                    description: None,
1411                    directives: Default::default(),
1412                    ty: ty!(String!).into(),
1413                    default_value: None,
1414                }),
1415            ),
1416            (
1417                name!("context"),
1418                Component::new(InputValueDefinition {
1419                    name: name!("context"),
1420                    description: None,
1421                    directives: Default::default(),
1422                    ty: ty!(String!).into(),
1423                    default_value: None,
1424                }),
1425            ),
1426            (
1427                name!("selection"),
1428                Component::new(InputValueDefinition {
1429                    name: name!("selection"),
1430                    description: None,
1431                    directives: Default::default(),
1432                    ty: ty!(join__FieldValue!).into(),
1433                    default_value: None,
1434                }),
1435            ),
1436        ]
1437        .into_iter()
1438        .collect(),
1439    }));
1440    supergraph
1441        .types
1442        .insert(join_context_argument_name, join_context_argument_input);
1443
1444    let join_graph_directive_definition = join_graph_directive_definition();
1445    supergraph.directive_definitions.insert(
1446        join_graph_directive_definition.name.clone(),
1447        Node::new(join_graph_directive_definition),
1448    );
1449
1450    let join_type_directive_definition = join_type_directive_definition();
1451    supergraph.directive_definitions.insert(
1452        join_type_directive_definition.name.clone(),
1453        Node::new(join_type_directive_definition),
1454    );
1455
1456    let join_field_directive_definition = join_field_directive_definition();
1457    supergraph.directive_definitions.insert(
1458        join_field_directive_definition.name.clone(),
1459        Node::new(join_field_directive_definition),
1460    );
1461
1462    let join_implements_directive_definition = join_implements_directive_definition();
1463    supergraph.directive_definitions.insert(
1464        join_implements_directive_definition.name.clone(),
1465        Node::new(join_implements_directive_definition),
1466    );
1467
1468    let join_union_member_directive_definition = join_union_member_directive_definition();
1469    supergraph.directive_definitions.insert(
1470        join_union_member_directive_definition.name.clone(),
1471        Node::new(join_union_member_directive_definition),
1472    );
1473
1474    let join_enum_value_directive_definition = join_enum_value_directive_definition();
1475    supergraph.directive_definitions.insert(
1476        join_enum_value_directive_definition.name.clone(),
1477        Node::new(join_enum_value_directive_definition),
1478    );
1479
1480    // scalar join__DirectiveArguments
1481    let join_directive_arguments_name = name!("join__DirectiveArguments");
1482    let join_directive_arguments_scalar = ExtendedType::Scalar(Node::new(ScalarType {
1483        directives: Default::default(),
1484        name: join_directive_arguments_name.clone(),
1485        description: None,
1486    }));
1487    supergraph.types.insert(
1488        join_directive_arguments_name,
1489        join_directive_arguments_scalar,
1490    );
1491
1492    let join_directive_directive_definition = join_directive_directive_definition();
1493    supergraph.directive_definitions.insert(
1494        join_directive_directive_definition.name.clone(),
1495        Node::new(join_directive_directive_definition),
1496    );
1497
1498    let (name, join_graph_enum_type) = join_graph_enum_type(subgraphs_and_enum_values);
1499    supergraph.types.insert(name, join_graph_enum_type.into());
1500}
1501
1502/// directive @enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
1503fn join_enum_value_directive_definition() -> DirectiveDefinition {
1504    DirectiveDefinition {
1505        name: name!("join__enumValue"),
1506        description: None,
1507        arguments: vec![Node::new(InputValueDefinition {
1508            name: name!("graph"),
1509            description: None,
1510            directives: Default::default(),
1511            ty: ty!(join__Graph!).into(),
1512            default_value: None,
1513        })],
1514        locations: vec![DirectiveLocation::EnumValue],
1515        repeatable: true,
1516    }
1517}
1518
1519/// directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
1520fn join_directive_directive_definition() -> DirectiveDefinition {
1521    DirectiveDefinition {
1522        name: name!("join__directive"),
1523        description: None,
1524        arguments: vec![
1525            Node::new(InputValueDefinition {
1526                name: name!("graphs"),
1527                description: None,
1528                directives: Default::default(),
1529                ty: ty!([join__Graph!]).into(),
1530                default_value: None,
1531            }),
1532            Node::new(InputValueDefinition {
1533                name: name!("name"),
1534                description: None,
1535                directives: Default::default(),
1536                ty: ty!(String!).into(),
1537                default_value: None,
1538            }),
1539            Node::new(InputValueDefinition {
1540                name: name!("args"),
1541                description: None,
1542                directives: Default::default(),
1543                ty: ty!(join__DirectiveArguments!).into(),
1544                default_value: None,
1545            }),
1546        ],
1547        locations: vec![
1548            DirectiveLocation::Schema,
1549            DirectiveLocation::Object,
1550            DirectiveLocation::Interface,
1551            DirectiveLocation::FieldDefinition,
1552        ],
1553        repeatable: true,
1554    }
1555}
1556
1557/// directive @field(
1558///   graph: Graph,
1559///   requires: FieldSet,
1560///   provides: FieldSet,
1561///   type: String,
1562///   external: Boolean,
1563///   override: String,
1564///   usedOverridden: Boolean
1565/// ) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
1566fn join_field_directive_definition() -> DirectiveDefinition {
1567    DirectiveDefinition {
1568        name: name!("join__field"),
1569        description: None,
1570        arguments: vec![
1571            Node::new(InputValueDefinition {
1572                name: name!("graph"),
1573                description: None,
1574                directives: Default::default(),
1575                ty: ty!(join__Graph).into(),
1576                default_value: None,
1577            }),
1578            Node::new(InputValueDefinition {
1579                name: name!("requires"),
1580                description: None,
1581                directives: Default::default(),
1582                ty: ty!(join__FieldSet).into(),
1583                default_value: None,
1584            }),
1585            Node::new(InputValueDefinition {
1586                name: name!("provides"),
1587                description: None,
1588                directives: Default::default(),
1589                ty: ty!(join__FieldSet).into(),
1590                default_value: None,
1591            }),
1592            Node::new(InputValueDefinition {
1593                name: name!("type"),
1594                description: None,
1595                directives: Default::default(),
1596                ty: ty!(String).into(),
1597                default_value: None,
1598            }),
1599            Node::new(InputValueDefinition {
1600                name: name!("external"),
1601                description: None,
1602                directives: Default::default(),
1603                ty: ty!(Boolean).into(),
1604                default_value: None,
1605            }),
1606            Node::new(InputValueDefinition {
1607                name: name!("override"),
1608                description: None,
1609                directives: Default::default(),
1610                ty: ty!(String).into(),
1611                default_value: None,
1612            }),
1613            Node::new(InputValueDefinition {
1614                name: JOIN_OVERRIDE_LABEL_ARGUMENT_NAME,
1615                description: None,
1616                directives: Default::default(),
1617                ty: ty!(String).into(),
1618                default_value: None,
1619            }),
1620            Node::new(InputValueDefinition {
1621                name: name!("usedOverridden"),
1622                description: None,
1623                directives: Default::default(),
1624                ty: ty!(Boolean).into(),
1625                default_value: None,
1626            }),
1627            Node::new(InputValueDefinition {
1628                name: name!("contextArguments"),
1629                description: None,
1630                directives: Default::default(),
1631                ty: ty!([join__ContextArgument!]).into(),
1632                default_value: None,
1633            }),
1634        ],
1635        locations: vec![
1636            DirectiveLocation::FieldDefinition,
1637            DirectiveLocation::InputFieldDefinition,
1638        ],
1639        repeatable: true,
1640    }
1641}
1642
1643// NOTE: the logic for constructing the contextArguments argument
1644// is not trivial and is not implemented here. For connectors "expansion",
1645// it's handled in carryover.rs.
1646fn join_field_applied_directive(
1647    subgraph_name: &EnumValue,
1648    requires: Option<&str>,
1649    provides: Option<&str>,
1650    external: bool,
1651    overrides: Option<(&str, Option<&str>)>, // from, label
1652    r#type: Option<&Type>,
1653) -> Directive {
1654    let mut join_field_directive = Directive {
1655        name: name!("join__field"),
1656        arguments: vec![Node::new(Argument {
1657            name: name!("graph"),
1658            value: Node::new(Value::Enum(subgraph_name.to_name())),
1659        })],
1660    };
1661    if let Some(required_fields) = requires {
1662        join_field_directive.arguments.push(Node::new(Argument {
1663            name: name!("requires"),
1664            value: required_fields.into(),
1665        }));
1666    }
1667    if let Some(provided_fields) = provides {
1668        join_field_directive.arguments.push(Node::new(Argument {
1669            name: name!("provides"),
1670            value: provided_fields.into(),
1671        }));
1672    }
1673    if external {
1674        join_field_directive.arguments.push(Node::new(Argument {
1675            name: name!("external"),
1676            value: external.into(),
1677        }));
1678    }
1679    if let Some((from, label)) = overrides {
1680        join_field_directive.arguments.push(Node::new(Argument {
1681            name: name!("override"),
1682            value: Node::new(Value::String(from.to_string())),
1683        }));
1684        if let Some(label) = label {
1685            join_field_directive.arguments.push(Node::new(Argument {
1686                name: name!("overrideLabel"),
1687                value: Node::new(Value::String(label.to_string())),
1688            }));
1689        }
1690    }
1691    if let Some(r#type) = r#type {
1692        join_field_directive.arguments.push(Node::new(Argument {
1693            name: name!("type"),
1694            value: r#type.to_string().into(),
1695        }));
1696    }
1697    join_field_directive
1698}
1699
1700/// directive @graph(name: String!, url: String!) on ENUM_VALUE
1701fn join_graph_directive_definition() -> DirectiveDefinition {
1702    DirectiveDefinition {
1703        name: name!("join__graph"),
1704        description: None,
1705        arguments: vec![
1706            Node::new(InputValueDefinition {
1707                name: name!("name"),
1708                description: None,
1709                directives: Default::default(),
1710                ty: ty!(String!).into(),
1711                default_value: None,
1712            }),
1713            Node::new(InputValueDefinition {
1714                name: name!("url"),
1715                description: None,
1716                directives: Default::default(),
1717                ty: ty!(String!).into(),
1718                default_value: None,
1719            }),
1720        ],
1721        locations: vec![DirectiveLocation::EnumValue],
1722        repeatable: false,
1723    }
1724}
1725
1726/// directive @implements(
1727///   graph: Graph!,
1728///   interface: String!
1729/// ) on OBJECT | INTERFACE
1730fn join_implements_directive_definition() -> DirectiveDefinition {
1731    DirectiveDefinition {
1732        name: name!("join__implements"),
1733        description: None,
1734        arguments: vec![
1735            Node::new(InputValueDefinition {
1736                name: name!("graph"),
1737                description: None,
1738                directives: Default::default(),
1739                ty: ty!(join__Graph!).into(),
1740                default_value: None,
1741            }),
1742            Node::new(InputValueDefinition {
1743                name: name!("interface"),
1744                description: None,
1745                directives: Default::default(),
1746                ty: ty!(String!).into(),
1747                default_value: None,
1748            }),
1749        ],
1750        locations: vec![DirectiveLocation::Interface, DirectiveLocation::Object],
1751        repeatable: true,
1752    }
1753}
1754
1755/// directive @type(
1756///   graph: Graph!,
1757///   key: FieldSet,
1758///   extension: Boolean! = false,
1759///   resolvable: Boolean = true,
1760///   isInterfaceObject: Boolean = false
1761/// ) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
1762fn join_type_directive_definition() -> DirectiveDefinition {
1763    DirectiveDefinition {
1764        name: name!("join__type"),
1765        description: None,
1766        arguments: vec![
1767            Node::new(InputValueDefinition {
1768                name: name!("graph"),
1769                description: None,
1770                directives: Default::default(),
1771                ty: ty!(join__Graph!).into(),
1772                default_value: None,
1773            }),
1774            Node::new(InputValueDefinition {
1775                name: name!("key"),
1776                description: None,
1777                directives: Default::default(),
1778                ty: ty!(join__FieldSet).into(),
1779                default_value: None,
1780            }),
1781            Node::new(InputValueDefinition {
1782                name: name!("extension"),
1783                description: None,
1784                directives: Default::default(),
1785                ty: ty!(Boolean!).into(),
1786                default_value: Some(Node::new(Value::Boolean(false))),
1787            }),
1788            Node::new(InputValueDefinition {
1789                name: name!("resolvable"),
1790                description: None,
1791                directives: Default::default(),
1792                ty: ty!(Boolean!).into(),
1793                default_value: Some(Node::new(Value::Boolean(true))),
1794            }),
1795            Node::new(InputValueDefinition {
1796                name: name!("isInterfaceObject"),
1797                description: None,
1798                directives: Default::default(),
1799                ty: ty!(Boolean!).into(),
1800                default_value: Some(Node::new(Value::Boolean(false))),
1801            }),
1802        ],
1803        locations: vec![
1804            DirectiveLocation::Enum,
1805            DirectiveLocation::InputObject,
1806            DirectiveLocation::Interface,
1807            DirectiveLocation::Object,
1808            DirectiveLocation::Scalar,
1809            DirectiveLocation::Union,
1810        ],
1811        repeatable: true,
1812    }
1813}
1814
1815/// directive @unionMember(graph: join__Graph!, member: String!) repeatable on UNION
1816fn join_union_member_directive_definition() -> DirectiveDefinition {
1817    DirectiveDefinition {
1818        name: name!("join__unionMember"),
1819        description: None,
1820        arguments: vec![
1821            Node::new(InputValueDefinition {
1822                name: name!("graph"),
1823                description: None,
1824                directives: Default::default(),
1825                ty: ty!(join__Graph!).into(),
1826                default_value: None,
1827            }),
1828            Node::new(InputValueDefinition {
1829                name: name!("member"),
1830                description: None,
1831                directives: Default::default(),
1832                ty: ty!(String!).into(),
1833                default_value: None,
1834            }),
1835        ],
1836        locations: vec![DirectiveLocation::Union],
1837        repeatable: true,
1838    }
1839}
1840
1841/// enum Graph
1842fn join_graph_enum_type(
1843    subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, EnumValue)>,
1844) -> (Name, EnumType) {
1845    let join_graph_enum_name = name!("join__Graph");
1846    let mut join_graph_enum_type = EnumType {
1847        description: None,
1848        name: join_graph_enum_name.clone(),
1849        directives: Default::default(),
1850        values: IndexMap::default(),
1851    };
1852    for (s, subgraph_name) in subgraphs_and_enum_values {
1853        let join_graph_applied_directive = Directive {
1854            name: name!("join__graph"),
1855            arguments: vec![
1856                (Node::new(Argument {
1857                    name: name!("name"),
1858                    value: s.name.as_str().into(),
1859                })),
1860                (Node::new(Argument {
1861                    name: name!("url"),
1862                    value: s.url.as_str().into(),
1863                })),
1864            ],
1865        };
1866        let graph = EnumValueDefinition {
1867            description: None,
1868            directives: DirectiveList(vec![Node::new(join_graph_applied_directive)]),
1869            value: subgraph_name.to_name(),
1870        };
1871        join_graph_enum_type
1872            .values
1873            .insert(graph.value.clone(), Component::new(graph));
1874    }
1875    (join_graph_enum_name, join_graph_enum_type)
1876}
1877
1878fn add_core_feature_inaccessible(supergraph: &mut Schema) {
1879    // @link(url: "https://specs.apollo.dev/inaccessible/v0.2")
1880    let spec = InaccessibleSpecDefinition::new(
1881        Version { major: 0, minor: 2 },
1882        Version { major: 2, minor: 0 },
1883    );
1884
1885    supergraph
1886        .schema_definition
1887        .make_mut()
1888        .directives
1889        .push(Component::new(Directive {
1890            name: name!("link"),
1891            arguments: vec![
1892                Node::new(Argument {
1893                    name: name!("url"),
1894                    value: spec.to_string().into(),
1895                }),
1896                Node::new(Argument {
1897                    name: name!("for"),
1898                    value: Node::new(Value::Enum(name!("SECURITY"))),
1899                }),
1900            ],
1901        }));
1902
1903    supergraph.directive_definitions.insert(
1904        INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
1905        Node::new(DirectiveDefinition {
1906            name: INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC,
1907            description: None,
1908            arguments: vec![],
1909            locations: vec![
1910                DirectiveLocation::FieldDefinition,
1911                DirectiveLocation::Object,
1912                DirectiveLocation::Interface,
1913                DirectiveLocation::Union,
1914                DirectiveLocation::ArgumentDefinition,
1915                DirectiveLocation::Scalar,
1916                DirectiveLocation::Enum,
1917                DirectiveLocation::EnumValue,
1918                DirectiveLocation::InputObject,
1919                DirectiveLocation::InputFieldDefinition,
1920            ],
1921            repeatable: false,
1922        }),
1923    );
1924}
1925
1926fn merge_directive(
1927    supergraph_directives: &mut IndexMap<Name, Node<DirectiveDefinition>>,
1928    directive: &Node<DirectiveDefinition>,
1929) {
1930    if !supergraph_directives.contains_key(&directive.name.clone()) {
1931        supergraph_directives.insert(directive.name.clone(), directive.clone());
1932    }
1933}
1934
1935#[cfg(test)]
1936mod tests;