• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / pg_graphql / 17217734549

25 Aug 2025 06:45PM UTC coverage: 91.359% (-1.9%) from 93.265%
17217734549

push

github

web-flow
Merge pull request #599 from supabase/or/error-types

Refactor Error responses to use a type vs String

245 of 566 new or added lines in 9 files covered. (43.29%)

1 existing line in 1 file now uncovered.

7549 of 8263 relevant lines covered (91.36%)

1135.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.57
/src/parser_util.rs
1
use crate::error::{GraphQLError, GraphQLResult};
2
use crate::graphql::{EnumSource, __InputValue, __Type, ___Type};
3
use crate::{gson, merge::merge};
4
use graphql_parser::query::*;
5
use std::collections::HashMap;
6
use std::hash::Hash;
7

8
pub fn alias_or_name<'a, T>(query_field: &graphql_parser::query::Field<'a, T>) -> String
34,338✔
9
where
34,338✔
10
    T: Text<'a> + Eq + AsRef<str>,
34,338✔
11
{
12
    query_field
34,338✔
13
        .alias
34,338✔
14
        .as_ref()
34,338✔
15
        .map(|x| x.as_ref().to_string())
34,338✔
16
        .unwrap_or_else(|| query_field.name.as_ref().to_string())
34,338✔
17
}
34,338✔
18

19
pub fn normalize_selection_set<'a, 'b, T>(
6,954✔
20
    selection_set: &'b SelectionSet<'a, T>,
6,954✔
21
    fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
6,954✔
22
    type_name: &String,            // for inline fragments
6,954✔
23
    variables: &serde_json::Value, // for directives
6,954✔
24
) -> GraphQLResult<Vec<Field<'a, T>>>
6,954✔
25
where
6,954✔
26
    T: Text<'a> + Eq + AsRef<str> + Clone,
6,954✔
27
    T::Value: Hash,
6,954✔
28
{
29
    let mut selections: Vec<Field<'a, T>> = vec![];
6,954✔
30

31
    for selection in &selection_set.items {
22,714✔
32
        let sel = selection;
15,762✔
33
        match normalize_selection(sel, fragment_definitions, type_name, variables) {
15,762✔
34
            Ok(sels) => selections.extend(sels),
15,760✔
35
            Err(err) => return Err(err),
2✔
36
        }
37
    }
38
    let selections = merge(selections)?;
6,952✔
39
    Ok(selections)
6,944✔
40
}
6,954✔
41

42
/// Combines @skip and @include
43
pub fn selection_is_skipped<'a, 'b, T>(
15,762✔
44
    query_selection: &'b Selection<'a, T>,
15,762✔
45
    variables: &serde_json::Value,
15,762✔
46
) -> GraphQLResult<bool>
15,762✔
47
where
15,762✔
48
    T: Text<'a> + Eq + AsRef<str>,
15,762✔
49
{
50
    let directives = match query_selection {
15,762✔
51
        Selection::Field(x) => &x.directives,
14,486✔
52
        Selection::FragmentSpread(x) => &x.directives,
1,262✔
53
        Selection::InlineFragment(x) => &x.directives,
14✔
54
    };
55

56
    if !directives.is_empty() {
15,762✔
57
        for directive in directives {
32✔
58
            let directive_name = directive.name.as_ref();
24✔
59
            match directive_name {
24✔
60
                "skip" => {
24✔
61
                    if directive.arguments.len() != 1 {
11✔
NEW
62
                        return Err(GraphQLError::validation(
×
NEW
63
                            "Incorrect arguments to directive @skip",
×
NEW
64
                        ));
×
65
                    }
11✔
66
                    let arg = &directive.arguments[0];
11✔
67
                    if arg.0.as_ref() != "if" {
11✔
NEW
68
                        return Err(GraphQLError::validation(format!(
×
NEW
69
                            "Unknown argument to @skip: {}",
×
NEW
70
                            arg.0.as_ref()
×
NEW
71
                        )));
×
72
                    }
11✔
73

74
                    // the argument to @skip(if: <value>)
75
                    match &arg.1 {
11✔
76
                        Value::Boolean(x) => {
8✔
77
                            if *x {
8✔
78
                                return Ok(true);
4✔
79
                            }
4✔
80
                        }
81
                        Value::Variable(var_name) => {
3✔
82
                            let var = variables.get(var_name.as_ref());
3✔
83
                            match var {
2✔
84
                                Some(serde_json::Value::Bool(bool_val)) => {
2✔
85
                                    if *bool_val {
2✔
86
                                        // skip immediately
87
                                        return Ok(true);
1✔
88
                                    }
1✔
89
                                }
90
                                _ => {
91
                                    return Err(GraphQLError::validation(
1✔
92
                                        "Value for \"if\" in @skip directive is required",
1✔
93
                                    ));
1✔
94
                                }
95
                            }
96
                        }
97
                        _ => (),
×
98
                    }
99
                }
100
                "include" => {
13✔
101
                    if directive.arguments.len() != 1 {
13✔
NEW
102
                        return Err(GraphQLError::validation(
×
NEW
103
                            "Incorrect arguments to directive @include",
×
NEW
104
                        ));
×
105
                    }
13✔
106
                    let arg = &directive.arguments[0];
13✔
107
                    if arg.0.as_ref() != "if" {
13✔
NEW
108
                        return Err(GraphQLError::validation(format!(
×
NEW
109
                            "Unknown argument to @include: {}",
×
NEW
110
                            arg.0.as_ref()
×
NEW
111
                        )));
×
112
                    }
13✔
113

114
                    // the argument to @include(if: <value>)
115
                    match &arg.1 {
13✔
116
                        Value::Boolean(x) => {
10✔
117
                            if !*x {
10✔
118
                                return Ok(true);
4✔
119
                            }
6✔
120
                        }
121
                        Value::Variable(var_name) => {
3✔
122
                            let var = variables.get(var_name.as_ref());
3✔
123
                            match var {
2✔
124
                                Some(serde_json::Value::Bool(bool_val)) => {
2✔
125
                                    if !bool_val {
2✔
126
                                        return Ok(true);
1✔
127
                                    }
1✔
128
                                }
129
                                _ => {
130
                                    return Err(GraphQLError::validation(
1✔
131
                                        "Value for \"if\" in @include directive is required",
1✔
132
                                    ));
1✔
133
                                }
134
                            }
135
                        }
136
                        _ => (),
×
137
                    }
138
                }
139
                _ => {
NEW
140
                    return Err(GraphQLError::validation(format!(
×
NEW
141
                        "Unknown directive {}",
×
NEW
142
                        directive_name
×
NEW
143
                    )))
×
144
                }
145
            }
146
        }
147
    }
15,742✔
148
    Ok(false)
15,750✔
149
}
15,762✔
150

151
/// Normalizes literal selections, fragment spreads, and inline fragments
152
pub fn normalize_selection<'a, 'b, T>(
15,762✔
153
    query_selection: &'b Selection<'a, T>,
15,762✔
154
    fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
15,762✔
155
    type_name: &String,            // for inline fragments
15,762✔
156
    variables: &serde_json::Value, // for directives
15,762✔
157
) -> GraphQLResult<Vec<Field<'a, T>>>
15,762✔
158
where
15,762✔
159
    T: Text<'a> + Eq + AsRef<str> + Clone,
15,762✔
160
    T::Value: Hash,
15,762✔
161
{
162
    let mut selections: Vec<Field<'a, T>> = vec![];
15,762✔
163

164
    if selection_is_skipped(query_selection, variables)? {
15,762✔
165
        return Ok(selections);
10✔
166
    }
15,750✔
167

168
    match query_selection {
15,750✔
169
        Selection::Field(field) => {
14,474✔
170
            selections.push(field.clone());
14,474✔
171
        }
14,474✔
172
        Selection::FragmentSpread(fragment_spread) => {
1,262✔
173
            let frag_name = &fragment_spread.fragment_name;
1,262✔
174

175
            // Fragments can have type conditions
176
            // https://spec.graphql.org/June2018/#sec-Type-Conditions
177
            // so we must check the type too...
178
            let frag_def = match fragment_definitions
1,262✔
179
                .iter()
1,262✔
180
                .filter(|x| &x.name == frag_name)
3,008✔
181
                .find(|x| match &x.type_condition {
1,262✔
182
                    // TODO match when no type condition is specified?
183
                    TypeCondition::On(frag_type_name) => frag_type_name.as_ref() == type_name,
1,262✔
184
                }) {
1,262✔
185
                Some(frag) => frag,
1,262✔
186
                None => {
NEW
187
                    return Err(GraphQLError::validation(format!(
×
188
                        "no fragment named {} on type {}",
×
189
                        frag_name.as_ref(),
×
190
                        type_name
×
NEW
191
                    )))
×
192
                }
193
            };
194

195
            // TODO handle directives?
196
            let frag_selections = normalize_selection_set(
1,262✔
197
                &frag_def.selection_set,
1,262✔
198
                fragment_definitions,
1,262✔
199
                type_name,
1,262✔
200
                variables,
1,262✔
201
            );
202
            match frag_selections {
1,262✔
203
                Ok(sels) => selections.extend(sels),
1,262✔
204
                Err(err) => return Err(err),
×
205
            };
206
        }
207
        Selection::InlineFragment(inline_fragment) => {
14✔
208
            let inline_fragment_applies: bool = match &inline_fragment.type_condition {
14✔
209
                Some(infrag) => match infrag {
12✔
210
                    TypeCondition::On(infrag_name) => infrag_name.as_ref() == type_name,
12✔
211
                },
212
                None => true,
2✔
213
            };
214

215
            if inline_fragment_applies {
14✔
216
                let infrag_selections = normalize_selection_set(
11✔
217
                    &inline_fragment.selection_set,
11✔
218
                    fragment_definitions,
11✔
219
                    type_name,
11✔
220
                    variables,
11✔
221
                )?;
×
222
                selections.extend(infrag_selections);
11✔
223
            }
3✔
224
        }
225
    }
226

227
    Ok(selections)
15,750✔
228
}
15,762✔
229

230
pub fn to_gson<'a, T>(
1,312✔
231
    graphql_value: &Value<'a, T>,
1,312✔
232
    variables: &serde_json::Value,
1,312✔
233
    variable_definitions: &Vec<VariableDefinition<'a, T>>,
1,312✔
234
) -> GraphQLResult<gson::Value>
1,312✔
235
where
1,312✔
236
    T: Text<'a> + AsRef<str>,
1,312✔
237
{
238
    let result = match graphql_value {
1,312✔
239
        Value::Null => gson::Value::Null,
18✔
240
        Value::Boolean(x) => gson::Value::Boolean(*x),
9✔
241
        Value::Int(x) => {
221✔
242
            let val = x.as_i64();
221✔
243
            match val {
221✔
244
                Some(num) => {
221✔
245
                    let i_val = gson::Number::Integer(num);
221✔
246
                    gson::Value::Number(i_val)
221✔
247
                }
NEW
248
                None => return Err(GraphQLError::type_error("Invalid Int input")),
×
249
            }
250
        }
251
        Value::Float(x) => {
19✔
252
            let val: gson::Number = gson::Number::Float(*x);
19✔
253
            gson::Value::Number(val)
19✔
254
        }
255
        Value::String(x) => gson::Value::String(x.to_owned()),
364✔
256
        Value::Enum(x) => gson::Value::String(x.as_ref().to_string()),
45✔
257
        Value::List(x_arr) => {
120✔
258
            let mut out_arr: Vec<gson::Value> = vec![];
120✔
259
            for x in x_arr {
322✔
260
                let val = to_gson(x, variables, variable_definitions)?;
202✔
261
                out_arr.push(val);
202✔
262
            }
263
            gson::Value::Array(out_arr)
120✔
264
        }
265
        Value::Object(obj) => {
436✔
266
            let mut out_map: HashMap<String, gson::Value> = HashMap::new();
436✔
267
            for (key, graphql_val) in obj.iter() {
516✔
268
                let val = to_gson(graphql_val, variables, variable_definitions)?;
516✔
269
                out_map.insert(key.as_ref().to_string(), val);
516✔
270
            }
271
            gson::Value::Object(out_map)
436✔
272
        }
273
        Value::Variable(var_name) => {
80✔
274
            let var = variables.get(var_name.as_ref());
80✔
275
            match var {
80✔
276
                Some(x) => gson::json_to_gson(x)?,
70✔
277
                None => {
278
                    let variable_default: Option<&graphql_parser::query::Value<'a, T>> =
10✔
279
                        variable_definitions
10✔
280
                            .iter()
10✔
281
                            .find(|var_def| var_def.name.as_ref() == var_name.as_ref())
13✔
282
                            .and_then(|x| x.default_value.as_ref());
10✔
283

284
                    match variable_default {
10✔
285
                        Some(x) => to_gson(x, variables, variable_definitions)?,
1✔
286
                        None => gson::Value::Absent,
9✔
287
                    }
288
                }
289
            }
290
        }
291
    };
292
    Ok(result)
1,312✔
293
}
1,312✔
294

295
pub fn validate_arg_from_type(type_: &__Type, value: &gson::Value) -> GraphQLResult<gson::Value> {
6,371✔
296
    use crate::graphql::Scalar;
297
    use crate::gson::Number as GsonNumber;
298
    use crate::gson::Value as GsonValue;
299

300
    let res: GsonValue = match type_ {
6,371✔
301
        __Type::Scalar(scalar) => {
3,111✔
302
            match scalar {
818✔
303
                Scalar::String(None) => match value {
404✔
304
                    GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
404✔
305
                    _ => {
NEW
306
                        return Err(GraphQLError::type_error(format!(
×
NEW
307
                            "Invalid input for {:?} type",
×
NEW
308
                            scalar
×
NEW
309
                        )))
×
310
                    }
311
                },
312
                Scalar::String(Some(max_length)) => match value {
414✔
313
                    GsonValue::Absent | GsonValue::Null => value.clone(),
347✔
314
                    GsonValue::String(string_content) => {
67✔
315
                        match string_content.len() as i32 > *max_length {
67✔
316
                            false => value.clone(),
64✔
317
                            true => {
318
                                return Err(GraphQLError::type_error(format!(
3✔
319
                                    "Invalid input for {} type. Maximum character length {}",
3✔
320
                                    scalar.name().unwrap_or("String".to_string()),
3✔
321
                                    max_length
3✔
322
                                )))
3✔
323
                            }
324
                        }
325
                    }
326
                    _ => {
NEW
327
                        return Err(GraphQLError::type_error(format!(
×
NEW
328
                            "Invalid input for {:?} type",
×
NEW
329
                            scalar
×
NEW
330
                        )))
×
331
                    }
332
                },
333
                Scalar::Int => match value {
228✔
334
                    GsonValue::Absent => value.clone(),
866✔
335
                    GsonValue::Null => value.clone(),
377✔
336
                    GsonValue::Number(GsonNumber::Integer(_)) => value.clone(),
228✔
337
                    _ => {
338
                        return Err(GraphQLError::type_error(format!(
5✔
339
                            "Invalid input for {:?} type",
5✔
340
                            scalar
5✔
341
                        )))
5✔
342
                    }
343
                },
344
                Scalar::Float => match value {
22✔
345
                    GsonValue::Absent => value.clone(),
2✔
346
                    GsonValue::Null => value.clone(),
×
347
                    GsonValue::Number(_) => value.clone(),
20✔
348
                    _ => {
NEW
349
                        return Err(GraphQLError::type_error(format!(
×
NEW
350
                            "Invalid input for {:?} type",
×
NEW
351
                            scalar
×
NEW
352
                        )))
×
353
                    }
354
                },
355
                Scalar::Boolean => match value {
12✔
356
                    GsonValue::Absent | GsonValue::Null | GsonValue::Boolean(_) => value.clone(),
12✔
357
                    _ => {
NEW
358
                        return Err(GraphQLError::type_error(format!(
×
NEW
359
                            "Invalid input for {:?} type",
×
NEW
360
                            scalar
×
NEW
361
                        )))
×
362
                    }
363
                },
364
                Scalar::Date => {
365
                    match value {
15✔
366
                        // XXX: future - validate date here
367
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
15✔
368
                        _ => {
NEW
369
                            return Err(GraphQLError::type_error(format!(
×
NEW
370
                                "Invalid input for {:?} type",
×
NEW
371
                                scalar
×
NEW
372
                            )))
×
373
                        }
374
                    }
375
                }
376
                Scalar::Time => {
377
                    match value {
15✔
378
                        // XXX: future - validate time here
379
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
15✔
380
                        _ => {
NEW
381
                            return Err(GraphQLError::type_error(format!(
×
NEW
382
                                "Invalid input for {:?} type",
×
NEW
383
                                scalar
×
NEW
384
                            )))
×
385
                        }
386
                    }
387
                }
388
                Scalar::Datetime => {
389
                    match value {
19✔
390
                        // XXX: future - validate datetime here
391
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
19✔
392
                        _ => {
NEW
393
                            return Err(GraphQLError::type_error(format!(
×
NEW
394
                                "Invalid input for {:?} type",
×
NEW
395
                                scalar
×
NEW
396
                            )))
×
397
                        }
398
                    }
399
                }
400
                Scalar::BigInt => match value {
32✔
401
                    GsonValue::Absent
402
                    | GsonValue::Null
403
                    | GsonValue::String(_)
404
                    | GsonValue::Number(_) => value.clone(),
32✔
405
                    _ => {
NEW
406
                        return Err(GraphQLError::type_error(format!(
×
NEW
407
                            "Invalid input for {:?} type",
×
NEW
408
                            scalar
×
NEW
409
                        )))
×
410
                    }
411
                },
412
                Scalar::UUID => {
413
                    match value {
22✔
414
                        // XXX: future - validate uuid here
415
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
22✔
416
                        _ => {
NEW
417
                            return Err(GraphQLError::type_error(format!(
×
NEW
418
                                "Invalid input for {:?} type",
×
NEW
419
                                scalar
×
NEW
420
                            )))
×
421
                        }
422
                    }
423
                }
424
                Scalar::JSON => {
425
                    match value {
20✔
426
                        // XXX: future - validate json here
427
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
20✔
428
                        _ => {
NEW
429
                            return Err(GraphQLError::type_error(format!(
×
NEW
430
                                "Invalid input for {:?} type",
×
NEW
431
                                scalar
×
NEW
432
                            )))
×
433
                        }
434
                    }
435
                }
436
                Scalar::Cursor => {
437
                    match value {
597✔
438
                        // XXX: future - validate cursor here
439
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
597✔
440
                        _ => {
NEW
441
                            return Err(GraphQLError::type_error(format!(
×
NEW
442
                                "Invalid input for {:?} type",
×
NEW
443
                                scalar
×
NEW
444
                            )))
×
445
                        }
446
                    }
447
                }
448
                Scalar::ID => {
449
                    match value {
23✔
450
                        // XXX: future - validate cursor here
451
                        GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
23✔
452
                        _ => {
NEW
453
                            return Err(GraphQLError::type_error(format!(
×
NEW
454
                                "Invalid input for {:?} type",
×
NEW
455
                                scalar
×
NEW
456
                            )))
×
457
                        }
458
                    }
459
                }
460
                Scalar::BigFloat => match value {
32✔
461
                    GsonValue::Absent | GsonValue::Null | GsonValue::String(_) => value.clone(),
30✔
462
                    _ => {
463
                        return Err(GraphQLError::type_error(format!(
2✔
464
                            "Invalid input for {:?} type. String required",
2✔
465
                            scalar
2✔
466
                        )))
2✔
467
                    }
468
                },
469
                // No validation possible for unknown types. Lean on postgres for parsing
470
                Scalar::Opaque => value.clone(),
8✔
471
            }
472
        }
473
        __Type::Enum(enum_) => {
200✔
474
            let enum_name = enum_.name().expect("enum type should have a name");
200✔
475
            match value {
200✔
476
                GsonValue::Absent => value.clone(),
1✔
477
                GsonValue::Null => value.clone(),
170✔
478
                GsonValue::String(user_input_string) => {
29✔
479
                    let matches_enum_value = enum_
29✔
480
                        .enum_values(true)
29✔
481
                        .into_iter()
29✔
482
                        .flatten()
29✔
483
                        .find(|x| x.name().as_str() == user_input_string);
50✔
484
                    match matches_enum_value {
29✔
485
                        Some(_) => {
486
                            match &enum_.enum_ {
28✔
487
                                EnumSource::Enum(e) => e
21✔
488
                                    .directives
21✔
489
                                    .mappings
21✔
490
                                    .as_ref()
21✔
491
                                    // Use mappings if available and mapped
492
                                    .and_then(|mappings| mappings.get_by_right(user_input_string))
21✔
493
                                    .map(|val| GsonValue::String(val.clone()))
21✔
494
                                    .unwrap_or_else(|| value.clone()),
21✔
495
                                EnumSource::FilterIs => value.clone(),
7✔
496
                            }
497
                        }
498
                        None => {
499
                            return Err(GraphQLError::type_error(format!(
1✔
500
                                "Invalid input for {} type",
1✔
501
                                enum_name
1✔
502
                            )))
1✔
503
                        }
504
                    }
505
                }
506
                _ => {
NEW
507
                    return Err(GraphQLError::type_error(format!(
×
NEW
508
                        "Invalid input for {} type",
×
NEW
509
                        enum_name
×
NEW
510
                    )))
×
511
                }
512
            }
513
        }
514
        __Type::OrderBy(enum_) => {
273✔
515
            let enum_name = enum_.name().expect("order by type should have a name");
273✔
516
            match value {
273✔
517
                GsonValue::Absent => value.clone(),
2✔
518
                GsonValue::Null => value.clone(),
208✔
519
                GsonValue::String(user_input_string) => {
63✔
520
                    let matches_enum_value = enum_
63✔
521
                        .enum_values(true)
63✔
522
                        .into_iter()
63✔
523
                        .flatten()
63✔
524
                        .find(|x| x.name().as_str() == user_input_string);
163✔
525
                    match matches_enum_value {
63✔
526
                        Some(_) => value.clone(),
58✔
527
                        None => {
528
                            return Err(GraphQLError::type_error(format!(
5✔
529
                                "Invalid input for {} type",
5✔
530
                                enum_name
5✔
531
                            )))
5✔
532
                        }
533
                    }
534
                }
535
                _ => {
NEW
536
                    return Err(GraphQLError::type_error(format!(
×
NEW
537
                        "Invalid input for {} type",
×
NEW
538
                        enum_name
×
NEW
539
                    )))
×
540
                }
541
            }
542
        }
543
        __Type::List(list_type) => {
879✔
544
            let inner_type: __Type = *list_type.type_.clone();
879✔
545
            match value {
879✔
546
                GsonValue::Absent => value.clone(),
243✔
547
                GsonValue::Null => value.clone(),
489✔
548
                GsonValue::Array(input_arr) => {
130✔
549
                    let mut output_arr = vec![];
130✔
550
                    for input_elem in input_arr {
327✔
551
                        let out_elem = validate_arg_from_type(&inner_type, input_elem)?;
212✔
552
                        output_arr.push(out_elem);
197✔
553
                    }
554
                    GsonValue::Array(output_arr)
115✔
555
                }
556
                _ => {
557
                    // Single elements must be coerced to a single element list
558
                    let out_elem = validate_arg_from_type(&inner_type, value)?;
17✔
559
                    GsonValue::Array(vec![out_elem])
13✔
560
                }
561
            }
562
        }
563
        __Type::NonNull(nonnull_type) => {
391✔
564
            let inner_type: __Type = *nonnull_type.type_.clone();
391✔
565
            let out_elem = validate_arg_from_type(&inner_type, value)?;
391✔
566
            match out_elem {
366✔
567
                GsonValue::Absent | GsonValue::Null => {
568
                    return Err(GraphQLError::type_error("Invalid input for NonNull type"))
28✔
569
                }
570
                _ => out_elem,
338✔
571
            }
572
        }
573
        __Type::InsertInput(_) => validate_arg_from_input_object(type_, value)?,
40✔
574
        __Type::UpdateInput(_) => validate_arg_from_input_object(type_, value)?,
28✔
575
        __Type::OrderByEntity(_) => validate_arg_from_input_object(type_, value)?,
54✔
576
        __Type::FilterType(_) => validate_arg_from_input_object(type_, value)?,
838✔
577
        __Type::FilterEntity(_) => validate_arg_from_input_object(type_, value)?,
557✔
578
        _ => {
NEW
579
            return Err(GraphQLError::type_error(format!(
×
580
                "Invalid Type used as input argument {}",
×
581
                type_.name().unwrap_or_default()
×
NEW
582
            )))
×
583
        }
584
    };
585
    Ok(res)
6,247✔
586
}
6,371✔
587

588
pub fn validate_arg_from_input_object(
1,517✔
589
    input_type: &__Type,
1,517✔
590
    value: &gson::Value,
1,517✔
591
) -> GraphQLResult<gson::Value> {
1,517✔
592
    use crate::graphql::__TypeKind;
593
    use crate::gson::Value as GsonValue;
594

595
    let input_type_name = input_type.name().unwrap_or_default();
1,517✔
596

597
    if input_type.kind() != __TypeKind::INPUT_OBJECT {
1,517✔
NEW
598
        return Err(GraphQLError::type_error(format!(
×
NEW
599
            "Invalid input type {}",
×
NEW
600
            input_type_name
×
NEW
601
        )));
×
602
    }
1,517✔
603

604
    let res: GsonValue = match value {
1,517✔
605
        GsonValue::Absent => value.clone(),
205✔
606
        GsonValue::Null => value.clone(),
830✔
607
        GsonValue::Object(input_obj) => {
470✔
608
            let mut out_map: HashMap<String, GsonValue> = HashMap::new();
470✔
609
            let type_input_fields: Vec<__InputValue> =
470✔
610
                input_type.input_fields().unwrap_or_default();
470✔
611

612
            // Confirm that there are no extra keys
613
            let mut extra_input_keys = vec![];
470✔
614
            for (k, _) in input_obj.iter() {
553✔
615
                if !type_input_fields.iter().map(|x| x.name()).any(|x| x == *k) {
1,670✔
616
                    extra_input_keys.push(k);
5✔
617
                }
548✔
618
            }
619
            if !extra_input_keys.is_empty() {
470✔
620
                return Err(GraphQLError::validation(format!(
5✔
621
                    "Input for type {} contains extra keys {:?}",
5✔
622
                    input_type_name, extra_input_keys
5✔
623
                )));
5✔
624
            }
465✔
625

626
            for obj_field in type_input_fields {
3,709✔
627
                let obj_field_type: __Type = obj_field.type_();
3,263✔
628
                let obj_field_key: String = obj_field.name();
3,263✔
629

630
                match input_obj.get(&obj_field_key) {
3,263✔
631
                    None => {
632
                        validate_arg_from_type(&obj_field_type, &GsonValue::Null)?;
2,699✔
633
                    }
634
                    Some(x) => {
564✔
635
                        let out_val = validate_arg_from_type(&obj_field_type, x)?;
564✔
636
                        out_map.insert(obj_field_key, out_val);
545✔
637
                    }
638
                };
639
            }
640
            GsonValue::Object(out_map)
446✔
641
        }
642
        _ => {
643
            return Err(GraphQLError::type_error(format!(
12✔
644
                "Invalid input for {} type",
12✔
645
                input_type_name
12✔
646
            )))
12✔
647
        }
648
    };
649
    Ok(res)
1,481✔
650
}
1,517✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc