Skip to main content

apollo_smith/
input_value.rs

1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::ty::Ty;
6use crate::DocumentBuilder;
7use apollo_compiler::ast;
8use apollo_compiler::Node;
9use arbitrary::Result as ArbitraryResult;
10use indexmap::IndexMap;
11use indexmap::IndexSet;
12
13#[derive(Debug, Clone, Copy)]
14pub enum Constness {
15    Const,
16    NonConst,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum InputValue {
21    Variable(Name),
22    Int(i32),
23    Float(f64),
24    String(String),
25    Boolean(bool),
26    Null,
27    Enum(Name),
28    List(Vec<InputValue>),
29    Object(Vec<(Name, InputValue)>),
30}
31
32impl From<InputValue> for ast::Value {
33    fn from(input_value: InputValue) -> Self {
34        match input_value {
35            InputValue::Variable(v) => Self::Variable(v.into()),
36            InputValue::Int(i) => Self::Int(i.into()),
37            InputValue::Float(f) => Self::Float(f.into()),
38            InputValue::String(s) => Self::String(s),
39            InputValue::Boolean(b) => Self::Boolean(b),
40            InputValue::Null => Self::Null,
41            InputValue::Enum(enm) => Self::Enum(enm.into()),
42            InputValue::List(l) => Self::List(l.into_iter().map(|v| Node::new(v.into())).collect()),
43            InputValue::Object(o) => Self::Object(
44                o.into_iter()
45                    .map(|(n, i)| (n.into(), Node::new(i.into())))
46                    .collect(),
47            ),
48        }
49    }
50}
51
52impl TryFrom<apollo_parser::cst::DefaultValue> for InputValue {
53    type Error = crate::FromError;
54
55    fn try_from(default_val: apollo_parser::cst::DefaultValue) -> Result<Self, Self::Error> {
56        default_val.value().unwrap().try_into()
57    }
58}
59
60impl TryFrom<apollo_parser::cst::Value> for InputValue {
61    type Error = crate::FromError;
62
63    fn try_from(value: apollo_parser::cst::Value) -> Result<Self, Self::Error> {
64        let smith_value = match value {
65            apollo_parser::cst::Value::Variable(variable) => {
66                Self::Variable(variable.name().unwrap().into())
67            }
68            apollo_parser::cst::Value::StringValue(val) => Self::String(val.into()),
69            apollo_parser::cst::Value::FloatValue(val) => Self::Float(val.try_into()?),
70            apollo_parser::cst::Value::IntValue(val) => Self::Int(val.try_into()?),
71            apollo_parser::cst::Value::BooleanValue(val) => Self::Boolean(val.try_into()?),
72            apollo_parser::cst::Value::NullValue(_val) => Self::Null,
73            apollo_parser::cst::Value::EnumValue(val) => Self::Enum(val.name().unwrap().into()),
74            apollo_parser::cst::Value::ListValue(val) => Self::List(
75                val.values()
76                    .map(Self::try_from)
77                    .collect::<Result<Vec<_>, _>>()?,
78            ),
79            apollo_parser::cst::Value::ObjectValue(val) => Self::Object(
80                val.object_fields()
81                    .map(|of| Ok((of.name().unwrap().into(), of.value().unwrap().try_into()?)))
82                    .collect::<Result<Vec<_>, crate::FromError>>()?,
83            ),
84        };
85        Ok(smith_value)
86    }
87}
88
89impl From<InputValue> for String {
90    fn from(input_val: InputValue) -> Self {
91        match input_val {
92            InputValue::Variable(v) => format!("${}", String::from(v)),
93            InputValue::Int(i) => format!("{i}"),
94            InputValue::Float(f) => format!("{f}"),
95            InputValue::String(s) => s,
96            InputValue::Boolean(b) => format!("{b}"),
97            InputValue::Null => String::from("null"),
98            InputValue::Enum(val) => val.into(),
99            InputValue::List(list) => format!(
100                "[{}]",
101                list.into_iter()
102                    .map(String::from)
103                    .collect::<Vec<String>>()
104                    .join(", ")
105            ),
106            InputValue::Object(obj) => format!(
107                "{{ {} }}",
108                obj.into_iter()
109                    .map(|(k, v)| format!("{}: {}", String::from(k), String::from(v)))
110                    .collect::<Vec<String>>()
111                    .join(", ")
112            ),
113        }
114    }
115}
116
117/// The __InputValueDef type represents field and directive arguments.
118///
119/// *InputValueDefinition*:
120///     Description? Name **:** Type DefaultValue? Directives?
121///
122/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-The-__InputValue-Type).
123#[derive(Debug, Clone, PartialEq)]
124pub struct InputValueDef {
125    pub(crate) description: Option<Description>,
126    pub(crate) name: Name,
127    pub(crate) ty: Ty,
128    pub(crate) default_value: Option<InputValue>,
129    pub(crate) directives: IndexMap<Name, Directive>,
130}
131
132impl From<InputValueDef> for ast::InputValueDefinition {
133    fn from(x: InputValueDef) -> Self {
134        Self {
135            description: x.description.map(Into::into),
136            name: x.name.into(),
137            ty: Node::new(x.ty.into()),
138            default_value: x.default_value.map(|x| Node::new(x.into())),
139            directives: Directive::to_ast(x.directives),
140        }
141    }
142}
143
144impl TryFrom<apollo_parser::cst::InputValueDefinition> for InputValueDef {
145    type Error = crate::FromError;
146
147    fn try_from(
148        input_val_def: apollo_parser::cst::InputValueDefinition,
149    ) -> Result<Self, Self::Error> {
150        Ok(Self {
151            description: input_val_def.description().map(Description::from),
152            name: input_val_def.name().unwrap().into(),
153            ty: input_val_def.ty().unwrap().into(),
154            default_value: input_val_def
155                .default_value()
156                .map(InputValue::try_from)
157                .transpose()?,
158            directives: input_val_def
159                .directives()
160                .map(Directive::convert_directives)
161                .transpose()?
162                .unwrap_or_default(),
163        })
164    }
165}
166
167impl DocumentBuilder<'_> {
168    /// Create an arbitrary `InputValue`
169    pub fn input_value(&mut self, constness: Constness) -> ArbitraryResult<InputValue> {
170        let index = match constness {
171            Constness::Const => self.u.int_in_range(0..=7usize)?,
172            Constness::NonConst => self.u.int_in_range(0..=8usize)?,
173        };
174        let val = match index {
175            // Int
176            0 => InputValue::Int(self.u.arbitrary()?),
177            // Float
178            1 => InputValue::Float(self.finite_f64()?),
179            // String
180            2 => InputValue::String(self.limited_string(40)?),
181            // Boolean
182            3 => InputValue::Boolean(self.u.arbitrary()?),
183            // Null
184            4 => InputValue::Null,
185            // Enum
186            5 => {
187                if !self.enum_type_defs.is_empty() {
188                    // TODO get rid of this clone
189                    let enum_choosed = self.choose_enum()?.clone();
190                    InputValue::Enum(self.arbitrary_variant(&enum_choosed)?.clone())
191                } else {
192                    self.input_value(constness)?
193                }
194            }
195            // List
196            6 => {
197                // FIXME: it's semantically wrong it should always be the same type inside
198                InputValue::List(
199                    (0..self.u.int_in_range(2..=4usize)?)
200                        .map(|_| self.input_value(constness))
201                        .collect::<ArbitraryResult<Vec<_>>>()?,
202                )
203            }
204            // Object
205            7 => InputValue::Object(
206                (0..self.u.int_in_range(2..=4usize)?)
207                    .map(|_| Ok((self.name()?, self.input_value(constness)?)))
208                    .collect::<ArbitraryResult<Vec<_>>>()?,
209            ),
210            // Variable TODO: only generate valid variable name (existing variables)
211            8 => InputValue::Variable(self.name()?),
212            _ => unreachable!(),
213        };
214
215        Ok(val)
216    }
217
218    pub fn input_value_for_type(&mut self, ty: &Ty) -> ArbitraryResult<InputValue> {
219        let gen_val = |doc_builder: &mut DocumentBuilder<'_>| -> ArbitraryResult<InputValue> {
220            if ty.is_builtin() {
221                match ty.name().name.as_str() {
222                    "String" => Ok(InputValue::String(doc_builder.limited_string(1000)?)),
223                    "Int" => Ok(InputValue::Int(doc_builder.u.arbitrary()?)),
224                    "Float" => Ok(InputValue::Float(doc_builder.finite_f64()?)),
225                    "Boolean" => Ok(InputValue::Boolean(doc_builder.u.arbitrary()?)),
226                    "ID" => Ok(InputValue::Int(doc_builder.u.arbitrary()?)),
227                    other => {
228                        unreachable!("{} is not a builtin", other);
229                    }
230                }
231            } else if let Some(enum_) = doc_builder
232                .enum_type_defs
233                .iter()
234                .find(|e| &e.name == ty.name())
235                .cloned()
236            {
237                Ok(InputValue::Enum(
238                    doc_builder.arbitrary_variant(&enum_)?.clone(),
239                ))
240            } else if let Some(input_object_ty) = doc_builder
241                .input_object_type_defs
242                .iter()
243                .find(|io| &io.name == ty.name())
244                .cloned()
245            {
246                Ok(InputValue::Object(
247                    input_object_ty
248                        .fields
249                        .iter()
250                        .map(|field_def| {
251                            Ok((
252                                field_def.name.clone(),
253                                doc_builder.input_value_for_type(&field_def.ty)?,
254                            ))
255                        })
256                        .collect::<ArbitraryResult<Vec<_>>>()?,
257                ))
258            } else if doc_builder
259                .scalar_type_defs
260                .iter()
261                .any(|s| &s.name == ty.name())
262            {
263                // Custom scalars accept any literal value; generate an Int to be entropy-efficient
264                Ok(InputValue::Int(doc_builder.u.arbitrary()?))
265            } else {
266                panic!("Type {} is not a valid input type", ty.name().name);
267            }
268        };
269
270        let val = match ty {
271            Ty::Named(_) => gen_val(self)?,
272            Ty::List(_) => {
273                let nb_elt = self.u.int_in_range(1..=25usize)?;
274                InputValue::List(
275                    (0..nb_elt)
276                        .map(|_| gen_val(self))
277                        .collect::<ArbitraryResult<Vec<InputValue>>>()?,
278                )
279            }
280            Ty::NonNull(_) => gen_val(self)?,
281        };
282
283        Ok(val)
284    }
285
286    /// Create an arbitrary list of `InputValueDef`. The caller passes
287    /// `directive_location` so directives applied to each value are
288    /// filtered against the right context — `InputFieldDefinition` for
289    /// input-object fields, `ArgumentDefinition` for argument lists on
290    /// fields and directives.
291    pub fn input_values_def(
292        &mut self,
293        directive_location: DirectiveLocation,
294        exclude: &IndexSet<Name>,
295        self_name: Option<&Name>,
296    ) -> ArbitraryResult<Vec<InputValueDef>> {
297        let arbitrary_iv_num = self.u.int_in_range(2..=5usize)?;
298        let mut input_values = Vec::with_capacity(arbitrary_iv_num - 1);
299
300        for i in 0..arbitrary_iv_num {
301            let description = self
302                .u
303                .arbitrary()
304                .unwrap_or(false)
305                .then(|| self.description())
306                .transpose()?;
307            let name = self.name_with_index(i)?;
308            let mut ty = self.choose_ty(&self.list_existing_input_types())?;
309            // Prevent required self-referential input object fields, which
310            // would make the type impossible to construct.
311            if self_name.is_some_and(|n| ty.name() == n) {
312                if let Ty::NonNull(inner) = ty {
313                    ty = *inner;
314                }
315            }
316            let directives = self.directives(directive_location)?;
317            // TODO: FIXME: it's not correct I need to generate default value corresponding to the ty above
318            let default_value = self
319                .u
320                .arbitrary()
321                .unwrap_or(false)
322                .then(|| self.input_value(Constness::Const))
323                .transpose()?;
324
325            if !exclude.contains(&name) {
326                input_values.push(InputValueDef {
327                    description,
328                    name,
329                    ty,
330                    default_value,
331                    directives,
332                });
333            }
334        }
335
336        Ok(input_values)
337    }
338    /// Create an arbitrary `InputValueDef`. The caller passes
339    /// `directive_location` for the same reason as
340    /// [`input_values_def`](Self::input_values_def).
341    pub fn input_value_def(
342        &mut self,
343        directive_location: DirectiveLocation,
344    ) -> ArbitraryResult<InputValueDef> {
345        let description = self
346            .u
347            .arbitrary()
348            .unwrap_or(false)
349            .then(|| self.description())
350            .transpose()?;
351        let name = self.name()?;
352        let ty = self.choose_ty(&self.list_existing_input_types())?;
353        let directives = self.directives(directive_location)?;
354        // TODO: FIXME: it's not correct I need to generate default value corresponding to the ty above
355        let default_value = self
356            .u
357            .arbitrary()
358            .unwrap_or(false)
359            .then(|| self.input_value(Constness::Const))
360            .transpose()?;
361
362        Ok(InputValueDef {
363            description,
364            name,
365            ty,
366            default_value,
367            directives,
368        })
369    }
370
371    fn finite_f64(&mut self) -> arbitrary::Result<f64> {
372        loop {
373            let val: f64 = self.u.arbitrary()?;
374            if val.is_finite() {
375                return Ok(val);
376            }
377        }
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384    use crate::InputObjectTypeDef;
385    use arbitrary::Unstructured;
386    use indexmap::IndexMap;
387
388    #[test]
389    fn test_input_value_for_type() {
390        let data: Vec<u8> = (0..=5000usize).map(|n| (n % 255) as u8).collect();
391        let mut u = Unstructured::new(&data);
392        let mut document_builder = DocumentBuilder::new(&mut u);
393        let my_nested_type = InputObjectTypeDef {
394            description: None,
395            name: Name {
396                name: String::from("my_nested_object"),
397            },
398            directives: IndexMap::new(),
399            fields: vec![InputValueDef {
400                description: None,
401                name: Name {
402                    name: String::from("value"),
403                },
404                ty: Ty::Named(Name {
405                    name: String::from("String"),
406                }),
407                default_value: None,
408                directives: IndexMap::new(),
409            }],
410            extend: false,
411        };
412
413        let my_object_type = InputObjectTypeDef {
414            description: None,
415            name: Name {
416                name: String::from("my_object"),
417            },
418            directives: IndexMap::new(),
419            fields: vec![InputValueDef {
420                description: None,
421                name: Name {
422                    name: String::from("first"),
423                },
424                ty: Ty::List(Box::new(Ty::Named(Name {
425                    name: String::from("my_nested_object"),
426                }))),
427                default_value: None,
428                directives: IndexMap::new(),
429            }],
430            extend: false,
431        };
432        document_builder.input_object_type_defs.push(my_nested_type);
433        document_builder.input_object_type_defs.push(my_object_type);
434
435        let input_val = document_builder
436            .input_value_for_type(&Ty::List(Box::new(Ty::Named(Name {
437                name: String::from("my_object"),
438            }))))
439            .unwrap();
440
441        let input_val_str = apollo_compiler::ast::Value::from(input_val)
442            .serialize()
443            .no_indent()
444            .to_string();
445
446        assert_eq!(
447            input_val_str.as_str(),
448            "[{first: [{value: \"EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJ\"}, {value: \"MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJK\"}]}]"
449        );
450    }
451}