Skip to content

Commit e882f8f

Browse files
committed
Fixed comments and added tests
1 parent 4cb6407 commit e882f8f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+484
-372
lines changed

src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLRequestHandler.fs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,35 @@ open FsToolkit.ErrorHandling
1515
open FSharp.Data.GraphQL.Server
1616
open FSharp.Data.GraphQL.Shared
1717

18+
/// <summary>
19+
/// Handles GraphQL requests using a provided root schema.
20+
/// </summary>
21+
/// <param name="httpContextAccessor">The accessor to the current HTTP context.</param>
22+
/// <param name="options">The options monitor for GraphQL options.</param>
23+
/// <param name="logger">The logger to log messages.</param>
1824
type DefaultGraphQLRequestHandler<'Root>
1925
(
20-
// The accessor to the current HTTP context
2126
httpContextAccessor : IHttpContextAccessor,
22-
// The options monitor for GraphQL options
2327
options : IOptionsMonitor<GraphQLOptions<'Root>>,
24-
// The logger to log messages
2528
logger : ILogger<DefaultGraphQLRequestHandler<'Root>>
2629
) =
2730
inherit GraphQLRequestHandler<'Root> (httpContextAccessor, options, logger)
2831

29-
/// Provides logic to parse and execute GraphQL request
32+
/// <summary>
33+
/// Provides logic to parse and execute GraphQL requests.
34+
/// </summary>
35+
/// <param name="httpContextAccessor">The accessor to the current HTTP context.</param>
36+
/// <param name="options">The options monitor for GraphQL options.</param>
37+
/// <param name="logger">The logger to log messages.</param>
3038
and [<AbstractClass>] GraphQLRequestHandler<'Root>
3139
(
32-
// The accessor to the current HTTP context
3340
httpContextAccessor : IHttpContextAccessor,
34-
// The options monitor for GraphQL options
3541
options : IOptionsMonitor<GraphQLOptions<'Root>>,
36-
// The logger to log messages
3742
logger : ILogger
3843
) =
3944

4045
let ctx = httpContextAccessor.HttpContext
41-
let inputContext = fun () -> (HttpContextRequestExecutionContext ctx) :> IInputExecutionContext
46+
let getInputContext = fun () -> (HttpContextRequestExecutionContext ctx) :> IInputExecutionContext
4247

4348
let toResponse { DocumentId = documentId; Content = content; Metadata = metadata } =
4449

@@ -143,8 +148,8 @@ and [<AbstractClass>] GraphQLRequestHandler<'Root>
143148
let executeIntrospectionQuery (executor : Executor<_>) (ast : Ast.Document voption) : Task<IResult> = task {
144149
let! result =
145150
match ast with
146-
| ValueNone -> executor.AsyncExecute (IntrospectionQuery.Definition, inputContext)
147-
| ValueSome ast -> executor.AsyncExecute (ast, inputContext)
151+
| ValueNone -> executor.AsyncExecute (IntrospectionQuery.Definition, getInputContext)
152+
| ValueSome ast -> executor.AsyncExecute (ast, getInputContext)
148153

149154
let response = result |> toResponse
150155
return (TypedResults.Ok response) :> IResult
@@ -229,7 +234,7 @@ and [<AbstractClass>] GraphQLRequestHandler<'Root>
229234

230235
let! result =
231236
Async.StartImmediateAsTask (
232-
executor.AsyncExecute (content.Ast, inputContext, root, ?variables = variables, ?operationName = operationName),
237+
executor.AsyncExecute (content.Ast, getInputContext, root, ?variables = variables, ?operationName = operationName),
233238
cancellationToken = ctx.RequestAborted
234239
)
235240

src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLWebsocketMiddleware.fs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ open System.Text.Json
1010
open System.Text.Json.Serialization
1111
open System.Threading
1212
open System.Threading.Tasks
13-
open FSharp.Data.GraphQL.Shared
1413
open Microsoft.AspNetCore.Http
1514
open Microsoft.Extensions.Hosting
1615
open Microsoft.Extensions.Logging
@@ -169,24 +168,24 @@ type GraphQLWebSocketMiddleware<'Root>
169168

170169
let sendMsg = sendMessageViaSocket serializerOptions socket
171170
let rcv () = socket |> rcvMsgViaSocket serializerOptions
172-
let inputContext = fun () -> (HttpContextRequestExecutionContext httpContext) :> IInputExecutionContext
171+
let getInputContext = fun () -> (HttpContextRequestExecutionContext httpContext) :> IInputExecutionContext
173172

174173
let sendOutput id (output : SubscriptionExecutionResult) =
175174
sendMsg (Next (id, output))
176175

177176
let sendSubscriptionResponseOutput id subscriptionResult =
178177
match subscriptionResult with
179178
| SubscriptionResult output -> { Data = ValueSome output; Errors = [] } |> sendOutput id
180-
| SubscriptionErrors (_, errors) ->
179+
| SubscriptionErrors (output, errors) ->
181180
logger.LogWarning ("Subscription errors: {subscriptionErrors}", (String.Join ('\n', errors |> Seq.map (fun x -> $"- %s{x.Message}"))))
182181
{ Data = ValueNone; Errors = errors } |> sendOutput id
183182

184183
let sendDeferredResponseOutput id deferredResult =
185184
match deferredResult with
186-
| DeferredResult (obj, _) ->
185+
| DeferredResult (obj, path) ->
187186
let output = obj :?> Dictionary<string, obj>
188187
{ Data = ValueSome output; Errors = [] } |> sendOutput id
189-
| DeferredErrors (_, errors, _) ->
188+
| DeferredErrors (obj, errors, _) ->
190189
logger.LogWarning (
191190
"Deferred response errors: {deferredErrors}",
192191
(String.Join ('\n', errors |> Seq.map (fun x -> $"- %s{x.Message}")))
@@ -276,11 +275,11 @@ type GraphQLWebSocketMiddleware<'Root>
276275
let variables = query.Variables |> Skippable.toOption
277276
let! planExecutionResult =
278277
let root = options.RootFactory httpContext
279-
options.SchemaExecutor.AsyncExecute (query.Query, inputContext, root, ?variables = variables)
278+
options.SchemaExecutor.AsyncExecute (query.Query, getInputContext, root, ?variables = variables)
280279
do! planExecutionResult |> applyPlanExecutionResult id socket
281280
with ex ->
282281
logger.LogError (ex, "Unexpected error during subscription with id '{id}'", id)
283-
do! sendMsg (Error (id, [Shared.NameValueLookup([ ("subscription", "Unexpected error during subscription" :> obj) ])]))
282+
do! sendMsg (Error (id, [NameValueLookup([ ("subscription", "Unexpected error during subscription" :> obj) ])]))
284283
| ClientComplete id ->
285284
"ClientComplete" |> logMsgWithIdReceived id
286285
subscriptions

src/FSharp.Data.GraphQL.Server.AspNetCore/Helpers.fs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ namespace FSharp.Data.GraphQL.Server.AspNetCore
33
open System
44
open System.Text
55
open FSharp.Data.GraphQL
6-
open FSharp.Data.GraphQL.Shared
76
open Microsoft.AspNetCore.Http
87

9-
108
[<AutoOpen>]
119
module Helpers =
1210

@@ -59,10 +57,7 @@ type HttpContextRequestExecutionContext (httpContext : HttpContext) =
5957
Error "Request does not have form content type"
6058
else
6159
let form = httpContext.Request.Form
62-
let maybeFile =
63-
form.Files
64-
|> Seq.tryFind (fun f -> f.Name = key)
6560

66-
match maybeFile with
67-
| Some file -> Ok (file.OpenReadStream())
68-
| None -> Error $"File with key '{key}' not found"
61+
match (form.Files |> Seq.vtryFind (fun f -> f.Name = key)) with
62+
| ValueSome file -> Ok (file.OpenReadStream())
63+
| ValueNone -> Error $"File with key '{key}' not found"

src/FSharp.Data.GraphQL.Server.AspNetCore/HttpContext.fs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ open System.Collections.Immutable
66
open System.IO
77
open System.Runtime.CompilerServices
88
open System.Text.Json
9-
open FSharp.Data.GraphQL
109
open Microsoft.AspNetCore.Http
1110
open Microsoft.Extensions.DependencyInjection
1211
open Microsoft.Extensions.Options
@@ -22,7 +21,7 @@ type HttpContext with
2221
/// </summary>
2322
/// <typeparam name="'T">Type to deserialize to</typeparam>
2423
/// <returns>
25-
/// Retruns a <see cref="System.Threading.Tasks.Task{T}"/>Deserialized object or
24+
/// Returns a <see cref="System.Threading.Tasks.Task{T}"/>Deserialized object or
2625
/// <see cref="ProblemDetails">ProblemDetails</see> as <see cref="IResult">IResult</see>
2726
/// if a body could not be deserialized.
2827
/// </returns>
@@ -32,10 +31,23 @@ type HttpContext with
3231
let request = ctx.Request
3332

3433
try
35-
if not request.Body.CanSeek then
36-
request.EnableBuffering()
34+
let! jsonStream =
35+
task {
36+
if request.HasFormContentType then
37+
let! form = request.ReadFormAsync(ctx.RequestAborted)
38+
match form.TryGetValue("operations") with
39+
| true, values when values.Count > 0 ->
40+
let bytes = System.Text.Encoding.UTF8.GetBytes(values[0])
41+
return new MemoryStream(bytes) :> Stream
42+
| _ ->
43+
return request.Body
44+
else
45+
if not request.Body.CanSeek then
46+
request.EnableBuffering()
47+
return request.Body
48+
}
3749

38-
return! JsonSerializer.DeserializeAsync<'T>(request.Body, serializerOptions, ctx.RequestAborted)
50+
return! JsonSerializer.DeserializeAsync<'T>(jsonStream, serializerOptions, ctx.RequestAborted)
3951
with :? JsonException ->
4052
let body = request.Body
4153
body.Seek(0, SeekOrigin.Begin) |> ignore

src/FSharp.Data.GraphQL.Server/Execution.fs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,11 @@ let private (|String|Other|) (o : obj) =
445445
| :? string as s -> String s
446446
| _ -> Other
447447

448-
let private executeQueryOrMutation (resultSet: (string * ExecutionInfo) []) (ctx: ExecutionContext) (objDef: ObjectDef) (inputContext : InputExecutionContextProvider) (rootValue : obj) : AsyncVal<GQLExecutionResult> =
448+
let private executeQueryOrMutation (resultSet: (string * ExecutionInfo) []) (ctx: ExecutionContext) (objDef: ObjectDef) (rootValue : obj) : AsyncVal<GQLExecutionResult> =
449449
let executeRootOperation (name, info) =
450450
let fDef = info.Definition
451451
let argDefs = ctx.FieldExecuteMap.GetArgs(ctx.ExecutionPlan.RootDef.Name, info.Definition.Name)
452-
match getArgumentValues argDefs info.Ast.Arguments inputContext ctx.Variables with
452+
match getArgumentValues argDefs info.Ast.Arguments ctx.GetInputContext ctx.Variables with
453453
| Error errs -> asyncVal { return Error (errs |> List.map GQLProblemDetails.OfError) }
454454
| Ok args ->
455455
let path = [ box info.Identifier ]
@@ -465,7 +465,7 @@ let private executeQueryOrMutation (resultSet: (string * ExecutionInfo) []) (ctx
465465
let execute = ctx.FieldExecuteMap.GetExecute(ctx.ExecutionPlan.RootDef.Name, info.Definition.Name)
466466
asyncVal {
467467
let! result =
468-
executeResolvers inputContext fieldCtx path rootValue (resolveField execute fieldCtx rootValue)
468+
executeResolvers ctx.GetInputContext fieldCtx path rootValue (resolveField execute fieldCtx rootValue)
469469
|> AsyncVal.rescue path ctx.Schema.ParseError
470470
let result =
471471
match result with
@@ -545,7 +545,7 @@ let private compileObject (objDef: ObjectDef) (executeFields: FieldDef -> unit)
545545
)
546546
)
547547

548-
let internal compileSchema (inputContext : InputExecutionContextProvider) (ctx : SchemaCompileContext) =
548+
let internal compileSchema (ctx : SchemaCompileContext) =
549549
ctx.Schema.TypeMap.ToSeq()
550550
|> Seq.iter (fun (tName, x) ->
551551
match x with
@@ -555,10 +555,10 @@ let internal compileSchema (inputContext : InputExecutionContextProvider) (ctx :
555555
match sub with
556556
| :? SubscriptionFieldDef as subField -> compileSubscriptionField subField
557557
| _ -> failwith $"Schema error: subscription object '%s{subDef.Name}' does have a field '%s{sub.Name}' that is not a subscription field definition."
558-
ctx.Schema.SubscriptionProvider.Register { Name = sub.Name; Filter = filter }) inputContext
558+
ctx.Schema.SubscriptionProvider.Register { Name = sub.Name; Filter = filter }) ctx.GetInputContext
559559
| Object objDef ->
560-
compileObject objDef (fun fieldDef -> ctx.FieldExecuteMap.SetExecute(tName, fieldDef)) inputContext
561-
| InputObject inputDef -> compileInputObject inputDef inputContext
560+
compileObject objDef (fun fieldDef -> ctx.FieldExecuteMap.SetExecute(tName, fieldDef)) ctx.GetInputContext
561+
| InputObject inputDef -> compileInputObject inputDef ctx.GetInputContext
562562
| _ -> ())
563563

564564
let internal coerceVariables (variables: VarDef list) (inputContext : InputExecutionContextProvider) (vars: ImmutableDictionary<string, JsonElement>) = result {
@@ -650,7 +650,7 @@ let internal coerceVariables (variables: VarDef list) (inputContext : InputExecu
650650

651651
#nowarn "0046"
652652

653-
let internal executeOperation (inputContext : InputExecutionContextProvider) (ctx : ExecutionContext) : AsyncVal<GQLExecutionResult> =
653+
let internal executeOperation (ctx : ExecutionContext) : AsyncVal<GQLExecutionResult> =
654654
let includeResults =
655655
ctx.ExecutionPlan.Fields
656656
|> List.map (
@@ -669,15 +669,15 @@ let internal executeOperation (inputContext : InputExecutionContextProvider) (ct
669669
|> Seq.map (fun info -> (info.Identifier, info))
670670
|> Seq.toArray
671671
match ctx.ExecutionPlan.Operation.OperationType with
672-
| Query -> executeQueryOrMutation resultSet ctx ctx.Schema.Query inputContext ctx.RootValue
672+
| Query -> executeQueryOrMutation resultSet ctx ctx.Schema.Query ctx.RootValue
673673
| Mutation ->
674674
match ctx.Schema.Mutation with
675-
| Some m -> executeQueryOrMutation resultSet ctx m inputContext ctx.RootValue
675+
| Some m -> executeQueryOrMutation resultSet ctx m ctx.RootValue
676676
| None -> raise(InvalidOperationException("Attempted to make a mutation but no mutation schema was present!"))
677677
| Subscription ->
678678
match ctx.Schema.Subscription with
679679
| Some s ->
680-
match executeSubscription resultSet inputContext ctx s ctx.RootValue with
680+
match executeSubscription resultSet ctx.GetInputContext ctx s ctx.RootValue with
681681
| Ok data -> AsyncVal.wrap(GQLExecutionResult.Stream(ctx.ExecutionPlan.DocumentId, data, ctx.Metadata))
682682
| Error errs -> asyncVal { return GQLExecutionResult.Error(ctx.ExecutionPlan.DocumentId, errs, ctx.Metadata) }
683683

0 commit comments

Comments
 (0)