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