-
Notifications
You must be signed in to change notification settings - Fork 76
Directive chooser middleware #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 2 commits
8d0b5ae
d509eac
5f0a9e4
249a3c7
4ada7b8
945e139
199fb6f
3dc8498
114a034
b57e020
1db75da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ open FSharp.Data.GraphQL.Execution | |
open System.IO | ||
open FSharp.Data.GraphQL | ||
open FSharp.Data.GraphQL.Types | ||
open FSharp.Data.GraphQL.Server.Middlewares | ||
|
||
type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult | ||
|
||
|
@@ -59,18 +60,24 @@ module HttpHandlers = | |
let body = readStream ctx.Request.Body | ||
let query = body |> tryParse "query" | ||
let variables = body |> tryParse "variables" |> mapString | ||
let buildMetadata fallbackDirectives = | ||
let chooser = | ||
[ DirectiveChooser.fallbackDefer; DirectiveChooser.fallbackStream; DirectiveChooser.fallbackLive ] | ||
|> DirectiveChooser.fromSeq | ||
|> DirectiveChooser.merge (DirectiveChooser.fallbackWhen (fun _ -> fallbackDirectives)) | ||
Metadata.WithDirectiveChooser(chooser) | ||
match query, variables with | ||
| Some query, Some variables -> | ||
printfn "Received query: %s" query | ||
printfn "Received variables: %A" variables | ||
let query = query |> removeSpacesAndNewLines | ||
let result = Schema.executor.AsyncExecute(query, variables = variables, data = Schema.root) |> Async.RunSynchronously | ||
let result = Schema.executor.AsyncExecute(query, variables = variables, data = Schema.root, meta = buildMetadata true) |> Async.RunSynchronously | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can define our choosers on a per request basis, as they are sent into the |
||
printfn "Result metadata: %A" result.Metadata | ||
return! okWithStr (json result) next ctx | ||
| Some query, None -> | ||
printfn "Received query: %s" query | ||
let query = query |> removeSpacesAndNewLines | ||
let result = Schema.executor.AsyncExecute(query) |> Async.RunSynchronously | ||
let result = Schema.executor.AsyncExecute(query, meta = buildMetadata true) |> Async.RunSynchronously | ||
printfn "Result metadata: %A" result.Metadata | ||
return! okWithStr (json result) next ctx | ||
| None, _ -> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,68 @@ | ||
namespace FSharp.Data.GraphQL.Server.Middlewares | ||
|
||
open FSharp.Data.GraphQL.Types | ||
open FSharp.Data.GraphQL.Ast | ||
|
||
/// A function that checks if a directive should be used in the exection of a query, or changed to a new directive. | ||
type DirectiveChooser = Directive -> Directive option | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function will be used to choose Directives. We can opt to transform them into a new Directive, or remove them from the operation. For example, if we remove a |
||
|
||
/// Basic operations on DirectiveChoosers. | ||
module DirectiveChooser = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let apply (directive : Directive) (chooser : DirectiveChooser) = | ||
chooser directive | ||
|
||
let keep : DirectiveChooser = | ||
let chooser = fun directive -> Some directive | ||
chooser | ||
|
||
let fallback : DirectiveChooser = | ||
let chooser = fun _ -> None | ||
chooser | ||
|
||
let acceptWhen (condition : Directive -> bool) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
if condition directive | ||
then keep directive | ||
else fallback directive | ||
chooser | ||
|
||
let fallbackWhen (condition : Directive -> bool) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
if condition directive | ||
then fallback directive | ||
else keep directive | ||
chooser | ||
|
||
let fallbackByName name = fallbackWhen (fun d -> d.Name = name) | ||
|
||
let fallbackDefer = fallbackByName "defer" | ||
|
||
let fallbackStream = fallbackByName "stream" | ||
|
||
let fallbackLive = fallbackByName "live" | ||
|
||
let compose (other : DirectiveChooser) (actual : DirectiveChooser) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match actual directive with | ||
| Some d -> other d | ||
| None -> None | ||
chooser | ||
|
||
let merge (other : DirectiveChooser) (actual : DirectiveChooser) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match other directive, actual directive with | ||
| d1, d2 when d1 = d2 -> d1 | ||
| Some d1, Some d2 when d1 <> d2 -> failwith "Can not merge DirectiveChoosers because they don't return the same directive." | ||
| _ -> None | ||
chooser | ||
|
||
let fromSeq (choosers : DirectiveChooser seq) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match Seq.length choosers with | ||
| 0 -> keep directive | ||
| _ -> choosers |> Seq.reduce (fun fst snd -> compose fst snd) |> apply directive | ||
chooser | ||
|
||
/// Contains extensions for the type system. | ||
[<AutoOpen>] | ||
module TypeSystemExtensions = | ||
|
@@ -23,4 +84,21 @@ module TypeSystemExtensions = | |
member this.Filter = | ||
match this.Args.TryFind("filter") with | ||
| Some (:? ObjectListFilter as f) -> Some f | ||
| _ -> None | ||
| _ -> None | ||
|
||
type Metadata with | ||
/// <summary> | ||
/// Creates a new instance of the current Metadata, adding a directive chooser function to it. | ||
/// Directive chooser will be used by a DirectiveFallbackMiddleware if configured in the Executor. | ||
/// </summary> | ||
/// <param name="chooser">The directive chooser to be added in the Metadata object.</param> | ||
member this.WithDirectiveChooser(chooser : DirectiveChooser) = | ||
this.Add("directiveChooser", chooser) | ||
|
||
/// <summary> | ||
/// Creates a new instance of Metadata, adding a directive chooser function to it. | ||
/// Directive chooser will be used by a DirectiveFallbackMiddleware if configured in the Executor. | ||
/// </summary> | ||
/// <param name="chooser">The directive chooser to be added in the Metadata object.</param> | ||
static member WithDirectiveChooser(chooser : DirectiveChooser) = | ||
Metadata.Empty.WithDirectiveChooser(chooser) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,7 +126,7 @@ let private directiveIncluder (directive: Directive) : Includer = | |
| None -> raise (GraphQLException (sprintf "Expected 'if' argument of directive '@%s' to have boolean value but got %A" directive.Name other)) | ||
|
||
let private incl: Includer = fun _ -> true | ||
let private excl: Includer = fun _ -> false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just removed this function as it is not referenced anywhere in the code. |
||
|
||
let private getIncluder (directives: Directive list) parentIncluder : Includer = | ||
directives | ||
|> List.fold (fun acc directive -> | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we can use
DirectiveChooser
module to quickly build and compose choosers. In this example, I'm fallbacking defer, stream and live directives whenfallbackDirectives
is true - in other words, they will be returned directly.