apollo_federation/schema/
mod.rs1use std::hash::Hash;
2use std::hash::Hasher;
3use std::ops::Deref;
4use std::sync::Arc;
5
6use apollo_compiler::Name;
7use apollo_compiler::Schema;
8use apollo_compiler::collections::IndexSet;
9use apollo_compiler::schema::ExtendedType;
10use apollo_compiler::validation::Valid;
11use referencer::Referencers;
12
13use crate::error::FederationError;
14use crate::error::SingleFederationError;
15use crate::link::LinksMetadata;
16use crate::link::federation_spec_definition::FEDERATION_ENTITY_TYPE_NAME_IN_SPEC;
17use crate::link::federation_spec_definition::get_federation_spec_definition_from_subgraph;
18use crate::schema::position::CompositeTypeDefinitionPosition;
19use crate::schema::position::DirectiveDefinitionPosition;
20use crate::schema::position::EnumTypeDefinitionPosition;
21use crate::schema::position::InputObjectTypeDefinitionPosition;
22use crate::schema::position::InterfaceTypeDefinitionPosition;
23use crate::schema::position::ObjectTypeDefinitionPosition;
24use crate::schema::position::ScalarTypeDefinitionPosition;
25use crate::schema::position::TypeDefinitionPosition;
26use crate::schema::position::UnionTypeDefinitionPosition;
27use crate::schema::subgraph_metadata::SubgraphMetadata;
28
29pub(crate) mod argument_composition_strategies;
30pub(crate) mod definitions;
31pub(crate) mod field_set;
32pub(crate) mod position;
33pub(crate) mod referencer;
34pub(crate) mod subgraph_metadata;
35
36fn compute_subgraph_metadata(
37 schema: &Valid<FederationSchema>,
38) -> Result<Option<SubgraphMetadata>, FederationError> {
39 Ok(
40 if let Ok(federation_spec_definition) = get_federation_spec_definition_from_subgraph(schema)
41 {
42 Some(SubgraphMetadata::new(schema, federation_spec_definition)?)
43 } else {
44 None
45 },
46 )
47}
48pub(crate) mod type_and_directive_specification;
49
50#[derive(Debug)]
52pub struct FederationSchema {
53 schema: Schema,
54 referencers: Referencers,
55 links_metadata: Option<Box<LinksMetadata>>,
56 subgraph_metadata: Option<Box<SubgraphMetadata>>,
59}
60
61impl FederationSchema {
62 pub(crate) fn schema(&self) -> &Schema {
63 &self.schema
64 }
65
66 pub fn into_inner(self) -> Schema {
68 self.schema
69 }
70
71 pub(crate) fn metadata(&self) -> Option<&LinksMetadata> {
72 self.links_metadata.as_deref()
73 }
74
75 pub(crate) fn referencers(&self) -> &Referencers {
76 &self.referencers
77 }
78
79 pub(crate) fn get_types(&self) -> impl Iterator<Item = TypeDefinitionPosition> + '_ {
81 self.schema
82 .types
83 .iter()
84 .filter(|(_, ty)| !ty.is_built_in())
85 .map(|(type_name, type_)| {
86 let type_name = type_name.clone();
87 match type_ {
88 ExtendedType::Scalar(_) => ScalarTypeDefinitionPosition { type_name }.into(),
89 ExtendedType::Object(_) => ObjectTypeDefinitionPosition { type_name }.into(),
90 ExtendedType::Interface(_) => {
91 InterfaceTypeDefinitionPosition { type_name }.into()
92 }
93 ExtendedType::Union(_) => UnionTypeDefinitionPosition { type_name }.into(),
94 ExtendedType::Enum(_) => EnumTypeDefinitionPosition { type_name }.into(),
95 ExtendedType::InputObject(_) => {
96 InputObjectTypeDefinitionPosition { type_name }.into()
97 }
98 }
99 })
100 }
101
102 pub(crate) fn get_directive_definitions(
103 &self,
104 ) -> impl Iterator<Item = DirectiveDefinitionPosition> + '_ {
105 self.schema
106 .directive_definitions
107 .keys()
108 .map(|name| DirectiveDefinitionPosition {
109 directive_name: name.clone(),
110 })
111 }
112
113 pub(crate) fn get_type(
114 &self,
115 type_name: Name,
116 ) -> Result<TypeDefinitionPosition, FederationError> {
117 let type_ =
118 self.schema
119 .types
120 .get(&type_name)
121 .ok_or_else(|| SingleFederationError::Internal {
122 message: format!("Schema has no type \"{}\"", type_name),
123 })?;
124 Ok(match type_ {
125 ExtendedType::Scalar(_) => ScalarTypeDefinitionPosition { type_name }.into(),
126 ExtendedType::Object(_) => ObjectTypeDefinitionPosition { type_name }.into(),
127 ExtendedType::Interface(_) => InterfaceTypeDefinitionPosition { type_name }.into(),
128 ExtendedType::Union(_) => UnionTypeDefinitionPosition { type_name }.into(),
129 ExtendedType::Enum(_) => EnumTypeDefinitionPosition { type_name }.into(),
130 ExtendedType::InputObject(_) => InputObjectTypeDefinitionPosition { type_name }.into(),
131 })
132 }
133
134 pub(crate) fn try_get_type(&self, type_name: Name) -> Option<TypeDefinitionPosition> {
135 self.get_type(type_name).ok()
136 }
137
138 pub(crate) fn possible_runtime_types(
145 &self,
146 composite_type_definition_position: CompositeTypeDefinitionPosition,
147 ) -> Result<IndexSet<ObjectTypeDefinitionPosition>, FederationError> {
148 Ok(match composite_type_definition_position {
149 CompositeTypeDefinitionPosition::Object(pos) => IndexSet::from_iter([pos]),
150 CompositeTypeDefinitionPosition::Interface(pos) => self
151 .referencers()
152 .get_interface_type(&pos.type_name)?
153 .object_types
154 .clone(),
155 CompositeTypeDefinitionPosition::Union(pos) => pos
156 .get(self.schema())?
157 .members
158 .iter()
159 .map(|t| ObjectTypeDefinitionPosition {
160 type_name: t.name.clone(),
161 })
162 .collect::<IndexSet<_>>(),
163 })
164 }
165
166 #[allow(clippy::result_large_err)] pub(crate) fn validate_or_return_self(
170 mut self,
171 ) -> Result<ValidFederationSchema, (Self, FederationError)> {
172 let schema = match self.schema.validate() {
173 Ok(schema) => schema.into_inner(),
174 Err(e) => {
175 self.schema = e.partial;
176 return Err((self, e.errors.into()));
177 }
178 };
179 ValidFederationSchema::new_assume_valid(FederationSchema { schema, ..self })
180 }
181
182 pub(crate) fn assume_valid(self) -> Result<ValidFederationSchema, FederationError> {
183 ValidFederationSchema::new_assume_valid(self).map_err(|(_schema, error)| error)
184 }
185
186 pub(crate) fn get_directive_definition(
187 &self,
188 name: &Name,
189 ) -> Option<DirectiveDefinitionPosition> {
190 self.schema
191 .directive_definitions
192 .contains_key(name)
193 .then(|| DirectiveDefinitionPosition {
194 directive_name: name.clone(),
195 })
196 }
197
198 pub(crate) fn entity_type(
200 &self,
201 ) -> Result<Option<UnionTypeDefinitionPosition>, FederationError> {
202 match self.schema.types.get(&FEDERATION_ENTITY_TYPE_NAME_IN_SPEC) {
207 Some(ExtendedType::Union(_)) => Ok(Some(UnionTypeDefinitionPosition {
208 type_name: FEDERATION_ENTITY_TYPE_NAME_IN_SPEC,
209 })),
210 Some(_) => Err(FederationError::internal(format!(
211 "Unexpectedly found non-union for federation spec's `{}` type definition",
212 FEDERATION_ENTITY_TYPE_NAME_IN_SPEC
213 ))),
214 None => Ok(None),
215 }
216 }
217}
218
219#[derive(Clone)]
221pub struct ValidFederationSchema {
222 schema: Arc<Valid<FederationSchema>>,
223}
224
225impl ValidFederationSchema {
226 pub fn new(schema: Valid<Schema>) -> Result<ValidFederationSchema, FederationError> {
227 let schema = FederationSchema::new(schema.into_inner())?;
228
229 Self::new_assume_valid(schema).map_err(|(_schema, error)| error)
230 }
231
232 #[allow(clippy::result_large_err)] fn new_assume_valid(
235 mut schema: FederationSchema,
236 ) -> Result<ValidFederationSchema, (FederationSchema, FederationError)> {
237 let valid_schema = Valid::assume_valid_ref(&schema);
244 let subgraph_metadata = match compute_subgraph_metadata(valid_schema) {
245 Ok(metadata) => metadata.map(Box::new),
246 Err(err) => return Err((schema, err)),
247 };
248 schema.subgraph_metadata = subgraph_metadata;
249
250 let schema = Arc::new(Valid::assume_valid(schema));
251 Ok(ValidFederationSchema { schema })
252 }
253
254 pub fn schema(&self) -> &Valid<Schema> {
256 Valid::assume_valid_ref(&self.schema.schema)
257 }
258
259 pub(crate) fn subgraph_metadata(&self) -> Option<&SubgraphMetadata> {
263 self.schema.subgraph_metadata.as_deref()
264 }
265
266 pub(crate) fn federation_type_name_in_schema(
267 &self,
268 name: Name,
269 ) -> Result<Name, FederationError> {
270 if name.starts_with('_') {
277 return Ok(name);
278 }
279
280 Err(FederationError::internal(
284 "typename should have been looked in a federation feature",
285 ))
286 }
287
288 pub(crate) fn is_interface_object_type(
289 &self,
290 type_definition_position: TypeDefinitionPosition,
291 ) -> Result<bool, FederationError> {
292 let Some(subgraph_metadata) = &self.subgraph_metadata else {
293 return Ok(false);
294 };
295 let Some(interface_object_directive_definition) = subgraph_metadata
296 .federation_spec_definition()
297 .interface_object_directive_definition(self)?
298 else {
299 return Ok(false);
300 };
301 match type_definition_position {
302 TypeDefinitionPosition::Object(type_) => Ok(type_
303 .get(self.schema())?
304 .directives
305 .has(&interface_object_directive_definition.name)),
306 _ => Ok(false),
307 }
308 }
309}
310
311impl Deref for ValidFederationSchema {
312 type Target = FederationSchema;
313
314 fn deref(&self) -> &Self::Target {
315 &self.schema
316 }
317}
318
319impl Eq for ValidFederationSchema {}
320
321impl PartialEq for ValidFederationSchema {
322 fn eq(&self, other: &ValidFederationSchema) -> bool {
323 Arc::ptr_eq(&self.schema, &other.schema)
324 }
325}
326
327impl Hash for ValidFederationSchema {
328 fn hash<H: Hasher>(&self, state: &mut H) {
329 Arc::as_ptr(&self.schema).hash(state);
330 }
331}
332
333impl std::fmt::Debug for ValidFederationSchema {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 write!(f, "ValidFederationSchema @ {:?}", Arc::as_ptr(&self.schema))
336 }
337}