From cfc5e8872966d6cb044ba2f4a0b03797580a4ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e?= Date: Thu, 14 Sep 2023 10:29:09 +0200 Subject: [PATCH] Fix error response on large numbers in query transformations (#3820) In GraphQL source text, you can write an integer outside the int32 range if a float is expected. apollo-rs parses these numbers as f64 inside `hir::Value::Int` because at that time, the expected type of the value is not known, especially because it's possible to parse queries without knowledge of the schema. In the defer label transformation, any apollo-rs `hir::Value::Int` was treated as an i32. This would cause an error if it's one such large integer. The fully proper way to convert `hir::Value` to another type (ie. encoder or serde_json, as is done elsewhere) would be to match on the actual GraphQL type declared by the schema. But the simpler thing to do is to just use a Float if the value is too big for an Int. This will round-trip correctly. See test for an example that used to fail: https://github.com/apollographql/router/blob/5b0f608bd4ff613bdcc1a99e0c1a13f7735f0070/apollo-router/src/query_planner/labeler.rs#L128-L129 --- **Checklist** Complete the checklist (and note appropriate exceptions) before the PR is marked ready-for-review. - [x] Changes are compatible[^1] - [ ] Documentation[^2] completed - [ ] Performance impact assessed and acceptable - Tests added and passing[^3] - [x] Unit Tests - [ ] Integration Tests - [ ] Manual Tests **Exceptions** *Note any exceptions here* **Notes** [^1]: It may be appropriate to bring upcoming changes to the attention of other (impacted) groups. Please endeavour to do this before seeking PR approval. The mechanism for doing this will vary considerably, so use your judgement as to how and when to do this. [^2]: Configuration is an important part of many changes. Where applicable please try to document configuration examples. [^3]: Tick whichever testing boxes are applicable. If you are adding Manual Tests, please document the manual testing (extensively) in the Exceptions. --- .changesets/fix_renee_fix_large_ints.md | 22 +++++++++++++++++++ apollo-router/src/query_planner/labeler.rs | 16 ++++++++++++++ ...er__tests__large_float_written_as_int.snap | 8 +++++++ apollo-router/src/spec/query/transform.rs | 7 +++--- 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 .changesets/fix_renee_fix_large_ints.md create mode 100644 apollo-router/src/query_planner/snapshots/apollo_router__query_planner__labeler__tests__large_float_written_as_int.snap diff --git a/.changesets/fix_renee_fix_large_ints.md b/.changesets/fix_renee_fix_large_ints.md new file mode 100644 index 0000000000..756249f92c --- /dev/null +++ b/.changesets/fix_renee_fix_large_ints.md @@ -0,0 +1,22 @@ +### Fix error response on large numbers in query transformations ([PR #3820](https://github.com/apollographql/router/pull/3820)) + +This bug caused the router to reject operations where a large hardcoded integer was used as input for a Float field: + +```graphql +# Schema +type Query { + field(argument: Float): Int! +} +# Operation +{ + field(argument: 123456789123) +} +``` + +Now the number is correctly interpreted as a Float. +This bug only affected hardcoded numbers, not numbers provided through variables. + + +--- + +By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/3820 \ No newline at end of file diff --git a/apollo-router/src/query_planner/labeler.rs b/apollo-router/src/query_planner/labeler.rs index 7bbb095eb7..45eac5215c 100644 --- a/apollo-router/src/query_planner/labeler.rs +++ b/apollo-router/src/query_planner/labeler.rs @@ -116,3 +116,19 @@ pub(crate) fn directive( Ok(encoder_directive) } + +#[cfg(test)] +mod tests { + use apollo_compiler::ApolloCompiler; + + use super::add_defer_labels; + + #[test] + fn large_float_written_as_int() { + let mut compiler = ApolloCompiler::new(); + compiler.add_type_system("type Query { field(id: Float): String! }", "schema.graphql"); + let file_id = compiler.add_executable(r#"{ field(id: 1234567890123) }"#, "query.graphql"); + let result = add_defer_labels(file_id, &compiler).unwrap(); + insta::assert_snapshot!(result); + } +} diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__labeler__tests__large_float_written_as_int.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__labeler__tests__large_float_written_as_int.snap new file mode 100644 index 0000000000..8be08a97cc --- /dev/null +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__labeler__tests__large_float_written_as_int.snap @@ -0,0 +1,8 @@ +--- +source: apollo-router/src/query_planner/labeler.rs +expression: result +--- +query { + field(id: 1234567890123) +} + diff --git a/apollo-router/src/spec/query/transform.rs b/apollo-router/src/spec/query/transform.rs index 76bfe0f9be..2a5571fc37 100644 --- a/apollo-router/src/spec/query/transform.rs +++ b/apollo-router/src/spec/query/transform.rs @@ -369,9 +369,10 @@ pub(crate) fn ty(hir: &hir::Type) -> apollo_encoder::Type_ { pub(crate) fn value(hir: &hir::Value) -> Result { Ok(match hir { hir::Value::Variable(val) => apollo_encoder::Value::Variable(val.name().into()), - hir::Value::Int { value, .. } => { - apollo_encoder::Value::Int(value.to_i32_checked().ok_or("Int value overflows i32")?) - } + hir::Value::Int { value, .. } => value + .to_i32_checked() + .map(apollo_encoder::Value::Int) + .unwrap_or_else(|| apollo_encoder::Value::Float(value.get())), hir::Value::Float { value, .. } => apollo_encoder::Value::Float(value.get()), hir::Value::String { value, .. } => apollo_encoder::Value::String(value.clone()), hir::Value::Boolean { value, .. } => apollo_encoder::Value::Boolean(*value),