Skip to main content

apollo_smith/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub(crate) mod argument;
4pub(crate) mod description;
5pub(crate) mod directive;
6pub(crate) mod document;
7pub(crate) mod enum_;
8pub(crate) mod field;
9pub(crate) mod fragment;
10pub mod generators;
11pub(crate) mod implements_graph;
12pub(crate) mod input_object;
13pub(crate) mod input_value;
14pub(crate) mod interface;
15pub(crate) mod name;
16pub(crate) mod object;
17pub(crate) mod operation;
18pub mod random;
19pub(crate) mod response;
20pub(crate) mod scalar;
21pub(crate) mod schema;
22pub(crate) mod selection_set;
23#[cfg(test)]
24pub(crate) mod snapshot_tests;
25pub(crate) mod ty;
26pub(crate) mod union;
27pub(crate) mod variable;
28
29use indexmap::IndexMap;
30use std::collections::HashSet;
31use std::fmt::Debug;
32
33#[derive(Debug, Clone, thiserror::Error)]
34pub enum FromError {
35    #[error("parse tree is missing a node")]
36    MissingNode,
37    #[error("invalid i32")]
38    ParseIntError(#[from] std::num::ParseIntError),
39    #[error("invalid f64")]
40    ParseFloatError(#[from] std::num::ParseFloatError),
41    #[error("invalid boolean")]
42    ParseBoolError(#[from] std::str::ParseBoolError),
43}
44
45use apollo_compiler::coordinate::TypeAttributeCoordinate;
46pub use arbitrary::Result;
47pub use arbitrary::Unstructured;
48use argument::Argument;
49pub use directive::DirectiveDef;
50pub use document::Document;
51pub use enum_::EnumTypeDef;
52use field::FieldDef;
53pub use fragment::FragmentDef;
54pub use generators::BooleanGenerator;
55pub use generators::FloatGenerator;
56pub use generators::Generator;
57pub use generators::Generators;
58pub use generators::IdGenerator;
59pub use generators::IntGenerator;
60pub use generators::StringGenerator;
61pub use input_object::InputObjectTypeDef;
62pub use interface::InterfaceTypeDef;
63use name::Name;
64pub use object::ObjectTypeDef;
65pub use operation::OperationDef;
66pub use random::RandProvider;
67pub use random::RandomProvider;
68pub use random::ResponseError;
69pub use response::ResponseBuilder;
70pub use scalar::ScalarTypeDef;
71pub use schema::SchemaDef;
72pub use serde_json_bytes::Value;
73use ty::Ty;
74pub use union::UnionTypeDef;
75
76const DEFAULT_MAX: usize = 50;
77
78/// DocumentBuilder is a struct to build an arbitrary valid GraphQL document
79///
80/// ```compile_fail
81/// // fuzz/fuzz_targets/my_apollo_smith_fuzz_target.rs
82/// #![no_main]
83///
84/// use libfuzzer_sys::fuzz_target;
85/// use arbitrary::Unstructured;
86/// use apollo_smith::DocumentBuilder;
87///
88/// fuzz_target!(|input: &[u8]| {
89///     let mut u = Unstructured::new(input);
90///     let document = DocumentBuilder::new(&mut u).build()?;
91///     let document_str = String::from(document);
92///
93///     // Your code here...
94/// });
95/// ```
96pub struct DocumentBuilder<'a> {
97    pub(crate) u: &'a mut Unstructured<'a>,
98    pub(crate) input_object_type_defs: Vec<InputObjectTypeDef>,
99    pub(crate) object_type_defs: Vec<ObjectTypeDef>,
100    pub(crate) interface_type_defs: Vec<InterfaceTypeDef>,
101    pub(crate) union_type_defs: Vec<UnionTypeDef>,
102    pub(crate) enum_type_defs: Vec<EnumTypeDef>,
103    pub(crate) scalar_type_defs: Vec<ScalarTypeDef>,
104    pub(crate) schema_def: Option<SchemaDef>,
105    pub(crate) directive_defs: Vec<DirectiveDef>,
106    pub(crate) operation_defs: Vec<OperationDef>,
107    pub(crate) fragment_defs: Vec<FragmentDef>,
108    // A graph with edges representing the "implements" relationship between types
109    pub(crate) implements_graph: implements_graph::ImplementsGraph,
110    // A stack to set current ObjectTypeDef
111    pub(crate) stack: Vec<Box<dyn StackedEntity>>,
112    // Useful to keep the same arguments for a specific field on a specific type
113    pub(crate) chosen_arguments: IndexMap<TypeAttributeCoordinate, Vec<Argument>>,
114    pub(crate) used_type_names: HashSet<String>,
115    // Maximum number of generated definitions per kind
116    max_scalar_types: usize,
117    max_enum_types: usize,
118    max_interface_types: usize,
119    max_object_types: usize,
120    max_union_types: usize,
121    max_input_object_types: usize,
122    max_fragment_definitions: usize,
123    max_directive_definitions: usize,
124    max_operation_definitions: usize,
125}
126
127impl Debug for DocumentBuilder<'_> {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        f.debug_struct("DocumentBuilder")
130            .field("input_object_type_defs", &self.input_object_type_defs)
131            .field("object_type_defs", &self.object_type_defs)
132            .field("interface_type_defs", &self.interface_type_defs)
133            .field("union_type_defs", &self.union_type_defs)
134            .field("enum_type_defs", &self.enum_type_defs)
135            .field("scalar_type_defs", &self.scalar_type_defs)
136            .field("schema_def", &self.schema_def)
137            .field("directive_defs", &self.directive_defs)
138            .field("operation_defs", &self.operation_defs)
139            .field("fragment_defs", &self.fragment_defs)
140            .finish()
141    }
142}
143
144impl<'a> DocumentBuilder<'a> {
145    /// Create an instance of `DocumentBuilder`
146    pub fn new(u: &'a mut Unstructured<'a>) -> Self {
147        Self {
148            u,
149            object_type_defs: Vec::new(),
150            interface_type_defs: Vec::new(),
151            enum_type_defs: Vec::new(),
152            schema_def: None,
153            directive_defs: Vec::new(),
154            operation_defs: Vec::new(),
155            fragment_defs: Vec::new(),
156            scalar_type_defs: Vec::new(),
157            union_type_defs: Vec::new(),
158            input_object_type_defs: Vec::new(),
159            implements_graph: implements_graph::ImplementsGraph::new(),
160            stack: Vec::new(),
161            chosen_arguments: IndexMap::new(),
162            used_type_names: HashSet::new(),
163            max_scalar_types: DEFAULT_MAX,
164            max_enum_types: DEFAULT_MAX,
165            max_interface_types: DEFAULT_MAX,
166            max_object_types: DEFAULT_MAX,
167            max_union_types: DEFAULT_MAX,
168            max_input_object_types: DEFAULT_MAX,
169            max_fragment_definitions: DEFAULT_MAX,
170            max_directive_definitions: DEFAULT_MAX,
171            max_operation_definitions: DEFAULT_MAX,
172        }
173    }
174
175    /// Set the maximum number of scalar type definitions (default 50).
176    pub fn max_scalar_types(mut self, max: usize) -> Self {
177        self.max_scalar_types = max;
178        self
179    }
180
181    /// Set the maximum number of enum type definitions (default 50).
182    pub fn max_enum_types(mut self, max: usize) -> Self {
183        self.max_enum_types = max;
184        self
185    }
186
187    /// Set the maximum number of interface type definitions (default 50).
188    pub fn max_interface_types(mut self, max: usize) -> Self {
189        self.max_interface_types = max;
190        self
191    }
192
193    /// Set the maximum number of object type definitions (default 50).
194    pub fn max_object_types(mut self, max: usize) -> Self {
195        self.max_object_types = max;
196        self
197    }
198
199    /// Set the maximum number of union type definitions (default 50).
200    pub fn max_union_types(mut self, max: usize) -> Self {
201        self.max_union_types = max;
202        self
203    }
204
205    /// Set the maximum number of input object type definitions (default 50).
206    pub fn max_input_object_types(mut self, max: usize) -> Self {
207        self.max_input_object_types = max;
208        self
209    }
210
211    /// Set the maximum number of fragment definitions (default 50).
212    pub fn max_fragment_definitions(mut self, max: usize) -> Self {
213        self.max_fragment_definitions = max;
214        self
215    }
216
217    /// Set the maximum number of directive definitions (default 50).
218    pub fn max_directive_definitions(mut self, max: usize) -> Self {
219        self.max_directive_definitions = max;
220        self
221    }
222
223    /// Set the maximum number of operation definitions (default 50).
224    pub fn max_operation_definitions(mut self, max: usize) -> Self {
225        self.max_operation_definitions = max;
226        self
227    }
228
229    /// Generate random definitions according to the configured maximums
230    /// and return the resulting [`Document`].
231    pub fn build(mut self) -> Result<Document> {
232        for _ in 0..self.u.int_in_range(1..=self.max_scalar_types)? {
233            let scalar_type_def = self.scalar_type_definition()?;
234            self.scalar_type_defs.push(scalar_type_def);
235        }
236
237        for _ in 0..self.u.int_in_range(1..=self.max_enum_types)? {
238            let enum_type_def = self.enum_type_definition()?;
239            self.enum_type_defs.push(enum_type_def);
240        }
241
242        for _ in 0..self.u.int_in_range(1..=self.max_interface_types)? {
243            let def = self.interface_type_definition()?;
244            self.implements_graph.node_for(&def.name);
245            for parent in &def.interfaces {
246                self.implements_graph.add_edge(&def.name, parent);
247            }
248            self.interface_type_defs.push(def);
249        }
250        self.backfill_inherited_interface_fields();
251
252        for _ in 0..self.u.int_in_range(1..=self.max_object_types)? {
253            let def = self.object_type_definition()?;
254            self.implements_graph.node_for(&def.name);
255            for parent in &def.implements_interfaces {
256                self.implements_graph.add_edge(&def.name, parent);
257            }
258            self.object_type_defs.push(def);
259        }
260        self.backfill_inherited_object_fields();
261
262        for _ in 0..self.u.int_in_range(1..=self.max_union_types)? {
263            let union_type_def = self.union_type_definition()?;
264            self.union_type_defs.push(union_type_def);
265        }
266
267        for _ in 0..self.u.int_in_range(1..=self.max_input_object_types)? {
268            let input_object_type_def = self.input_object_type_definition()?;
269            self.input_object_type_defs.push(input_object_type_def);
270        }
271
272        for _ in 0..self.u.int_in_range(1..=self.max_fragment_definitions)? {
273            let fragment_def = self.fragment_definition()?;
274            self.fragment_defs.push(fragment_def);
275        }
276
277        for _ in 0..self.u.int_in_range(1..=self.max_directive_definitions)? {
278            let directive_def = self.directive_def()?;
279            self.directive_defs.push(directive_def);
280        }
281
282        let schema_def = self.schema_definition()?;
283        self.schema_def = Some(schema_def);
284
285        // An anonymous operation may only exist as the sole operation
286        // in a document, so any time we're producing more than one,
287        // every operation must be named.
288        let num_ops = self.u.int_in_range(1..=self.max_operation_definitions)?;
289        let require_named = num_ops > 1;
290        for _ in 0..num_ops {
291            let operation_def = self.operation_definition_in_document(require_named)?;
292            if let Some(operation_def) = operation_def {
293                self.operation_defs.push(operation_def);
294            }
295        }
296
297        self.prune_unused_fragments();
298
299        Ok(Document {
300            schema_definition: self.schema_def,
301            object_type_definitions: self.object_type_defs,
302            interface_type_definitions: self.interface_type_defs,
303            enum_type_definitions: self.enum_type_defs,
304            directive_definitions: self.directive_defs,
305            operation_definitions: self.operation_defs,
306            fragment_definitions: self.fragment_defs,
307            scalar_type_definitions: self.scalar_type_defs,
308            union_type_definitions: self.union_type_defs,
309            input_object_type_definitions: self.input_object_type_defs,
310        })
311    }
312
313    /// Create an instance of `DocumentBuilder` given a `Document` to be able to call
314    /// methods on DocumentBuilder and generate valid entities like for example an operation
315    pub fn with_document(u: &'a mut Unstructured<'a>, document: Document) -> Result<Self> {
316        let mut implements_graph = implements_graph::ImplementsGraph::new();
317        for itf in &document.interface_type_definitions {
318            implements_graph.node_for(&itf.name);
319            for parent in &itf.interfaces {
320                implements_graph.add_edge(&itf.name, parent);
321            }
322        }
323        for obj in &document.object_type_definitions {
324            implements_graph.node_for(&obj.name);
325            for parent in &obj.implements_interfaces {
326                implements_graph.add_edge(&obj.name, parent);
327            }
328        }
329        let mut builder = Self {
330            u,
331            object_type_defs: document.object_type_definitions,
332            interface_type_defs: document.interface_type_definitions,
333            enum_type_defs: document.enum_type_definitions,
334            schema_def: document.schema_definition,
335            directive_defs: document.directive_definitions,
336            operation_defs: document.operation_definitions,
337            fragment_defs: document.fragment_definitions,
338            scalar_type_defs: document.scalar_type_definitions,
339            union_type_defs: document.union_type_definitions,
340            input_object_type_defs: document.input_object_type_definitions,
341            implements_graph,
342            stack: Vec::new(),
343            chosen_arguments: IndexMap::new(),
344            used_type_names: HashSet::new(),
345            max_scalar_types: DEFAULT_MAX,
346            max_enum_types: DEFAULT_MAX,
347            max_interface_types: DEFAULT_MAX,
348            max_object_types: DEFAULT_MAX,
349            max_union_types: DEFAULT_MAX,
350            max_input_object_types: DEFAULT_MAX,
351            max_fragment_definitions: DEFAULT_MAX,
352            max_directive_definitions: DEFAULT_MAX,
353            max_operation_definitions: DEFAULT_MAX,
354        };
355        for def in &builder.object_type_defs {
356            builder.used_type_names.insert(def.name.name.clone());
357        }
358        for def in &builder.interface_type_defs {
359            builder.used_type_names.insert(def.name.name.clone());
360        }
361        for def in &builder.enum_type_defs {
362            builder.used_type_names.insert(def.name.name.clone());
363        }
364        for def in &builder.directive_defs {
365            builder.used_type_names.insert(def.name.name.clone());
366        }
367        for def in &builder.union_type_defs {
368            builder.used_type_names.insert(def.name.name.clone());
369        }
370        for def in &builder.input_object_type_defs {
371            builder.used_type_names.insert(def.name.name.clone());
372        }
373        for def in &builder.scalar_type_defs {
374            builder.used_type_names.insert(def.name.name.clone());
375        }
376        for def in &builder.fragment_defs {
377            builder.used_type_names.insert(def.name.name.clone());
378        }
379        for def in &builder.operation_defs {
380            if let Some(name) = &def.name {
381                builder.used_type_names.insert(name.name.clone());
382            }
383        }
384
385        Ok(builder)
386    }
387
388    /// Returns whether the provided `Unstructured` is now empty
389    pub fn input_exhausted(&self) -> bool {
390        self.u.is_empty()
391    }
392
393    pub(crate) fn stack_ty(&mut self, ty: &Ty) -> bool {
394        if ty.is_builtin() {
395            return false;
396        }
397        let type_name = ty.name();
398
399        if let Some(object_ty) = self
400            .object_type_defs
401            .iter()
402            .find(|object_ty_def| &object_ty_def.name == type_name)
403            .cloned()
404        {
405            self.stack.push(Box::new(object_ty));
406            true
407        } else if let Some(itf_type) = self
408            .interface_type_defs
409            .iter()
410            .find(|itf_type_def| &itf_type_def.name == type_name)
411            .cloned()
412        {
413            self.stack.push(Box::new(itf_type));
414            true
415        } else if let Some(_enum_ty) = self
416            .enum_type_defs
417            .iter()
418            .find(|object_ty_def| &object_ty_def.name == type_name)
419            .cloned()
420        {
421            false
422        } else {
423            todo!("'{:?}' need to implement for union, scalar, ...", type_name);
424        }
425    }
426
427    /// Validation requires every fragment definition to be referenced by some
428    /// operation (directly or transitively). Drop fragments that never get
429    /// spread to avoid producing invalid documents.
430    ///
431    /// We walk transitively from operations so chains like `op -> A -> B` keep
432    /// both A and B, while `A -> B` with no operation referencing A drops both
433    /// (B is only reachable through A, which is itself unused).
434    fn prune_unused_fragments(&mut self) {
435        let reachable =
436            fragment::reachable_fragment_names(&self.operation_defs, &self.fragment_defs);
437        self.fragment_defs.retain(|f| reachable.contains(&f.name));
438    }
439}
440
441pub(crate) trait StackedEntity {
442    fn name(&self) -> &Name;
443    fn fields_def(&self) -> &[FieldDef];
444}