async_graphql/dynamic/
interface.rs

1use indexmap::{IndexMap, IndexSet};
2
3use super::{directive::to_meta_directive_invocation, Directive};
4use crate::{
5    dynamic::{InputValue, SchemaError, TypeRef},
6    registry::{Deprecation, MetaField, MetaType, Registry},
7};
8
9/// A GraphQL interface field type
10///
11/// # Examples
12///
13/// ```
14/// use async_graphql::{dynamic::*, value, Value};
15///
16/// let obj_a = Object::new("MyObjA")
17///     .implement("MyInterface")
18///     .field(Field::new("a", TypeRef::named_nn(TypeRef::INT), |_| {
19///         FieldFuture::new(async { Ok(Some(Value::from(100))) })
20///     }))
21///     .field(Field::new("b", TypeRef::named_nn(TypeRef::INT), |_| {
22///         FieldFuture::new(async { Ok(Some(Value::from(200))) })
23///     }));
24///
25/// let obj_b = Object::new("MyObjB")
26///     .implement("MyInterface")
27///     .field(Field::new("a", TypeRef::named_nn(TypeRef::INT), |_| {
28///         FieldFuture::new(async { Ok(Some(Value::from(300))) })
29///     }))
30///     .field(Field::new("c", TypeRef::named_nn(TypeRef::INT), |_| {
31///         FieldFuture::new(async { Ok(Some(Value::from(400))) })
32///     }));
33///
34/// let interface = Interface::new("MyInterface").field(InterfaceField::new("a", TypeRef::named_nn(TypeRef::INT)));
35///
36/// let query = Object::new("Query")
37///     .field(Field::new("valueA", TypeRef::named_nn(interface.type_name()), |_| {
38///         FieldFuture::new(async {
39///             Ok(Some(FieldValue::with_type(FieldValue::NULL, "MyObjA")))
40///         })
41///     }))
42///     .field(Field::new("valueB", TypeRef::named_nn(interface.type_name()), |_| {
43///         FieldFuture::new(async {
44///             Ok(Some(FieldValue::with_type(FieldValue::NULL, "MyObjB")))
45///         })
46///     }));
47///
48/// # tokio::runtime::Runtime::new().unwrap().block_on(async move {
49///
50/// let schema = Schema::build(query.type_name(), None, None)
51///     .register(obj_a)
52///     .register(obj_b)
53///     .register(interface)
54///     .register(query)
55///     .finish()?;
56///
57/// let query = r#"
58///     fragment A on MyObjA { b }
59///
60///     fragment B on MyObjB { c }
61///
62///     {
63///         valueA { a ...A ...B }
64///         valueB { a ...A ...B }
65///     }
66/// "#;
67///
68/// assert_eq!(
69///     schema.execute(query).await.into_result().unwrap().data,
70///     value!({
71///         "valueA": {
72///             "a": 100,
73///             "b": 200,
74///         },
75///         "valueB": {
76///             "a": 300,
77///             "c": 400,
78///         }
79///     })
80/// );
81///
82/// # Ok::<_, SchemaError>(())
83/// # }).unwrap();
84/// ```
85#[derive(Debug)]
86pub struct InterfaceField {
87    pub(crate) name: String,
88    pub(crate) description: Option<String>,
89    pub(crate) arguments: IndexMap<String, InputValue>,
90    pub(crate) ty: TypeRef,
91    pub(crate) deprecation: Deprecation,
92    pub(crate) external: bool,
93    pub(crate) requires: Option<String>,
94    pub(crate) provides: Option<String>,
95    pub(crate) shareable: bool,
96    pub(crate) inaccessible: bool,
97    pub(crate) tags: Vec<String>,
98    pub(crate) override_from: Option<String>,
99    pub(crate) directives: Vec<Directive>,
100}
101
102impl InterfaceField {
103    /// Create a GraphQL interface field type
104    pub fn new(name: impl Into<String>, ty: impl Into<TypeRef>) -> Self {
105        Self {
106            name: name.into(),
107            description: None,
108            arguments: Default::default(),
109            ty: ty.into(),
110            deprecation: Deprecation::NoDeprecated,
111            external: false,
112            requires: None,
113            provides: None,
114            shareable: false,
115            inaccessible: false,
116            tags: Vec::new(),
117            override_from: None,
118            directives: Vec::new(),
119        }
120    }
121
122    impl_set_description!();
123    impl_set_deprecation!();
124    impl_set_external!();
125    impl_set_requires!();
126    impl_set_provides!();
127    impl_set_shareable!();
128    impl_set_inaccessible!();
129    impl_set_tags!();
130    impl_set_override_from!();
131    impl_directive!();
132
133    /// Add an argument to the field
134    #[inline]
135    pub fn argument(mut self, input_value: InputValue) -> Self {
136        self.arguments.insert(input_value.name.clone(), input_value);
137        self
138    }
139}
140
141/// A GraphQL interface type
142#[derive(Debug)]
143pub struct Interface {
144    pub(crate) name: String,
145    pub(crate) description: Option<String>,
146    pub(crate) fields: IndexMap<String, InterfaceField>,
147    pub(crate) implements: IndexSet<String>,
148    keys: Vec<String>,
149    extends: bool,
150    inaccessible: bool,
151    tags: Vec<String>,
152    pub(crate) directives: Vec<Directive>,
153}
154
155impl Interface {
156    /// Create a GraphQL interface type
157    #[inline]
158    pub fn new(name: impl Into<String>) -> Self {
159        Self {
160            name: name.into(),
161            description: None,
162            fields: Default::default(),
163            implements: Default::default(),
164            keys: Vec::new(),
165            extends: false,
166            inaccessible: false,
167            tags: Vec::new(),
168            directives: Vec::new(),
169        }
170    }
171
172    impl_set_description!();
173    impl_set_extends!();
174    impl_set_inaccessible!();
175    impl_set_tags!();
176    impl_directive!();
177
178    /// Add a field to the interface type
179    #[inline]
180    pub fn field(mut self, field: InterfaceField) -> Self {
181        assert!(
182            !self.fields.contains_key(&field.name),
183            "Field `{}` already exists",
184            field.name
185        );
186        self.fields.insert(field.name.clone(), field);
187        self
188    }
189
190    /// Add an implement to the interface type
191    #[inline]
192    pub fn implement(mut self, interface: impl Into<String>) -> Self {
193        let interface = interface.into();
194        assert!(
195            !self.implements.contains(&interface),
196            "Implement `{}` already exists",
197            interface
198        );
199        self.implements.insert(interface);
200        self
201    }
202
203    /// Add an entity key
204    ///
205    /// See also: [`Object::key`](crate::dynamic::Object::key)
206    pub fn key(mut self, fields: impl Into<String>) -> Self {
207        self.keys.push(fields.into());
208        self
209    }
210
211    /// Returns the type name
212    #[inline]
213    pub fn type_name(&self) -> &str {
214        &self.name
215    }
216
217    #[inline]
218    pub(crate) fn is_entity(&self) -> bool {
219        !self.keys.is_empty()
220    }
221
222    pub(crate) fn register(&self, registry: &mut Registry) -> Result<(), SchemaError> {
223        let mut fields = IndexMap::new();
224
225        for field in self.fields.values() {
226            let mut args = IndexMap::new();
227
228            for argument in field.arguments.values() {
229                args.insert(argument.name.clone(), argument.to_meta_input_value());
230            }
231
232            fields.insert(
233                field.name.clone(),
234                MetaField {
235                    name: field.name.clone(),
236                    description: field.description.clone(),
237                    args,
238                    ty: field.ty.to_string(),
239                    deprecation: field.deprecation.clone(),
240                    cache_control: Default::default(),
241                    external: field.external,
242                    requires: field.requires.clone(),
243                    provides: field.provides.clone(),
244                    visible: None,
245                    shareable: field.shareable,
246                    inaccessible: field.inaccessible,
247                    tags: field.tags.clone(),
248                    override_from: field.override_from.clone(),
249                    compute_complexity: None,
250                    directive_invocations: to_meta_directive_invocation(field.directives.clone()),
251                },
252            );
253        }
254
255        registry.types.insert(
256            self.name.clone(),
257            MetaType::Interface {
258                name: self.name.clone(),
259                description: self.description.clone(),
260                fields,
261                possible_types: Default::default(),
262                extends: self.extends,
263                keys: if !self.keys.is_empty() {
264                    Some(self.keys.clone())
265                } else {
266                    None
267                },
268                visible: None,
269                inaccessible: self.inaccessible,
270                tags: self.tags.clone(),
271                rust_typename: None,
272                directive_invocations: to_meta_directive_invocation(self.directives.clone()),
273            },
274        );
275
276        Ok(())
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use async_graphql_parser::Pos;
283
284    use crate::{dynamic::*, value, PathSegment, ServerError, Value};
285
286    #[tokio::test]
287    async fn basic_interface() {
288        let obj_a = Object::new("MyObjA")
289            .implement("MyInterface")
290            .field(Field::new("a", TypeRef::named(TypeRef::INT), |_| {
291                FieldFuture::new(async { Ok(Some(Value::from(100))) })
292            }))
293            .field(Field::new("b", TypeRef::named(TypeRef::INT), |_| {
294                FieldFuture::new(async { Ok(Some(Value::from(200))) })
295            }));
296
297        let obj_b = Object::new("MyObjB")
298            .implement("MyInterface")
299            .field(Field::new("a", TypeRef::named(TypeRef::INT), |_| {
300                FieldFuture::new(async { Ok(Some(Value::from(300))) })
301            }))
302            .field(Field::new("c", TypeRef::named(TypeRef::INT), |_| {
303                FieldFuture::new(async { Ok(Some(Value::from(400))) })
304            }));
305
306        let interface = Interface::new("MyInterface")
307            .field(InterfaceField::new("a", TypeRef::named(TypeRef::INT)));
308
309        let query = Object::new("Query")
310            .field(Field::new(
311                "valueA",
312                TypeRef::named_nn(interface.type_name()),
313                |_| FieldFuture::new(async { Ok(Some(FieldValue::NULL.with_type("MyObjA"))) }),
314            ))
315            .field(Field::new(
316                "valueB",
317                TypeRef::named_nn(interface.type_name()),
318                |_| FieldFuture::new(async { Ok(Some(FieldValue::NULL.with_type("MyObjB"))) }),
319            ));
320
321        let schema = Schema::build(query.type_name(), None, None)
322            .register(obj_a)
323            .register(obj_b)
324            .register(interface)
325            .register(query)
326            .finish()
327            .unwrap();
328
329        let query = r#"
330        fragment A on MyObjA {
331            b
332        }
333
334        fragment B on MyObjB {
335            c
336        }
337
338        {
339            valueA { __typename a ...A ...B }
340            valueB { __typename a ...A ...B }
341        }
342        "#;
343        assert_eq!(
344            schema.execute(query).await.into_result().unwrap().data,
345            value!({
346                "valueA": {
347                    "__typename": "MyObjA",
348                    "a": 100,
349                    "b": 200,
350                },
351                "valueB": {
352                    "__typename": "MyObjB",
353                    "a": 300,
354                    "c": 400,
355                }
356            })
357        );
358    }
359
360    #[tokio::test]
361    async fn does_not_implement() {
362        let obj_a = Object::new("MyObjA")
363            .field(Field::new("a", TypeRef::named(TypeRef::INT), |_| {
364                FieldFuture::new(async { Ok(Some(Value::from(100))) })
365            }))
366            .field(Field::new("b", TypeRef::named(TypeRef::INT), |_| {
367                FieldFuture::new(async { Ok(Some(Value::from(200))) })
368            }));
369
370        let interface = Interface::new("MyInterface")
371            .field(InterfaceField::new("a", TypeRef::named(TypeRef::INT)));
372
373        let query = Object::new("Query").field(Field::new(
374            "valueA",
375            TypeRef::named_nn(interface.type_name()),
376            |_| FieldFuture::new(async { Ok(Some(FieldValue::NULL.with_type("MyObjA"))) }),
377        ));
378
379        let schema = Schema::build(query.type_name(), None, None)
380            .register(obj_a)
381            .register(interface)
382            .register(query)
383            .finish()
384            .unwrap();
385
386        let query = r#"
387        {
388            valueA { a }
389        }
390        "#;
391        assert_eq!(
392            schema.execute(query).await.into_result().unwrap_err(),
393            vec![ServerError {
394                message: "internal: object \"MyObjA\" does not implement interface \"MyInterface\""
395                    .to_owned(),
396                source: None,
397                locations: vec![Pos {
398                    column: 13,
399                    line: 3
400                }],
401                path: vec![PathSegment::Field("valueA".to_owned())],
402                extensions: None,
403            }]
404        );
405    }
406    #[tokio::test]
407    async fn query_type_condition() {
408        struct MyObjA;
409        let obj_a = Object::new("MyObjA")
410            .implement("MyInterface")
411            .field(Field::new("a", TypeRef::named(TypeRef::INT), |_| {
412                FieldFuture::new(async { Ok(Some(Value::from(100))) })
413            }))
414            .field(Field::new("b", TypeRef::named(TypeRef::INT), |_| {
415                FieldFuture::new(async { Ok(Some(Value::from(200))) })
416            }));
417        let interface = Interface::new("MyInterface")
418            .field(InterfaceField::new("a", TypeRef::named(TypeRef::INT)));
419        let query = Object::new("Query");
420        let query = query.field(Field::new(
421            "valueA",
422            TypeRef::named_nn(obj_a.type_name()),
423            |_| FieldFuture::new(async { Ok(Some(FieldValue::owned_any(MyObjA))) }),
424        ));
425        let schema = Schema::build(query.type_name(), None, None)
426            .register(obj_a)
427            .register(interface)
428            .register(query)
429            .finish()
430            .unwrap();
431        let query = r#"
432        {
433            valueA { __typename
434            b
435            ... on MyInterface { a } }
436        }
437        "#;
438        assert_eq!(
439            schema.execute(query).await.into_result().unwrap().data,
440            value!({
441                "valueA": {
442                    "__typename": "MyObjA",
443                    "b": 200,
444                    "a": 100,
445                }
446            })
447        );
448    }
449}