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