apollo_compiler/introspection/
resolvers.rs1use crate::collections::HashMap;
2use crate::execution::resolver::ResolvedValue;
3use crate::execution::resolver::Resolver;
4use crate::execution::resolver::ResolverError;
5use crate::response::JsonMap;
6use crate::schema;
7use crate::schema::Implementers;
8use crate::schema::Name;
9use crate::Node;
10use crate::Schema;
11use std::borrow::Cow;
12
13#[derive(Clone, Copy)]
14pub(crate) struct SchemaWithImplementersMap<'a> {
15 pub(crate) schema: &'a Schema,
16 pub(crate) implementers_map: &'a HashMap<Name, Implementers>,
17}
18
19impl<'a> SchemaWithImplementersMap<'a> {
20 fn implementers_of(&self, interface_name: &str) -> impl Iterator<Item = &'a Name> {
21 self.implementers_map
22 .get(interface_name)
23 .into_iter()
24 .flat_map(|implementers| &implementers.objects)
25 }
26}
27
28impl<'a> std::ops::Deref for SchemaWithImplementersMap<'a> {
29 type Target = &'a Schema;
30
31 fn deref(&self) -> &Self::Target {
32 &self.schema
33 }
34}
35
36pub(crate) struct IntrospectionRootResolver<'a>(pub(crate) SchemaWithImplementersMap<'a>);
37
38struct TypeDefResolver<'a> {
39 schema: SchemaWithImplementersMap<'a>,
40 name: &'a str,
41 def: &'a schema::ExtendedType,
42}
43
44pub(crate) struct TypeResolver<'a> {
46 schema: SchemaWithImplementersMap<'a>,
47 ty: Cow<'a, schema::Type>,
48}
49
50struct DirectiveResolver<'a> {
51 schema: SchemaWithImplementersMap<'a>,
52 def: &'a schema::DirectiveDefinition,
53}
54
55struct FieldResolver<'a> {
56 schema: SchemaWithImplementersMap<'a>,
57 def: &'a schema::FieldDefinition,
58}
59
60struct EnumValueResolver<'a> {
61 schema: SchemaWithImplementersMap<'a>,
62 def: &'a schema::EnumValueDefinition,
63}
64
65struct InputValueResolver<'a> {
66 schema: SchemaWithImplementersMap<'a>,
67 def: &'a schema::InputValueDefinition,
68}
69
70fn type_def(schema: SchemaWithImplementersMap<'_>, name: impl AsRef<str>) -> ResolvedValue<'_> {
71 ResolvedValue::opt_object(
72 schema
73 .types
74 .get_key_value(name.as_ref())
75 .map(|(name, def)| TypeDefResolver { schema, name, def }),
76 )
77}
78
79fn type_def_opt<'a>(
80 schema: SchemaWithImplementersMap<'a>,
81 name: &Option<impl AsRef<str>>,
82) -> ResolvedValue<'a> {
83 if let Some(name) = name.as_ref() {
84 type_def(schema, name)
85 } else {
86 ResolvedValue::null()
87 }
88}
89
90fn ty<'a>(schema: SchemaWithImplementersMap<'a>, ty: &'a schema::Type) -> ResolvedValue<'a> {
91 if let schema::Type::Named(name) = ty {
92 type_def(schema, name)
93 } else {
94 ResolvedValue::object(TypeResolver {
95 schema,
96 ty: Cow::Borrowed(ty),
97 })
98 }
99}
100
101fn deprecation_reason<'a>(
102 schema: &SchemaWithImplementersMap<'_>,
103 opt_directive: Option<&Node<schema::Directive>>,
104) -> ResolvedValue<'a> {
105 ResolvedValue::leaf(
106 opt_directive
107 .and_then(|directive| directive.argument_by_name("reason", schema.schema).ok())
108 .and_then(|arg| arg.as_str()),
109 )
110}
111
112impl Resolver for IntrospectionRootResolver<'_> {
113 fn type_name(&self) -> &'static str {
114 unreachable!()
115 }
116
117 fn resolve_field<'a>(
118 &'a self,
119 field_name: &'a str,
120 arguments: &'a JsonMap,
121 ) -> Result<ResolvedValue<'a>, ResolverError> {
122 match field_name {
123 "__schema" => Ok(ResolvedValue::object(self.0)),
124 "__type" => {
125 let name = arguments["name"].as_str().unwrap();
126 Ok(type_def(self.0, name))
127 }
128 _ => unreachable!(),
131 }
132 }
133
134 fn skip_field(&self, field_name: &str) -> bool {
135 !matches!(field_name, "__schema" | "__type" | "__typename")
136 }
137}
138
139impl_resolver! {
140 for SchemaWithImplementersMap<'_>:
141
142 __typename = "__Schema";
143
144 fn description(&self_) {
145 Ok(ResolvedValue::leaf(self_.schema_definition.description.as_deref()))
146 }
147
148 fn types(&self_) {
149 Ok(ResolvedValue::list(self_.types.iter().map(|(name, def)| {
150 ResolvedValue::object(TypeDefResolver { schema: *self_, name, def })
151 })))
152 }
153
154 fn directives(&self_) {
155 Ok(ResolvedValue::list(self_.directive_definitions.values().map(|def| {
156 ResolvedValue::object(DirectiveResolver { schema: *self_, def })
157 })))
158 }
159
160 fn queryType(&self_) {
161 Ok(type_def_opt(*self_, &self_.schema_definition.query))
162 }
163
164 fn mutationType(&self_) {
165 Ok(type_def_opt(*self_, &self_.schema_definition.mutation))
166 }
167
168 fn subscriptionType(&self_) {
169 Ok(type_def_opt(*self_, &self_.schema_definition.subscription))
170 }
171}
172
173impl_resolver! {
174 for TypeDefResolver<'_>:
175
176 __typename = "__Type";
177
178 fn kind(&self_) {
179 Ok(ResolvedValue::leaf(match self_.def {
180 schema::ExtendedType::Scalar(_) => "SCALAR",
181 schema::ExtendedType::Object(_) => "OBJECT",
182 schema::ExtendedType::Interface(_) => "INTERFACE",
183 schema::ExtendedType::Union(_) => "UNION",
184 schema::ExtendedType::Enum(_) => "ENUM",
185 schema::ExtendedType::InputObject(_) => "INPUT_OBJECT",
186 }))
187 }
188
189 fn name(&self_) {
190 Ok(ResolvedValue::leaf(self_.name))
191 }
192
193 fn description(&self_) {
194 Ok(ResolvedValue::leaf(self_.def.description().map(|desc| desc.as_str())))
195 }
196
197 fn fields(&self_, args) {
198 let fields = match self_.def {
199 schema::ExtendedType::Object(def) => &def.fields,
200 schema::ExtendedType::Interface(def) => &def.fields,
201 schema::ExtendedType::Scalar(_) |
202 schema::ExtendedType::Union(_) |
203 schema::ExtendedType::Enum(_) |
204 schema::ExtendedType::InputObject(_) => return Ok(ResolvedValue::null()),
205 };
206 let include_deprecated = include_deprecated(args);
207 Ok(ResolvedValue::list(fields
208 .values()
209 .filter(move |def| {
210 include_deprecated || def.directives.get("deprecated").is_none()
211 })
212 .map(|def| {
213 ResolvedValue::object(FieldResolver { schema: self_.schema, def })
214 })
215 ))
216 }
217
218 fn interfaces(&self_) {
219 let implements_interfaces = match self_.def {
220 schema::ExtendedType::Object(def) => &def.implements_interfaces,
221 schema::ExtendedType::Interface(def) => &def.implements_interfaces,
222 schema::ExtendedType::Scalar(_) |
223 schema::ExtendedType::Union(_) |
224 schema::ExtendedType::Enum(_) |
225 schema::ExtendedType::InputObject(_) => return Ok(ResolvedValue::null()),
226 };
227 Ok(ResolvedValue::list(implements_interfaces.iter().filter_map(|name| {
228 self_.schema.types.get(&name.name).map(|def| {
229 ResolvedValue::object(TypeDefResolver { schema: self_.schema, name, def })
230 })
231 })))
232 }
233
234 fn possibleTypes(&self_) {
235 macro_rules! types {
236 ($names: expr) => {
237 Ok(ResolvedValue::list($names.filter_map(move |name| {
238 self_.schema.types.get(name).map(move |def| {
239 ResolvedValue::object(TypeDefResolver { schema: self_.schema, name, def })
240 })
241 })))
242 }
243 }
244 match self_.def {
245 schema::ExtendedType::Interface(_) => types!(self_.schema.implementers_of(self_.name)),
246 schema::ExtendedType::Union(def) => types!(def.members.iter().map(|c| &c.name)),
247 schema::ExtendedType::Object(_) |
248 schema::ExtendedType::Scalar(_) |
249 schema::ExtendedType::Enum(_) |
250 schema::ExtendedType::InputObject(_) => Ok(ResolvedValue::null()),
251 }
252 }
253
254 fn enumValues(&self_, args) {
255 let schema::ExtendedType::Enum(def) = self_.def else {
256 return Ok(ResolvedValue::null());
257 };
258 let include_deprecated = include_deprecated(args);
259 Ok(ResolvedValue::list(def
260 .values
261 .values()
262 .filter(move |def| {
263 include_deprecated || def.directives.get("deprecated").is_none()
264 })
265 .map(|def| {
266 ResolvedValue::object(EnumValueResolver { schema: self_.schema, def })
267 })
268 ))
269 }
270
271 fn inputFields(&self_, args) {
272 let schema::ExtendedType::InputObject(def) = self_.def else {
273 return Ok(ResolvedValue::null());
274 };
275 let include_deprecated = include_deprecated(args);
276 Ok(ResolvedValue::list(def
277 .fields
278 .values()
279 .filter(move |def| {
280 include_deprecated || def.directives.get("deprecated").is_none()
281 })
282 .map(|def| {
283 ResolvedValue::object(InputValueResolver { schema: self_.schema, def })
284 })
285 ))
286 }
287
288 fn ofType() {
289 Ok(ResolvedValue::null())
290 }
291
292 fn specifiedByURL(&self_) {
293 let schema::ExtendedType::Scalar(def) = self_.def else {
294 return Ok(ResolvedValue::null())
295 };
296 Ok(ResolvedValue::leaf(def
297 .directives.get("specifiedBy")
298 .and_then(|dir| dir.specified_argument_by_name("url"))
299 .and_then(|arg| arg.as_str())
300 ))
301 }
302}
303
304impl_resolver! {
306 for TypeResolver<'_>:
307
308 __typename = "__Type";
309
310 fn kind(&self_) {
311 Ok(ResolvedValue::leaf(match &*self_.ty {
312 schema::Type::Named(_) => unreachable!(),
313 schema::Type::List(_) => "LIST",
314 schema::Type::NonNullNamed(_) |
315 schema::Type::NonNullList(_) => "NON_NULL",
316 }))
317 }
318
319 fn ofType(&self_) {
320 Ok(match &*self_.ty {
321 schema::Type::Named(_) => unreachable!(),
322 schema::Type::List(inner) => ty(self_.schema, inner),
323 schema::Type::NonNullNamed(inner) => type_def(self_.schema, inner),
324 schema::Type::NonNullList(inner) => ResolvedValue::object(Self {
325 schema: self_.schema,
326 ty: Cow::Owned(schema::Type::List(inner.clone()))
327 }),
328 })
329 }
330
331 fn name() { Ok(ResolvedValue::null()) }
332 fn description() { Ok(ResolvedValue::null()) }
333 fn fields() { Ok(ResolvedValue::null()) }
334 fn interfaces() { Ok(ResolvedValue::null()) }
335 fn possibleTypes() { Ok(ResolvedValue::null()) }
336 fn enumValues() { Ok(ResolvedValue::null()) }
337 fn inputFields() { Ok(ResolvedValue::null()) }
338 fn specifiedByURL() { Ok(ResolvedValue::null()) }
339}
340
341impl_resolver! {
342 for DirectiveResolver<'_>:
343
344 __typename = "__Directive";
345
346 fn name(&self_) {
347 Ok(ResolvedValue::leaf(self_.def.name.as_str()))
348 }
349
350 fn description(&self_) {
351 Ok(ResolvedValue::leaf(self_.def.description.as_deref()))
352 }
353
354 fn args(&self_, args) {
355 let include_deprecated = include_deprecated(args);
356 Ok(ResolvedValue::list(self_
357 .def
358 .arguments
359 .iter()
360 .filter(move |def| {
361 include_deprecated || def.directives.get("deprecated").is_none()
362 })
363 .map(|def| {
364 ResolvedValue::object(InputValueResolver { schema: self_.schema, def })
365 })
366 ))
367 }
368
369 fn locations(&self_) {
370 Ok(ResolvedValue::list(self_.def.locations.iter().map(|loc| {
371 ResolvedValue::leaf(loc.name())
372 })))
373 }
374
375 fn isRepeatable(&self_) {
376 Ok(ResolvedValue::leaf(self_.def.repeatable))
377 }
378}
379
380impl_resolver! {
381 for FieldResolver<'_>:
382
383 __typename = "__Field";
384
385 fn name(&self_) {
386 Ok(ResolvedValue::leaf(self_.def.name.as_str()))
387 }
388
389 fn description(&self_) {
390 Ok(ResolvedValue::leaf(self_.def.description.as_deref()))
391 }
392
393 fn args(&self_, args) {
394 let include_deprecated = include_deprecated(args);
395 Ok(ResolvedValue::list(self_
396 .def
397 .arguments
398 .iter()
399 .filter(move |def| {
400 include_deprecated || def.directives.get("deprecated").is_none()
401 })
402 .map(|def| {
403 ResolvedValue::object(InputValueResolver { schema: self_.schema, def })
404 })
405 ))
406 }
407
408 fn type(&self_) {
409 Ok(ty(self_.schema, &self_.def.ty))
410 }
411
412 fn isDeprecated(&self_) {
413 Ok(ResolvedValue::leaf(self_.def.directives.get("deprecated").is_some()))
414 }
415
416 fn deprecationReason(&self_) {
417 Ok(deprecation_reason(&self_.schema, self_.def.directives.get("deprecated")))
418 }
419}
420
421impl_resolver! {
422 for EnumValueResolver<'_>:
423
424 __typename = "__EnumValue";
425
426 fn name(&self_) {
427 Ok(ResolvedValue::leaf(self_.def.value.as_str()))
428 }
429
430 fn description(&self_) {
431 Ok(ResolvedValue::leaf(self_.def.description.as_deref()))
432 }
433
434 fn isDeprecated(&self_) {
435 Ok(ResolvedValue::leaf(self_.def.directives.get("deprecated").is_some()))
436 }
437
438 fn deprecationReason(&self_) {
439 Ok(deprecation_reason(&self_.schema, self_.def.directives.get("deprecated")))
440 }
441}
442
443impl_resolver! {
444 for InputValueResolver<'_>:
445
446 __typename = "__InputValue";
447
448 fn name(&self_) {
449 Ok(ResolvedValue::leaf(self_.def.name.as_str()))
450 }
451
452 fn description(&self_) {
453 Ok(ResolvedValue::leaf(self_.def.description.as_deref()))
454 }
455
456 fn type(&self_) {
457 Ok(ty(self_.schema, &self_.def.ty))
458 }
459
460 fn defaultValue(&self_) {
461 Ok(ResolvedValue::leaf(self_.def.default_value.as_ref().map(|val| {
462 val.serialize().no_indent().to_string()
463 })))
464 }
465
466 fn isDeprecated(&self_) {
467 Ok(ResolvedValue::leaf(self_.def.directives.get("deprecated").is_some()))
468 }
469
470 fn deprecationReason(&self_) {
471 Ok(deprecation_reason(&self_.schema, self_.def.directives.get("deprecated")))
472 }
473}
474
475fn include_deprecated(args: &JsonMap) -> bool {
477 match &args["includeDeprecated"] {
478 serde_json_bytes::Value::Bool(b) => *b,
479 serde_json_bytes::Value::Null => false,
480 _ => unreachable!(),
481 }
482}