1use std::fmt::Display;
2use std::fmt::Formatter;
3use std::ops::Range;
4
5use apollo_compiler::Node;
6use apollo_compiler::Schema;
7use apollo_compiler::collections::IndexMap;
8use apollo_compiler::collections::IndexSet;
9use apollo_compiler::name;
10use apollo_compiler::parser::LineColumn;
11use apollo_compiler::schema::ComponentName;
12use apollo_compiler::schema::ExtendedType;
13use apollo_compiler::schema::ObjectType;
14use apollo_compiler::validation::DiagnosticList;
15use apollo_compiler::validation::Valid;
16use indexmap::map::Entry;
17
18use crate::ValidFederationSubgraph;
19use crate::compat::coerce_and_validate_schema_values;
20use crate::error::FederationError;
21use crate::error::MultipleFederationErrors;
22use crate::error::SingleFederationError;
23use crate::link::Link;
24use crate::link::link_spec_definition::LINK_DIRECTIVE_NAME_IN_SPEC;
25use crate::link::spec::Identity;
26use crate::subgraph::spec::ANY_SCALAR_NAME;
27use crate::subgraph::spec::AppliedFederationLink;
28use crate::subgraph::spec::CONTEXTFIELDVALUE_SCALAR_NAME;
29use crate::subgraph::spec::ENTITIES_QUERY;
30use crate::subgraph::spec::ENTITY_UNION_NAME;
31use crate::subgraph::spec::FEDERATION_V2_DIRECTIVE_NAMES;
32use crate::subgraph::spec::FederationSpecDefinitions;
33use crate::subgraph::spec::KEY_DIRECTIVE_NAME;
34use crate::subgraph::spec::LinkSpecDefinitions;
35use crate::subgraph::spec::SERVICE_SDL_QUERY;
36use crate::subgraph::spec::SERVICE_TYPE;
37
38pub mod spec;
39pub mod typestate; pub struct Subgraph {
42 pub name: String,
43 pub url: String,
44 pub schema: Schema,
45}
46
47impl Subgraph {
48 pub fn new(name: &str, url: &str, schema_str: &str) -> Result<Self, FederationError> {
49 let schema = Schema::parse(schema_str, name)?;
50 Ok(Self {
52 name: name.to_string(),
53 url: url.to_string(),
54 schema,
55 })
56 }
57
58 pub fn parse_and_expand(
59 name: &str,
60 url: &str,
61 schema_str: &str,
62 ) -> Result<ValidSubgraph, FederationError> {
63 let mut schema = Schema::builder()
64 .adopt_orphan_extensions()
65 .parse(schema_str, name)
66 .build()?;
67
68 let mut imported_federation_definitions: Option<FederationSpecDefinitions> = None;
69 let mut imported_link_definitions: Option<LinkSpecDefinitions> = None;
70 let default_link_name = LINK_DIRECTIVE_NAME_IN_SPEC;
71 let link_directives = schema
72 .schema_definition
73 .directives
74 .get_all(&default_link_name);
75
76 for directive in link_directives {
77 let link_directive =
78 Link::from_directive_application_when_link_spec_unknown(directive, &schema)?;
79 if link_directive.url.identity == Identity::federation_identity() {
80 if imported_federation_definitions.is_some() {
81 let msg = "Invalid use of @link in schema: invalid graphql schema - multiple @link imports for the federation specification are not supported";
82 return Err(SingleFederationError::InvalidLinkDirectiveUsage {
83 message: msg.to_owned(),
84 }
85 .into());
86 }
87
88 imported_federation_definitions =
89 Some(FederationSpecDefinitions::from_link(link_directive)?);
90 } else if link_directive.url.identity == Identity::link_identity() {
91 if imported_link_definitions.is_some() {
93 let msg = "Invalid use of @link in schema: invalid graphql schema - multiple @link imports for the link specification are not supported";
94 return Err(SingleFederationError::InvalidLinkDirectiveUsage {
95 message: msg.to_owned(),
96 }
97 .into());
98 }
99
100 imported_link_definitions = Some(LinkSpecDefinitions::new(link_directive));
101 }
102 }
103
104 Self::populate_missing_type_definitions(
106 &mut schema,
107 imported_federation_definitions,
108 imported_link_definitions,
109 )?;
110 coerce_and_validate_schema_values(&mut schema)?;
111 let schema = schema.validate()?;
112 Ok(ValidSubgraph {
113 name: name.to_owned(),
114 url: url.to_owned(),
115 schema,
116 })
117 }
118
119 fn populate_missing_type_definitions(
120 schema: &mut Schema,
121 imported_federation_definitions: Option<FederationSpecDefinitions>,
122 imported_link_definitions: Option<LinkSpecDefinitions>,
123 ) -> Result<(), FederationError> {
124 let link_spec_definitions = match imported_link_definitions {
126 Some(definitions) => definitions,
127 None => {
128 let defaults = LinkSpecDefinitions::default();
130 schema
131 .schema_definition
132 .make_mut()
133 .directives
134 .push(defaults.applied_link_directive());
135 defaults
136 }
137 };
138 Self::populate_missing_link_definitions(schema, link_spec_definitions)?;
139
140 let fed_definitions = match imported_federation_definitions {
142 Some(definitions) => definitions,
143 None => {
144 let defaults = FederationSpecDefinitions::default()?;
147 schema
148 .schema_definition
149 .make_mut()
150 .directives
151 .push(defaults.applied_link_directive());
152 defaults
153 }
154 };
155 Self::populate_missing_federation_directive_definitions(schema, &fed_definitions)?;
156 Self::populate_missing_federation_types(schema, &fed_definitions)
157 }
158
159 fn populate_missing_link_definitions(
160 schema: &mut Schema,
161 link_spec_definitions: LinkSpecDefinitions,
162 ) -> Result<(), FederationError> {
163 let purpose_enum_name = &link_spec_definitions.purpose_enum_name;
164 schema
165 .types
166 .entry(purpose_enum_name.clone())
167 .or_insert_with(|| {
168 link_spec_definitions
169 .link_purpose_enum_definition(purpose_enum_name.clone())
170 .into()
171 });
172 let import_scalar_name = &link_spec_definitions.import_scalar_name;
173 schema
174 .types
175 .entry(import_scalar_name.clone())
176 .or_insert_with(|| {
177 link_spec_definitions
178 .import_scalar_definition(import_scalar_name.clone())
179 .into()
180 });
181 if let Entry::Vacant(entry) = schema
182 .directive_definitions
183 .entry(LINK_DIRECTIVE_NAME_IN_SPEC)
184 {
185 entry.insert(link_spec_definitions.link_directive_definition()?.into());
186 }
187 Ok(())
188 }
189
190 fn populate_missing_federation_directive_definitions(
191 schema: &mut Schema,
192 fed_definitions: &FederationSpecDefinitions,
193 ) -> Result<(), FederationError> {
194 let fieldset_scalar_name = &fed_definitions.fieldset_scalar_name;
196 schema
197 .types
198 .entry(fieldset_scalar_name.clone())
199 .or_insert_with(|| {
200 fed_definitions
201 .fieldset_scalar_definition(fieldset_scalar_name.clone())
202 .into()
203 });
204
205 let namespaced_contextfieldvalue_scalar_name =
207 fed_definitions.namespaced_type_name(&CONTEXTFIELDVALUE_SCALAR_NAME, false);
208 if let Entry::Vacant(entry) = schema
209 .types
210 .entry(namespaced_contextfieldvalue_scalar_name.clone())
211 {
212 let type_definition = fed_definitions.contextfieldvalue_scalar_definition(&Some(
213 namespaced_contextfieldvalue_scalar_name,
214 ));
215 entry.insert(type_definition.into());
216 }
217
218 for directive_name in &FEDERATION_V2_DIRECTIVE_NAMES {
219 let namespaced_directive_name =
220 fed_definitions.namespaced_type_name(directive_name, true);
221 if let Entry::Vacant(entry) = schema
222 .directive_definitions
223 .entry(namespaced_directive_name.clone())
224 {
225 let directive_definition = fed_definitions.directive_definition(
226 directive_name,
227 &Some(namespaced_directive_name.to_owned()),
228 )?;
229 entry.insert(directive_definition.into());
230 }
231 }
232 Ok(())
233 }
234
235 fn populate_missing_federation_types(
236 schema: &mut Schema,
237 fed_definitions: &FederationSpecDefinitions,
238 ) -> Result<(), FederationError> {
239 schema
240 .types
241 .entry(SERVICE_TYPE)
242 .or_insert_with(|| fed_definitions.service_object_type_definition());
243
244 let entities = Self::locate_entities(schema, fed_definitions);
245 let entities_present = !entities.is_empty();
246 if entities_present {
247 schema
248 .types
249 .entry(ENTITY_UNION_NAME)
250 .or_insert_with(|| fed_definitions.entity_union_definition(entities));
251 schema
252 .types
253 .entry(ANY_SCALAR_NAME)
254 .or_insert_with(|| fed_definitions.any_scalar_definition());
255 }
256
257 let query_type_name = schema
258 .schema_definition
259 .make_mut()
260 .query
261 .get_or_insert(ComponentName::from(name!("Query")));
262 if let ExtendedType::Object(query_type) = schema
263 .types
264 .entry(query_type_name.name.clone())
265 .or_insert(ExtendedType::Object(Node::new(ObjectType {
266 description: None,
267 name: query_type_name.name.clone(),
268 directives: Default::default(),
269 fields: IndexMap::default(),
270 implements_interfaces: IndexSet::default(),
271 })))
272 {
273 let query_type = query_type.make_mut();
274 query_type
275 .fields
276 .entry(SERVICE_SDL_QUERY)
277 .or_insert_with(|| fed_definitions.service_sdl_query_field());
278 if entities_present {
279 query_type
281 .fields
282 .entry(ENTITIES_QUERY)
283 .or_insert_with(|| fed_definitions.entities_query_field());
284 }
285 }
286 Ok(())
287 }
288
289 fn locate_entities(
290 schema: &mut Schema,
291 fed_definitions: &FederationSpecDefinitions,
292 ) -> IndexSet<ComponentName> {
293 let mut entities = Vec::new();
294 let immutable_type_map = schema.types.to_owned();
295 for (named_type, extended_type) in immutable_type_map.iter() {
296 let is_entity = extended_type
297 .directives()
298 .iter()
299 .find(|d| {
300 d.name
301 == fed_definitions
302 .namespaced_type_name(&KEY_DIRECTIVE_NAME, true)
303 .as_str()
304 })
305 .map(|_| true)
306 .unwrap_or(false);
307 if is_entity {
308 entities.push(named_type);
309 }
310 }
311 let entity_set: IndexSet<ComponentName> =
312 entities.iter().map(|e| ComponentName::from(*e)).collect();
313 entity_set
314 }
315}
316
317impl std::fmt::Debug for Subgraph {
318 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
319 write!(f, r#"name: {}, urL: {}"#, self.name, self.url)
320 }
321}
322
323pub struct ValidSubgraph {
324 pub name: String,
325 pub url: String,
326 pub schema: Valid<Schema>,
327}
328
329impl std::fmt::Debug for ValidSubgraph {
330 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
331 write!(f, r#"name: {}, url: {}"#, self.name, self.url)
332 }
333}
334
335impl From<ValidFederationSubgraph> for ValidSubgraph {
336 fn from(value: ValidFederationSubgraph) -> Self {
337 Self {
338 name: value.name,
339 url: value.url,
340 schema: value.schema.schema().clone(),
341 }
342 }
343}
344
345#[derive(Clone, Debug)]
346pub(crate) struct SingleSubgraphError {
347 pub(crate) error: SingleFederationError,
348 pub(crate) locations: Vec<Range<LineColumn>>,
349}
350
351#[derive(Clone, Debug)]
357pub struct SubgraphError {
358 pub(crate) subgraph: String,
359 pub(crate) errors: Vec<SingleSubgraphError>,
360}
361
362impl SubgraphError {
363 pub(crate) fn new_without_locations(
365 subgraph: impl Into<String>,
366 error: impl Into<FederationError>,
367 ) -> Self {
368 let subgraph = subgraph.into();
369 let error: FederationError = error.into();
370 SubgraphError {
371 subgraph,
372 errors: error
373 .errors()
374 .into_iter()
375 .map(|e| SingleSubgraphError {
376 error: e.clone(),
377 locations: Vec::new(),
378 })
379 .collect(),
380 }
381 }
382
383 #[allow(dead_code)]
388 pub(crate) fn from_federation_error(
389 subgraph: impl Into<String>,
390 error: impl Into<FederationError>,
391 locations: Vec<Range<LineColumn>>,
392 ) -> Self {
393 let error: FederationError = error.into();
394 let errors = error
395 .errors()
396 .into_iter()
397 .map(|e| SingleSubgraphError {
398 error: e.clone(),
399 locations: locations.clone(),
400 })
401 .collect();
402 SubgraphError {
403 subgraph: subgraph.into(),
404 errors,
405 }
406 }
407
408 pub(crate) fn from_diagnostic_list(
410 subgraph: impl Into<String>,
411 errors: DiagnosticList,
412 ) -> Self {
413 let subgraph = subgraph.into();
414 SubgraphError {
415 subgraph,
416 errors: errors
417 .iter()
418 .map(|d| SingleSubgraphError {
419 error: SingleFederationError::InvalidGraphQL {
420 message: d.to_string(),
421 },
422 locations: d.line_column_range().iter().cloned().collect(),
423 })
424 .collect(),
425 }
426 }
427
428 pub(crate) fn into_federation_error(self) -> FederationError {
431 MultipleFederationErrors::from_iter(self.errors.into_iter().map(|e| e.error)).into()
432 }
433
434 pub fn format_errors(&self) -> Vec<(String, String)> {
442 self.errors
443 .iter()
444 .map(|e| {
445 let error = &e.error;
446 (
447 error.code_string(),
448 format!("[{subgraph}] {error}", subgraph = self.subgraph),
449 )
450 })
451 .collect()
452 }
453}
454
455impl Display for SubgraphError {
456 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
457 for (code, message) in self.format_errors() {
458 writeln!(f, "{code} {message}")?;
459 }
460 Ok(())
461 }
462}
463
464pub mod test_utils {
465
466 use super::SubgraphError;
467 use super::typestate::Expanded;
468 use super::typestate::Subgraph;
469 use super::typestate::Validated;
470
471 pub enum BuildOption {
472 AsIs,
473 AsFed2,
474 }
475
476 pub fn build_inner(
477 schema_str: &str,
478 build_option: BuildOption,
479 ) -> Result<Subgraph<Validated>, SubgraphError> {
480 let name = "S";
481 let subgraph =
482 Subgraph::parse(name, &format!("http://{name}"), schema_str).expect("valid schema");
483 let subgraph = if matches!(build_option, BuildOption::AsFed2) {
484 subgraph.into_fed2_test_subgraph(true)?
485 } else {
486 subgraph
487 };
488 Ok(subgraph
489 .expand_links()?
490 .normalize_root_types()?
491 .assume_validated())
492 }
493
494 pub fn build_inner_expanded(
495 schema_str: &str,
496 build_option: BuildOption,
497 ) -> Result<Subgraph<Expanded>, SubgraphError> {
498 let name = "S";
499 let subgraph =
500 Subgraph::parse(name, &format!("http://{name}"), schema_str).expect("valid schema");
501 let subgraph = if matches!(build_option, BuildOption::AsFed2) {
502 subgraph.into_fed2_test_subgraph(true)?
503 } else {
504 subgraph
505 };
506 subgraph.expand_links_without_validation()
507 }
508
509 pub fn build_and_validate(schema_str: &str) -> Subgraph<Validated> {
510 build_inner(schema_str, BuildOption::AsIs).expect("expanded subgraph to be valid")
511 }
512
513 pub fn build_and_expand(schema_str: &str) -> Subgraph<Expanded> {
514 build_inner_expanded(schema_str, BuildOption::AsIs).expect("expanded subgraph to be valid")
515 }
516
517 pub fn build_for_errors_with_option(
518 schema: &str,
519 build_option: BuildOption,
520 ) -> Vec<(String, String)> {
521 build_inner(schema, build_option)
522 .expect_err("subgraph error was expected")
523 .format_errors()
524 }
525
526 pub fn build_for_errors(schema: &str) -> Vec<(String, String)> {
528 build_for_errors_with_option(schema, BuildOption::AsFed2)
529 }
530
531 pub fn remove_indentation(s: &str) -> String {
532 let first_empty_lines = s.lines().take_while(|line| line.trim().is_empty()).count();
534 let last_empty_lines = s
535 .lines()
536 .rev()
537 .take_while(|line| line.trim().is_empty())
538 .count();
539
540 let lines = s
542 .lines()
543 .skip(first_empty_lines)
544 .take(s.lines().count() - first_empty_lines - last_empty_lines);
545
546 let indentation = lines
548 .clone()
549 .map(|line| line.chars().take_while(|c| *c == ' ').count())
550 .min()
551 .unwrap_or(0);
552
553 lines
555 .map(|line| {
556 line.trim_end()
557 .chars()
558 .skip(indentation)
559 .collect::<String>()
560 })
561 .collect::<Vec<_>>()
562 .join("\n")
563 }
564
565 pub fn check_errors(a: &[(String, String)], b: &[(&str, &str)]) -> Result<(), String> {
567 if a.len() != b.len() {
568 return Err(format!(
569 "Mismatched error counts: {} != {}\n\nexpected:\n{}\n\nactual:\n{}",
570 b.len(),
571 a.len(),
572 b.iter()
573 .map(|(code, msg)| { format!("- {code}: {msg}") })
574 .collect::<Vec<_>>()
575 .join("\n"),
576 a.iter()
577 .map(|(code, msg)| { format!("+ {code}: {msg}") })
578 .collect::<Vec<_>>()
579 .join("\n"),
580 ));
581 }
582
583 let b_iter = b
585 .iter()
586 .map(|(code, message)| (*code, remove_indentation(message)));
587 let diff: Vec<_> = a
588 .iter()
589 .map(|(code, message)| (code.as_str(), remove_indentation(message)))
590 .zip(b_iter)
591 .filter(|(a_i, b_i)| a_i.0 != b_i.0 || a_i.1 != b_i.1)
592 .collect();
593 if diff.is_empty() {
594 Ok(())
595 } else {
596 Err(format!(
597 "Mismatched errors:\n{}\n",
598 diff.iter()
599 .map(|(a_i, b_i)| { format!("- {}: {}\n+ {}: {}", b_i.0, b_i.1, a_i.0, a_i.1) })
600 .collect::<Vec<_>>()
601 .join("\n")
602 ))
603 }
604 }
605
606 #[macro_export]
607 macro_rules! assert_errors {
608 ($a:expr, $b:expr) => {
609 match apollo_federation::subgraph::test_utils::check_errors(&$a, &$b) {
610 Ok(()) => {
611 }
613 Err(e) => {
614 panic!("{e}")
615 }
616 }
617 };
618 }
619}
620
621pub fn schema_diff_expanded_from_initial(schema_str: String) -> Result<String, FederationError> {
625 let initial_schema = Schema::parse(schema_str, "")?;
627
628 let initial_subgraph =
630 typestate::Subgraph::new("S", "http://S", initial_schema.clone(), Default::default());
631 let expanded_subgraph = initial_subgraph
632 .map_err(|e| e.into_federation_error())?
633 .expand_links_without_validation()
634 .map_err(|e| e.into_federation_error())?;
635
636 let mut diff = String::new();
638
639 for (dir_name, dir_def) in &expanded_subgraph.schema().schema().directive_definitions {
641 if !initial_schema.directive_definitions.contains_key(dir_name) {
642 diff.push_str(&dir_def.to_string());
643 diff.push('\n');
644 }
645 }
646
647 for (named_ty, extended_ty) in &expanded_subgraph.schema().schema().types {
649 if !initial_schema.types.contains_key(named_ty) {
650 diff.push_str(&extended_ty.to_string());
651 }
652 }
653
654 Ok(diff)
655}
656
657#[cfg(test)]
658mod tests {
659 use crate::subgraph::schema_diff_expanded_from_initial;
660
661 #[test]
662 fn returns_correct_schema_diff_for_fed_2_0() {
663 let schema_string = r#"
664 extend schema @link(url: "https://specs.apollo.dev/federation/v2.0")
665
666 type Query {
667 s: String
668 }"#
669 .to_string();
670
671 let diff = schema_diff_expanded_from_initial(schema_string);
672
673 insta::assert_snapshot!(diff.unwrap_or_default(), @r#"directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
674directive @federation__key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
675directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION
676directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION
677directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
678directive @federation__tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
679directive @federation__extends on OBJECT | INTERFACE
680directive @federation__shareable on OBJECT | FIELD_DEFINITION
681directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
682directive @federation__override(from: String!) on FIELD_DEFINITION
683enum link__Purpose {
684 """
685 `SECURITY` features provide metadata necessary to securely resolve fields.
686 """
687 SECURITY
688 """
689 `EXECUTION` features provide metadata necessary for operation execution.
690 """
691 EXECUTION
692}
693scalar link__Import
694scalar federation__FieldSet
695scalar _Any
696type _Service {
697 sdl: String
698}"#);
699 }
700
701 #[test]
702 fn returns_correct_schema_diff_for_fed_2_4() {
703 let schema_string = r#"
704 extend schema @link(url: "https://specs.apollo.dev/federation/v2.4")
705
706 type Query {
707 s: String
708 }"#
709 .to_string();
710
711 let diff = schema_diff_expanded_from_initial(schema_string);
712
713 insta::assert_snapshot!(diff.unwrap_or_default(), @r#"directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
714directive @federation__key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
715directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION
716directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION
717directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
718directive @federation__tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
719directive @federation__extends on OBJECT | INTERFACE
720directive @federation__shareable repeatable on OBJECT | FIELD_DEFINITION
721directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
722directive @federation__override(from: String!) on FIELD_DEFINITION
723directive @federation__composeDirective(name: String) repeatable on SCHEMA
724directive @federation__interfaceObject on OBJECT
725enum link__Purpose {
726 """
727 `SECURITY` features provide metadata necessary to securely resolve fields.
728 """
729 SECURITY
730 """
731 `EXECUTION` features provide metadata necessary for operation execution.
732 """
733 EXECUTION
734}
735scalar link__Import
736scalar federation__FieldSet
737scalar _Any
738type _Service {
739 sdl: String
740}"#);
741 }
742
743 #[test]
744 fn returns_correct_schema_diff_for_fed_2_9() {
745 let schema_string = r#"
746 extend schema @link(url: "https://specs.apollo.dev/federation/v2.9")
747
748 type Query {
749 s: String
750 }"#
751 .to_string();
752
753 let diff = schema_diff_expanded_from_initial(schema_string);
754
755 insta::assert_snapshot!(diff.unwrap_or_default(), @r#"directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
756directive @federation__key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
757directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION
758directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION
759directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
760directive @federation__tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
761directive @federation__extends on OBJECT | INTERFACE
762directive @federation__shareable repeatable on OBJECT | FIELD_DEFINITION
763directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
764directive @federation__override(from: String!, label: String) on FIELD_DEFINITION
765directive @federation__composeDirective(name: String) repeatable on SCHEMA
766directive @federation__interfaceObject on OBJECT
767directive @federation__authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
768directive @federation__requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
769directive @federation__policy(policies: [[federation__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
770directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | UNION
771directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION
772directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR
773directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION
774enum link__Purpose {
775 """
776 `SECURITY` features provide metadata necessary to securely resolve fields.
777 """
778 SECURITY
779 """
780 `EXECUTION` features provide metadata necessary for operation execution.
781 """
782 EXECUTION
783}
784scalar link__Import
785scalar federation__FieldSet
786scalar federation__Scope
787scalar federation__Policy
788scalar federation__ContextFieldValue
789scalar _Any
790type _Service {
791 sdl: String
792}"#);
793 }
794}