From d0604236c5826c24a512e45ea61f55397aed0142 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Wed, 16 Aug 2023 19:39:09 +0400 Subject: [PATCH 1/6] Added `ValueObjectScalarDefinition` --- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 49 ++++++++++++++++++- .../Variables and Inputs/CoercionTests.fs | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index 64fe199ea..dce53bf9d 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -1007,7 +1007,7 @@ and [] ScalarDefinition<'Val> = Name : string /// Optional type description. Description : string option - /// A function used to retrieve a .NET object from provided GraphQL query. + /// A function used to retrieve a .NET object from provided GraphQL query or JsonElement variable. CoerceInput : InputParameterValue -> Result<'Val, IGQLError list> /// A function used to set a surrogate representation to be /// returned as a query result. @@ -1048,6 +1048,53 @@ and [] ScalarDefinition<'Val> = override x.GetHashCode() = x.Name.GetHashCode() override x.ToString() = x.Name + "!" +/// Concrete representation of the scalar types wrapped into a value object. +and [] ValueObjectScalarDefinition<'Primitive, 'Val> = + { /// Name of the scalar type. + Name : string + /// Optional type description. + Description : string option + /// A function used to retrieve a .NET object from provided GraphQL query or JsonElement variable. + CoerceInput : InputParameterValue -> Result<'Val, IGQLError list> + /// A function used to set a surrogate representation to be + /// returned as a query result. + CoerceOutput : obj -> 'Primitive option } + + interface TypeDef with + member _.Type = typeof<'Val> + + member x.MakeNullable() = + let nullable : NullableDefinition<_> = { OfType = x } + upcast nullable + + member x.MakeList() = + let list: ListOfDefinition<_,_> = { OfType = x } + upcast list + + interface InputDef + interface OutputDef + + interface ScalarDef with + member x.Name = x.Name + member x.Description = x.Description + member x.CoerceInput input = x.CoerceInput input |> Result.map box + member x.CoerceOutput value = (x.CoerceOutput value) |> Option.map box + + interface InputDef<'Val> + interface OutputDef<'Val> + interface LeafDef + + interface NamedDef with + member x.Name = x.Name + + override x.Equals y = + match y with + | :? ValueObjectScalarDefinition<'Primitive, 'Val> as s -> x.Name = s.Name + | _ -> false + + override x.GetHashCode() = x.Name.GetHashCode() + override x.ToString() = x.Name + "!" + /// A GraphQL representation of single case of the enum type. /// Enum value return value is always represented as string. and EnumVal = diff --git a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs index b1cebe281..d533f9972 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs @@ -17,7 +17,7 @@ let private testCoercion graphQLType (expected: 't) actual = let result = (scalar.CoerceInput actual) |> Result.map (fun x -> downcast x) match result with | Ok x -> equals expected x - | Error _ -> raise (Exception(sprintf "Expected %A to be able to be coerced to %A" actual expected)) + | Error _ -> raise (Exception $"Expected %A{actual} to be able to be coerced to %A{expected}") [] From a05d9dc97346f09d3fe66fa5aa30671d19d03700 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Mon, 28 Aug 2023 01:20:05 +0400 Subject: [PATCH 2/6] Changed `IDType` implementation to support only string and int64 Ids, fixed built-in scalars input coercion and added tests for that --- src/FSharp.Data.GraphQL.Server.Relay/Node.fs | 2 +- src/FSharp.Data.GraphQL.Server/Linq.fs | 3 +- .../SchemaDefinitions.fs | 193 +++++++++++++----- tests/FSharp.Data.GraphQL.Tests/LinqTests.fs | 4 +- .../Variables and Inputs/CoercionTests.fs | 79 +++++-- 5 files changed, 217 insertions(+), 64 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Server.Relay/Node.fs b/src/FSharp.Data.GraphQL.Server.Relay/Node.fs index 8d17bba97..743afef1e 100644 --- a/src/FSharp.Data.GraphQL.Server.Relay/Node.fs +++ b/src/FSharp.Data.GraphQL.Server.Relay/Node.fs @@ -78,6 +78,6 @@ module GlobalId = Define.Interface( name = "Node", description = "An object that can be uniquely identified by its id", - fields = [ Define.Field("id", IDType) ], + fields = [ Define.Field("id", IDType) ], resolveType = resolveTypeFun possibleTypes) diff --git a/src/FSharp.Data.GraphQL.Server/Linq.fs b/src/FSharp.Data.GraphQL.Server/Linq.fs index 1458caa60..be3b2cf3d 100644 --- a/src/FSharp.Data.GraphQL.Server/Linq.fs +++ b/src/FSharp.Data.GraphQL.Server/Linq.fs @@ -171,7 +171,8 @@ let private applyId: ArgApplication = fun expression callable -> let p0 = Expression.Parameter tSource let idProperty = memberExpr callable.Type "id" p0 // Func predicate = p0 => p0 == value - let predicate = Expression.Lambda(Expression.Equal(idProperty, Expression.Constant (extractValueIfOption callable)), p0) + let toStringMethodInfo = idProperty.Type.GetMethod("ToString", Array.empty) + let predicate = Expression.Lambda(Expression.Equal(Expression.Call(idProperty, toStringMethodInfo), Expression.Constant (extractValueIfOption callable)), p0) let where = methods.Where.MakeGenericMethod [| tSource |] upcast Expression.Call(null, where, expression, predicate) diff --git a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs index 6354dc055..0dd35820b 100644 --- a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs @@ -21,14 +21,27 @@ module SchemaDefinitions = type InputValue with member inputValue.GetCoerceError(destinationType) = - let getMessage inputType value = $"Inline value '{value}' of type %s{inputType} cannot be converted to %s{destinationType}" + let getMessage inputType value = $"Inline value '{value}' of type %s{inputType} cannot be converted into %s{destinationType}" let message = match inputValue with | IntValue value -> getMessage "integer" value | FloatValue value -> getMessage "float" value | BooleanValue value -> getMessage "boolean" value | StringValue value -> getMessage "string" value - | NullValue -> $"Inline null value cannot be converted to {destinationType}" + | NullValue -> $"Inline value 'null' cannot be converted into {destinationType}" + | EnumValue value -> getMessage "enum" value + | value -> raise <| NotSupportedException $"{value} cannot be passed as scalar input" + Error [{ new IGQLError with member _.Message = message }] + + member inputValue.GetCoerceRangeError(destinationType, minValue, maxValue) = + let getMessage inputType value = $"Inline value '{value}' of type %s{inputType} cannot be converted into %s{destinationType} of range from {minValue} to {maxValue}" + let message = + match inputValue with + | IntValue value -> getMessage "integer" value + | FloatValue value -> getMessage "float" value + | BooleanValue value -> getMessage "boolean" value + | StringValue value -> getMessage "string" value + | NullValue -> $"Inline value 'null' cannot be converted into {destinationType}" | EnumValue value -> getMessage "enum" value | value -> raise <| NotSupportedException $"{value} cannot be passed as scalar input" Error [{ new IGQLError with member _.Message = message }] @@ -36,16 +49,18 @@ module SchemaDefinitions = type JsonElement with member e.GetDeserializeError(destinationType, minValue, maxValue ) = - Error [{ new IGQLError with member _.Message = $"Cannot deserialize JSON value '{e.GetRawText()}' into %s{destinationType} of range from {minValue} to {maxValue}" }] + let jsonValue = match e.ValueKind with JsonValueKind.String -> e.GetString() | _ -> e.GetRawText() + Error [{ new IGQLError with member _.Message = $"JSON value '{jsonValue}' of kind '{e.ValueKind}' cannot be deserialized into %s{destinationType} of range from {minValue} to {maxValue}" }] member e.GetDeserializeError(destinationType) = - Error [{ new IGQLError with member _.Message = $"Cannot deserialize JSON value '{e.GetRawText()}' into %s{destinationType}" }] + let jsonValue = match e.ValueKind with JsonValueKind.String -> e.GetString() | _ -> e.GetRawText() + Error [{ new IGQLError with member _.Message = $"JSON value '{jsonValue}' of kind '{e.ValueKind}' cannot be deserialized into %s{destinationType}" }] let getParseRangeError (destinationType, minValue, maxValue) value = - Error [{ new IGQLError with member _.Message = $"Cannot parse '%s{value}' into %s{destinationType} of range from {minValue} to {maxValue}" }] + Error [{ new IGQLError with member _.Message = $"Inline value '%s{value}' cannot be parsed into %s{destinationType} of range from {minValue} to {maxValue}" }] let getParseError destinationType value = - Error [{ new IGQLError with member _.Message = $"Cannot parse '%s{value}' into %s{destinationType}" }] + Error [{ new IGQLError with member _.Message = $"Inline value '%s{value}' cannot be paprsed into %s{destinationType}" }] open System.Globalization @@ -173,12 +188,12 @@ module SchemaDefinitions = | _ -> Some(x.ToString()) /// Tries to convert any value to generic type parameter. - let coerceIdValue (x : obj) : 't option = + let coerceIdValue (x : obj) : string option = match x with | null -> None - | :? string as s -> Some (downcast Convert.ChangeType(s, typeof<'t>)) - | Option o -> Some(downcast Convert.ChangeType(o, typeof<'t>)) - | _ -> Some(downcast Convert.ChangeType(x, typeof<'t>)) + | :? string as s -> Some s + | Option o -> Some(string o) + | _ -> Some(string x) /// Tries to resolve AST query input to int. @@ -189,45 +204,40 @@ module SchemaDefinitions = match e.TryGetInt32() with | true, value -> Ok value | false, _ -> e.GetDeserializeError(destinationType, Int32.MinValue, Int32.MaxValue) - | Variable e -> e.GetDeserializeError destinationType + | Variable e when e.ValueKind = JsonValueKind.True -> Ok 1 + | Variable e when e.ValueKind = JsonValueKind.False -> Ok 0 + | Variable e -> e.GetDeserializeError (destinationType, Int32.MinValue, Int32.MaxValue) | InlineConstant (IntValue i) -> Ok (int i) - | InlineConstant (FloatValue f) -> Ok (int f) - | InlineConstant (StringValue s) -> - match Int32.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with - | true, i -> Ok i - | false, _ -> getParseRangeError(destinationType, Int32.MinValue, Int32.MaxValue) s | InlineConstant (BooleanValue b) -> Ok (if b then 1 else 0) - | InlineConstant value -> value.GetCoerceError destinationType + | InlineConstant value -> value.GetCoerceRangeError(destinationType, Int32.MinValue, Int32.MaxValue) /// Tries to resolve AST query input to int64. let coerceLongInput = let destinationType = "integer" function - | Variable e when e.ValueKind = JsonValueKind.Number -> Ok (e.GetInt64()) - | Variable e -> e.GetDeserializeError destinationType + | Variable e when e.ValueKind = JsonValueKind.Number -> + match e.TryGetInt64() with + | true, value -> Ok value + | false, _ -> e.GetDeserializeError(destinationType, Int64.MinValue, Int64.MaxValue) + | Variable e when e.ValueKind = JsonValueKind.True -> Ok 1L + | Variable e when e.ValueKind = JsonValueKind.False -> Ok 0L + | Variable e -> e.GetDeserializeError (destinationType, Int64.MinValue, Int64.MaxValue) | InlineConstant (IntValue i) -> Ok (int64 i) - | InlineConstant (FloatValue f) -> Ok(int64 f) - | InlineConstant (StringValue s) -> - match Int64.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with - | true, i -> Ok i - | false, _ -> getParseRangeError(destinationType, Int64.MinValue, Int64.MaxValue) s | InlineConstant (BooleanValue b) -> Ok(if b then 1L else 0L) - | InlineConstant value -> value.GetCoerceError destinationType + | InlineConstant value -> value.GetCoerceRangeError(destinationType, Int64.MinValue, Int64.MaxValue) /// Tries to resolve AST query input to double. let coerceFloatInput = let destinationType = "float" function | Variable e when e.ValueKind = JsonValueKind.Number -> Ok (e.GetDouble()) - | Variable e -> e.GetDeserializeError destinationType + | Variable e when e.ValueKind = JsonValueKind.True -> Ok 1. + | Variable e when e.ValueKind = JsonValueKind.False -> Ok 0. + | Variable e -> e.GetDeserializeError (destinationType, Double.MinValue, Double.MaxValue) | InlineConstant (IntValue i) -> Ok(double i) | InlineConstant (FloatValue f) -> Ok f - | InlineConstant (StringValue s) -> - match Double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with - | true, i -> Ok i - | false, _ -> getParseRangeError(destinationType, Double.MinValue, Double.MaxValue) s | InlineConstant (BooleanValue b) -> Ok(if b then 1. else 0.) - | InlineConstant value -> value.GetCoerceError destinationType + | InlineConstant value -> value.GetCoerceRangeError(destinationType, Double.MinValue, Double.MaxValue) /// Tries to resolve AST query input to string. let coerceStringInput = @@ -244,6 +254,7 @@ module SchemaDefinitions = | InlineConstant (FloatValue f) -> Ok(f.ToString(CultureInfo.InvariantCulture)) | InlineConstant (StringValue s) -> Ok s | InlineConstant (BooleanValue b) -> Ok (if b then "true" else "false") + | InlineConstant (EnumValue e) -> Ok e | InlineConstant value -> value.GetCoerceError destinationType let coerceEnumInput = @@ -257,25 +268,29 @@ module SchemaDefinitions = function | Variable e when e.ValueKind = JsonValueKind.True -> Ok true | Variable e when e.ValueKind = JsonValueKind.False -> Ok false + | Variable e when e.ValueKind = JsonValueKind.Number -> Ok (if e.GetDouble() = 0. then false else true) | Variable e -> e.GetDeserializeError destinationType | InlineConstant (IntValue i) -> Ok(if i = 0L then false else true) | InlineConstant (FloatValue f) -> Ok(if f = 0. then false else true) - | InlineConstant (StringValue s) -> - match Boolean.TryParse(s) with - | true, i -> Ok i - | false, _ -> getParseError destinationType s | InlineConstant (BooleanValue b) -> Ok b | InlineConstant value -> value.GetCoerceError destinationType /// Tries to resolve AST query input to provided generic type. - let coerceIdInput input : Result<'t, IGQLError list> = + let coerceIdInput input : Result = + let destinationType = "identifier" match input with - | Variable e when e.ValueKind = JsonValueKind.String -> Ok (downcast Convert.ChangeType(e.GetString() , typeof<'t>)) - | Variable e when e.ValueKind = JsonValueKind.Number -> Ok (downcast Convert.ChangeType(e.GetInt32() , typeof<'t>)) - | Variable e -> e.GetDeserializeError typeof<'t>.Name - | InlineConstant (IntValue i) -> Ok(downcast Convert.ChangeType(i, typeof<'t>)) - | InlineConstant (StringValue s) -> Ok(downcast Convert.ChangeType(s, typeof<'t>)) - | InlineConstant value -> value.GetCoerceError "id" + | Variable e when e.ValueKind = JsonValueKind.String -> Ok (e.GetString()) + | Variable e when e.ValueKind = JsonValueKind.Number -> + try + e.GetInt64() |> ignore + Ok (e.GetRawText()) + with :? FormatException -> + e.GetDeserializeError(destinationType, Int64.MinValue, Int64.MaxValue) + | Variable e -> e.GetDeserializeError destinationType + | InlineConstant (IntValue i) -> Ok(string i) + | InlineConstant (FloatValue i) -> (FloatValue i).GetCoerceRangeError(destinationType, Int64.MinValue, Int64.MaxValue) + | InlineConstant (StringValue s) -> Ok s + | InlineConstant value -> value.GetCoerceError destinationType /// Tries to resolve AST query input to URI. let coerceUriInput = @@ -305,7 +320,7 @@ module SchemaDefinitions = match DateTimeOffset.TryParse(s) with | true, date -> Ok date | false, _ -> getParseRangeError(destinationType, DateTimeOffset.MinValue, DateTimeOffset.MaxValue) s - | InlineConstant value -> value.GetCoerceError destinationType + | InlineConstant value -> value.GetCoerceRangeError(destinationType, DateTimeOffset.MinValue, DateTimeOffset.MaxValue) /// Tries to resolve AST query input to DateOnly. let coerceDateOnlyInput = @@ -321,7 +336,7 @@ module SchemaDefinitions = match DateOnly.TryParse(s) with | true, date -> Ok date | false, _ -> getParseRangeError(destinationType, DateOnly.MinValue, DateOnly.MaxValue) s - | InlineConstant value -> value.GetCoerceError destinationType + | InlineConstant value -> value.GetCoerceRangeError(destinationType, DateOnly.MinValue, DateOnly.MaxValue) /// Tries to resolve AST query input to Guid. let coerceGuidInput = @@ -402,7 +417,7 @@ module SchemaDefinitions = CoerceOutput = coerceStringValue } /// GraphQL type for custom identifier - let IDType<'Val> : ScalarDefinition<'Val> = + let IDType : ScalarDefinition = { Name = "ID" Description = Some @@ -518,7 +533,35 @@ module SchemaDefinitions = /// Creates GraphQL type definition for user defined scalars. /// /// Type name. Must be unique in scope of the current schema. - /// Function used to resolve .NET object from GraphQL query AST. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member Scalar(name : string, coerceInput : InputParameterValue -> Result<'T, string>, + coerceOutput : obj -> 'T option, ?description : string) : ScalarDefinition<'T> = + { Name = name + Description = description + CoerceInput = coerceInput >> Result.mapError (fun msg -> { new IGQLError with member _.Message = msg } |> List.singleton) + CoerceOutput = coerceOutput } + + /// + /// Creates GraphQL type definition for user defined scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member Scalar(name : string, coerceInput : InputParameterValue -> Result<'T, string list>, + coerceOutput : obj -> 'T option, ?description : string) : ScalarDefinition<'T> = + { Name = name + Description = description + CoerceInput = coerceInput >> Result.mapError (List.map (fun msg -> { new IGQLError with member _.Message = msg })) + CoerceOutput = coerceOutput } + + /// + /// Creates GraphQL type definition for user defined scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member Scalar(name : string, coerceInput : InputParameterValue -> Result<'T, IGQLError>, @@ -532,7 +575,7 @@ module SchemaDefinitions = /// Creates GraphQL type definition for user defined scalars. /// /// Type name. Must be unique in scope of the current schema. - /// Function used to resolve .NET object from GraphQL query AST. + /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member Scalar(name : string, coerceInput : InputParameterValue -> Result<'T, IGQLError list>, @@ -542,6 +585,62 @@ module SchemaDefinitions = CoerceInput = coerceInput CoerceOutput = coerceOutput } + /// + /// Creates GraphQL type definition for user defined value object scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string>, + coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + { Name = name + Description = description + CoerceInput = coerceInput >> Result.mapError (fun msg -> { new IGQLError with member _.Message = msg } |> List.singleton) + CoerceOutput = coerceOutput } + + /// + /// Creates GraphQL type definition for user defined value object scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string list>, + coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + { Name = name + Description = description + CoerceInput = coerceInput >> Result.mapError (List.map (fun msg -> { new IGQLError with member _.Message = msg })) + CoerceOutput = coerceOutput } + + /// + /// Creates GraphQL type definition for user defined value object scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError>, + coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + { Name = name + Description = description + CoerceInput = coerceInput >> Result.mapError List.singleton + CoerceOutput = coerceOutput } + + /// + /// Creates GraphQL type definition for user defined value object scalars. + /// + /// Type name. Must be unique in scope of the current schema. + /// Function used to resolve .NET object from GraphQL query AST or variable. + /// Function used to cross cast to .NET types. + /// Optional scalar description. Usefull for generating documentation. + static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError list>, + coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + { Name = name + Description = description + CoerceInput = coerceInput + CoerceOutput = coerceOutput } + /// /// Creates GraphQL type definition for user defined enums. /// diff --git a/tests/FSharp.Data.GraphQL.Tests/LinqTests.fs b/tests/FSharp.Data.GraphQL.Tests/LinqTests.fs index 7c3fb39dd..25edd90a3 100644 --- a/tests/FSharp.Data.GraphQL.Tests/LinqTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/LinqTests.fs @@ -24,7 +24,7 @@ let Contact = Define.Object("Contact", [ Define.Field("email", StringType, fun _ let Person = Define.Object("Person", - [ Define.Field("id", IDType, fun _ x -> x.ID) + [ Define.Field("id", IDType, fun _ x -> string x.ID) Define.AutoField("firstName", StringType) Define.Field("lastName", StringType, fun _ x -> x.LastName) Define.Field("fullName", StringType, fun _ x -> x.FirstName + " " + x.LastName) @@ -60,7 +60,7 @@ let resolveRoot ctx () = result let linqArgs = - [ Define.Input("id", Nullable IDType) + [ Define.Input("id", Nullable IDType) Define.Input("skip", Nullable IntType) Define.Input("take", Nullable IntType) Define.Input("orderBy", Nullable StringType) diff --git a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs index d533f9972..fcd69a94b 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs @@ -7,6 +7,7 @@ module FSharp.Data.GraphQL.Tests.CoercionTests #nowarn "25" open System +open System.Text.Json open Xunit open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Types @@ -17,45 +18,97 @@ let private testCoercion graphQLType (expected: 't) actual = let result = (scalar.CoerceInput actual) |> Result.map (fun x -> downcast x) match result with | Ok x -> equals expected x - | Error _ -> raise (Exception $"Expected %A{actual} to be able to be coerced to %A{expected}") + | Error _ -> Assert.Fail $"Expected %A{actual} to be able to be coerced to %A{expected}" + +let private testCoercionError graphQLType (expectedErrorMeesage: string) actual = + let (Scalar scalar) = graphQLType + let result = (scalar.CoerceInput actual) |> Result.map (fun x -> downcast x) + match result with + | Ok _ -> Assert.Fail($"Expected %A{actual} to not be able to be coerced to %A{graphQLType.Type.Name}") + | Error errs -> equals expectedErrorMeesage errs.Head.Message [] let ``Int coerces input`` () = + testCoercion IntType 123 (Variable (JsonDocument.Parse "123").RootElement) testCoercion IntType 123 (InlineConstant (IntValue 123L)) - testCoercion IntType 123 (InlineConstant (FloatValue 123.4)) - testCoercion IntType 123 (InlineConstant (StringValue "123")) + testCoercionError IntType "JSON value '123.4' of kind 'Number' cannot be deserialized into integer of range from -2147483648 to 2147483647" (Variable (JsonDocument.Parse "123.4").RootElement) + testCoercionError IntType "Inline value '123,4' of type float cannot be converted into integer of range from -2147483648 to 2147483647" (InlineConstant (FloatValue 123.4)) + testCoercion IntType 1 (Variable (JsonDocument.Parse "true").RootElement) testCoercion IntType 1 (InlineConstant (BooleanValue true)) + testCoercion IntType 0 (Variable (JsonDocument.Parse "false").RootElement) testCoercion IntType 0 (InlineConstant (BooleanValue false)) + testCoercionError IntType "JSON value 'enum' of kind 'String' cannot be deserialized into integer of range from -2147483648 to 2147483647" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError IntType "Inline value 'enum' of type enum cannot be converted into integer of range from -2147483648 to 2147483647" (InlineConstant (EnumValue "enum")) + +[] +let ``Long coerces input`` () = + testCoercion LongType 123L (Variable (JsonDocument.Parse "123").RootElement) + testCoercion LongType 123L (InlineConstant (IntValue 123L)) + testCoercionError LongType "JSON value '123.4' of kind 'Number' cannot be deserialized into integer of range from -9223372036854775808 to 9223372036854775807" (Variable (JsonDocument.Parse "123.4").RootElement) + testCoercionError LongType "Inline value '123,4' of type float cannot be converted into integer of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) + testCoercion LongType 1L (Variable (JsonDocument.Parse "true").RootElement) + testCoercion LongType 1L (InlineConstant (BooleanValue true)) + testCoercion LongType 0L (Variable (JsonDocument.Parse "false").RootElement) + testCoercion LongType 0L (InlineConstant (BooleanValue false)) + testCoercionError LongType "JSON value 'enum' of kind 'String' cannot be deserialized into integer of range from -9223372036854775808 to 9223372036854775807" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError LongType "Inline value 'enum' of type enum cannot be converted into integer of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (EnumValue "enum")) [] let ``Float coerces input`` () = + testCoercion FloatType 123. (Variable (JsonDocument.Parse "123").RootElement) testCoercion FloatType 123. (InlineConstant (IntValue 123L)) + testCoercion FloatType 123.4 (Variable (JsonDocument.Parse "123.4").RootElement) testCoercion FloatType 123.4 (InlineConstant (FloatValue 123.4)) - testCoercion FloatType 123.4 (InlineConstant (StringValue "123.4")) + testCoercion FloatType 1. (Variable (JsonDocument.Parse "true").RootElement) testCoercion FloatType 1. (InlineConstant (BooleanValue true)) + testCoercion FloatType 0. (Variable (JsonDocument.Parse "false").RootElement) testCoercion FloatType 0. (InlineConstant (BooleanValue false)) - -[] -let ``Long coerces input`` () = - testCoercion LongType 123L (InlineConstant (IntValue 123L)) - testCoercion LongType 123L (InlineConstant (FloatValue 123.4)) - testCoercion LongType 123L (InlineConstant (StringValue "123")) - testCoercion LongType 1L (InlineConstant (BooleanValue true)) - testCoercion LongType 0L (InlineConstant (BooleanValue false)) + testCoercionError FloatType "JSON value 'enum' of kind 'String' cannot be deserialized into float of range from -1,7976931348623157E+308 to 1,7976931348623157E+308" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError FloatType "Inline value 'enum' of type enum cannot be converted into float of range from -1,7976931348623157E+308 to 1,7976931348623157E+308" (InlineConstant (EnumValue "enum")) [] let ``Boolean coerces input`` () = - testCoercion BooleanType true (InlineConstant (IntValue 123L)) + testCoercion BooleanType false (Variable (JsonDocument.Parse "0").RootElement) testCoercion BooleanType false (InlineConstant (IntValue 0L)) + testCoercion BooleanType true (Variable (JsonDocument.Parse "123").RootElement) + testCoercion BooleanType true (InlineConstant (IntValue 123L)) + testCoercion BooleanType true (Variable (JsonDocument.Parse "123.4").RootElement) testCoercion BooleanType true (InlineConstant (FloatValue 123.4)) + testCoercion BooleanType true (Variable (JsonDocument.Parse "true").RootElement) testCoercion BooleanType true (InlineConstant (BooleanValue true)) + testCoercion BooleanType false (Variable (JsonDocument.Parse "false").RootElement) testCoercion BooleanType false (InlineConstant (BooleanValue false)) + testCoercionError BooleanType "JSON value 'enum' of kind 'String' cannot be deserialized into boolean" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError BooleanType "Inline value 'enum' of type enum cannot be converted into boolean" (InlineConstant (EnumValue "enum")) [] let ``String coerces input`` () = + testCoercion StringType "123" (Variable (JsonDocument.Parse "123").RootElement) testCoercion StringType "123" (InlineConstant (IntValue 123L)) + testCoercion StringType "123.4" (Variable (JsonDocument.Parse "123.4").RootElement) testCoercion StringType "123.4" (InlineConstant (FloatValue 123.4)) + testCoercion StringType "abc123.4" (Variable (JsonDocument.Parse "\"abc123.4\"").RootElement) testCoercion StringType "acb123.4" (InlineConstant (StringValue "acb123.4")) + testCoercion StringType "true" (Variable (JsonDocument.Parse "true").RootElement) testCoercion StringType "true" (InlineConstant (BooleanValue true)) + testCoercion StringType "false" (Variable (JsonDocument.Parse "false").RootElement) testCoercion StringType "false" (InlineConstant (BooleanValue false)) + testCoercion StringType "enum" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercion StringType "enum" (InlineConstant (EnumValue "enum")) + +[] +let ``ID coerces input`` () = + testCoercion IDType "123" (Variable (JsonDocument.Parse "123").RootElement) + testCoercion IDType "123" (InlineConstant (IntValue 123L)) + testCoercionError IDType "JSON value '123.4' of kind 'Number' cannot be deserialized into identifier of range from -9223372036854775808 to 9223372036854775807" (Variable (JsonDocument.Parse "123.4").RootElement) + testCoercionError IDType "Inline value '123,4' of type float cannot be converted into identifier of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) + testCoercion IDType "abc123.4" (Variable (JsonDocument.Parse "\"abc123.4\"").RootElement) + testCoercion IDType "acb123.4" (InlineConstant (StringValue "acb123.4")) + testCoercionError IDType "JSON value 'true' of kind 'True' cannot be deserialized into identifier" (Variable (JsonDocument.Parse "true").RootElement) + testCoercionError IDType "Inline value 'True' of type boolean cannot be converted into identifier" (InlineConstant (BooleanValue true)) + testCoercionError IDType "JSON value 'false' of kind 'False' cannot be deserialized into identifier" (Variable (JsonDocument.Parse "false").RootElement) + testCoercionError IDType "Inline value 'False' of type boolean cannot be converted into identifier" (InlineConstant (BooleanValue false)) + // We have no idea that it is an enum + testCoercion IDType "enum" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError IDType "Inline value 'enum' of type enum cannot be converted into identifier" (InlineConstant (EnumValue "enum")) From e25c729916a6008d8b4e30527bc6b4ef3d7bba5a Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Tue, 12 Sep 2023 04:14:46 +0400 Subject: [PATCH 3/6] Fixed error message for inline value parsing Co-authored-by: Valber M. Silva de Souza --- src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs index 0dd35820b..b36718fbd 100644 --- a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs @@ -60,7 +60,7 @@ module SchemaDefinitions = Error [{ new IGQLError with member _.Message = $"Inline value '%s{value}' cannot be parsed into %s{destinationType} of range from {minValue} to {maxValue}" }] let getParseError destinationType value = - Error [{ new IGQLError with member _.Message = $"Inline value '%s{value}' cannot be paprsed into %s{destinationType}" }] + Error [{ new IGQLError with member _.Message = $"Inline value '%s{value}' cannot be parsed into %s{destinationType}" }] open System.Globalization From 0bf1f1ba1f71012ce8ee95d47c29c16bd21556d3 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Tue, 12 Sep 2023 04:37:21 +0400 Subject: [PATCH 4/6] Fixed tests after review, implemented `UseInvariantCultureAttribute` --- .../FSharp.Data.GraphQL.Tests.fsproj | 1 + .../TestAttributes.fs | 22 +++++++++++++++++++ .../Variables and Inputs/CoercionTests.fs | 16 +++++++------- 3 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 tests/FSharp.Data.GraphQL.Tests/TestAttributes.fs diff --git a/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj b/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj index 8581cc6f2..2bf279a36 100644 --- a/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj +++ b/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj @@ -24,6 +24,7 @@ + diff --git a/tests/FSharp.Data.GraphQL.Tests/TestAttributes.fs b/tests/FSharp.Data.GraphQL.Tests/TestAttributes.fs new file mode 100644 index 000000000..4d3cdc491 --- /dev/null +++ b/tests/FSharp.Data.GraphQL.Tests/TestAttributes.fs @@ -0,0 +1,22 @@ +namespace FSharp.Data.GraphQL.Tests + +open System +open System.Globalization +open Xunit.Sdk + +type UseInvariantCultureAttribute() = + inherit BeforeAfterTestAttribute() + + let mutable _originalUICulture: CultureInfo = null + let mutable _originalCulture: CultureInfo = null + + override _.Before (methodUnderTest) = + _originalUICulture <- CultureInfo.CurrentUICulture + _originalCulture <- CultureInfo.CurrentCulture + + CultureInfo.CurrentUICulture <- CultureInfo.InvariantCulture + CultureInfo.CurrentCulture <- CultureInfo.InvariantCulture + + override _.After (methodUnderTest) = + CultureInfo.CurrentUICulture <- _originalUICulture + CultureInfo.CurrentCulture <- _originalCulture diff --git a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs index fcd69a94b..c1282e101 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Variables and Inputs/CoercionTests.fs @@ -2,6 +2,7 @@ /// Copyright (c) 2015-Mar 2016 Kevin Thompson @kthompson // Copyright (c) 2016 Bazinga Technologies Inc +[] module FSharp.Data.GraphQL.Tests.CoercionTests #nowarn "25" @@ -20,20 +21,19 @@ let private testCoercion graphQLType (expected: 't) actual = | Ok x -> equals expected x | Error _ -> Assert.Fail $"Expected %A{actual} to be able to be coerced to %A{expected}" -let private testCoercionError graphQLType (expectedErrorMeesage: string) actual = +let private testCoercionError graphQLType (expectedErrorMessage: string) actual = let (Scalar scalar) = graphQLType let result = (scalar.CoerceInput actual) |> Result.map (fun x -> downcast x) match result with | Ok _ -> Assert.Fail($"Expected %A{actual} to not be able to be coerced to %A{graphQLType.Type.Name}") - | Error errs -> equals expectedErrorMeesage errs.Head.Message - + | Error errs -> equals expectedErrorMessage errs.Head.Message [] let ``Int coerces input`` () = testCoercion IntType 123 (Variable (JsonDocument.Parse "123").RootElement) testCoercion IntType 123 (InlineConstant (IntValue 123L)) testCoercionError IntType "JSON value '123.4' of kind 'Number' cannot be deserialized into integer of range from -2147483648 to 2147483647" (Variable (JsonDocument.Parse "123.4").RootElement) - testCoercionError IntType "Inline value '123,4' of type float cannot be converted into integer of range from -2147483648 to 2147483647" (InlineConstant (FloatValue 123.4)) + testCoercionError IntType "Inline value '123.4' of type float cannot be converted into integer of range from -2147483648 to 2147483647" (InlineConstant (FloatValue 123.4)) testCoercion IntType 1 (Variable (JsonDocument.Parse "true").RootElement) testCoercion IntType 1 (InlineConstant (BooleanValue true)) testCoercion IntType 0 (Variable (JsonDocument.Parse "false").RootElement) @@ -46,7 +46,7 @@ let ``Long coerces input`` () = testCoercion LongType 123L (Variable (JsonDocument.Parse "123").RootElement) testCoercion LongType 123L (InlineConstant (IntValue 123L)) testCoercionError LongType "JSON value '123.4' of kind 'Number' cannot be deserialized into integer of range from -9223372036854775808 to 9223372036854775807" (Variable (JsonDocument.Parse "123.4").RootElement) - testCoercionError LongType "Inline value '123,4' of type float cannot be converted into integer of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) + testCoercionError LongType "Inline value '123.4' of type float cannot be converted into integer of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) testCoercion LongType 1L (Variable (JsonDocument.Parse "true").RootElement) testCoercion LongType 1L (InlineConstant (BooleanValue true)) testCoercion LongType 0L (Variable (JsonDocument.Parse "false").RootElement) @@ -64,8 +64,8 @@ let ``Float coerces input`` () = testCoercion FloatType 1. (InlineConstant (BooleanValue true)) testCoercion FloatType 0. (Variable (JsonDocument.Parse "false").RootElement) testCoercion FloatType 0. (InlineConstant (BooleanValue false)) - testCoercionError FloatType "JSON value 'enum' of kind 'String' cannot be deserialized into float of range from -1,7976931348623157E+308 to 1,7976931348623157E+308" (Variable (JsonDocument.Parse "\"enum\"").RootElement) - testCoercionError FloatType "Inline value 'enum' of type enum cannot be converted into float of range from -1,7976931348623157E+308 to 1,7976931348623157E+308" (InlineConstant (EnumValue "enum")) + testCoercionError FloatType "JSON value 'enum' of kind 'String' cannot be deserialized into float of range from -1.7976931348623157E+308 to 1.7976931348623157E+308" (Variable (JsonDocument.Parse "\"enum\"").RootElement) + testCoercionError FloatType "Inline value 'enum' of type enum cannot be converted into float of range from -1.7976931348623157E+308 to 1.7976931348623157E+308" (InlineConstant (EnumValue "enum")) [] let ``Boolean coerces input`` () = @@ -102,7 +102,7 @@ let ``ID coerces input`` () = testCoercion IDType "123" (Variable (JsonDocument.Parse "123").RootElement) testCoercion IDType "123" (InlineConstant (IntValue 123L)) testCoercionError IDType "JSON value '123.4' of kind 'Number' cannot be deserialized into identifier of range from -9223372036854775808 to 9223372036854775807" (Variable (JsonDocument.Parse "123.4").RootElement) - testCoercionError IDType "Inline value '123,4' of type float cannot be converted into identifier of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) + testCoercionError IDType "Inline value '123.4' of type float cannot be converted into identifier of range from -9223372036854775808 to 9223372036854775807" (InlineConstant (FloatValue 123.4)) testCoercion IDType "abc123.4" (Variable (JsonDocument.Parse "\"abc123.4\"").RootElement) testCoercion IDType "acb123.4" (InlineConstant (StringValue "acb123.4")) testCoercionError IDType "JSON value 'true' of kind 'True' cannot be deserialized into identifier" (Variable (JsonDocument.Parse "true").RootElement) From 1971a064917f3b163fdec647422866852d514040 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Tue, 12 Sep 2023 04:05:41 +0400 Subject: [PATCH 5/6] Renamed `ValueObjectScalar'2` to `Scalar'2` --- .../SchemaDefinitions.fs | 8 +-- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 53 ++----------------- 2 files changed, 8 insertions(+), 53 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs index b36718fbd..c960ad603 100644 --- a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs @@ -593,7 +593,7 @@ module SchemaDefinitions = /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string>, - coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description CoerceInput = coerceInput >> Result.mapError (fun msg -> { new IGQLError with member _.Message = msg } |> List.singleton) @@ -607,7 +607,7 @@ module SchemaDefinitions = /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string list>, - coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description CoerceInput = coerceInput >> Result.mapError (List.map (fun msg -> { new IGQLError with member _.Message = msg })) @@ -621,7 +621,7 @@ module SchemaDefinitions = /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError>, - coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description CoerceInput = coerceInput >> Result.mapError List.singleton @@ -635,7 +635,7 @@ module SchemaDefinitions = /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError list>, - coerceOutput : obj -> 'Primitive option, ?description : string) : ValueObjectScalarDefinition<'Primitive, 'Wrapper> = + coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description CoerceInput = coerceInput diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index dce53bf9d..21373f7a4 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -1001,55 +1001,8 @@ and ScalarDef = inherit LeafDef end -/// Concrete representation of the scalar types. -and [] ScalarDefinition<'Val> = - { /// Name of the scalar type. - Name : string - /// Optional type description. - Description : string option - /// A function used to retrieve a .NET object from provided GraphQL query or JsonElement variable. - CoerceInput : InputParameterValue -> Result<'Val, IGQLError list> - /// A function used to set a surrogate representation to be - /// returned as a query result. - CoerceOutput : obj -> 'Val option } - - interface TypeDef with - member _.Type = typeof<'Val> - - member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } - upcast nullable - - member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } - upcast list - - interface InputDef - interface OutputDef - - interface ScalarDef with - member x.Name = x.Name - member x.Description = x.Description - member x.CoerceInput input = x.CoerceInput input |> Result.map box - member x.CoerceOutput value = (x.CoerceOutput value) |> Option.map box - - interface InputDef<'Val> - interface OutputDef<'Val> - interface LeafDef - - interface NamedDef with - member x.Name = x.Name - - override x.Equals y = - match y with - | :? ScalarDefinition<'Val> as s -> x.Name = s.Name - | _ -> false - - override x.GetHashCode() = x.Name.GetHashCode() - override x.ToString() = x.Name + "!" - /// Concrete representation of the scalar types wrapped into a value object. -and [] ValueObjectScalarDefinition<'Primitive, 'Val> = +and [] ScalarDefinition<'Primitive, 'Val> = { /// Name of the scalar type. Name : string /// Optional type description. @@ -1089,12 +1042,14 @@ and [] ValueObjectScalarDefinition<'Primitive, 'Va override x.Equals y = match y with - | :? ValueObjectScalarDefinition<'Primitive, 'Val> as s -> x.Name = s.Name + | :? ScalarDefinition<'Primitive, 'Val> as s -> x.Name = s.Name | _ -> false override x.GetHashCode() = x.Name.GetHashCode() override x.ToString() = x.Name + "!" +and ScalarDefinition<'Val> = ScalarDefinition<'Val, 'Val> + /// A GraphQL representation of single case of the enum type. /// Enum value return value is always represented as string. and EnumVal = From 4940832d7504bcdcdbf2a56613523380adfa32a8 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Tue, 12 Sep 2023 04:11:41 +0400 Subject: [PATCH 6/6] Renamed `Define.ValueObjectScalar` to `Define.WrappedScalar` --- src/FSharp.Data.GraphQL.Server/Values.fs | 1 + .../SchemaDefinitions.fs | 24 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Server/Values.fs b/src/FSharp.Data.GraphQL.Server/Values.fs index d932da5ed..bd126b569 100644 --- a/src/FSharp.Data.GraphQL.Server/Values.fs +++ b/src/FSharp.Data.GraphQL.Server/Values.fs @@ -13,6 +13,7 @@ open System.Reflection open System.Text.Json open FsToolkit.ErrorHandling +open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Errors open FSharp.Data.GraphQL.Types diff --git a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs index c960ad603..9b30a9850 100644 --- a/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs @@ -530,7 +530,7 @@ module SchemaDefinitions = type Define = /// - /// Creates GraphQL type definition for user defined scalars. + /// Creates GraphQL type definition for user defined scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. @@ -544,7 +544,7 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined scalars. + /// Creates GraphQL type definition for user defined scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. @@ -558,7 +558,7 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined scalars. + /// Creates GraphQL type definition for user defined scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. @@ -572,7 +572,7 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined scalars. + /// Creates GraphQL type definition for user defined scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. @@ -586,13 +586,13 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined value object scalars. + /// Creates GraphQL type definition for user defined wrapped scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. - static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string>, + static member WrappedScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string>, coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description @@ -600,13 +600,13 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined value object scalars. + /// Creates GraphQL type definition for user defined wrapped scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. - static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string list>, + static member WrappedScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, string list>, coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description @@ -614,13 +614,13 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined value object scalars. + /// Creates GraphQL type definition for user defined wrapped scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. - static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError>, + static member WrappedScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError>, coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description @@ -628,13 +628,13 @@ module SchemaDefinitions = CoerceOutput = coerceOutput } /// - /// Creates GraphQL type definition for user defined value object scalars. + /// Creates GraphQL type definition for user defined wrapped scalar. /// /// Type name. Must be unique in scope of the current schema. /// Function used to resolve .NET object from GraphQL query AST or variable. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. - static member ValueObjectScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError list>, + static member WrappedScalar(name : string, coerceInput : InputParameterValue -> Result<'Wrapper, IGQLError list>, coerceOutput : obj -> 'Primitive option, ?description : string) : ScalarDefinition<'Primitive, 'Wrapper> = { Name = name Description = description