From 02b231aa50220622ac7d252d8a3f9988d9b14ddf Mon Sep 17 00:00:00 2001 From: VectorTetra Date: Fri, 14 Feb 2025 15:35:16 +0200 Subject: [PATCH 1/2] Implemented case-sensitive input parameters match --- src/FSharp.Data.GraphQL.Server/Values.fs | 3 +- .../Variables and Inputs/InputComplexTests.fs | 34 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Server/Values.fs b/src/FSharp.Data.GraphQL.Server/Values.fs index 8f93e8bf..8196ccaf 100644 --- a/src/FSharp.Data.GraphQL.Server/Values.fs +++ b/src/FSharp.Data.GraphQL.Server/Values.fs @@ -121,7 +121,8 @@ let rec internal compileByType (fun (allParameters : _ ResizeArray) param -> match objDef.Fields - |> Array.tryFind (fun field -> field.Name = param.Name) + // TODO: Improve parameter name matching logic + |> Array.tryFind (fun field -> String.Equals (field.Name, param.Name, StringComparison.InvariantCultureIgnoreCase)) with | Some field -> let isParameterSkippable = ReflectionHelper.isParameterSkippable param diff --git a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputComplexTests.fs b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputComplexTests.fs index d0fb398e..af362580 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputComplexTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputComplexTests.fs @@ -38,9 +38,9 @@ let TestComplexScalar = ) type TestInput = - { mand : string - opt1 : string option - opt2 : string option + { Mand : string + Opt : string option + GLCode : string option // for case-insensitive name match test optSeq : string option seq option voptSeq : string option seq voption // string voption seq voption is too hard to implement optArr : string option array option @@ -53,8 +53,8 @@ let TestInputObject = name = "TestInputObject", fields = [ Define.Input ("mand", StringType) - Define.Input ("opt1", Nullable StringType) - Define.Input ("opt2", Nullable TestComplexScalar) + Define.Input ("opt", Nullable StringType) + Define.Input ("glCode", Nullable TestComplexScalar) Define.Input ("optSeq", Nullable (ListOf (Nullable StringType))) Define.Input ("voptSeq", Nullable (ListOf (Nullable StringType))) Define.Input ("optArr", Nullable (InputArrayOf (Nullable StringType))) @@ -72,13 +72,13 @@ let schema = Schema (TestType) [] let ``Execute handles objects and nullability using inline structs with complex input`` () = let ast = - parse """{ fieldWithObjectInput(input: {mand: "baz", opt1: "foo", optSeq: ["bar"], optArr: ["baf"]}) }""" + parse """{ fieldWithObjectInput(input: {mand: "baz", opt: "foo", optSeq: ["bar"], optArr: ["baf"]}) }""" let result = sync <| Executor(schema).AsyncExecute (ast) let expected = NameValueLookup.ofList [ "fieldWithObjectInput", - upcast """{"mand":"baz","opt1":"foo","opt2":null,"optSeq":["bar"],"voptSeq":null,"optArr":["baf"],"voptArr":null}""" ] + upcast """{"mand":"baz","opt":"foo","glCode":null,"optSeq":["bar"],"voptSeq":null,"optArr":["baf"],"voptArr":null}""" ] ensureDirect result <| fun data errors -> empty errors @@ -87,22 +87,22 @@ let ``Execute handles objects and nullability using inline structs with complex // See https://spec.graphql.org/October2021/#sec-List [] let ``Execute handles objects and nullability using inline structs and properly parses single value to list`` () = - let ast = parse """{ fieldWithObjectInput(input: {mand:"baz", opt1: "foo", optSeq: "bar"}) }""" + let ast = parse """{ fieldWithObjectInput(input: {mand:"baz", opt: "foo", optSeq: "bar"}) }""" let result = sync <| Executor(schema).AsyncExecute (ast) let expected = - NameValueLookup.ofList [ "fieldWithObjectInput", upcast """{"mand":"baz", "opt1":"foo", "optSeq":["bar"], "opt2":null, "optArr":null}""" ] + NameValueLookup.ofList [ "fieldWithObjectInput", upcast """{"mand":"baz", "opt":"foo", "optSeq":["bar"], "glCode":null, "optArr":null}""" ] ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) [] let ``Execute handles objects and nullability using inline structs and properly coerces complex scalar types`` () = - let ast = parse """{ fieldWithObjectInput(input: {mand: "foo", opt2: "SerializedValue"}) }""" + let ast = parse """{ fieldWithObjectInput(input: {mand: "foo", glCode: "SerializedValue"}) }""" let result = sync <| Executor(schema).AsyncExecute (ast) let expected = NameValueLookup.ofList [ "fieldWithObjectInput", - upcast """{"mand":"foo","opt1":null,"opt2":"DeserializedValue","optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}""" ] + upcast """{"mand":"foo","opt":null,"glCode":"DeserializedValue","optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}""" ] ensureDirect result <| fun data errors -> empty errors @@ -116,7 +116,7 @@ let paramsWithValueInput input = .RootElement.Deserialize> (serializerOptions) let testInputObject = - """{"mand":"baz","opt1":"foo","opt2":null,"optSeq":["bar"],"voptSeq":["bar"],"optArr":null,"voptArr":null}""" + """{"mand":"baz","opt":"foo","glCode":null,"optSeq":["bar"],"voptSeq":["bar"],"optArr":null,"voptArr":null}""" [] let ``Execute handles variables with complex inputs`` () = @@ -137,7 +137,7 @@ let ``Execute handles variables with complex inputs`` () = let ``Execute handles variables with default value when no value was provided`` () = let ast = parse - """query q($input: TestInputObject = {mand:"baz", opt1: "foo", optSeq: ["bar"], voptSeq:["bar"]}) { + """query q($input: TestInputObject = {mand:"baz", opt: "foo", optSeq: ["bar"], voptSeq:["bar"]}) { fieldWithObjectInput(input: $input) }""" @@ -155,7 +155,7 @@ let ``Execute handles variables and errors on null for nested non-nulls`` () = fieldWithObjectInput(input: $input) }""" - let testInputObject = """{"mand":null, "opt1":"foo", "optSeq":["bar"], "voptSeq":["bar"]}""" + let testInputObject = """{"mand":null, "opt":"foo", "optSeq":["bar"], "voptSeq":["bar"]}""" let params' = paramsWithValueInput testInputObject let result = sync <| Executor(schema).AsyncExecute (ast, variables = params') ensureRequestError result <| fun [ error ] -> @@ -185,7 +185,7 @@ let ``Execute handles variables and errors on omission of nested non-nulls`` () fieldWithObjectInput(input: $input) }""" - let testInputObject = """{"opt1":"foo","optSeq":["bar"]}""" + let testInputObject = """{"opt":"foo","optSeq":["bar"]}""" let params' = paramsWithValueInput testInputObject let result = sync <| Executor(schema).AsyncExecute (ast, variables = params') ensureRequestError result <| fun [ error ] -> @@ -199,7 +199,7 @@ let ``Execute handles list inputs and nullability and does not allow invalid typ """query q($input: TestInputObject!) { fieldWithObjectInput(input: $input) }""" - // as that kind of an error inside of opt1 query is guaranteed to fail in every call, we're gonna to fail noisy here + // as that kind of an error inside of opt query is guaranteed to fail in every call, we're gonna to fail noisy here let testInputList = "[\"A\",\"B\"]" let params' = paramsWithValueInput testInputList let result = sync <| Executor(schema).AsyncExecute (ast, variables = params') @@ -217,7 +217,7 @@ let ``Execute handles list inputs and nullability and does not allow unknown typ """query q($input: UnknownType!) { fieldWithObjectInput(input: $input) }""" - // as that kind of an error inside of opt1 query is guaranteed to fail in every call, we're gonna to fail noisy here + // as that kind of an error inside of opt query is guaranteed to fail in every call, we're gonna to fail noisy here let testInputValue = "\"whoknows\"" let params' = paramsWithValueInput testInputValue let result = sync <| Executor(schema).AsyncExecute (ast, variables = params') From af67e667de908783c5245469dbb18aa059b90997 Mon Sep 17 00:00:00 2001 From: VectorTetra Date: Fri, 14 Feb 2025 16:19:31 +0200 Subject: [PATCH 2/2] Fixed InputNestedTests --- .../Variables and Inputs/InputNestedTests.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputNestedTests.fs b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputNestedTests.fs index 15de95bf..25cdaf06 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputNestedTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/InputNestedTests.fs @@ -110,7 +110,7 @@ let ``Execute handles nested input objects and nullability using inline structs NameValueLookup.ofList [ "fieldWithNestedInputObject", upcast - """{"n":"optSeq","no":{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nvo":{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nl":[],"nlo":null,"nlvo":null}""" + """{"n":"optSeq","no":{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nvo":{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nl":[],"nlo":null,"nlvo":null}""" ] ensureDirect result @@ -127,7 +127,7 @@ let ``Execute handles nested input objects and nullability using inline structs NameValueLookup.ofList [ "fieldWithNestedInputObject", upcast - """{"n":"optSeq","no":{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nvo":{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nl":[],"nlo":[],"nlvo":[]}""" + """{"n":"optSeq","no":{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nvo":{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null},"nl":[],"nlo":[],"nlvo":[]}""" ] ensureDirect result @@ -144,7 +144,7 @@ let ``Execute handles nested input objects and nullability using inline structs NameValueLookup.ofList [ "fieldWithNestedInputObject", upcast - """{"n":"optSeq","no":null,"nvo":null,"nl":[{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}],"nlo":[{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}],"nlvo":[{"mand":"mand","opt1":null,"opt2":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}]}""" + """{"n":"optSeq","no":null,"nvo":null,"nl":[{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}],"nlo":[{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}],"nlvo":[{"mand":"mand","opt":null,"glCode":null,"optSeq":null,"voptSeq":null,"optArr":null,"voptArr":null}]}""" ] ensureDirect result