-
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 6 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.composeSeq | ||
|> 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 |
---|---|---|
@@ -0,0 +1,105 @@ | ||
namespace FSharp.Data.GraphQL.Server.Middlewares | ||
|
||
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 | ||
|
||
/// Basic operations on DirectiveChoosers. | ||
[<RequireQualifiedAccess>] | ||
module DirectiveChooser = | ||
/// Apply a chooser to a directive. | ||
let apply (directive : Directive) (chooser : DirectiveChooser) = | ||
chooser directive | ||
|
||
/// Builds a chooser that, given a Directive x, returns Some x. | ||
let keep : DirectiveChooser = | ||
let chooser = fun directive -> Some directive | ||
chooser | ||
|
||
/// Builds a chooser that, for any Directive, returns None. | ||
let fallback : DirectiveChooser = | ||
let chooser = fun _ -> None | ||
chooser | ||
|
||
/// Builds a chooser that, when run, runs actual chooser, and if it returns Some directive x, maps | ||
/// x directive using mapper function to y directive, and return Some y. Otherwise, returns None. | ||
let map (mapper : Directive -> Directive) (actual : DirectiveChooser) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match actual directive with | ||
| Some d -> mapper d |> keep | ||
| None -> None | ||
chooser | ||
|
||
/// Builds a chooser that, given a Directive x, apply the condition filter function to x, | ||
/// and if it returns true, returns Some x. Otherwise, returns None. | ||
let keepWhen (condition : Directive -> bool) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
if condition directive | ||
then keep directive | ||
else fallback directive | ||
chooser | ||
|
||
/// Builds a chooser that, given a Directive x, apply the condition filter function to x, | ||
/// and if it returns true, returns None. Otherwise, returns Some x. | ||
let fallbackWhen (condition : Directive -> bool) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
if condition directive | ||
then fallback directive | ||
else keep directive | ||
chooser | ||
|
||
/// Builds a chooser that, given a Directive x, if x.Name equals given name, returns None. | ||
/// Otherwise, returns Some x. | ||
let fallbackByName name = fallbackWhen (fun d -> d.Name = name) | ||
|
||
/// Builds a chooser that, given a Directive x, if x.Name is 'defer', returns None. | ||
/// Otherwise, returns Some x. | ||
let fallbackDefer = fallbackByName "defer" | ||
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. On a second thought, I'm not sure if we need all of those helpers on the |
||
|
||
/// Builds a chooser that, given a Directive x, if x.Name is 'stream', returns None. | ||
/// Otherwise, returns Some x. | ||
let fallbackStream = fallbackByName "stream" | ||
|
||
/// Builds a chooser that, given a Directive x, if x.Name is 'live', returns None. | ||
/// Otherwise, returns Some x. | ||
let fallbackLive = fallbackByName "live" | ||
|
||
/// Builds a chooser that, when run, runs actual chooser, and if it returns Some directive x, | ||
/// uses that directive to run other chooser and return its result. If actual chooser returns None, | ||
/// returns None. | ||
let compose (other : DirectiveChooser) (actual : DirectiveChooser) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match actual directive with | ||
| Some d -> other d | ||
| None -> None | ||
chooser | ||
|
||
/// Builds a chooser that, when run, runs actual chooser and other chooser: if any of the choosers return | ||
/// None, then returns None. Otherwise, compose actual into other, run the composed chooser, and return its result. | ||
let merge (other : DirectiveChooser) (actual : DirectiveChooser) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match actual directive, other directive with | ||
|Some _, Some _ -> compose other actual |> apply directive | ||
| _ -> None | ||
chooser | ||
|
||
/// Reduces a sequence of choosers into a single chooser, by applying reducer function. | ||
let reduceSeq reducer (choosers : DirectiveChooser seq) = | ||
choosers |> Seq.reduce reducer | ||
|
||
/// Reduces a sequence of choosers into a single chooser, by applying the compose function to reduce it. | ||
let composeSeq (choosers : DirectiveChooser seq) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match Seq.length choosers with | ||
| 0 -> keep directive | ||
| _ -> choosers |> reduceSeq compose |> apply directive | ||
chooser | ||
|
||
/// Reduces a sequence of choosers into a single chooser, by applying thee merge function to reduce it. | ||
let mergeSeq (choosers : DirectiveChooser seq) : DirectiveChooser = | ||
let chooser = fun directive -> | ||
match Seq.length choosers with | ||
| 0 -> keep directive | ||
| _ -> choosers |> reduceSeq merge |> apply directive | ||
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.