1use std::hash::Hash;
2use std::hash::Hasher;
3use std::ops::Deref;
4use std::ops::Range;
5use std::sync::Arc;
6
7use apollo_compiler::Name;
8use apollo_compiler::Node;
9use apollo_compiler::Schema;
10use apollo_compiler::ast::Directive;
11use apollo_compiler::ast::FieldDefinition;
12use apollo_compiler::ast::Value;
13use apollo_compiler::collections::IndexSet;
14use apollo_compiler::executable::FieldSet;
15use apollo_compiler::parser::LineColumn;
16use apollo_compiler::schema::ComponentOrigin;
17use apollo_compiler::schema::ExtendedType;
18use apollo_compiler::schema::ExtensionId;
19use apollo_compiler::schema::SchemaDefinition;
20use apollo_compiler::validation::Valid;
21use apollo_compiler::validation::WithErrors;
22use itertools::Itertools;
23use position::DirectiveTargetPosition;
24use position::FieldArgumentDefinitionPosition;
25use position::ObjectOrInterfaceTypeDefinitionPosition;
26use position::TagDirectiveTargetPosition;
27use referencer::Referencers;
28
29use crate::bail;
30use crate::error::FederationError;
31use crate::error::SingleFederationError;
32use crate::internal_error;
33use crate::link::Link;
34use crate::link::LinksMetadata;
35use crate::link::context_spec_definition::ContextSpecDefinition;
36use crate::link::cost_spec_definition;
37use crate::link::cost_spec_definition::CostSpecDefinition;
38use crate::link::federation_spec_definition::CacheTagDirectiveArguments;
39use crate::link::federation_spec_definition::ComposeDirectiveArguments;
40use crate::link::federation_spec_definition::ContextDirectiveArguments;
41use crate::link::federation_spec_definition::FEDERATION_ENTITY_TYPE_NAME_IN_SPEC;
42use crate::link::federation_spec_definition::FEDERATION_FIELDS_ARGUMENT_NAME;
43use crate::link::federation_spec_definition::FEDERATION_FIELDSET_TYPE_NAME_IN_SPEC;
44use crate::link::federation_spec_definition::FEDERATION_SERVICE_TYPE_NAME_IN_SPEC;
45use crate::link::federation_spec_definition::FederationSpecDefinition;
46use crate::link::federation_spec_definition::FromContextDirectiveArguments;
47use crate::link::federation_spec_definition::KeyDirectiveArguments;
48use crate::link::federation_spec_definition::ProvidesDirectiveArguments;
49use crate::link::federation_spec_definition::RequiresDirectiveArguments;
50use crate::link::federation_spec_definition::TagDirectiveArguments;
51use crate::link::federation_spec_definition::get_federation_spec_definition_from_subgraph;
52use crate::link::spec::Version;
53use crate::link::spec_definition::SPEC_REGISTRY;
54use crate::link::spec_definition::SpecDefinition;
55use crate::schema::position::CompositeTypeDefinitionPosition;
56use crate::schema::position::DirectiveDefinitionPosition;
57use crate::schema::position::EnumTypeDefinitionPosition;
58use crate::schema::position::HasAppliedDirectives;
59use crate::schema::position::InputObjectTypeDefinitionPosition;
60use crate::schema::position::InterfaceTypeDefinitionPosition;
61use crate::schema::position::ObjectOrInterfaceFieldDefinitionPosition;
62use crate::schema::position::ObjectTypeDefinitionPosition;
63use crate::schema::position::ScalarTypeDefinitionPosition;
64use crate::schema::position::TypeDefinitionPosition;
65use crate::schema::position::UnionTypeDefinitionPosition;
66use crate::schema::subgraph_metadata::SubgraphMetadata;
67
68pub(crate) mod argument_composition_strategies;
69pub(crate) mod blueprint;
70pub(crate) mod definitions;
71pub(crate) mod directive_location;
72pub(crate) mod field_set;
73pub(crate) mod locations;
74pub(crate) mod position;
75pub(crate) mod referencer;
76pub(crate) mod schema_upgrader;
77pub(crate) mod subgraph_metadata;
78pub(crate) mod validators;
79
80pub(crate) fn compute_subgraph_metadata(
81 schema: &FederationSchema,
82) -> Result<Option<SubgraphMetadata>, FederationError> {
83 Ok(
84 if let Ok(federation_spec_definition) = get_federation_spec_definition_from_subgraph(schema)
85 {
86 Some(SubgraphMetadata::new(schema, federation_spec_definition)?)
87 } else {
88 None
89 },
90 )
91}
92pub(crate) mod type_and_directive_specification;
93
94#[derive(Clone, Debug)]
96pub struct FederationSchema {
97 schema: Schema,
98 referencers: Referencers,
99 links_metadata: Option<Box<LinksMetadata>>,
100 subgraph_metadata: Option<Box<SubgraphMetadata>>,
103}
104
105impl FederationSchema {
106 pub(crate) fn schema(&self) -> &Schema {
107 &self.schema
108 }
109
110 pub(crate) fn schema_mut(&mut self) -> &mut Schema {
111 &mut self.schema
112 }
113
114 pub fn into_inner(self) -> Schema {
116 self.schema
117 }
118
119 pub(crate) fn metadata(&self) -> Option<&LinksMetadata> {
120 self.links_metadata.as_deref()
121 }
122
123 pub(crate) fn referencers(&self) -> &Referencers {
124 &self.referencers
125 }
126
127 pub(crate) fn get_types(&self) -> impl Iterator<Item = TypeDefinitionPosition> {
129 self.schema
130 .types
131 .iter()
132 .filter(|(_, ty)| !ty.is_built_in())
133 .map(|(type_name, type_)| {
134 let type_name = type_name.clone();
135 match type_ {
136 ExtendedType::Scalar(_) => ScalarTypeDefinitionPosition { type_name }.into(),
137 ExtendedType::Object(_) => ObjectTypeDefinitionPosition { type_name }.into(),
138 ExtendedType::Interface(_) => {
139 InterfaceTypeDefinitionPosition { type_name }.into()
140 }
141 ExtendedType::Union(_) => UnionTypeDefinitionPosition { type_name }.into(),
142 ExtendedType::Enum(_) => EnumTypeDefinitionPosition { type_name }.into(),
143 ExtendedType::InputObject(_) => {
144 InputObjectTypeDefinitionPosition { type_name }.into()
145 }
146 }
147 })
148 }
149
150 pub(crate) fn get_directive_definitions(
151 &self,
152 ) -> impl Iterator<Item = DirectiveDefinitionPosition> {
153 self.schema
154 .directive_definitions
155 .keys()
156 .map(|name| DirectiveDefinitionPosition {
157 directive_name: name.clone(),
158 })
159 }
160
161 pub(crate) fn get_type(
162 &self,
163 type_name: Name,
164 ) -> Result<TypeDefinitionPosition, FederationError> {
165 let type_ =
166 self.schema
167 .types
168 .get(&type_name)
169 .ok_or_else(|| SingleFederationError::Internal {
170 message: format!("Schema has no type \"{type_name}\""),
171 })?;
172 Ok(match type_ {
173 ExtendedType::Scalar(_) => ScalarTypeDefinitionPosition { type_name }.into(),
174 ExtendedType::Object(_) => ObjectTypeDefinitionPosition { type_name }.into(),
175 ExtendedType::Interface(_) => InterfaceTypeDefinitionPosition { type_name }.into(),
176 ExtendedType::Union(_) => UnionTypeDefinitionPosition { type_name }.into(),
177 ExtendedType::Enum(_) => EnumTypeDefinitionPosition { type_name }.into(),
178 ExtendedType::InputObject(_) => InputObjectTypeDefinitionPosition { type_name }.into(),
179 })
180 }
181
182 pub(crate) fn try_get_type(&self, type_name: Name) -> Option<TypeDefinitionPosition> {
183 self.get_type(type_name).ok()
184 }
185
186 pub(crate) fn is_root_type(&self, type_name: &Name) -> bool {
187 self.schema()
188 .schema_definition
189 .iter_root_operations()
190 .any(|op| *op.1 == *type_name)
191 }
192
193 pub(crate) fn is_subscription_root_type(&self, type_name: &Name) -> bool {
194 let subscription = &self.schema().schema_definition.subscription;
195 subscription.as_ref().is_some_and(|name| name == type_name)
196 }
197
198 pub(crate) fn possible_runtime_types(
205 &self,
206 composite_type_definition_position: CompositeTypeDefinitionPosition,
207 ) -> Result<IndexSet<ObjectTypeDefinitionPosition>, FederationError> {
208 Ok(match composite_type_definition_position {
209 CompositeTypeDefinitionPosition::Object(pos) => IndexSet::from_iter([pos]),
210 CompositeTypeDefinitionPosition::Interface(pos) => self
211 .referencers()
212 .get_interface_type(&pos.type_name)?
213 .object_types
214 .clone(),
215 CompositeTypeDefinitionPosition::Union(pos) => pos
216 .get(self.schema())?
217 .members
218 .iter()
219 .map(|t| ObjectTypeDefinitionPosition {
220 type_name: t.name.clone(),
221 })
222 .collect::<IndexSet<_>>(),
223 })
224 }
225
226 pub(crate) fn all_implementation_types(
230 &self,
231 interface_type_definition_position: &InterfaceTypeDefinitionPosition,
232 ) -> Result<IndexSet<ObjectOrInterfaceTypeDefinitionPosition>, FederationError> {
233 let referencers = self
234 .referencers()
235 .get_interface_type(&interface_type_definition_position.type_name)?;
236 Ok(referencers
237 .object_types
238 .iter()
239 .cloned()
240 .map(ObjectOrInterfaceTypeDefinitionPosition::from)
241 .chain(
242 referencers
243 .interface_types
244 .iter()
245 .cloned()
246 .map(ObjectOrInterfaceTypeDefinitionPosition::from),
247 )
248 .collect())
249 }
250
251 #[allow(clippy::result_large_err)] pub(crate) fn validate_or_return_self(
255 mut self,
256 ) -> Result<ValidFederationSchema, (Self, FederationError)> {
257 let schema = match self.schema.validate() {
258 Ok(schema) => schema.into_inner(),
259 Err(e) => {
260 self.schema = e.partial;
261 return Err((self, e.errors.into()));
262 }
263 };
264 ValidFederationSchema::new_assume_valid(FederationSchema { schema, ..self })
265 }
266
267 pub(crate) fn assume_valid(self) -> Result<ValidFederationSchema, FederationError> {
268 ValidFederationSchema::new_assume_valid(self).map_err(|(_schema, error)| error)
269 }
270
271 pub(crate) fn get_directive_definition(
272 &self,
273 name: &Name,
274 ) -> Option<DirectiveDefinitionPosition> {
275 self.schema
276 .directive_definitions
277 .contains_key(name)
278 .then(|| DirectiveDefinitionPosition {
279 directive_name: name.clone(),
280 })
281 }
282
283 pub(crate) fn entity_type(
286 &self,
287 ) -> Result<Option<UnionTypeDefinitionPosition>, FederationError> {
288 match self.schema.types.get(&FEDERATION_ENTITY_TYPE_NAME_IN_SPEC) {
293 Some(ExtendedType::Union(_)) => Ok(Some(UnionTypeDefinitionPosition {
294 type_name: FEDERATION_ENTITY_TYPE_NAME_IN_SPEC,
295 })),
296 Some(_) => Err(FederationError::internal(format!(
297 "Unexpectedly found non-union for federation spec's `{FEDERATION_ENTITY_TYPE_NAME_IN_SPEC}` type definition"
298 ))),
299 None => Ok(None),
300 }
301 }
302
303 pub(crate) fn service_type(&self) -> Result<ObjectTypeDefinitionPosition, FederationError> {
305 match self.schema.types.get(&FEDERATION_SERVICE_TYPE_NAME_IN_SPEC) {
307 Some(ExtendedType::Object(_)) => Ok(ObjectTypeDefinitionPosition {
308 type_name: FEDERATION_SERVICE_TYPE_NAME_IN_SPEC,
309 }),
310 Some(_) => bail!(
311 "Unexpected type found for federation spec's `{spec_name}` type definition",
312 spec_name = FEDERATION_SERVICE_TYPE_NAME_IN_SPEC,
313 ),
314 None => bail!(
315 "Unexpected: type not found for federation spec's `{spec_name}`",
316 spec_name = FEDERATION_SERVICE_TYPE_NAME_IN_SPEC,
317 ),
318 }
319 }
320
321 pub(crate) fn is_fed_2(&self) -> bool {
324 self.federation_link()
325 .is_some_and(|link| link.url.version.satisfies(&Version { major: 2, minor: 0 }))
326 }
327
328 fn federation_link(&self) -> Option<&Arc<Link>> {
330 self.metadata().and_then(|metadata| {
331 metadata
332 .by_identity
333 .get(FederationSpecDefinition::latest().identity())
334 })
335 }
336
337 pub(crate) fn field_set_type(&self) -> Result<ScalarTypeDefinitionPosition, FederationError> {
339 let name_in_schema =
340 self.federation_type_name_in_schema(FEDERATION_FIELDSET_TYPE_NAME_IN_SPEC)?;
341 match self.schema.types.get(&name_in_schema) {
342 Some(ExtendedType::Scalar(_)) => Ok(ScalarTypeDefinitionPosition {
343 type_name: name_in_schema,
344 }),
345 Some(_) => bail!(
346 "Unexpected type found for federation spec's `{name_in_schema}` type definition"
347 ),
348 None => {
349 bail!("Unexpected: type not found for federation spec's `{name_in_schema}`")
350 }
351 }
352 }
353
354 pub(crate) fn federation_type_name_in_schema(
359 &self,
360 name: Name,
361 ) -> Result<Name, FederationError> {
362 if name.starts_with('_') {
370 return Ok(name);
371 }
372
373 if self.is_fed_2() {
374 let Some(links) = self.metadata() else {
375 bail!("Schema should be a core schema")
376 };
377 let Some(federation_link) = links
378 .by_identity
379 .get(FederationSpecDefinition::latest().identity())
380 else {
381 bail!("Schema should have the latest federation link")
382 };
383 Ok(federation_link.type_name_in_schema(&name))
384 } else {
385 Name::new(&format!("_{name}"))
387 .map_err(|e| internal_error!("Invalid name `_{name}`: {e}"))
388 }
389 }
390
391 pub(crate) fn compose_directive_applications(
392 &self,
393 ) -> FallibleDirectiveIterator<ComposeDirectiveDirective<'_>> {
394 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
395 let compose_directive_definition = federation_spec.compose_directive_definition(self)?;
396 let directives = self
397 .schema()
398 .schema_definition
399 .directives
400 .get_all(&compose_directive_definition.name)
401 .map(|d| {
402 let arguments = federation_spec.compose_directive_arguments(d);
403 arguments.map(|args| ComposeDirectiveDirective { arguments: args })
404 })
405 .collect();
406 Ok(directives)
407 }
408
409 pub(crate) fn context_directive_applications(
411 &self,
412 ) -> FallibleDirectiveIterator<ContextDirective<'_>> {
413 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
414 let context_directive_definition = federation_spec.context_directive_definition(self)?;
415 let context_directive_referencers = self
416 .referencers()
417 .get_directive(&context_directive_definition.name);
418
419 let mut applications = Vec::new();
420 for interface_type_position in &context_directive_referencers.interface_types {
421 match interface_type_position.get(self.schema()) {
422 Ok(interface_type) => {
423 let directives = &interface_type.directives;
424 for directive in directives.get_all(&context_directive_definition.name) {
425 let arguments = federation_spec.context_directive_arguments(directive);
426 applications.push(arguments.map(|args| ContextDirective {
427 arguments: args,
428 target: interface_type_position.clone().into(),
429 }));
430 }
431 }
432 Err(error) => applications.push(Err(error.into())),
433 }
434 }
435 for object_type_position in &context_directive_referencers.object_types {
436 match object_type_position.get(self.schema()) {
437 Ok(object_type) => {
438 let directives = &object_type.directives;
439 for directive in directives.get_all(&context_directive_definition.name) {
440 let arguments = federation_spec.context_directive_arguments(directive);
441 applications.push(arguments.map(|args| ContextDirective {
442 arguments: args,
443 target: object_type_position.clone().into(),
444 }));
445 }
446 }
447 Err(error) => applications.push(Err(error.into())),
448 }
449 }
450 for union_type_position in &context_directive_referencers.union_types {
451 match union_type_position.get(self.schema()) {
452 Ok(union_type) => {
453 let directives = &union_type.directives;
454 for directive in directives.get_all(&context_directive_definition.name) {
455 let arguments = federation_spec.context_directive_arguments(directive);
456 applications.push(arguments.map(|args| ContextDirective {
457 arguments: args,
458 target: union_type_position.clone().into(),
459 }));
460 }
461 }
462 Err(error) => applications.push(Err(error.into())),
463 }
464 }
465 Ok(applications)
466 }
467
468 pub(crate) fn context_directive_applications_in_supergraph(
470 &self,
471 context_spec: &ContextSpecDefinition,
472 ) -> FallibleDirectiveIterator<ContextDirective<'_>> {
473 let context_directive_definition = context_spec.context_directive_definition(self)?;
474 let context_directive_referencers = self
475 .referencers()
476 .get_directive(&context_directive_definition.name);
477 let mut applications = Vec::new();
478 for type_pos in context_directive_referencers.composite_type_positions() {
479 let directive_apps =
480 type_pos.get_applied_directives(self, &context_directive_definition.name);
481 for app in directive_apps {
482 let arguments = context_spec.context_directive_arguments(app);
483 applications.push(arguments.map(|args| ContextDirective {
484 arguments: ContextDirectiveArguments { name: args.name },
487 target: type_pos.clone(),
488 }));
489 }
490 }
491 Ok(applications)
492 }
493
494 #[allow(clippy::wrong_self_convention)]
495 pub(crate) fn from_context_directive_applications(
496 &self,
497 ) -> FallibleDirectiveIterator<FromContextDirective<'_>> {
498 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
499 let from_context_directive_definition =
500 federation_spec.from_context_directive_definition(self)?;
501 let from_context_directive_referencers = self
502 .referencers()
503 .get_directive(&from_context_directive_definition.name);
504
505 let mut applications = Vec::new();
506
507 for directive_argument_position in &from_context_directive_referencers.directive_arguments {
509 applications.push(Err(SingleFederationError::ContextNotSet {
510 message: format!(
511 "@fromContext argument cannot be used on a directive definition argument \"{}\".",
512 directive_argument_position
513 ),
514 }
515 .into()));
516 }
517 for interface_field_argument_position in
518 &from_context_directive_referencers.interface_field_arguments
519 {
520 match interface_field_argument_position.get(self.schema()) {
521 Ok(interface_field_argument) => {
522 let directives = &interface_field_argument.directives;
523 for directive in directives.get_all(&from_context_directive_definition.name) {
524 let arguments = federation_spec.from_context_directive_arguments(directive);
525 applications.push(arguments.map(|args| FromContextDirective {
526 arguments: args,
527 target: interface_field_argument_position.clone().into(),
528 }));
529 }
530 }
531 Err(error) => applications.push(Err(error.into())),
532 }
533 }
534 for object_field_argument_position in
535 &from_context_directive_referencers.object_field_arguments
536 {
537 match object_field_argument_position.get(self.schema()) {
538 Ok(object_field_argument) => {
539 let directives = &object_field_argument.directives;
540 for directive in directives.get_all(&from_context_directive_definition.name) {
541 let arguments = federation_spec.from_context_directive_arguments(directive);
542 applications.push(arguments.map(|args| FromContextDirective {
543 arguments: args,
544 target: object_field_argument_position.clone().into(),
545 }));
546 }
547 }
548 Err(error) => applications.push(Err(error.into())),
549 }
550 }
551 Ok(applications)
552 }
553
554 pub(crate) fn key_directive_applications(&self) -> FallibleDirectiveIterator<KeyDirective<'_>> {
555 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
556 let key_directive_definition = federation_spec.key_directive_definition(self)?;
557 let key_directive_referencers = self
558 .referencers()
559 .get_directive(&key_directive_definition.name);
560
561 let mut applications: Vec<Result<KeyDirective, FederationError>> = Vec::new();
562 for object_type_position in &key_directive_referencers.object_types {
563 match object_type_position.get(self.schema()) {
564 Ok(object_type) => {
565 let directives = &object_type.directives;
566 for directive in directives.get_all(&key_directive_definition.name) {
567 if !matches!(
568 directive
569 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
570 .map(|arg| arg.as_ref()),
571 Ok(Value::String(_)),
572 ) {
573 applications.push(Err(SingleFederationError::KeyInvalidFieldsType {
576 target_type: object_type_position.type_name.clone(),
577 application: directive.to_string(),
578 }
579 .into()))
580 } else {
581 let arguments = federation_spec.key_directive_arguments(directive);
582 applications.push(arguments.map(|args| KeyDirective {
583 arguments: args,
584 schema_directive: directive,
585 sibling_directives: directives,
586 target: object_type_position.clone().into(),
587 }));
588 }
589 }
590 }
591 Err(error) => applications.push(Err(error.into())),
592 }
593 }
594 for interface_type_position in &key_directive_referencers.interface_types {
595 match interface_type_position.get(self.schema()) {
596 Ok(interface_type) => {
597 let directives = &interface_type.directives;
598 for directive in directives.get_all(&key_directive_definition.name) {
599 let arguments = federation_spec.key_directive_arguments(directive);
600 applications.push(arguments.map(|args| KeyDirective {
601 arguments: args,
602 schema_directive: directive,
603 sibling_directives: directives,
604 target: interface_type_position.clone().into(),
605 }));
606 }
607 }
608 Err(error) => applications.push(Err(error.into())),
609 }
610 }
611 Ok(applications)
612 }
613
614 pub(crate) fn provides_directive_applications(
615 &self,
616 ) -> FallibleDirectiveIterator<ProvidesDirective<'_>> {
617 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
618 let provides_directive_definition = federation_spec.provides_directive_definition(self)?;
619 let provides_directive_referencers = self
620 .referencers()
621 .get_directive(&provides_directive_definition.name);
622
623 let mut applications: Vec<Result<ProvidesDirective, FederationError>> = Vec::new();
624 for field_definition_position in provides_directive_referencers.object_or_interface_fields()
625 {
626 match field_definition_position.get(self.schema()) {
627 Ok(field_definition) => {
628 let directives = &field_definition.directives;
629 for provides_directive_application in
630 directives.get_all(&provides_directive_definition.name)
631 {
632 if !matches!(
633 provides_directive_application
634 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
635 .map(|arg| arg.as_ref()),
636 Ok(Value::String(_)),
637 ) {
638 applications.push(Err(
641 SingleFederationError::ProvidesInvalidFieldsType {
642 coordinate: field_definition_position.coordinate(),
643 application: provides_directive_application.to_string(),
644 }
645 .into(),
646 ))
647 } else {
648 let arguments = federation_spec
649 .provides_directive_arguments(provides_directive_application);
650 applications.push(arguments.map(|args| ProvidesDirective {
651 arguments: args,
652 schema_directive: provides_directive_application,
653 target: field_definition_position.clone(),
654 target_return_type: field_definition.ty.inner_named_type(),
655 }));
656 }
657 }
658 }
659 Err(error) => applications.push(Err(error.into())),
660 }
661 }
662 Ok(applications)
663 }
664
665 pub(crate) fn requires_directive_applications(
666 &self,
667 ) -> FallibleDirectiveIterator<RequiresDirective<'_>> {
668 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
669 let requires_directive_definition = federation_spec.requires_directive_definition(self)?;
670 let requires_directive_referencers = self
671 .referencers()
672 .get_directive(&requires_directive_definition.name);
673
674 let mut applications = Vec::new();
675 for field_definition_position in requires_directive_referencers.object_or_interface_fields()
676 {
677 match field_definition_position.get(self.schema()) {
678 Ok(field_definition) => {
679 let directives = &field_definition.directives;
680 for requires_directive_application in
681 directives.get_all(&requires_directive_definition.name)
682 {
683 if !matches!(
684 requires_directive_application
685 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
686 .map(|arg| arg.as_ref()),
687 Ok(Value::String(_)),
688 ) {
689 applications.push(Err(
692 SingleFederationError::RequiresInvalidFieldsType {
693 coordinate: field_definition_position.coordinate(),
694 application: requires_directive_application.to_string(),
695 }
696 .into(),
697 ))
698 } else {
699 let arguments = federation_spec
700 .requires_directive_arguments(requires_directive_application);
701 applications.push(arguments.map(|args| RequiresDirective {
702 arguments: args,
703 schema_directive: requires_directive_application,
704 target: field_definition_position.clone(),
705 }));
706 }
707 }
708 }
709 Err(error) => applications.push(Err(error.into())),
710 }
711 }
712 Ok(applications)
713 }
714
715 pub(crate) fn tag_directive_applications(&self) -> FallibleDirectiveIterator<TagDirective<'_>> {
716 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
717 let tag_directive_definition = federation_spec.tag_directive_definition(self)?;
718 let tag_directive_referencers = self
719 .referencers()
720 .get_directive(&tag_directive_definition.name);
721
722 let mut applications = Vec::new();
723 if let Some(schema_position) = &tag_directive_referencers.schema {
725 let schema_def = schema_position.get(self.schema());
726 let directives = &schema_def.directives;
727 for tag_directive_application in directives.get_all(&tag_directive_definition.name) {
728 let arguments = federation_spec.tag_directive_arguments(tag_directive_application);
729 applications.push(arguments.map(|args| TagDirective {
730 arguments: args,
731 target: TagDirectiveTargetPosition::Schema(schema_position.clone()),
732 directive: tag_directive_application,
733 }));
734 }
735 }
736 for interface_type_position in &tag_directive_referencers.interface_types {
738 match interface_type_position.get(self.schema()) {
739 Ok(interface_type) => {
740 let directives = &interface_type.directives;
741 for tag_directive_application in
742 directives.get_all(&tag_directive_definition.name)
743 {
744 let arguments =
745 federation_spec.tag_directive_arguments(tag_directive_application);
746 applications.push(arguments.map(|args| TagDirective {
747 arguments: args,
748 target: TagDirectiveTargetPosition::Interface(
749 interface_type_position.clone(),
750 ),
751 directive: tag_directive_application,
752 }));
753 }
754 }
755 Err(error) => applications.push(Err(error.into())),
756 }
757 }
758 for field_definition_position in &tag_directive_referencers.interface_fields {
760 match field_definition_position.get(self.schema()) {
761 Ok(field_definition) => {
762 let directives = &field_definition.directives;
763 for tag_directive_application in
764 directives.get_all(&tag_directive_definition.name)
765 {
766 let arguments =
767 federation_spec.tag_directive_arguments(tag_directive_application);
768 applications.push(arguments.map(|args| TagDirective {
769 arguments: args,
770 target: TagDirectiveTargetPosition::InterfaceField(
771 field_definition_position.clone(),
772 ),
773 directive: tag_directive_application,
774 }));
775 }
776 }
777 Err(error) => applications.push(Err(error.into())),
778 }
779 }
780 for argument_definition_position in &tag_directive_referencers.interface_field_arguments {
782 match argument_definition_position.get(self.schema()) {
783 Ok(argument_definition) => {
784 let directives = &argument_definition.directives;
785 for tag_directive_application in
786 directives.get_all(&tag_directive_definition.name)
787 {
788 let arguments =
789 federation_spec.tag_directive_arguments(tag_directive_application);
790 applications.push(arguments.map(|args| TagDirective {
791 arguments: args,
792 target: TagDirectiveTargetPosition::ArgumentDefinition(
793 argument_definition_position.clone().into(),
794 ),
795 directive: tag_directive_application,
796 }));
797 }
798 }
799 Err(error) => applications.push(Err(error.into())),
800 }
801 }
802 for object_type_position in &tag_directive_referencers.object_types {
804 match object_type_position.get(self.schema()) {
805 Ok(object_type) => {
806 let directives = &object_type.directives;
807 for tag_directive_application in
808 directives.get_all(&tag_directive_definition.name)
809 {
810 let arguments =
811 federation_spec.tag_directive_arguments(tag_directive_application);
812 applications.push(arguments.map(|args| TagDirective {
813 arguments: args,
814 target: TagDirectiveTargetPosition::Object(
815 object_type_position.clone(),
816 ),
817 directive: tag_directive_application,
818 }));
819 }
820 }
821 Err(error) => applications.push(Err(error.into())),
822 }
823 }
824 for field_definition_position in &tag_directive_referencers.object_fields {
826 match field_definition_position.get(self.schema()) {
827 Ok(field_definition) => {
828 let directives = &field_definition.directives;
829 for tag_directive_application in
830 directives.get_all(&tag_directive_definition.name)
831 {
832 let arguments =
833 federation_spec.tag_directive_arguments(tag_directive_application);
834 applications.push(arguments.map(|args| TagDirective {
835 arguments: args,
836 target: TagDirectiveTargetPosition::ObjectField(
837 field_definition_position.clone(),
838 ),
839 directive: tag_directive_application,
840 }));
841 }
842 }
843 Err(error) => applications.push(Err(error.into())),
844 }
845 }
846 for argument_definition_position in &tag_directive_referencers.object_field_arguments {
848 match argument_definition_position.get(self.schema()) {
849 Ok(argument_definition) => {
850 let directives = &argument_definition.directives;
851 for tag_directive_application in
852 directives.get_all(&tag_directive_definition.name)
853 {
854 let arguments =
855 federation_spec.tag_directive_arguments(tag_directive_application);
856 applications.push(arguments.map(|args| TagDirective {
857 arguments: args,
858 target: TagDirectiveTargetPosition::ArgumentDefinition(
859 argument_definition_position.clone().into(),
860 ),
861 directive: tag_directive_application,
862 }));
863 }
864 }
865 Err(error) => applications.push(Err(error.into())),
866 }
867 }
868 for union_type_position in &tag_directive_referencers.union_types {
870 match union_type_position.get(self.schema()) {
871 Ok(union_type) => {
872 let directives = &union_type.directives;
873 for tag_directive_application in
874 directives.get_all(&tag_directive_definition.name)
875 {
876 let arguments =
877 federation_spec.tag_directive_arguments(tag_directive_application);
878 applications.push(arguments.map(|args| TagDirective {
879 arguments: args,
880 target: TagDirectiveTargetPosition::Union(union_type_position.clone()),
881 directive: tag_directive_application,
882 }));
883 }
884 }
885 Err(error) => applications.push(Err(error.into())),
886 }
887 }
888
889 for scalar_type_position in &tag_directive_referencers.scalar_types {
891 match scalar_type_position.get(self.schema()) {
892 Ok(scalar_type) => {
893 let directives = &scalar_type.directives;
894 for tag_directive_application in
895 directives.get_all(&tag_directive_definition.name)
896 {
897 let arguments =
898 federation_spec.tag_directive_arguments(tag_directive_application);
899 applications.push(arguments.map(|args| TagDirective {
900 arguments: args,
901 target: TagDirectiveTargetPosition::Scalar(
902 scalar_type_position.clone(),
903 ),
904 directive: tag_directive_application,
905 }));
906 }
907 }
908 Err(error) => applications.push(Err(error.into())),
909 }
910 }
911 for enum_type_position in &tag_directive_referencers.enum_types {
913 match enum_type_position.get(self.schema()) {
914 Ok(enum_type) => {
915 let directives = &enum_type.directives;
916 for tag_directive_application in
917 directives.get_all(&tag_directive_definition.name)
918 {
919 let arguments =
920 federation_spec.tag_directive_arguments(tag_directive_application);
921 applications.push(arguments.map(|args| TagDirective {
922 arguments: args,
923 target: TagDirectiveTargetPosition::Enum(enum_type_position.clone()),
924 directive: tag_directive_application,
925 }));
926 }
927 }
928 Err(error) => applications.push(Err(error.into())),
929 }
930 }
931 for enum_value_position in &tag_directive_referencers.enum_values {
933 match enum_value_position.get(self.schema()) {
934 Ok(enum_value) => {
935 let directives = &enum_value.directives;
936 for tag_directive_application in
937 directives.get_all(&tag_directive_definition.name)
938 {
939 let arguments =
940 federation_spec.tag_directive_arguments(tag_directive_application);
941 applications.push(arguments.map(|args| TagDirective {
942 arguments: args,
943 target: TagDirectiveTargetPosition::EnumValue(
944 enum_value_position.clone(),
945 ),
946 directive: tag_directive_application,
947 }));
948 }
949 }
950 Err(error) => applications.push(Err(error.into())),
951 }
952 }
953 for input_object_type_position in &tag_directive_referencers.input_object_types {
955 match input_object_type_position.get(self.schema()) {
956 Ok(input_object_type) => {
957 let directives = &input_object_type.directives;
958 for tag_directive_application in
959 directives.get_all(&tag_directive_definition.name)
960 {
961 let arguments =
962 federation_spec.tag_directive_arguments(tag_directive_application);
963 applications.push(arguments.map(|args| TagDirective {
964 arguments: args,
965 target: TagDirectiveTargetPosition::InputObject(
966 input_object_type_position.clone(),
967 ),
968 directive: tag_directive_application,
969 }));
970 }
971 }
972 Err(error) => applications.push(Err(error.into())),
973 }
974 }
975 for input_field_definition_position in &tag_directive_referencers.input_object_fields {
977 match input_field_definition_position.get(self.schema()) {
978 Ok(input_field_definition) => {
979 let directives = &input_field_definition.directives;
980 for tag_directive_application in
981 directives.get_all(&tag_directive_definition.name)
982 {
983 let arguments =
984 federation_spec.tag_directive_arguments(tag_directive_application);
985 applications.push(arguments.map(|args| TagDirective {
986 arguments: args,
987 target: TagDirectiveTargetPosition::InputObjectFieldDefinition(
988 input_field_definition_position.clone(),
989 ),
990 directive: tag_directive_application,
991 }));
992 }
993 }
994 Err(error) => applications.push(Err(error.into())),
995 }
996 }
997 for directive_definition_position in &tag_directive_referencers.directive_arguments {
999 match directive_definition_position.get(self.schema()) {
1000 Ok(directive_definition) => {
1001 let directives = &directive_definition.directives;
1002 for tag_directive_application in
1003 directives.get_all(&tag_directive_definition.name)
1004 {
1005 let arguments =
1006 federation_spec.tag_directive_arguments(tag_directive_application);
1007 applications.push(arguments.map(|args| TagDirective {
1008 arguments: args,
1009 target: TagDirectiveTargetPosition::DirectiveArgumentDefinition(
1010 directive_definition_position.clone(),
1011 ),
1012 directive: tag_directive_application,
1013 }));
1014 }
1015 }
1016 Err(error) => applications.push(Err(error.into())),
1017 }
1018 }
1019
1020 Ok(applications)
1021 }
1022
1023 pub(crate) fn list_size_directive_applications(
1024 &self,
1025 ) -> FallibleDirectiveIterator<ListSizeDirective<'_>> {
1026 let Some(list_size_directive_name) = CostSpecDefinition::list_size_directive_name(self)?
1027 else {
1028 return Ok(Vec::new());
1029 };
1030 let list_size_directive_referencers = self
1031 .referencers()
1032 .get_directive(list_size_directive_name.as_str());
1033
1034 let mut applications = Vec::new();
1035 for field_definition_position in
1036 list_size_directive_referencers.object_or_interface_fields()
1037 {
1038 let field_definition = field_definition_position.get(self.schema())?;
1039 match CostSpecDefinition::list_size_directive_from_field_definition(
1040 self,
1041 field_definition,
1042 ) {
1043 Ok(Some(list_size_directive)) => {
1044 applications.push(Ok(ListSizeDirective {
1045 directive: list_size_directive,
1046 parent_type: field_definition_position.type_name().clone(),
1047 target: field_definition,
1048 }));
1049 }
1050 Ok(None) => {
1051 }
1053 Err(error) => {
1054 applications.push(Err(error));
1055 }
1056 }
1057 }
1058
1059 Ok(applications)
1060 }
1061
1062 pub(crate) fn cache_tag_directive_applications(
1063 &self,
1064 ) -> FallibleDirectiveIterator<CacheTagDirective<'_>> {
1065 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
1066 let Ok(cache_tag_directive_definition) =
1067 federation_spec.cache_tag_directive_definition(self)
1068 else {
1069 return Ok(Vec::new());
1070 };
1071
1072 let result = self
1073 .referencers()
1074 .get_directive_applications(self, &cache_tag_directive_definition.name)
1075 .map(|(pos, application)| {
1076 let arguments = federation_spec.cache_tag_directive_arguments(application);
1077 arguments.map(|args| CacheTagDirective {
1078 arguments: args,
1079 target: pos,
1080 })
1081 })
1082 .collect();
1083 Ok(result)
1084 }
1085
1086 pub(crate) fn is_interface(&self, type_name: &Name) -> bool {
1087 self.referencers().interface_types.contains_key(type_name)
1088 }
1089
1090 pub(crate) fn all_features(&self) -> Result<Vec<&'static dyn SpecDefinition>, FederationError> {
1091 let Some(links) = self.metadata() else {
1092 return Ok(Vec::new());
1093 };
1094
1095 let mut features: Vec<&'static dyn SpecDefinition> =
1096 Vec::with_capacity(links.all_links().len());
1097
1098 for link in links.all_links() {
1099 if let Some(spec) = SPEC_REGISTRY.get_definition(&link.url) {
1100 features.push(*spec);
1101 } else if let Some(supported_versions) = SPEC_REGISTRY.get_versions(&link.url.identity)
1102 {
1103 return Err(
1104 SingleFederationError::UnknownLinkVersion {
1105 message: format!(
1106 "Detected unsupported {} specification version {}. Please upgrade to a composition version which supports that version, or select one of the following supported versions: {}.",
1107 link.url.identity.name,
1108 link.url.version,
1109 supported_versions.iter().join(", ")
1110 ),
1111 }.into());
1112 }
1113 }
1114
1115 Ok(features)
1116 }
1117
1118 pub(crate) fn node_locations<T>(
1119 &self,
1120 node: &Node<T>,
1121 ) -> impl Iterator<Item = Range<LineColumn>> {
1122 node.line_column_range(&self.schema().sources).into_iter()
1123 }
1124}
1125
1126type FallibleDirectiveIterator<D> = Result<Vec<Result<D, FederationError>>, FederationError>;
1127
1128#[derive(Clone)]
1129pub(crate) struct ComposeDirectiveDirective<'schema> {
1130 pub(crate) arguments: ComposeDirectiveArguments<'schema>,
1132}
1133
1134pub(crate) struct ContextDirective<'schema> {
1135 arguments: ContextDirectiveArguments<'schema>,
1137 target: CompositeTypeDefinitionPosition,
1139}
1140
1141impl ContextDirective<'_> {
1142 pub(crate) fn arguments(&self) -> &ContextDirectiveArguments<'_> {
1143 &self.arguments
1144 }
1145
1146 pub(crate) fn target(&self) -> &CompositeTypeDefinitionPosition {
1147 &self.target
1148 }
1149}
1150
1151pub(crate) struct FromContextDirective<'schema> {
1152 arguments: FromContextDirectiveArguments<'schema>,
1154 target: FieldArgumentDefinitionPosition,
1156}
1157
1158pub(crate) struct KeyDirective<'schema> {
1159 arguments: KeyDirectiveArguments<'schema>,
1161 schema_directive: &'schema apollo_compiler::schema::Component<Directive>,
1163 sibling_directives: &'schema apollo_compiler::schema::DirectiveList,
1165 target: ObjectOrInterfaceTypeDefinitionPosition,
1167}
1168
1169impl HasFields for KeyDirective<'_> {
1170 fn fields(&self) -> &str {
1171 self.arguments.fields
1172 }
1173
1174 fn target_type(&self) -> &Name {
1175 self.target.type_name()
1176 }
1177}
1178
1179impl KeyDirective<'_> {
1180 pub(crate) fn target(&self) -> &ObjectOrInterfaceTypeDefinitionPosition {
1181 &self.target
1182 }
1183}
1184
1185pub(crate) struct ListSizeDirective<'schema> {
1186 directive: cost_spec_definition::ListSizeDirective,
1188 parent_type: Name,
1190 target: &'schema FieldDefinition,
1192}
1193
1194pub(crate) struct ProvidesDirective<'schema> {
1195 arguments: ProvidesDirectiveArguments<'schema>,
1197 schema_directive: &'schema Node<Directive>,
1199 target: ObjectOrInterfaceFieldDefinitionPosition,
1203 target_return_type: &'schema Name,
1205}
1206
1207impl HasFields for ProvidesDirective<'_> {
1208 fn fields(&self) -> &str {
1210 self.arguments.fields
1211 }
1212
1213 fn target_type(&self) -> &Name {
1215 self.target_return_type
1216 }
1217}
1218
1219pub(crate) struct RequiresDirective<'schema> {
1220 arguments: RequiresDirectiveArguments<'schema>,
1222 schema_directive: &'schema Node<Directive>,
1224 target: ObjectOrInterfaceFieldDefinitionPosition,
1228}
1229
1230impl HasFields for RequiresDirective<'_> {
1231 fn fields(&self) -> &str {
1232 self.arguments.fields
1233 }
1234
1235 fn target_type(&self) -> &Name {
1236 self.target.type_name()
1237 }
1238}
1239
1240pub(crate) struct TagDirective<'schema> {
1241 arguments: TagDirectiveArguments<'schema>,
1243 target: TagDirectiveTargetPosition, directive: &'schema Node<Directive>,
1247}
1248
1249pub(crate) struct CacheTagDirective<'schema> {
1250 arguments: CacheTagDirectiveArguments<'schema>,
1252 target: DirectiveTargetPosition,
1254}
1255
1256pub(crate) trait HasFields {
1257 fn fields(&self) -> &str;
1258 fn target_type(&self) -> &Name;
1259
1260 fn parse_fields(&self, schema: &Schema) -> Result<FieldSet, WithErrors<FieldSet>> {
1261 FieldSet::parse(
1262 Valid::assume_valid_ref(schema),
1263 self.target_type().clone(),
1264 self.fields(),
1265 "field_set.graphql",
1266 )
1267 }
1268}
1269
1270#[derive(Clone)]
1272pub struct ValidFederationSchema {
1273 schema: Arc<Valid<FederationSchema>>,
1274}
1275
1276impl ValidFederationSchema {
1277 pub fn new(schema: Valid<Schema>) -> Result<ValidFederationSchema, FederationError> {
1278 let schema = FederationSchema::new(schema.into_inner())?;
1279
1280 Self::new_assume_valid(schema).map_err(|(_schema, error)| error)
1281 }
1282
1283 #[allow(clippy::result_large_err)] pub fn new_assume_valid(
1286 mut schema: FederationSchema,
1287 ) -> Result<ValidFederationSchema, (FederationSchema, FederationError)> {
1288 let valid_schema = Valid::assume_valid_ref(&schema);
1295 let subgraph_metadata = match compute_subgraph_metadata(valid_schema) {
1296 Ok(metadata) => metadata.map(Box::new),
1297 Err(err) => return Err((schema, err)),
1298 };
1299 schema.subgraph_metadata = subgraph_metadata;
1300
1301 let schema = Arc::new(Valid::assume_valid(schema));
1302 Ok(ValidFederationSchema { schema })
1303 }
1304
1305 pub fn schema(&self) -> &Valid<Schema> {
1307 Valid::assume_valid_ref(&self.schema.schema)
1308 }
1309
1310 pub(crate) fn subgraph_metadata(&self) -> Option<&SubgraphMetadata> {
1314 self.schema.subgraph_metadata.as_deref()
1315 }
1316
1317 pub(crate) fn federation_type_name_in_schema(
1318 &self,
1319 name: Name,
1320 ) -> Result<Name, FederationError> {
1321 if name.starts_with('_') {
1328 return Ok(name);
1329 }
1330
1331 Err(FederationError::internal(
1335 "typename should have been looked in a federation feature",
1336 ))
1337 }
1338
1339 pub(crate) fn is_interface_object_type(
1340 &self,
1341 type_definition_position: TypeDefinitionPosition,
1342 ) -> Result<bool, FederationError> {
1343 let Some(subgraph_metadata) = &self.subgraph_metadata else {
1344 return Ok(false);
1345 };
1346 let Some(interface_object_directive_definition) = subgraph_metadata
1347 .federation_spec_definition()
1348 .interface_object_directive_definition(self)?
1349 else {
1350 return Ok(false);
1351 };
1352 match type_definition_position {
1353 TypeDefinitionPosition::Object(type_) => Ok(type_
1354 .get(self.schema())?
1355 .directives
1356 .has(&interface_object_directive_definition.name)),
1357 _ => Ok(false),
1358 }
1359 }
1360}
1361
1362impl Deref for ValidFederationSchema {
1363 type Target = FederationSchema;
1364
1365 fn deref(&self) -> &Self::Target {
1366 &self.schema
1367 }
1368}
1369
1370impl Eq for ValidFederationSchema {}
1371
1372impl PartialEq for ValidFederationSchema {
1373 fn eq(&self, other: &ValidFederationSchema) -> bool {
1374 Arc::ptr_eq(&self.schema, &other.schema)
1375 }
1376}
1377
1378impl Hash for ValidFederationSchema {
1379 fn hash<H: Hasher>(&self, state: &mut H) {
1380 Arc::as_ptr(&self.schema).hash(state);
1381 }
1382}
1383
1384impl std::fmt::Debug for ValidFederationSchema {
1385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1386 write!(f, "ValidFederationSchema @ {:?}", Arc::as_ptr(&self.schema))
1387 }
1388}
1389
1390impl From<ValidFederationSchema> for FederationSchema {
1391 fn from(value: ValidFederationSchema) -> Self {
1392 Arc::unwrap_or_clone(value.schema).into_inner()
1393 }
1394}
1395
1396pub(crate) trait SchemaElement {
1397 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin>;
1400
1401 fn definition_and_extensions(&self) -> (bool, IndexSet<&ExtensionId>) {
1404 let mut extensions = IndexSet::default();
1405 let mut has_definition = false;
1406 for origin in self.iter_origins() {
1407 if let Some(extension_id) = origin.extension_id() {
1408 extensions.insert(extension_id);
1409 } else {
1410 has_definition = true;
1411 }
1412 }
1413 (has_definition, extensions)
1414 }
1415
1416 fn extensions(&self) -> IndexSet<&ExtensionId> {
1417 self.definition_and_extensions().1
1418 }
1419
1420 fn has_extension_elements(&self) -> bool {
1421 !self.extensions().is_empty()
1422 }
1423
1424 fn origin_to_use(&self) -> ComponentOrigin {
1425 let extensions = self.extensions();
1426 let first_extension = extensions.first();
1429 if let Some(first_extension) = first_extension {
1430 ComponentOrigin::Extension((*first_extension).clone())
1432 } else {
1433 ComponentOrigin::Definition
1436 }
1437 }
1438}
1439
1440impl SchemaElement for SchemaDefinition {
1441 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1442 self.iter_origins()
1443 }
1444}
1445
1446impl SchemaElement for ExtendedType {
1447 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1448 self.iter_origins()
1449 }
1450}