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