Skip to main content

apollo_smith/
object.rs

1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::field::FieldDef;
5use crate::interface::base_def_index;
6use crate::interface::def_indices_with_name;
7use crate::interface::field_signatures_for;
8use crate::interface::parent_fields_from_defs;
9use crate::interface::unique_names;
10use crate::name::Name;
11use crate::DocumentBuilder;
12use crate::StackedEntity;
13use apollo_compiler::ast;
14use apollo_compiler::Node;
15use arbitrary::Result as ArbitraryResult;
16use indexmap::IndexMap;
17use indexmap::IndexSet;
18
19/// Object types represent concrete instantiations of sets of fields.
20///
21/// The introspection types (e.g. `__Type`, `__Field`, etc) are examples of
22/// objects.
23///
24/// *ObjectTypeDefinition*:
25///     Description? **type** Name ImplementsInterfaces? Directives? FieldsDefinition?
26///
27/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Object).
28#[derive(Debug, Clone)]
29pub struct ObjectTypeDef {
30    pub(crate) description: Option<Description>,
31    pub(crate) name: Name,
32    pub(crate) implements_interfaces: IndexSet<Name>,
33    pub(crate) directives: IndexMap<Name, Directive>,
34    pub(crate) fields_def: Vec<FieldDef>,
35    pub(crate) extend: bool,
36}
37
38impl From<ObjectTypeDef> for ast::Definition {
39    fn from(x: ObjectTypeDef) -> Self {
40        if x.extend {
41            ast::ObjectTypeExtension {
42                name: x.name.into(),
43                implements_interfaces: x
44                    .implements_interfaces
45                    .into_iter()
46                    .map(Into::into)
47                    .collect(),
48                directives: Directive::to_ast(x.directives),
49                fields: x
50                    .fields_def
51                    .into_iter()
52                    .map(|x| Node::new(x.into()))
53                    .collect(),
54            }
55            .into()
56        } else {
57            ast::ObjectTypeDefinition {
58                description: x.description.map(Into::into),
59                name: x.name.into(),
60                implements_interfaces: x
61                    .implements_interfaces
62                    .into_iter()
63                    .map(Into::into)
64                    .collect(),
65                directives: Directive::to_ast(x.directives),
66                fields: x
67                    .fields_def
68                    .into_iter()
69                    .map(|x| Node::new(x.into()))
70                    .collect(),
71            }
72            .into()
73        }
74    }
75}
76
77impl TryFrom<apollo_parser::cst::ObjectTypeDefinition> for ObjectTypeDef {
78    type Error = crate::FromError;
79
80    fn try_from(object_def: apollo_parser::cst::ObjectTypeDefinition) -> Result<Self, Self::Error> {
81        Ok(Self {
82            name: object_def
83                .name()
84                .expect("object type definition must have a name")
85                .into(),
86            description: object_def.description().map(Description::from),
87            directives: object_def
88                .directives()
89                .map(Directive::convert_directives)
90                .transpose()?
91                .unwrap_or_default(),
92            implements_interfaces: object_def
93                .implements_interfaces()
94                .map(|impl_int| {
95                    impl_int
96                        .named_types()
97                        .map(|n| n.name().unwrap().into())
98                        .collect()
99                })
100                .unwrap_or_default(),
101            extend: false,
102            fields_def: object_def
103                .fields_definition()
104                .expect("object type definition must have fields definition")
105                .field_definitions()
106                .map(FieldDef::try_from)
107                .collect::<Result<Vec<_>, _>>()?,
108        })
109    }
110}
111
112impl TryFrom<apollo_parser::cst::ObjectTypeExtension> for ObjectTypeDef {
113    type Error = crate::FromError;
114
115    fn try_from(object_def: apollo_parser::cst::ObjectTypeExtension) -> Result<Self, Self::Error> {
116        Ok(Self {
117            name: object_def
118                .name()
119                .expect("object type definition must have a name")
120                .into(),
121            description: None,
122            directives: object_def
123                .directives()
124                .map(Directive::convert_directives)
125                .transpose()?
126                .unwrap_or_default(),
127            implements_interfaces: object_def
128                .implements_interfaces()
129                .map(|impl_int| {
130                    impl_int
131                        .named_types()
132                        .map(|n| n.name().unwrap().into())
133                        .collect()
134                })
135                .unwrap_or_default(),
136            extend: true,
137            fields_def: object_def
138                .fields_definition()
139                .expect("object type definition must have fields definition")
140                .field_definitions()
141                .map(FieldDef::try_from)
142                .collect::<Result<Vec<_>, _>>()?,
143        })
144    }
145}
146
147impl DocumentBuilder<'_> {
148    /// Create an arbitrary `ObjectTypeDef`
149    pub fn object_type_definition(&mut self) -> ArbitraryResult<ObjectTypeDef> {
150        let extend = !self.object_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
151        let description = self
152            .u
153            .arbitrary()
154            .unwrap_or(false)
155            .then(|| self.description())
156            .transpose()?;
157        let name = if extend {
158            let available_objects: Vec<&Name> = self
159                .object_type_defs
160                .iter()
161                .filter_map(|object| {
162                    if object.extend {
163                        None
164                    } else {
165                        Some(&object.name)
166                    }
167                })
168                .collect();
169            (*self.u.choose(&available_objects)?).clone()
170        } else {
171            self.type_name()?
172        };
173
174        // Extensions can add new `implements` clauses but must not
175        // duplicate prior picks or overwrite a field signature the
176        // type already commits to. Objects can't appear in the
177        // interface graph, so no cycle protection is needed.
178        let existing_field_signatures = field_signatures_for(&self.object_type_defs, &name);
179        let implements_interfaces = self.additional_implements(&existing_field_signatures, None)?;
180        let exclude_fields: IndexSet<Name> = existing_field_signatures
181            .keys()
182            .map(|k| Name::new(k.clone()))
183            .collect();
184        let fields_def = self.fields_definition(&exclude_fields)?;
185        let directives = self.directives(DirectiveLocation::Object)?;
186
187        if extend
188            && directives.is_empty()
189            && fields_def.is_empty()
190            && implements_interfaces.is_empty()
191        {
192            return Err(arbitrary::Error::IncorrectFormat);
193        }
194
195        Ok(ObjectTypeDef {
196            description,
197            directives,
198            implements_interfaces,
199            name,
200            fields_def,
201            extend,
202        })
203    }
204
205    /// Reconcile each object's `implements` clause and fields once
206    /// the interface backfill has finished. Each interface already
207    /// holds its full inherited field set by that point, so the
208    /// object only needs to copy from its direct parents.
209    pub(crate) fn backfill_inherited_object_fields(&mut self) {
210        for name in unique_names(&self.object_type_defs) {
211            let Some(base_idx) = base_def_index(&self.object_type_defs, &name) else {
212                continue;
213            };
214            self.expand_transitive_object_implementations(&name, base_idx);
215
216            let parents = self.implements_graph.direct_parents(&name);
217            let mut inherited_fields = parent_fields_from_defs(&parents, &self.interface_type_defs);
218            // Rewrite object-declared fields to the parent interface's
219            // signature so the object satisfies the interface contract
220            // exactly (matching type + args).
221            let def_indices = def_indices_with_name(&self.object_type_defs, &name);
222            for &i in &def_indices {
223                for f in self.object_type_defs[i].fields_def.iter_mut() {
224                    if let Some(parent_fdef) = inherited_fields.shift_remove(&f.name.name) {
225                        *f = parent_fdef;
226                    }
227                }
228            }
229            // Append the interface fields the object never declared.
230            self.object_type_defs[base_idx]
231                .fields_def
232                .extend(inherited_fields.into_values());
233        }
234    }
235
236    /// Write `name`'s transitive interface parents onto its base def, skipping any already declared by an extension.
237    fn expand_transitive_object_implementations(&mut self, name: &Name, base_idx: usize) {
238        let mut all_implemented_interfaces = self.implements_graph.closure(name);
239        // Closure includes `name`, but an object never implements itself.
240        all_implemented_interfaces.shift_remove(name);
241
242        let interfaces_declared_by_extensions: IndexSet<Name> = self
243            .object_type_defs
244            .iter()
245            .filter(|o| o.extend && &o.name == name)
246            .flat_map(|o| o.implements_interfaces.iter().cloned())
247            .collect();
248        let interfaces_to_add = all_implemented_interfaces
249            .into_iter()
250            .filter(|p| !interfaces_declared_by_extensions.contains(p));
251        self.object_type_defs[base_idx]
252            .implements_interfaces
253            .extend(interfaces_to_add);
254    }
255}
256
257impl StackedEntity for ObjectTypeDef {
258    fn name(&self) -> &Name {
259        &self.name
260    }
261
262    fn fields_def(&self) -> &[FieldDef] {
263        &self.fields_def
264    }
265}