/rust/registry/src/index.crates.io-1949cf8c6b5b557f/async-graphql-7.0.16/src/dynamic/resolve.rs
Line | Count | Source |
1 | | use std::{borrow::Cow, pin::Pin}; |
2 | | |
3 | | use async_graphql_derive::SimpleObject; |
4 | | use async_graphql_parser::{types::Field, Positioned}; |
5 | | use futures_util::{future::BoxFuture, Future, FutureExt}; |
6 | | use indexmap::IndexMap; |
7 | | |
8 | | use crate::{ |
9 | | dynamic::{ |
10 | | field::FieldValueInner, FieldFuture, FieldValue, Object, ObjectAccessor, ResolverContext, |
11 | | Schema, Type, TypeRef, |
12 | | }, |
13 | | extensions::ResolveInfo, |
14 | | parser::types::Selection, |
15 | | resolver_utils::create_value_object, |
16 | | Context, ContextSelectionSet, Error, IntrospectionMode, Name, SDLExportOptions, ServerError, |
17 | | ServerResult, Value, |
18 | | }; |
19 | | |
20 | | /// Federation service |
21 | | #[derive(SimpleObject)] |
22 | | #[graphql(internal, name = "_Service")] |
23 | | struct Service { |
24 | | sdl: Option<String>, |
25 | | } |
26 | | |
27 | | type BoxFieldFuture<'a> = Pin<Box<dyn Future<Output = ServerResult<(Name, Value)>> + 'a + Send>>; |
28 | | |
29 | 0 | pub(crate) async fn resolve_container( |
30 | 0 | schema: &Schema, |
31 | 0 | object: &Object, |
32 | 0 | ctx: &ContextSelectionSet<'_>, |
33 | 0 | parent_value: &FieldValue<'_>, |
34 | 0 | serial: bool, |
35 | 0 | ) -> ServerResult<Option<Value>> { |
36 | 0 | let mut fields = Vec::new(); |
37 | 0 | collect_fields(&mut fields, schema, object, ctx, parent_value)?; |
38 | | |
39 | 0 | let res = if !serial { |
40 | 0 | futures_util::future::try_join_all(fields).await? |
41 | | } else { |
42 | 0 | let mut results = Vec::with_capacity(fields.len()); |
43 | 0 | for field in fields { |
44 | 0 | results.push(field.await?); |
45 | | } |
46 | 0 | results |
47 | | }; |
48 | | |
49 | 0 | Ok(Some(create_value_object(res))) |
50 | 0 | } |
51 | | |
52 | 0 | fn collect_typename_field<'a>( |
53 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
54 | 0 | object: &'a Object, |
55 | 0 | field: &'a Positioned<Field>, |
56 | 0 | ) { |
57 | 0 | fields.push( |
58 | 0 | async move { |
59 | 0 | Ok(( |
60 | 0 | field.node.response_key().node.clone(), |
61 | 0 | Value::from(object.name.as_str()), |
62 | 0 | )) |
63 | 0 | } |
64 | 0 | .boxed(), |
65 | | ) |
66 | 0 | } |
67 | | |
68 | 0 | fn collect_schema_field<'a>( |
69 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
70 | 0 | ctx: &ContextSelectionSet<'a>, |
71 | 0 | field: &'a Positioned<Field>, |
72 | 0 | ) { |
73 | 0 | let ctx = ctx.clone(); |
74 | 0 | fields.push( |
75 | 0 | async move { |
76 | 0 | let ctx_field = ctx.with_field(field); |
77 | 0 | let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set); |
78 | 0 | ctx_obj.is_for_introspection = true; |
79 | 0 | let visible_types = ctx.schema_env.registry.find_visible_types(&ctx_field); |
80 | 0 | let value = crate::OutputType::resolve( |
81 | 0 | &crate::model::__Schema::new(&ctx.schema_env.registry, &visible_types), |
82 | 0 | &ctx_obj, |
83 | 0 | ctx_field.item, |
84 | 0 | ) |
85 | 0 | .await?; |
86 | 0 | Ok((field.node.response_key().node.clone(), value)) |
87 | 0 | } |
88 | 0 | .boxed(), |
89 | | ); |
90 | 0 | } |
91 | | |
92 | 0 | fn collect_type_field<'a>( |
93 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
94 | 0 | ctx: &ContextSelectionSet<'a>, |
95 | 0 | field: &'a Positioned<Field>, |
96 | 0 | ) { |
97 | 0 | let ctx = ctx.clone(); |
98 | 0 | fields.push( |
99 | 0 | async move { |
100 | 0 | let ctx_field = ctx.with_field(field); |
101 | 0 | let (_, type_name) = ctx_field.param_value::<String>("name", None)?; |
102 | 0 | let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set); |
103 | 0 | ctx_obj.is_for_introspection = true; |
104 | 0 | let visible_types = ctx.schema_env.registry.find_visible_types(&ctx_field); |
105 | 0 | let value = crate::OutputType::resolve( |
106 | 0 | &ctx.schema_env |
107 | 0 | .registry |
108 | 0 | .types |
109 | 0 | .get(&type_name) |
110 | 0 | .filter(|_| visible_types.contains(type_name.as_str())) |
111 | 0 | .map(|ty| { |
112 | 0 | crate::model::__Type::new_simple( |
113 | 0 | &ctx.schema_env.registry, |
114 | 0 | &visible_types, |
115 | 0 | ty, |
116 | | ) |
117 | 0 | }), |
118 | 0 | &ctx_obj, |
119 | 0 | ctx_field.item, |
120 | | ) |
121 | 0 | .await?; |
122 | 0 | Ok((field.node.response_key().node.clone(), value)) |
123 | 0 | } |
124 | 0 | .boxed(), |
125 | | ); |
126 | 0 | } |
127 | | |
128 | 0 | fn collect_service_field<'a>( |
129 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
130 | 0 | ctx: &ContextSelectionSet<'a>, |
131 | 0 | field: &'a Positioned<Field>, |
132 | 0 | ) { |
133 | 0 | let ctx = ctx.clone(); |
134 | 0 | fields.push( |
135 | 0 | async move { |
136 | 0 | let ctx_field = ctx.with_field(field); |
137 | 0 | let mut ctx_obj = ctx.with_selection_set(&ctx_field.item.node.selection_set); |
138 | 0 | ctx_obj.is_for_introspection = true; |
139 | | |
140 | 0 | let output_type = crate::OutputType::resolve( |
141 | 0 | &Service { |
142 | 0 | sdl: Some( |
143 | 0 | ctx.schema_env |
144 | 0 | .registry |
145 | 0 | .export_sdl(SDLExportOptions::new().federation().compose_directive()), |
146 | 0 | ), |
147 | 0 | }, |
148 | 0 | &ctx_obj, |
149 | 0 | ctx_field.item, |
150 | 0 | ) |
151 | 0 | .await?; |
152 | | |
153 | 0 | Ok((field.node.response_key().node.clone(), output_type)) |
154 | 0 | } |
155 | 0 | .boxed(), |
156 | | ); |
157 | 0 | } |
158 | | |
159 | 0 | fn collect_entities_field<'a>( |
160 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
161 | 0 | schema: &'a Schema, |
162 | 0 | ctx: &ContextSelectionSet<'a>, |
163 | 0 | parent_value: &'a FieldValue, |
164 | 0 | field: &'a Positioned<Field>, |
165 | 0 | ) { |
166 | 0 | let ctx = ctx.clone(); |
167 | 0 | fields.push( |
168 | 0 | async move { |
169 | 0 | let ctx_field = ctx.with_field(field); |
170 | 0 | let entity_resolver = schema.0.entity_resolver.as_ref().ok_or_else(|| { |
171 | 0 | ctx_field.set_error_path( |
172 | 0 | Error::new("internal: missing entity resolver") |
173 | 0 | .into_server_error(ctx_field.item.pos), |
174 | | ) |
175 | 0 | })?; |
176 | 0 | let entity_type = TypeRef::named_list_nn("_Entity"); |
177 | | |
178 | 0 | let arguments = ObjectAccessor(Cow::Owned( |
179 | 0 | field |
180 | 0 | .node |
181 | 0 | .arguments |
182 | 0 | .iter() |
183 | 0 | .map(|(name, value)| { |
184 | 0 | ctx_field |
185 | 0 | .resolve_input_value(value.clone()) |
186 | 0 | .map(|value| (name.node.clone(), value)) |
187 | 0 | }) |
188 | 0 | .collect::<ServerResult<IndexMap<Name, Value>>>()?, |
189 | | )); |
190 | | |
191 | 0 | let field_future = (entity_resolver)(ResolverContext { |
192 | 0 | ctx: &ctx_field, |
193 | 0 | args: arguments, |
194 | 0 | parent_value, |
195 | 0 | }); |
196 | | |
197 | 0 | let field_value = match field_future { |
198 | 0 | FieldFuture::Future(fut) => { |
199 | 0 | fut.await.map_err(|err| err.into_server_error(field.pos))? |
200 | | } |
201 | 0 | FieldFuture::Value(value) => value, |
202 | | }; |
203 | 0 | let value = resolve(schema, &ctx_field, &entity_type, field_value.as_ref()) |
204 | 0 | .await? |
205 | 0 | .unwrap_or_default(); |
206 | 0 | Ok((field.node.response_key().node.clone(), value)) |
207 | 0 | } |
208 | 0 | .boxed(), |
209 | | ); |
210 | 0 | } |
211 | | |
212 | 0 | fn collect_field<'a>( |
213 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
214 | 0 | schema: &'a Schema, |
215 | 0 | object: &'a Object, |
216 | 0 | ctx: &ContextSelectionSet<'a>, |
217 | 0 | parent_value: &'a FieldValue, |
218 | 0 | field_def: &'a crate::dynamic::Field, |
219 | 0 | field: &'a Positioned<Field>, |
220 | 0 | ) { |
221 | 0 | let ctx = ctx.clone(); |
222 | 0 | fields.push( |
223 | 0 | async move { |
224 | 0 | let ctx_field = ctx.with_field(field); |
225 | 0 | let arguments = ObjectAccessor(Cow::Owned({ |
226 | 0 | let mut args = field |
227 | 0 | .node |
228 | 0 | .arguments |
229 | 0 | .iter() |
230 | 0 | .map(|(name, value)| { |
231 | 0 | ctx_field |
232 | 0 | .resolve_input_value(value.clone()) |
233 | 0 | .map(|value| (name.node.clone(), value)) |
234 | 0 | }) |
235 | 0 | .collect::<ServerResult<IndexMap<Name, Value>>>()?; |
236 | 0 | field_def.arguments.iter().for_each(|(name, arg)| { |
237 | 0 | if let Some(def) = &arg.default_value { |
238 | 0 | if !args.contains_key(name.as_str()) { |
239 | 0 | args.insert(Name::new(name), def.clone()); |
240 | 0 | } |
241 | 0 | } |
242 | 0 | }); |
243 | 0 | args |
244 | | })); |
245 | | |
246 | 0 | let resolve_info = ResolveInfo { |
247 | 0 | path_node: ctx_field.path_node.as_ref().unwrap(), |
248 | 0 | parent_type: &object.name, |
249 | 0 | return_type: &field_def.ty_str, |
250 | 0 | name: &field.node.name.node, |
251 | 0 | alias: field.node.alias.as_ref().map(|alias| &*alias.node), |
252 | 0 | is_for_introspection: ctx_field.is_for_introspection, |
253 | 0 | field: &field.node, |
254 | | }; |
255 | 0 | let resolve_fut = async { |
256 | 0 | let field_future = (field_def.resolver_fn)(ResolverContext { |
257 | 0 | ctx: &ctx_field, |
258 | 0 | args: arguments, |
259 | 0 | parent_value, |
260 | 0 | }); |
261 | | |
262 | 0 | let field_value = match field_future { |
263 | 0 | FieldFuture::Value(field_value) => field_value, |
264 | 0 | FieldFuture::Future(future) => future |
265 | 0 | .await |
266 | 0 | .map_err(|err| err.into_server_error(field.pos))?, |
267 | | }; |
268 | | |
269 | 0 | let value = |
270 | 0 | resolve(schema, &ctx_field, &field_def.ty, field_value.as_ref()).await?; |
271 | | |
272 | 0 | Ok(value) |
273 | 0 | }; |
274 | 0 | futures_util::pin_mut!(resolve_fut); |
275 | | |
276 | 0 | let res_value = ctx_field |
277 | 0 | .query_env |
278 | 0 | .extensions |
279 | 0 | .resolve(resolve_info, &mut resolve_fut) |
280 | 0 | .await? |
281 | 0 | .unwrap_or_default(); |
282 | 0 | Ok((field.node.response_key().node.clone(), res_value)) |
283 | 0 | } |
284 | 0 | .boxed(), |
285 | | ); |
286 | 0 | } |
287 | | |
288 | 0 | fn collect_fields<'a>( |
289 | 0 | fields: &mut Vec<BoxFieldFuture<'a>>, |
290 | 0 | schema: &'a Schema, |
291 | 0 | object: &'a Object, |
292 | 0 | ctx: &ContextSelectionSet<'a>, |
293 | 0 | parent_value: &'a FieldValue, |
294 | 0 | ) -> ServerResult<()> { |
295 | 0 | for selection in &ctx.item.node.items { |
296 | 0 | match &selection.node { |
297 | 0 | Selection::Field(field) => { |
298 | 0 | if field.node.name.node == "__typename" { |
299 | 0 | collect_typename_field(fields, object, field); |
300 | 0 | continue; |
301 | 0 | } |
302 | | |
303 | 0 | if object.name == schema.0.env.registry.query_type |
304 | 0 | && matches!( |
305 | 0 | ctx.schema_env.registry.introspection_mode, |
306 | | IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly |
307 | | ) |
308 | 0 | && matches!( |
309 | 0 | ctx.query_env.introspection_mode, |
310 | | IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly, |
311 | | ) |
312 | | { |
313 | | // is query root |
314 | 0 | if field.node.name.node == "__schema" { |
315 | 0 | collect_schema_field(fields, ctx, field); |
316 | 0 | continue; |
317 | 0 | } else if field.node.name.node == "__type" { |
318 | 0 | collect_type_field(fields, ctx, field); |
319 | 0 | continue; |
320 | 0 | } else if ctx.schema_env.registry.enable_federation |
321 | 0 | && field.node.name.node == "_service" |
322 | | { |
323 | 0 | collect_service_field(fields, ctx, field); |
324 | 0 | continue; |
325 | 0 | } else if ctx.schema_env.registry.enable_federation |
326 | 0 | && field.node.name.node == "_entities" |
327 | | { |
328 | 0 | collect_entities_field(fields, schema, ctx, parent_value, field); |
329 | 0 | continue; |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | 0 | if ctx.schema_env.registry.introspection_mode |
334 | 0 | == IntrospectionMode::IntrospectionOnly |
335 | 0 | || ctx.query_env.introspection_mode == IntrospectionMode::IntrospectionOnly |
336 | | { |
337 | 0 | fields.push( |
338 | 0 | async move { Ok((field.node.response_key().node.clone(), Value::Null)) } |
339 | 0 | .boxed(), |
340 | | ); |
341 | 0 | continue; |
342 | 0 | } |
343 | | |
344 | 0 | if let Some(field_def) = object.fields.get(field.node.name.node.as_str()) { |
345 | 0 | collect_field(fields, schema, object, ctx, parent_value, field_def, field); |
346 | 0 | } |
347 | | } |
348 | 0 | selection => { |
349 | 0 | let (type_condition, selection_set) = match selection { |
350 | 0 | Selection::Field(_) => unreachable!(), |
351 | 0 | Selection::FragmentSpread(spread) => { |
352 | 0 | let fragment = ctx.query_env.fragments.get(&spread.node.fragment_name.node); |
353 | 0 | let fragment = match fragment { |
354 | 0 | Some(fragment) => fragment, |
355 | | None => { |
356 | 0 | return Err(ServerError::new( |
357 | 0 | format!( |
358 | 0 | "Unknown fragment \"{}\".", |
359 | 0 | spread.node.fragment_name.node |
360 | 0 | ), |
361 | 0 | Some(spread.pos), |
362 | 0 | )); |
363 | | } |
364 | | }; |
365 | 0 | ( |
366 | 0 | Some(&fragment.node.type_condition), |
367 | 0 | &fragment.node.selection_set, |
368 | 0 | ) |
369 | | } |
370 | 0 | Selection::InlineFragment(fragment) => ( |
371 | 0 | fragment.node.type_condition.as_ref(), |
372 | 0 | &fragment.node.selection_set, |
373 | 0 | ), |
374 | | }; |
375 | | |
376 | 0 | let type_condition = |
377 | 0 | type_condition.map(|condition| condition.node.on.node.as_str()); |
378 | 0 | let introspection_type_name = &object.name; |
379 | | |
380 | 0 | let type_condition_matched = match type_condition { |
381 | 0 | None => true, |
382 | 0 | Some(type_condition) if type_condition == introspection_type_name => true, |
383 | 0 | Some(type_condition) if object.implements.contains(type_condition) => true, |
384 | 0 | _ => false, |
385 | | }; |
386 | 0 | if type_condition_matched { |
387 | 0 | collect_fields( |
388 | 0 | fields, |
389 | 0 | schema, |
390 | 0 | object, |
391 | 0 | &ctx.with_selection_set(selection_set), |
392 | 0 | parent_value, |
393 | 0 | )?; |
394 | 0 | } |
395 | | } |
396 | | } |
397 | | } |
398 | | |
399 | 0 | Ok(()) |
400 | 0 | } |
401 | | |
402 | 0 | pub(crate) fn resolve<'a>( |
403 | 0 | schema: &'a Schema, |
404 | 0 | ctx: &'a Context<'a>, |
405 | 0 | type_ref: &'a TypeRef, |
406 | 0 | value: Option<&'a FieldValue>, |
407 | 0 | ) -> BoxFuture<'a, ServerResult<Option<Value>>> { |
408 | 0 | async move { |
409 | 0 | match (type_ref, value) { |
410 | 0 | (TypeRef::Named(type_name), Some(value)) => { |
411 | 0 | resolve_value(schema, ctx, &schema.0.types[type_name.as_ref()], value).await |
412 | | } |
413 | 0 | (TypeRef::Named(_), None) => Ok(None), |
414 | | |
415 | 0 | (TypeRef::NonNull(type_ref), Some(value)) => { |
416 | 0 | resolve(schema, ctx, type_ref, Some(value)).await |
417 | | } |
418 | 0 | (TypeRef::NonNull(_), None) => Err(ctx.set_error_path( |
419 | 0 | Error::new("internal: non-null types require a return value") |
420 | 0 | .into_server_error(ctx.item.pos), |
421 | 0 | )), |
422 | | |
423 | 0 | (TypeRef::List(type_ref), Some(FieldValue(FieldValueInner::List(values)))) => { |
424 | 0 | resolve_list(schema, ctx, type_ref, values).await |
425 | | } |
426 | | ( |
427 | 0 | TypeRef::List(type_ref), |
428 | 0 | Some(FieldValue(FieldValueInner::Value(Value::List(values)))), |
429 | | ) => { |
430 | 0 | let values = values |
431 | 0 | .iter() |
432 | 0 | .cloned() |
433 | 0 | .map(FieldValue::value) |
434 | 0 | .collect::<Vec<_>>(); |
435 | 0 | resolve_list(schema, ctx, type_ref, &values).await |
436 | | } |
437 | 0 | (TypeRef::List(_), Some(_)) => Err(ctx.set_error_path( |
438 | 0 | Error::new("internal: expects an array").into_server_error(ctx.item.pos), |
439 | 0 | )), |
440 | 0 | (TypeRef::List(_), None) => Ok(None), |
441 | | } |
442 | 0 | } |
443 | 0 | .boxed() |
444 | 0 | } |
445 | | |
446 | 0 | async fn resolve_list<'a>( |
447 | 0 | schema: &'a Schema, |
448 | 0 | ctx: &'a Context<'a>, |
449 | 0 | type_ref: &'a TypeRef, |
450 | 0 | values: &[FieldValue<'_>], |
451 | 0 | ) -> ServerResult<Option<Value>> { |
452 | 0 | let mut futures = Vec::with_capacity(values.len()); |
453 | 0 | for (idx, value) in values.iter().enumerate() { |
454 | 0 | let ctx_item = ctx.with_index(idx); |
455 | | |
456 | 0 | futures.push(async move { |
457 | 0 | let parent_type = format!("[{}]", type_ref); |
458 | 0 | let return_type = type_ref.to_string(); |
459 | 0 | let resolve_info = ResolveInfo { |
460 | 0 | path_node: ctx_item.path_node.as_ref().unwrap(), |
461 | 0 | parent_type: &parent_type, |
462 | 0 | return_type: &return_type, |
463 | 0 | name: ctx.item.node.name.node.as_str(), |
464 | 0 | alias: ctx |
465 | 0 | .item |
466 | 0 | .node |
467 | 0 | .alias |
468 | 0 | .as_ref() |
469 | 0 | .map(|alias| alias.node.as_str()), |
470 | 0 | is_for_introspection: ctx_item.is_for_introspection, |
471 | 0 | field: &ctx_item.item.node, |
472 | | }; |
473 | | |
474 | 0 | let resolve_fut = async { resolve(schema, &ctx_item, type_ref, Some(value)).await }; |
475 | 0 | futures_util::pin_mut!(resolve_fut); |
476 | | |
477 | 0 | let res_value = ctx_item |
478 | 0 | .query_env |
479 | 0 | .extensions |
480 | 0 | .resolve(resolve_info, &mut resolve_fut) |
481 | 0 | .await?; |
482 | 0 | Ok::<_, ServerError>(res_value.unwrap_or_default()) |
483 | 0 | }); |
484 | | } |
485 | 0 | let values = futures_util::future::try_join_all(futures).await?; |
486 | 0 | Ok(Some(Value::List(values))) |
487 | 0 | } |
488 | | |
489 | 0 | async fn resolve_value( |
490 | 0 | schema: &Schema, |
491 | 0 | ctx: &Context<'_>, |
492 | 0 | field_type: &Type, |
493 | 0 | value: &FieldValue<'_>, |
494 | 0 | ) -> ServerResult<Option<Value>> { |
495 | 0 | match (field_type, &value.0) { |
496 | 0 | (Type::Scalar(scalar), FieldValueInner::Value(value)) if scalar.validate(value) => { |
497 | 0 | Ok(Some(value.clone())) |
498 | | } |
499 | 0 | (Type::Scalar(scalar), _) => Err(ctx.set_error_path( |
500 | 0 | Error::new(format!( |
501 | 0 | "internal: invalid value for scalar \"{}\", expected \"FieldValue::Value\"", |
502 | 0 | scalar.name |
503 | 0 | )) |
504 | 0 | .into_server_error(ctx.item.pos), |
505 | 0 | )), |
506 | | |
507 | 0 | (Type::Object(object), _) => { |
508 | 0 | resolve_container( |
509 | 0 | schema, |
510 | 0 | object, |
511 | 0 | &ctx.with_selection_set(&ctx.item.node.selection_set), |
512 | 0 | value, |
513 | 0 | true, |
514 | 0 | ) |
515 | 0 | .await |
516 | | } |
517 | | |
518 | 0 | (Type::InputObject(obj), _) => Err(ctx.set_error_path( |
519 | 0 | Error::new(format!( |
520 | 0 | "internal: cannot use input object \"{}\" as output value", |
521 | 0 | obj.name |
522 | 0 | )) |
523 | 0 | .into_server_error(ctx.item.pos), |
524 | 0 | )), |
525 | | |
526 | 0 | (Type::Enum(e), FieldValueInner::Value(Value::Enum(name))) => { |
527 | 0 | if !e.enum_values.contains_key(name.as_str()) { |
528 | 0 | return Err(ctx.set_error_path( |
529 | 0 | Error::new(format!("internal: invalid item for enum \"{}\"", e.name)) |
530 | 0 | .into_server_error(ctx.item.pos), |
531 | 0 | )); |
532 | 0 | } |
533 | 0 | Ok(Some(Value::Enum(name.clone()))) |
534 | | } |
535 | 0 | (Type::Enum(e), FieldValueInner::Value(Value::String(name))) => { |
536 | 0 | if !e.enum_values.contains_key(name) { |
537 | 0 | return Err(ctx.set_error_path( |
538 | 0 | Error::new(format!("internal: invalid item for enum \"{}\"", e.name)) |
539 | 0 | .into_server_error(ctx.item.pos), |
540 | 0 | )); |
541 | 0 | } |
542 | 0 | Ok(Some(Value::Enum(Name::new(name)))) |
543 | | } |
544 | 0 | (Type::Enum(e), _) => Err(ctx.set_error_path( |
545 | 0 | Error::new(format!("internal: invalid item for enum \"{}\"", e.name)) |
546 | 0 | .into_server_error(ctx.item.pos), |
547 | 0 | )), |
548 | | |
549 | 0 | (Type::Interface(interface), FieldValueInner::WithType { value, ty }) => { |
550 | 0 | let is_contains_obj = schema |
551 | 0 | .0 |
552 | 0 | .env |
553 | 0 | .registry |
554 | 0 | .types |
555 | 0 | .get(&interface.name) |
556 | 0 | .and_then(|meta_type| { |
557 | 0 | meta_type |
558 | 0 | .possible_types() |
559 | 0 | .map(|possible_types| possible_types.contains(ty.as_ref())) |
560 | 0 | }) |
561 | 0 | .unwrap_or_default(); |
562 | 0 | if !is_contains_obj { |
563 | 0 | return Err(ctx.set_error_path( |
564 | 0 | Error::new(format!( |
565 | 0 | "internal: object \"{}\" does not implement interface \"{}\"", |
566 | 0 | ty, interface.name, |
567 | 0 | )) |
568 | 0 | .into_server_error(ctx.item.pos), |
569 | 0 | )); |
570 | 0 | } |
571 | | |
572 | 0 | let object_type = schema |
573 | 0 | .0 |
574 | 0 | .types |
575 | 0 | .get(ty.as_ref()) |
576 | 0 | .ok_or_else(|| { |
577 | 0 | ctx.set_error_path( |
578 | 0 | Error::new(format!("internal: object \"{}\" does not registered", ty)) |
579 | 0 | .into_server_error(ctx.item.pos), |
580 | | ) |
581 | 0 | })? |
582 | 0 | .as_object() |
583 | 0 | .ok_or_else(|| { |
584 | 0 | ctx.set_error_path( |
585 | 0 | Error::new(format!("internal: type \"{}\" is not object", ty)) |
586 | 0 | .into_server_error(ctx.item.pos), |
587 | | ) |
588 | 0 | })?; |
589 | | |
590 | 0 | resolve_container( |
591 | 0 | schema, |
592 | 0 | object_type, |
593 | 0 | &ctx.with_selection_set(&ctx.item.node.selection_set), |
594 | 0 | value, |
595 | 0 | true, |
596 | 0 | ) |
597 | 0 | .await |
598 | | } |
599 | 0 | (Type::Interface(interface), _) => Err(ctx.set_error_path( |
600 | 0 | Error::new(format!( |
601 | 0 | "internal: invalid value for interface \"{}\", expected \"FieldValue::WithType\"", |
602 | 0 | interface.name |
603 | 0 | )) |
604 | 0 | .into_server_error(ctx.item.pos), |
605 | 0 | )), |
606 | | |
607 | 0 | (Type::Union(union), FieldValueInner::WithType { value, ty }) => { |
608 | 0 | if !union.possible_types.contains(ty.as_ref()) { |
609 | 0 | return Err(ctx.set_error_path( |
610 | 0 | Error::new(format!( |
611 | 0 | "internal: union \"{}\" does not contain object \"{}\"", |
612 | 0 | union.name, ty, |
613 | 0 | )) |
614 | 0 | .into_server_error(ctx.item.pos), |
615 | 0 | )); |
616 | 0 | } |
617 | | |
618 | 0 | let object_type = schema |
619 | 0 | .0 |
620 | 0 | .types |
621 | 0 | .get(ty.as_ref()) |
622 | 0 | .ok_or_else(|| { |
623 | 0 | ctx.set_error_path( |
624 | 0 | Error::new(format!("internal: object \"{}\" does not registered", ty)) |
625 | 0 | .into_server_error(ctx.item.pos), |
626 | | ) |
627 | 0 | })? |
628 | 0 | .as_object() |
629 | 0 | .ok_or_else(|| { |
630 | 0 | ctx.set_error_path( |
631 | 0 | Error::new(format!("internal: type \"{}\" is not object", ty)) |
632 | 0 | .into_server_error(ctx.item.pos), |
633 | | ) |
634 | 0 | })?; |
635 | | |
636 | 0 | resolve_container( |
637 | 0 | schema, |
638 | 0 | object_type, |
639 | 0 | &ctx.with_selection_set(&ctx.item.node.selection_set), |
640 | 0 | value, |
641 | 0 | true, |
642 | 0 | ) |
643 | 0 | .await |
644 | | } |
645 | 0 | (Type::Union(union), _) => Err(ctx.set_error_path( |
646 | 0 | Error::new(format!( |
647 | 0 | "internal: invalid value for union \"{}\", expected \"FieldValue::WithType\"", |
648 | 0 | union.name |
649 | 0 | )) |
650 | 0 | .into_server_error(ctx.item.pos), |
651 | 0 | )), |
652 | 0 | (Type::Subscription(subscription), _) => Err(ctx.set_error_path( |
653 | 0 | Error::new(format!( |
654 | 0 | "internal: cannot use subscription \"{}\" as output value", |
655 | 0 | subscription.name |
656 | 0 | )) |
657 | 0 | .into_server_error(ctx.item.pos), |
658 | 0 | )), |
659 | 0 | (Type::Upload, _) => Err(ctx.set_error_path( |
660 | 0 | Error::new("internal: cannot use upload as output value") |
661 | 0 | .into_server_error(ctx.item.pos), |
662 | 0 | )), |
663 | | } |
664 | 0 | } |