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