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