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