async_graphql/dynamic/
interface.rs

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