apollo_federation/
api_schema.rs1use apollo_compiler::Node;
3use apollo_compiler::name;
4use apollo_compiler::schema::DirectiveDefinition;
5use apollo_compiler::schema::DirectiveLocation;
6use apollo_compiler::schema::InputValueDefinition;
7use apollo_compiler::ty;
8
9use crate::error::FederationError;
10use crate::link::inaccessible_spec_definition::InaccessibleSpecDefinition;
11use crate::schema::FederationSchema;
12use crate::schema::ValidFederationSchema;
13use crate::schema::position;
14
15fn remove_core_feature_elements(schema: &mut FederationSchema) -> Result<(), FederationError> {
17 let Some(metadata) = schema.metadata() else {
18 return Ok(());
19 };
20
21 let types_for_removal = schema
24 .get_types()
25 .filter(|position| metadata.source_link_of_type(position.type_name()).is_some())
26 .collect::<Vec<_>>();
27
28 let directives_for_removal = schema
29 .get_directive_definitions()
30 .filter(|position| {
31 metadata
32 .source_link_of_directive(&position.directive_name)
33 .is_some()
34 })
35 .collect::<Vec<_>>();
36
37 for position in &types_for_removal {
40 match position {
41 position::TypeDefinitionPosition::Object(position) => {
42 let object = position.get(schema.schema())?.clone();
43 object
44 .fields
45 .keys()
46 .map(|field_name| position.field(field_name.clone()))
47 .try_for_each(|child| child.remove(schema))?;
48 }
49 position::TypeDefinitionPosition::Interface(position) => {
50 let interface = position.get(schema.schema())?.clone();
51 interface
52 .fields
53 .keys()
54 .map(|field_name| position.field(field_name.clone()))
55 .try_for_each(|child| child.remove(schema))?;
56 }
57 position::TypeDefinitionPosition::InputObject(position) => {
58 let input_object = position.get(schema.schema())?.clone();
59 input_object
60 .fields
61 .keys()
62 .map(|field_name| position.field(field_name.clone()))
63 .try_for_each(|child| child.remove(schema))?;
64 }
65 position::TypeDefinitionPosition::Enum(position) => {
66 let enum_ = position.get(schema.schema())?.clone();
67 enum_
68 .values
69 .keys()
70 .map(|field_name| position.value(field_name.clone()))
71 .try_for_each(|child| child.remove(schema))?;
72 }
73 _ => {}
74 }
75 }
76
77 for position in &directives_for_removal {
78 position.remove(schema)?;
79 }
80
81 for position in &types_for_removal {
82 match position {
83 position::TypeDefinitionPosition::Object(position) => {
84 position.remove(schema)?;
85 }
86 position::TypeDefinitionPosition::Interface(position) => {
87 position.remove(schema)?;
88 }
89 position::TypeDefinitionPosition::InputObject(position) => {
90 position.remove(schema)?;
91 }
92 position::TypeDefinitionPosition::Enum(position) => {
93 position.remove(schema)?;
94 }
95 position::TypeDefinitionPosition::Scalar(position) => {
96 position.remove(schema)?;
97 }
98 position::TypeDefinitionPosition::Union(position) => {
99 position.remove(schema)?;
100 }
101 }
102 }
103
104 Ok(())
105}
106
107#[derive(Debug, Default, Clone)]
108pub struct ApiSchemaOptions {
109 pub include_defer: bool,
110 pub include_stream: bool,
111}
112
113pub(crate) fn to_api_schema(
114 schema: ValidFederationSchema,
115 options: ApiSchemaOptions,
116) -> Result<ValidFederationSchema, FederationError> {
117 let mut api_schema = FederationSchema::from(schema);
119
120 if let Some(defer) = api_schema.get_directive_definition(&name!("defer")) {
124 defer.remove(&mut api_schema)?;
125 }
126 if let Some(stream) = api_schema.get_directive_definition(&name!("stream")) {
127 stream.remove(&mut api_schema)?;
128 }
129
130 if let Some(inaccessible_spec) = InaccessibleSpecDefinition::get_from_schema(&api_schema)? {
131 inaccessible_spec.validate_inaccessible(&api_schema)?;
132 inaccessible_spec.remove_inaccessible_elements(&mut api_schema)?;
133 }
134
135 remove_core_feature_elements(&mut api_schema)?;
136
137 let mut schema = api_schema.into_inner();
138
139 if options.include_defer {
140 schema
141 .directive_definitions
142 .insert(name!("defer"), defer_definition());
143 }
144
145 if options.include_stream {
146 schema
147 .directive_definitions
148 .insert(name!("stream"), stream_definition());
149 }
150
151 crate::compat::make_print_schema_compatible(&mut schema);
152
153 ValidFederationSchema::new(schema.validate()?)
154}
155
156fn defer_definition() -> Node<DirectiveDefinition> {
157 Node::new(DirectiveDefinition {
158 description: None,
159 name: name!("defer"),
160 arguments: vec![
161 Node::new(InputValueDefinition {
162 description: None,
163 name: name!("label"),
164 ty: ty!(String).into(),
165 default_value: None,
166 directives: Default::default(),
167 }),
168 Node::new(InputValueDefinition {
169 description: None,
170 name: name!("if"),
171 ty: ty!(Boolean!).into(),
172 default_value: Some(true.into()),
173 directives: Default::default(),
174 }),
175 ],
176 repeatable: false,
177 locations: vec![
178 DirectiveLocation::FragmentSpread,
179 DirectiveLocation::InlineFragment,
180 ],
181 })
182}
183
184fn stream_definition() -> Node<DirectiveDefinition> {
185 Node::new(DirectiveDefinition {
186 description: None,
187 name: name!("stream"),
188 arguments: vec![
189 Node::new(InputValueDefinition {
190 description: None,
191 name: name!("label"),
192 ty: ty!(String).into(),
193 default_value: None,
194 directives: Default::default(),
195 }),
196 Node::new(InputValueDefinition {
197 description: None,
198 name: name!("if"),
199 ty: ty!(Boolean!).into(),
200 default_value: Some(true.into()),
201 directives: Default::default(),
202 }),
203 Node::new(InputValueDefinition {
204 description: None,
205 name: name!("initialCount"),
206 ty: ty!(Int).into(),
207 default_value: Some(0.into()),
208 directives: Default::default(),
209 }),
210 ],
211 repeatable: false,
212 locations: vec![DirectiveLocation::Field],
213 })
214}