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