Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/FSharp.Data.GraphQL.Server.Relay/Node.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>) ],
fields = [ Define.Field("id", IDType) ],
resolveType = resolveTypeFun possibleTypes)

3 changes: 2 additions & 1 deletion src/FSharp.Data.GraphQL.Server/Linq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ let private applyId: ArgApplication = fun expression callable ->
let p0 = Expression.Parameter tSource
let idProperty = memberExpr callable.Type "id" p0
// Func<tSource, bool> 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)

Expand Down
1 change: 1 addition & 0 deletions src/FSharp.Data.GraphQL.Server/Values.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
197 changes: 148 additions & 49 deletions src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions src/FSharp.Data.GraphQL.Shared/TypeSystem.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,17 +1001,17 @@ and ScalarDef =
inherit LeafDef
end

/// Concrete representation of the scalar types.
and [<CustomEquality; NoComparison>] ScalarDefinition<'Val> =
/// Concrete representation of the scalar types wrapped into a value object.
and [<CustomEquality; NoComparison>] ScalarDefinition<'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.
/// 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 }
CoerceOutput : obj -> 'Primitive option }

interface TypeDef with
member _.Type = typeof<'Val>
Expand Down Expand Up @@ -1042,12 +1042,14 @@ and [<CustomEquality; NoComparison>] ScalarDefinition<'Val> =

override x.Equals y =
match y with
| :? ScalarDefinition<'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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

<ItemGroup>
<Compile Include="..\..\samples\star-wars-api\JSON.fs" Link="JSON.fs" />
<Compile Include="TestAttributes.fs" />
<Compile Include="Helpers.fs" />
<Compile Include="Literals.fs" />
<Compile Include="PropertyTrackingTests.fs" />
Expand Down
4 changes: 2 additions & 2 deletions tests/FSharp.Data.GraphQL.Tests/LinqTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ let Contact = Define.Object("Contact", [ Define.Field("email", StringType, fun _

let Person =
Define.Object<Person>("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)
Expand Down Expand Up @@ -60,7 +60,7 @@ let resolveRoot ctx () =
result

let linqArgs =
[ Define.Input("id", Nullable IDType<int>)
[ Define.Input("id", Nullable IDType)
Define.Input("skip", Nullable IntType)
Define.Input("take", Nullable IntType)
Define.Input("orderBy", Nullable StringType)
Expand Down
22 changes: 22 additions & 0 deletions tests/FSharp.Data.GraphQL.Tests/TestAttributes.fs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
/// Copyright (c) 2015-Mar 2016 Kevin Thompson @kthompson
// Copyright (c) 2016 Bazinga Technologies Inc

[<UseInvariantCulture>]
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
Expand All @@ -17,45 +19,96 @@ 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 _ -> Assert.Fail $"Expected %A{actual} to be able to be coerced to %A{expected}"
Comment thread
xperiandri marked this conversation as resolved.

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 expectedErrorMessage errs.Head.Message

[<Fact>]
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"))

[<Fact>]
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"))

[<Fact>]
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))

[<Fact>]
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"))

[<Fact>]
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"))

[<Fact>]
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"))

[<Fact>]
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"))