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::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::metadata::LinksMetadata;
53use crate::link::spec::Version;
54use crate::link::spec_definition::SpecDefinition;
55use crate::link::spec_registry::SPEC_REGISTRY;
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 self.try_get_type(type_name).ok_or_else(|| {
172 SingleFederationError::Internal {
173 message: format!("Schema has no type \"{type_name}\""),
174 }
175 .into()
176 })
177 }
178
179 pub(crate) fn try_get_type(&self, type_name: &Name) -> Option<TypeDefinitionPosition> {
180 let type_ = self.schema.types.get(type_name)?;
181 let type_name = type_name.clone();
182 Some(match type_ {
183 ExtendedType::Scalar(_) => ScalarTypeDefinitionPosition { type_name }.into(),
184 ExtendedType::Object(_) => ObjectTypeDefinitionPosition { type_name }.into(),
185 ExtendedType::Interface(_) => InterfaceTypeDefinitionPosition { type_name }.into(),
186 ExtendedType::Union(_) => UnionTypeDefinitionPosition { type_name }.into(),
187 ExtendedType::Enum(_) => EnumTypeDefinitionPosition { type_name }.into(),
188 ExtendedType::InputObject(_) => InputObjectTypeDefinitionPosition { type_name }.into(),
189 })
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.for_identity(FederationSpecDefinition::latest().identity())
348 })
349 }
350
351 pub(crate) fn field_set_type(&self) -> Result<ScalarTypeDefinitionPosition, FederationError> {
353 let name_in_schema =
354 self.federation_type_name_in_schema(FEDERATION_FIELDSET_TYPE_NAME_IN_SPEC)?;
355 match self.schema.types.get(&name_in_schema) {
356 Some(ExtendedType::Scalar(_)) => Ok(ScalarTypeDefinitionPosition {
357 type_name: name_in_schema,
358 }),
359 Some(_) => bail!(
360 "Unexpected type found for federation spec's `{name_in_schema}` type definition"
361 ),
362 None => {
363 bail!("Unexpected: type not found for federation spec's `{name_in_schema}`")
364 }
365 }
366 }
367
368 pub(crate) fn federation_type_name_in_schema(
373 &self,
374 name: Name,
375 ) -> Result<Name, FederationError> {
376 if name.starts_with('_') {
384 return Ok(name);
385 }
386
387 if self.is_fed_2() {
388 let Some(links) = self.metadata() else {
389 bail!("Schema should be a core schema")
390 };
391 let Some(federation_link) =
392 links.for_identity(FederationSpecDefinition::latest().identity())
393 else {
394 bail!("Schema should have the latest federation link")
395 };
396 Ok(federation_link.type_name_in_schema(&name))
397 } else {
398 Name::new(&format!("_{name}"))
400 .map_err(|e| internal_error!("Invalid name `_{name}`: {e}"))
401 }
402 }
403
404 pub(crate) fn compose_directive_applications(
405 &self,
406 ) -> FallibleDirectiveIterator<ComposeDirectiveDirective<'_>> {
407 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
408 let compose_directive_definition = federation_spec.compose_directive_definition(self)?;
409 let directives = self
410 .schema()
411 .schema_definition
412 .directives
413 .get_all(&compose_directive_definition.name)
414 .map(|d| {
415 let arguments = federation_spec.compose_directive_arguments(d);
416 arguments.map(|args| ComposeDirectiveDirective { arguments: args })
417 })
418 .collect();
419 Ok(directives)
420 }
421
422 pub(crate) fn context_directive_applications(
424 &self,
425 ) -> FallibleDirectiveIterator<ContextDirective<'_>> {
426 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
427 let context_directive_definition = federation_spec.context_directive_definition(self)?;
428 let context_directive_referencers = self
429 .referencers()
430 .get_directive(&context_directive_definition.name);
431
432 let mut applications = Vec::new();
433 for interface_type_position in &context_directive_referencers.interface_types {
434 match interface_type_position.get(self.schema()) {
435 Ok(interface_type) => {
436 let directives = &interface_type.directives;
437 for directive in directives.get_all(&context_directive_definition.name) {
438 let arguments = federation_spec.context_directive_arguments(directive);
439 applications.push(arguments.map(|args| ContextDirective {
440 arguments: args,
441 target: interface_type_position.clone().into(),
442 }));
443 }
444 }
445 Err(error) => applications.push(Err(error.into())),
446 }
447 }
448 for object_type_position in &context_directive_referencers.object_types {
449 match object_type_position.get(self.schema()) {
450 Ok(object_type) => {
451 let directives = &object_type.directives;
452 for directive in directives.get_all(&context_directive_definition.name) {
453 let arguments = federation_spec.context_directive_arguments(directive);
454 applications.push(arguments.map(|args| ContextDirective {
455 arguments: args,
456 target: object_type_position.clone().into(),
457 }));
458 }
459 }
460 Err(error) => applications.push(Err(error.into())),
461 }
462 }
463 for union_type_position in &context_directive_referencers.union_types {
464 match union_type_position.get(self.schema()) {
465 Ok(union_type) => {
466 let directives = &union_type.directives;
467 for directive in directives.get_all(&context_directive_definition.name) {
468 let arguments = federation_spec.context_directive_arguments(directive);
469 applications.push(arguments.map(|args| ContextDirective {
470 arguments: args,
471 target: union_type_position.clone().into(),
472 }));
473 }
474 }
475 Err(error) => applications.push(Err(error.into())),
476 }
477 }
478 Ok(applications)
479 }
480
481 pub(crate) fn context_directive_applications_in_supergraph(
483 &self,
484 context_spec: &ContextSpecDefinition,
485 ) -> FallibleDirectiveIterator<ContextDirective<'_>> {
486 let context_directive_definition = context_spec.context_directive_definition(self)?;
487 let context_directive_referencers = self
488 .referencers()
489 .get_directive(&context_directive_definition.name);
490 let mut applications = Vec::new();
491 for type_pos in context_directive_referencers.composite_type_positions() {
492 let directive_apps =
493 type_pos.get_applied_directives(self, &context_directive_definition.name);
494 for app in directive_apps {
495 let arguments = context_spec.context_directive_arguments(app);
496 applications.push(arguments.map(|args| ContextDirective {
497 arguments: ContextDirectiveArguments { name: args.name },
500 target: type_pos.clone(),
501 }));
502 }
503 }
504 Ok(applications)
505 }
506
507 #[allow(clippy::wrong_self_convention)]
508 pub(crate) fn from_context_directive_applications(
509 &self,
510 ) -> FallibleDirectiveIterator<FromContextDirective<'_>> {
511 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
512 let from_context_directive_definition =
513 federation_spec.from_context_directive_definition(self)?;
514 let from_context_directive_referencers = self
515 .referencers()
516 .get_directive(&from_context_directive_definition.name);
517
518 let mut applications = Vec::new();
519
520 for directive_argument_position in &from_context_directive_referencers.directive_arguments {
522 applications.push(Err(SingleFederationError::ContextNotSet {
523 message: format!(
524 "@fromContext argument cannot be used on a directive definition argument \"{}\".",
525 directive_argument_position
526 ),
527 }
528 .into()));
529 }
530 for interface_field_argument_position in
531 &from_context_directive_referencers.interface_field_arguments
532 {
533 match interface_field_argument_position.get(self.schema()) {
534 Ok(interface_field_argument) => {
535 let directives = &interface_field_argument.directives;
536 for directive in directives.get_all(&from_context_directive_definition.name) {
537 let arguments = federation_spec.from_context_directive_arguments(directive);
538 applications.push(arguments.map(|args| FromContextDirective {
539 arguments: args,
540 target: interface_field_argument_position.clone().into(),
541 }));
542 }
543 }
544 Err(error) => applications.push(Err(error.into())),
545 }
546 }
547 for object_field_argument_position in
548 &from_context_directive_referencers.object_field_arguments
549 {
550 match object_field_argument_position.get(self.schema()) {
551 Ok(object_field_argument) => {
552 let directives = &object_field_argument.directives;
553 for directive in directives.get_all(&from_context_directive_definition.name) {
554 let arguments = federation_spec.from_context_directive_arguments(directive);
555 applications.push(arguments.map(|args| FromContextDirective {
556 arguments: args,
557 target: object_field_argument_position.clone().into(),
558 }));
559 }
560 }
561 Err(error) => applications.push(Err(error.into())),
562 }
563 }
564 Ok(applications)
565 }
566
567 pub(crate) fn key_directive_applications(&self) -> FallibleDirectiveIterator<KeyDirective<'_>> {
568 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
569 let key_directive_definition = federation_spec.key_directive_definition(self)?;
570 let key_directive_referencers = self
571 .referencers()
572 .get_directive(&key_directive_definition.name);
573
574 let mut applications: Vec<Result<KeyDirective, FederationError>> = Vec::new();
575 for object_type_position in &key_directive_referencers.object_types {
576 match object_type_position.get(self.schema()) {
577 Ok(object_type) => {
578 let directives = &object_type.directives;
579 for directive in directives.get_all(&key_directive_definition.name) {
580 if !matches!(
581 directive
582 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
583 .map(|arg| arg.as_ref()),
584 Ok(Value::String(_)),
585 ) {
586 applications.push(Err(SingleFederationError::KeyInvalidFieldsType {
589 target_type: object_type_position.type_name.clone(),
590 application: directive.to_string(),
591 }
592 .into()))
593 } else {
594 let arguments = federation_spec.key_directive_arguments(directive);
595 applications.push(arguments.map(|args| KeyDirective {
596 arguments: args,
597 schema_directive: directive,
598 sibling_directives: directives,
599 target: object_type_position.clone().into(),
600 }));
601 }
602 }
603 }
604 Err(error) => applications.push(Err(error.into())),
605 }
606 }
607 for interface_type_position in &key_directive_referencers.interface_types {
608 match interface_type_position.get(self.schema()) {
609 Ok(interface_type) => {
610 let directives = &interface_type.directives;
611 for directive in directives.get_all(&key_directive_definition.name) {
612 let arguments = federation_spec.key_directive_arguments(directive);
613 applications.push(arguments.map(|args| KeyDirective {
614 arguments: args,
615 schema_directive: directive,
616 sibling_directives: directives,
617 target: interface_type_position.clone().into(),
618 }));
619 }
620 }
621 Err(error) => applications.push(Err(error.into())),
622 }
623 }
624 Ok(applications)
625 }
626
627 pub(crate) fn provides_directive_applications(
628 &self,
629 ) -> FallibleDirectiveIterator<ProvidesDirective<'_>> {
630 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
631 let provides_directive_definition = federation_spec.provides_directive_definition(self)?;
632 let provides_directive_referencers = self
633 .referencers()
634 .get_directive(&provides_directive_definition.name);
635
636 let mut applications: Vec<Result<ProvidesDirective, FederationError>> = Vec::new();
637 for field_definition_position in provides_directive_referencers.object_or_interface_fields()
638 {
639 match field_definition_position.get(self.schema()) {
640 Ok(field_definition) => {
641 let directives = &field_definition.directives;
642 for provides_directive_application in
643 directives.get_all(&provides_directive_definition.name)
644 {
645 if !matches!(
646 provides_directive_application
647 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
648 .map(|arg| arg.as_ref()),
649 Ok(Value::String(_)),
650 ) {
651 applications.push(Err(
654 SingleFederationError::ProvidesInvalidFieldsType {
655 coordinate: field_definition_position.coordinate(),
656 application: provides_directive_application.to_string(),
657 }
658 .into(),
659 ))
660 } else {
661 let arguments = federation_spec
662 .provides_directive_arguments(provides_directive_application);
663 applications.push(arguments.map(|args| ProvidesDirective {
664 arguments: args,
665 schema_directive: provides_directive_application,
666 target: field_definition_position.clone(),
667 target_return_type: field_definition.ty.inner_named_type(),
668 }));
669 }
670 }
671 }
672 Err(error) => applications.push(Err(error.into())),
673 }
674 }
675 Ok(applications)
676 }
677
678 pub(crate) fn requires_directive_applications(
679 &self,
680 ) -> FallibleDirectiveIterator<RequiresDirective<'_>> {
681 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
682 let requires_directive_definition = federation_spec.requires_directive_definition(self)?;
683 let requires_directive_referencers = self
684 .referencers()
685 .get_directive(&requires_directive_definition.name);
686
687 let mut applications = Vec::new();
688 for field_definition_position in requires_directive_referencers.object_or_interface_fields()
689 {
690 match field_definition_position.get(self.schema()) {
691 Ok(field_definition) => {
692 let directives = &field_definition.directives;
693 for requires_directive_application in
694 directives.get_all(&requires_directive_definition.name)
695 {
696 if !matches!(
697 requires_directive_application
698 .argument_by_name(&FEDERATION_FIELDS_ARGUMENT_NAME, self.schema())
699 .map(|arg| arg.as_ref()),
700 Ok(Value::String(_)),
701 ) {
702 applications.push(Err(
705 SingleFederationError::RequiresInvalidFieldsType {
706 coordinate: field_definition_position.coordinate(),
707 application: requires_directive_application.to_string(),
708 }
709 .into(),
710 ))
711 } else {
712 let arguments = federation_spec
713 .requires_directive_arguments(requires_directive_application);
714 applications.push(arguments.map(|args| RequiresDirective {
715 arguments: args,
716 schema_directive: requires_directive_application,
717 target: field_definition_position.clone(),
718 }));
719 }
720 }
721 }
722 Err(error) => applications.push(Err(error.into())),
723 }
724 }
725 Ok(applications)
726 }
727
728 pub(crate) fn tag_directive_applications(&self) -> FallibleDirectiveIterator<TagDirective<'_>> {
729 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
730 let tag_directive_definition = federation_spec.tag_directive_definition(self)?;
731 let tag_directive_referencers = self
732 .referencers()
733 .get_directive(&tag_directive_definition.name);
734
735 let mut applications = Vec::new();
736 if let Some(schema_position) = &tag_directive_referencers.schema {
738 let schema_def = schema_position.get(self.schema());
739 let directives = &schema_def.directives;
740 for tag_directive_application in directives.get_all(&tag_directive_definition.name) {
741 let arguments = federation_spec.tag_directive_arguments(tag_directive_application);
742 applications.push(arguments.map(|args| TagDirective {
743 arguments: args,
744 target: TagDirectiveTargetPosition::Schema(schema_position.clone()),
745 directive: tag_directive_application,
746 }));
747 }
748 }
749 for interface_type_position in &tag_directive_referencers.interface_types {
751 match interface_type_position.get(self.schema()) {
752 Ok(interface_type) => {
753 let directives = &interface_type.directives;
754 for tag_directive_application in
755 directives.get_all(&tag_directive_definition.name)
756 {
757 let arguments =
758 federation_spec.tag_directive_arguments(tag_directive_application);
759 applications.push(arguments.map(|args| TagDirective {
760 arguments: args,
761 target: TagDirectiveTargetPosition::Interface(
762 interface_type_position.clone(),
763 ),
764 directive: tag_directive_application,
765 }));
766 }
767 }
768 Err(error) => applications.push(Err(error.into())),
769 }
770 }
771 for field_definition_position in &tag_directive_referencers.interface_fields {
773 match field_definition_position.get(self.schema()) {
774 Ok(field_definition) => {
775 let directives = &field_definition.directives;
776 for tag_directive_application in
777 directives.get_all(&tag_directive_definition.name)
778 {
779 let arguments =
780 federation_spec.tag_directive_arguments(tag_directive_application);
781 applications.push(arguments.map(|args| TagDirective {
782 arguments: args,
783 target: TagDirectiveTargetPosition::InterfaceField(
784 field_definition_position.clone(),
785 ),
786 directive: tag_directive_application,
787 }));
788 }
789 }
790 Err(error) => applications.push(Err(error.into())),
791 }
792 }
793 for argument_definition_position in &tag_directive_referencers.interface_field_arguments {
795 match argument_definition_position.get(self.schema()) {
796 Ok(argument_definition) => {
797 let directives = &argument_definition.directives;
798 for tag_directive_application in
799 directives.get_all(&tag_directive_definition.name)
800 {
801 let arguments =
802 federation_spec.tag_directive_arguments(tag_directive_application);
803 applications.push(arguments.map(|args| TagDirective {
804 arguments: args,
805 target: TagDirectiveTargetPosition::ArgumentDefinition(
806 argument_definition_position.clone().into(),
807 ),
808 directive: tag_directive_application,
809 }));
810 }
811 }
812 Err(error) => applications.push(Err(error.into())),
813 }
814 }
815 for object_type_position in &tag_directive_referencers.object_types {
817 match object_type_position.get(self.schema()) {
818 Ok(object_type) => {
819 let directives = &object_type.directives;
820 for tag_directive_application in
821 directives.get_all(&tag_directive_definition.name)
822 {
823 let arguments =
824 federation_spec.tag_directive_arguments(tag_directive_application);
825 applications.push(arguments.map(|args| TagDirective {
826 arguments: args,
827 target: TagDirectiveTargetPosition::Object(
828 object_type_position.clone(),
829 ),
830 directive: tag_directive_application,
831 }));
832 }
833 }
834 Err(error) => applications.push(Err(error.into())),
835 }
836 }
837 for field_definition_position in &tag_directive_referencers.object_fields {
839 match field_definition_position.get(self.schema()) {
840 Ok(field_definition) => {
841 let directives = &field_definition.directives;
842 for tag_directive_application in
843 directives.get_all(&tag_directive_definition.name)
844 {
845 let arguments =
846 federation_spec.tag_directive_arguments(tag_directive_application);
847 applications.push(arguments.map(|args| TagDirective {
848 arguments: args,
849 target: TagDirectiveTargetPosition::ObjectField(
850 field_definition_position.clone(),
851 ),
852 directive: tag_directive_application,
853 }));
854 }
855 }
856 Err(error) => applications.push(Err(error.into())),
857 }
858 }
859 for argument_definition_position in &tag_directive_referencers.object_field_arguments {
861 match argument_definition_position.get(self.schema()) {
862 Ok(argument_definition) => {
863 let directives = &argument_definition.directives;
864 for tag_directive_application in
865 directives.get_all(&tag_directive_definition.name)
866 {
867 let arguments =
868 federation_spec.tag_directive_arguments(tag_directive_application);
869 applications.push(arguments.map(|args| TagDirective {
870 arguments: args,
871 target: TagDirectiveTargetPosition::ArgumentDefinition(
872 argument_definition_position.clone().into(),
873 ),
874 directive: tag_directive_application,
875 }));
876 }
877 }
878 Err(error) => applications.push(Err(error.into())),
879 }
880 }
881 for union_type_position in &tag_directive_referencers.union_types {
883 match union_type_position.get(self.schema()) {
884 Ok(union_type) => {
885 let directives = &union_type.directives;
886 for tag_directive_application in
887 directives.get_all(&tag_directive_definition.name)
888 {
889 let arguments =
890 federation_spec.tag_directive_arguments(tag_directive_application);
891 applications.push(arguments.map(|args| TagDirective {
892 arguments: args,
893 target: TagDirectiveTargetPosition::Union(union_type_position.clone()),
894 directive: tag_directive_application,
895 }));
896 }
897 }
898 Err(error) => applications.push(Err(error.into())),
899 }
900 }
901
902 for scalar_type_position in &tag_directive_referencers.scalar_types {
904 match scalar_type_position.get(self.schema()) {
905 Ok(scalar_type) => {
906 let directives = &scalar_type.directives;
907 for tag_directive_application in
908 directives.get_all(&tag_directive_definition.name)
909 {
910 let arguments =
911 federation_spec.tag_directive_arguments(tag_directive_application);
912 applications.push(arguments.map(|args| TagDirective {
913 arguments: args,
914 target: TagDirectiveTargetPosition::Scalar(
915 scalar_type_position.clone(),
916 ),
917 directive: tag_directive_application,
918 }));
919 }
920 }
921 Err(error) => applications.push(Err(error.into())),
922 }
923 }
924 for enum_type_position in &tag_directive_referencers.enum_types {
926 match enum_type_position.get(self.schema()) {
927 Ok(enum_type) => {
928 let directives = &enum_type.directives;
929 for tag_directive_application in
930 directives.get_all(&tag_directive_definition.name)
931 {
932 let arguments =
933 federation_spec.tag_directive_arguments(tag_directive_application);
934 applications.push(arguments.map(|args| TagDirective {
935 arguments: args,
936 target: TagDirectiveTargetPosition::Enum(enum_type_position.clone()),
937 directive: tag_directive_application,
938 }));
939 }
940 }
941 Err(error) => applications.push(Err(error.into())),
942 }
943 }
944 for enum_value_position in &tag_directive_referencers.enum_values {
946 match enum_value_position.get(self.schema()) {
947 Ok(enum_value) => {
948 let directives = &enum_value.directives;
949 for tag_directive_application in
950 directives.get_all(&tag_directive_definition.name)
951 {
952 let arguments =
953 federation_spec.tag_directive_arguments(tag_directive_application);
954 applications.push(arguments.map(|args| TagDirective {
955 arguments: args,
956 target: TagDirectiveTargetPosition::EnumValue(
957 enum_value_position.clone(),
958 ),
959 directive: tag_directive_application,
960 }));
961 }
962 }
963 Err(error) => applications.push(Err(error.into())),
964 }
965 }
966 for input_object_type_position in &tag_directive_referencers.input_object_types {
968 match input_object_type_position.get(self.schema()) {
969 Ok(input_object_type) => {
970 let directives = &input_object_type.directives;
971 for tag_directive_application in
972 directives.get_all(&tag_directive_definition.name)
973 {
974 let arguments =
975 federation_spec.tag_directive_arguments(tag_directive_application);
976 applications.push(arguments.map(|args| TagDirective {
977 arguments: args,
978 target: TagDirectiveTargetPosition::InputObject(
979 input_object_type_position.clone(),
980 ),
981 directive: tag_directive_application,
982 }));
983 }
984 }
985 Err(error) => applications.push(Err(error.into())),
986 }
987 }
988 for input_field_definition_position in &tag_directive_referencers.input_object_fields {
990 match input_field_definition_position.get(self.schema()) {
991 Ok(input_field_definition) => {
992 let directives = &input_field_definition.directives;
993 for tag_directive_application in
994 directives.get_all(&tag_directive_definition.name)
995 {
996 let arguments =
997 federation_spec.tag_directive_arguments(tag_directive_application);
998 applications.push(arguments.map(|args| TagDirective {
999 arguments: args,
1000 target: TagDirectiveTargetPosition::InputObjectFieldDefinition(
1001 input_field_definition_position.clone(),
1002 ),
1003 directive: tag_directive_application,
1004 }));
1005 }
1006 }
1007 Err(error) => applications.push(Err(error.into())),
1008 }
1009 }
1010 for directive_definition_position in &tag_directive_referencers.directive_arguments {
1012 match directive_definition_position.get(self.schema()) {
1013 Ok(directive_definition) => {
1014 let directives = &directive_definition.directives;
1015 for tag_directive_application in
1016 directives.get_all(&tag_directive_definition.name)
1017 {
1018 let arguments =
1019 federation_spec.tag_directive_arguments(tag_directive_application);
1020 applications.push(arguments.map(|args| TagDirective {
1021 arguments: args,
1022 target: TagDirectiveTargetPosition::DirectiveArgumentDefinition(
1023 directive_definition_position.clone(),
1024 ),
1025 directive: tag_directive_application,
1026 }));
1027 }
1028 }
1029 Err(error) => applications.push(Err(error.into())),
1030 }
1031 }
1032
1033 Ok(applications)
1034 }
1035
1036 pub(crate) fn list_size_directive_applications(
1037 &self,
1038 ) -> FallibleDirectiveIterator<ListSizeDirective<'_>> {
1039 let Some(list_size_directive_name) = CostSpecDefinition::list_size_directive_name(self)
1040 else {
1041 return Ok(Vec::new());
1042 };
1043 let list_size_directive_referencers = self
1044 .referencers()
1045 .get_directive(list_size_directive_name.as_str());
1046
1047 let mut applications = Vec::new();
1048 for field_definition_position in
1049 list_size_directive_referencers.object_or_interface_fields()
1050 {
1051 let field_definition = field_definition_position.get(self.schema())?;
1052 match CostSpecDefinition::list_size_directive_from_field_definition(
1053 self,
1054 field_definition,
1055 ) {
1056 Some(list_size_directive) => {
1057 applications.push(Ok(ListSizeDirective {
1058 directive: list_size_directive,
1059 parent_type: field_definition_position.type_name().clone(),
1060 target: field_definition,
1061 }));
1062 }
1063 None => {
1064 }
1066 }
1067 }
1068
1069 Ok(applications)
1070 }
1071
1072 pub(crate) fn cache_tag_directive_applications(
1073 &self,
1074 ) -> FallibleDirectiveIterator<CacheTagDirective<'_>> {
1075 let federation_spec = get_federation_spec_definition_from_subgraph(self)?;
1076 let Ok(cache_tag_directive_definition) =
1077 federation_spec.cache_tag_directive_definition(self)
1078 else {
1079 return Ok(Vec::new());
1080 };
1081
1082 let result = self
1083 .referencers()
1084 .get_directive_applications(self, &cache_tag_directive_definition.name)
1085 .map(|(pos, application)| {
1086 let arguments = federation_spec.cache_tag_directive_arguments(application);
1087 arguments.map(|args| CacheTagDirective {
1088 arguments: args,
1089 target: pos,
1090 })
1091 })
1092 .collect();
1093 Ok(result)
1094 }
1095
1096 pub(crate) fn is_interface(&self, type_name: &Name) -> bool {
1097 self.referencers().interface_types.contains_key(type_name)
1098 }
1099
1100 pub(crate) fn all_features(&self) -> Result<Vec<&'static dyn SpecDefinition>, FederationError> {
1101 let Some(links) = self.metadata() else {
1102 return Ok(Vec::new());
1103 };
1104
1105 let mut features: Vec<&'static dyn SpecDefinition> =
1106 Vec::with_capacity(links.all_links().len());
1107
1108 for link in links.all_links() {
1109 if let Some(spec) = SPEC_REGISTRY.get_definition(&link.url) {
1110 features.push(*spec);
1111 } else if let Some(supported_versions) = SPEC_REGISTRY.get_versions(&link.url.identity)
1112 {
1113 return Err(
1114 SingleFederationError::UnknownLinkVersion {
1115 message: format!(
1116 "Detected unsupported {} specification version {}. Please upgrade to a composition version which supports that version, or select one of the following supported versions: {}.",
1117 link.url.identity.name,
1118 link.url.version,
1119 supported_versions.iter().join(", ")
1120 ),
1121 }.into());
1122 }
1123 }
1124
1125 Ok(features)
1126 }
1127
1128 pub(crate) fn node_locations<T>(
1129 &self,
1130 node: &Node<T>,
1131 ) -> impl Iterator<Item = Range<LineColumn>> {
1132 node.line_column_range(&self.schema().sources).into_iter()
1133 }
1134}
1135
1136type FallibleDirectiveIterator<D> = Result<Vec<Result<D, FederationError>>, FederationError>;
1137
1138#[derive(Clone)]
1139pub(crate) struct ComposeDirectiveDirective<'schema> {
1140 pub(crate) arguments: ComposeDirectiveArguments<'schema>,
1142}
1143
1144pub(crate) struct ContextDirective<'schema> {
1145 arguments: ContextDirectiveArguments<'schema>,
1147 target: CompositeTypeDefinitionPosition,
1149}
1150
1151impl ContextDirective<'_> {
1152 pub(crate) fn arguments(&self) -> &ContextDirectiveArguments<'_> {
1153 &self.arguments
1154 }
1155
1156 pub(crate) fn target(&self) -> &CompositeTypeDefinitionPosition {
1157 &self.target
1158 }
1159}
1160
1161pub(crate) struct FromContextDirective<'schema> {
1162 arguments: FromContextDirectiveArguments<'schema>,
1164 target: FieldArgumentDefinitionPosition,
1166}
1167
1168pub(crate) struct KeyDirective<'schema> {
1169 arguments: KeyDirectiveArguments<'schema>,
1171 schema_directive: &'schema apollo_compiler::schema::Component<Directive>,
1173 sibling_directives: &'schema apollo_compiler::schema::DirectiveList,
1175 target: ObjectOrInterfaceTypeDefinitionPosition,
1177}
1178
1179impl HasFields for KeyDirective<'_> {
1180 fn fields(&self) -> &str {
1181 self.arguments.fields
1182 }
1183
1184 fn target_type(&self) -> &Name {
1185 self.target.type_name()
1186 }
1187}
1188
1189impl KeyDirective<'_> {
1190 pub(crate) fn target(&self) -> &ObjectOrInterfaceTypeDefinitionPosition {
1191 &self.target
1192 }
1193}
1194
1195pub(crate) struct ListSizeDirective<'schema> {
1196 directive: cost_spec_definition::ListSizeDirective,
1198 parent_type: Name,
1200 target: &'schema FieldDefinition,
1202}
1203
1204pub(crate) struct ProvidesDirective<'schema> {
1205 arguments: ProvidesDirectiveArguments<'schema>,
1207 schema_directive: &'schema Node<Directive>,
1209 target: ObjectOrInterfaceFieldDefinitionPosition,
1213 target_return_type: &'schema Name,
1215}
1216
1217impl HasFields for ProvidesDirective<'_> {
1218 fn fields(&self) -> &str {
1220 self.arguments.fields
1221 }
1222
1223 fn target_type(&self) -> &Name {
1225 self.target_return_type
1226 }
1227}
1228
1229pub(crate) struct RequiresDirective<'schema> {
1230 arguments: RequiresDirectiveArguments<'schema>,
1232 schema_directive: &'schema Node<Directive>,
1234 target: ObjectOrInterfaceFieldDefinitionPosition,
1238}
1239
1240impl HasFields for RequiresDirective<'_> {
1241 fn fields(&self) -> &str {
1242 self.arguments.fields
1243 }
1244
1245 fn target_type(&self) -> &Name {
1246 self.target.type_name()
1247 }
1248}
1249
1250pub(crate) struct TagDirective<'schema> {
1251 arguments: TagDirectiveArguments<'schema>,
1253 target: TagDirectiveTargetPosition, directive: &'schema Node<Directive>,
1257}
1258
1259pub(crate) struct CacheTagDirective<'schema> {
1260 arguments: CacheTagDirectiveArguments<'schema>,
1262 target: DirectiveTargetPosition,
1264}
1265
1266pub(crate) trait HasFields {
1267 fn fields(&self) -> &str;
1268 fn target_type(&self) -> &Name;
1269
1270 fn parse_fields(&self, schema: &Schema) -> Result<FieldSet, WithErrors<FieldSet>> {
1271 FieldSet::parse(
1272 Valid::assume_valid_ref(schema),
1273 self.target_type().clone(),
1274 self.fields(),
1275 "field_set.graphql",
1276 )
1277 }
1278}
1279
1280#[derive(Clone)]
1282pub struct ValidFederationSchema {
1283 schema: Arc<Valid<FederationSchema>>,
1284}
1285
1286impl ValidFederationSchema {
1287 pub fn new(schema: Valid<Schema>) -> Result<ValidFederationSchema, FederationError> {
1288 let schema = FederationSchema::new(schema.into_inner())?;
1289
1290 Self::new_assume_valid(schema).map_err(|(_schema, error)| error)
1291 }
1292
1293 #[allow(clippy::result_large_err)] pub fn new_assume_valid(
1296 mut schema: FederationSchema,
1297 ) -> Result<ValidFederationSchema, (FederationSchema, FederationError)> {
1298 if let Some(links_metadata) = &schema.links_metadata
1301 && let Err(error) = links_metadata.validate_no_shadowing_imports(&schema)
1302 {
1303 return Err((schema, error));
1304 }
1305 let valid_schema = Valid::assume_valid_ref(&schema);
1312 let subgraph_metadata = match compute_subgraph_metadata(valid_schema) {
1313 Ok(metadata) => metadata.map(Box::new),
1314 Err(err) => return Err((schema, err)),
1315 };
1316 schema.subgraph_metadata = subgraph_metadata;
1317
1318 let schema = Arc::new(Valid::assume_valid(schema));
1319 Ok(ValidFederationSchema { schema })
1320 }
1321
1322 pub fn schema(&self) -> &Valid<Schema> {
1324 Valid::assume_valid_ref(&self.schema.schema)
1325 }
1326
1327 pub(crate) fn subgraph_metadata(&self) -> Option<&SubgraphMetadata> {
1331 self.schema.subgraph_metadata.as_deref()
1332 }
1333
1334 pub(crate) fn federation_type_name_in_schema(
1335 &self,
1336 name: Name,
1337 ) -> Result<Name, FederationError> {
1338 if name.starts_with('_') {
1345 return Ok(name);
1346 }
1347
1348 Err(FederationError::internal(
1352 "typename should have been looked in a federation feature",
1353 ))
1354 }
1355
1356 pub(crate) fn is_interface_object_type(
1357 &self,
1358 type_definition_position: TypeDefinitionPosition,
1359 ) -> Result<bool, FederationError> {
1360 let Some(subgraph_metadata) = &self.subgraph_metadata else {
1361 return Ok(false);
1362 };
1363 let Some(interface_object_directive_definition) = subgraph_metadata
1364 .federation_spec_definition()
1365 .interface_object_directive_definition(self)?
1366 else {
1367 return Ok(false);
1368 };
1369 match type_definition_position {
1370 TypeDefinitionPosition::Object(type_) => Ok(type_
1371 .get(self.schema())?
1372 .directives
1373 .has(&interface_object_directive_definition.name)),
1374 _ => Ok(false),
1375 }
1376 }
1377}
1378
1379impl Deref for ValidFederationSchema {
1380 type Target = FederationSchema;
1381
1382 fn deref(&self) -> &Self::Target {
1383 &self.schema
1384 }
1385}
1386
1387impl Eq for ValidFederationSchema {}
1388
1389impl PartialEq for ValidFederationSchema {
1390 fn eq(&self, other: &ValidFederationSchema) -> bool {
1391 Arc::ptr_eq(&self.schema, &other.schema)
1392 }
1393}
1394
1395impl Hash for ValidFederationSchema {
1396 fn hash<H: Hasher>(&self, state: &mut H) {
1397 Arc::as_ptr(&self.schema).hash(state);
1398 }
1399}
1400
1401impl std::fmt::Debug for ValidFederationSchema {
1402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1403 write!(f, "ValidFederationSchema @ {:?}", Arc::as_ptr(&self.schema))
1404 }
1405}
1406
1407impl From<ValidFederationSchema> for FederationSchema {
1408 fn from(value: ValidFederationSchema) -> Self {
1409 Arc::unwrap_or_clone(value.schema).into_inner()
1410 }
1411}
1412
1413pub(crate) trait SchemaElement {
1414 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin>;
1417
1418 fn definition_and_extensions(&self) -> (bool, IndexSet<&ExtensionId>) {
1421 let mut extensions = IndexSet::default();
1422 let mut has_definition = false;
1423 for origin in self.iter_origins() {
1424 if let Some(extension_id) = origin.extension_id() {
1425 extensions.insert(extension_id);
1426 } else {
1427 has_definition = true;
1428 }
1429 }
1430 (has_definition, extensions)
1431 }
1432
1433 fn extensions(&self) -> IndexSet<&ExtensionId> {
1434 self.definition_and_extensions().1
1435 }
1436
1437 fn has_extension_elements(&self) -> bool {
1438 !self.extensions().is_empty()
1439 }
1440
1441 fn origin_to_use(&self) -> ComponentOrigin {
1442 let (has_definition, extensions) = self.definition_and_extensions();
1443 if !has_definition && let Some(first_extension) = extensions.first() {
1448 return ComponentOrigin::Extension((*first_extension).clone());
1449 }
1450 ComponentOrigin::Definition
1451 }
1452}
1453
1454impl SchemaElement for SchemaDefinition {
1455 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1456 self.iter_origins()
1457 }
1458}
1459
1460impl SchemaElement for ExtendedType {
1461 fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1462 self.iter_origins()
1463 }
1464}
1465
1466pub(crate) fn same_type(t1: &Type, t2: &Type) -> bool {
1467 match (t1, t2) {
1468 (Type::Named(n1), Type::Named(n2)) => n1 == n2,
1469 (Type::NonNullNamed(n1), Type::NonNullNamed(n2)) => n1 == n2,
1470 (Type::List(inner1), Type::List(inner2)) => same_type(inner1, inner2),
1471 (Type::NonNullList(inner1), Type::NonNullList(inner2)) => same_type(inner1, inner2),
1472 _ => false,
1473 }
1474}