diff --git a/src/FSharp.Data.GraphQL.Server/Execution.fs b/src/FSharp.Data.GraphQL.Server/Execution.fs index 3b897a5f9..6f7532227 100644 --- a/src/FSharp.Data.GraphQL.Server/Execution.fs +++ b/src/FSharp.Data.GraphQL.Server/Execution.fs @@ -376,7 +376,6 @@ let rec private direct (returnDef : OutputDef) (ctx : ResolveFieldContext) (path match Map.tryFind resolvedDef.Name typeMap with | Some fields -> executeObjectFields fields name resolvedDef ctx path value | None -> KeyValuePair(name, null) |> ResolverResult.data |> AsyncVal.wrap - //| None -> raiseErrors <| interfaceImplError iDef.Name resolvedDef.Name path ctx | Union uDef -> let possibleTypesFn = ctx.Schema.GetPossibleTypes @@ -389,7 +388,6 @@ let rec private direct (returnDef : OutputDef) (ctx : ResolveFieldContext) (path match Map.tryFind resolvedDef.Name typeMap with | Some fields -> executeObjectFields fields name resolvedDef ctx path (uDef.ResolveValue value) | None -> KeyValuePair(name, null) |> ResolverResult.data |> AsyncVal.wrap - //| None -> raiseErrors <| unionImplError uDef.Name resolvedDef.Name path ctx | _ -> failwithf "Unexpected value of returnDef: %O" returnDef @@ -451,7 +449,7 @@ and private streamed (options : BufferedStreamOptions) (innerDef : OutputDef) (c |> Array.mapi resolveItem |> Observable.ofAsyncValSeq |> buffer - ResolverResult.defered (KeyValuePair (info.Identifier, null)) stream |> AsyncVal.wrap + ResolverResult.defered (KeyValuePair (info.Identifier, box [])) stream |> AsyncVal.wrap | _ -> raise <| GQLMessageException (ErrorMessages.expectedEnumerableValue ctx.ExecutionInfo.Identifier (value.GetType())) and private live (ctx : ResolveFieldContext) (path : FieldPath) (parent : obj) (value : obj) = @@ -511,18 +509,21 @@ and private executeResolvers (ctx : ResolveFieldContext) (path : FieldPath) (par | Ok None when ctx.ExecutionInfo.IsNullable -> return Ok (KeyValuePair(name, null), None, []) | Error errs -> return Error errs | Ok None -> return Error (nullResolverError name path ctx) - | Ok (Some v) -> return! onSuccess ctx path parent v + | Ok (Some v) -> + match! onSuccess ctx path parent v with + | Ok (kvp, _, _) when not ctx.ExecutionInfo.IsNullable && kvp.Value = null -> return Error (nullResolverError name path ctx) + | result -> return result } match info.Kind, returnDef with | ResolveDeferred innerInfo, _ when innerInfo.IsNullable -> // We can only defer nullable fields deferred - |> resolveWith { ctx with ExecutionInfo = { innerInfo with IsNullable = false } } + |> resolveWith { ctx with ExecutionInfo = innerInfo } | ResolveDeferred innerInfo, _ -> raiseErrors <| deferredNullableError (innerInfo.Identifier) (innerInfo.ReturnDef.ToString()) path ctx | ResolveStreamed (innerInfo, mode), HasList innerDef -> // We can only stream lists streamed mode innerDef - |> resolveWith { ctx with ExecutionInfo = innerInfo; } + |> resolveWith { ctx with ExecutionInfo = innerInfo } | ResolveStreamed (innerInfo, _), _ -> raiseErrors <| streamListError innerInfo.Identifier (returnDef.ToString()) path ctx | ResolveLive innerInfo, _ -> diff --git a/tests/FSharp.Data.GraphQL.Tests/AbstractionTests.fs b/tests/FSharp.Data.GraphQL.Tests/AbstractionTests.fs index bc99354bb..6db12f74e 100644 --- a/tests/FSharp.Data.GraphQL.Tests/AbstractionTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/AbstractionTests.fs @@ -116,7 +116,7 @@ let ``Execute handles execution of abstract types: isTypeOf is used to resolve r empty errors data |> equals (upcast expected) -[] +[] let ``Execute handles execution of abstract types: not specified Interface types produce error`` () = let query = """{ @@ -130,7 +130,7 @@ let ``Execute handles execution of abstract types: not specified Interface types let result = sync <| schemaWithInterface.Value.AsyncExecute (parse query) ensureRequestError result <| fun [ petsError ] -> - petsError |> ensureValidationError "Field 'pets' does not allow nulls and list values." [ "pets"; "0" ] + petsError |> ensureExecutionError "Non-Null field pets resolved as a null!" [ "pets"; 1 ] let query = """{ @@ -144,7 +144,7 @@ let ``Execute handles execution of abstract types: not specified Interface types let result = sync <| schemaWithInterface.Value.AsyncExecute (parse query) ensureRequestError result <| fun [ petsError ] -> - petsError |> ensureValidationError "Field 'pets' does not allow nulls and list values." [ "pets"; "0" ] + petsError |> ensureExecutionError "Non-Null field pets resolved as a null!" [ "pets"; 0 ] [] let ``Execute handles execution of abstract types: not specified Interface types must be filtered out if they allow null`` () = @@ -322,7 +322,7 @@ let ``Execute handles execution of abstract types: isTypeOf is used to resolve r empty errors data |> equals (upcast expected) -[] +[] let ``Execute handles execution of abstract types: not specified Union types produce error`` () = let query = """{ @@ -336,7 +336,7 @@ let ``Execute handles execution of abstract types: not specified Union types pro let result = sync <| schemaWithUnion.Value.AsyncExecute (parse query) ensureRequestError result <| fun [ petsError ] -> - petsError |> ensureValidationError "Field 'pets' does not allow nulls and list values." [ "pets"; "0" ] + petsError |> ensureExecutionError "Non-Null field pets resolved as a null!" [ "pets"; 1 ] let query = """{ @@ -350,7 +350,7 @@ let ``Execute handles execution of abstract types: not specified Union types pro let result = sync <| schemaWithUnion.Value.AsyncExecute (parse query) ensureRequestError result <| fun [ petsError ] -> - petsError |> ensureValidationError "Field 'pets' does not allow nulls and list values." [ "pets"; "0" ] + petsError |> ensureExecutionError "Non-Null field pets resolved as a null!" [ "pets"; 0 ] [] let ``Execute handles execution of abstract types: not specified Union types must be filtered out`` () =