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
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,25 +279,27 @@ type OperationExecutionMiddleware =
ExecutionContext -> (ExecutionContext -> AsyncVal<GQLResponse>) -> AsyncVal<GQLResponse>

type IExecutorMiddleware =
abstract CompileSchema : SchemaCompileMiddleware option
abstract PlanOperation : OperationPlanningMiddleware option
abstract ExecuteOperationAsync : OperationExecutionMiddleware option
abstract CompileSchema : SchemaCompileMiddleware voption
abstract PostCompileSchema : SchemaPostCompileMiddleware voption
Comment thread
xperiandri marked this conversation as resolved.
abstract PlanOperation : OperationPlanningMiddleware voption
abstract ExecuteOperationAsync : OperationExecutionMiddleware voption
```

Optionally, for ease of implementation, concrete class to derive from can be used, receiving only the optional sub-middleware functions in the constructor:

```fsharp
type ExecutorMiddleware(?compile, ?plan, ?execute) =
type ExecutorMiddleware([<Struct>] ?compile, [<Struct>] ?postCompile, [<Struct>] ?plan, [<Struct>] ?execute) =
interface IExecutorMiddleware with
member _.CompileSchema = compile
member _.PostCompileSchema = postCompile
member _.PlanOperation = plan
member _.ExecuteOperationAsync = execute
```

Each of the middleware functions act like an intercept function, with two parameters: the context of the phase, the function of the next middleware (or the actual phase itself, which is the last to run), and the return value. Those functions can be passed as an argument to the constructor of the `Executor<'Root>` object:

```fsharp
let middleware = [ ExecutorMiddleware(compileFn, planningFn, executionFn) ]
let middleware = [ ExecutorMiddleware(compile = compileFn, plan = planningFn, execute = executionFn) ]
let executor = Executor(schema, middleware)
```

Expand Down
12 changes: 6 additions & 6 deletions src/FSharp.Data.GraphQL.Server.Middleware/DefineExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ module DefineExtensions =
/// A boolean flag indicating if the values of the threshold and the weight of the current query should
/// be reported to the metadata object in the GQLResponse.
/// </param>
static member QueryWeightMiddleware(threshold : float, ?reportToMetadata : bool) : IExecutorMiddleware =
let reportToMetadata = defaultArg reportToMetadata false
static member QueryWeightMiddleware(threshold : float, [<Struct>] ?reportToMetadata : bool) : IExecutorMiddleware =
let reportToMetadata = defaultValueArg reportToMetadata false
upcast QueryWeightMiddleware(threshold, reportToMetadata)

/// <summary>
Expand All @@ -35,8 +35,8 @@ module DefineExtensions =
/// This argument can be used on the query to specify a filter with operations like "less than", "equals", etc. on the
/// field of the specified object of 'ObjectType type.
/// </remarks>
static member ObjectListFilterMiddleware<'ObjectType, 'ListType>(?reportToMetadata : bool) : IExecutorMiddleware =
let reportToMetadata = defaultArg reportToMetadata false
static member ObjectListFilterMiddleware<'ObjectType, 'ListType>([<Struct>] ?reportToMetadata : bool) : IExecutorMiddleware =
let reportToMetadata = defaultValueArg reportToMetadata false
upcast ObjectListFilterMiddleware<'ObjectType, 'ListType>(reportToMetadata)

/// <summary>
Expand All @@ -47,6 +47,6 @@ module DefineExtensions =
/// An optional function to resolve the name of the identity field based on the object definition.
/// If no function is provided, it takes the default "Id" value as the identity field.
/// </param>
static member LiveQueryMiddleware(?identityName : IdentityNameResolver) : IExecutorMiddleware =
let identityName = defaultArg identityName (fun _ -> "Id")
static member LiveQueryMiddleware([<Struct>] ?identityName : IdentityNameResolver) : IExecutorMiddleware =
let identityName = defaultValueArg identityName (fun _ -> "Id")
upcast LiveQueryMiddleware(identityName)
24 changes: 12 additions & 12 deletions src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ type internal QueryWeightMiddleware (threshold : float, reportToMetadata : bool)
if pass then next ctx else error ctx

interface IExecutorMiddleware with
member _.CompileSchema = None
member _.PostCompileSchema = None
member _.PlanOperation = None
member _.ExecuteOperationAsync = Some (middleware threshold)
member _.CompileSchema = ValueNone
member _.PostCompileSchema = ValueNone
member _.PlanOperation = ValueNone
member _.ExecuteOperationAsync = ValueSome (middleware threshold)

type internal ObjectListFilterMiddleware<'ObjectType, 'ListType> (reportToMetadata : bool) =

Expand Down Expand Up @@ -153,10 +153,10 @@ type internal ObjectListFilterMiddleware<'ObjectType, 'ListType> (reportToMetada
return GQLExecutionResult.RequestError (ctx.ExecutionPlan.DocumentId, (errs |> List.map GQLProblemDetails.OfError), ctx.Metadata)
}
interface IExecutorMiddleware with
member _.CompileSchema = Some compileMiddleware
member _.PostCompileSchema = None
member _.PlanOperation = None
member _.ExecuteOperationAsync = Some reportMiddleware
member _.CompileSchema = ValueSome compileMiddleware
member _.PostCompileSchema = ValueNone
member _.PlanOperation = ValueNone
member _.ExecuteOperationAsync = ValueSome reportMiddleware

/// A function that resolves an identity name for a schema object, based on a object definition of it.
type IdentityNameResolver = ObjectDef -> string
Expand Down Expand Up @@ -203,7 +203,7 @@ type internal LiveQueryMiddleware (identityNameResolver : IdentityNameResolver)
next ctx

interface IExecutorMiddleware with
member _.CompileSchema = Some middleware
member _.PostCompileSchema = None
member _.PlanOperation = None
member _.ExecuteOperationAsync = None
member _.CompileSchema = ValueSome middleware
member _.PostCompileSchema = ValueNone
member _.PlanOperation = ValueNone
member _.ExecuteOperationAsync = ValueNone
2 changes: 1 addition & 1 deletion src/FSharp.Data.GraphQL.Server/DefineExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module DefineExtensions =
/// <param name="postCompile">The schema post-compile sub-middleware function.</param>
/// <param name="plan">The operation planning sub-middleware function.</param>
/// <param name="execute">The operation execution sub-middleware function.</param>
static member ExecutorMiddleware(?compile, ?postCompile, ?plan, ?execute) : IExecutorMiddleware =
static member ExecutorMiddleware([<Struct>] ?compile, [<Struct>] ?postCompile, [<Struct>] ?plan, [<Struct>] ?execute) : IExecutorMiddleware =
{ new IExecutorMiddleware with
member _.CompileSchema = compile
member _.PostCompileSchema = postCompile
Expand Down
18 changes: 9 additions & 9 deletions src/FSharp.Data.GraphQL.Server/Executor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ type OperationExecutionMiddleware =
/// A middleware can have one to three sub-middlewares, one for each phase of the query execution.
type IExecutorMiddleware =
/// Defines the sub-middleware that intercepts the schema compile process of the Executor.
abstract CompileSchema : SchemaCompileMiddleware option
abstract CompileSchema : SchemaCompileMiddleware voption
/// Defines the sub-middleware that executes after the schema compilation phase of the Executor is complete.
abstract PostCompileSchema : SchemaPostCompileMiddleware option
abstract PostCompileSchema : SchemaPostCompileMiddleware voption
/// Defines the sub-middleware that intercepts the operation planning phase of the Executor.
abstract PlanOperation : OperationPlanningMiddleware option
abstract PlanOperation : OperationPlanningMiddleware voption
/// Defines the sub-middleware that intercepts the operation execution phase of the Executor.
abstract ExecuteOperationAsync : OperationExecutionMiddleware option
abstract ExecuteOperationAsync : OperationExecutionMiddleware voption

/// A simple, concrete implementation for the IExecutorMiddleware interface.
type ExecutorMiddleware(?compile, ?postCompile, ?plan, ?execute) =
type ExecutorMiddleware([<Struct>] ?compile, [<Struct>] ?postCompile, [<Struct>] ?plan, [<Struct>] ?execute) =
interface IExecutorMiddleware with
member _.CompileSchema = compile
member _.PostCompileSchema = postCompile
Expand All @@ -77,7 +77,7 @@ type Executor<'Root>(schema: ISchema<'Root>, middlewares : IExecutorMiddleware s

let middlewaresList = Seq.toList middlewares

let rec runMiddlewares (phaseSel : IExecutorMiddleware -> ('ctx -> ('ctx -> 'res) -> 'res) option)
let rec runMiddlewares (phaseSel : IExecutorMiddleware -> ('ctx -> ('ctx -> 'res) -> 'res) voption)
(initialCtx : 'ctx)
(onComplete : 'ctx -> 'res)
: 'res =
Expand All @@ -86,8 +86,8 @@ type Executor<'Root>(schema: ISchema<'Root>, middlewares : IExecutorMiddleware s
| [] -> onComplete ctx
| m :: ms ->
match (phaseSel m) with
| Some f -> f ctx (fun ctx' -> go ctx' ms)
| None -> go ctx ms
| ValueSome f -> f ctx (fun ctx' -> go ctx' ms)
| ValueNone -> go ctx ms
go initialCtx middlewaresList

do
Expand Down Expand Up @@ -124,7 +124,7 @@ type Executor<'Root>(schema: ISchema<'Root>, middlewares : IExecutorMiddleware s
FieldExecuteMap = fieldExecuteMap
Metadata = executionPlan.Metadata }
let executorMiddlewareFunc = fun (executorMiddleware : IExecutorMiddleware) ->
executorMiddleware.ExecuteOperationAsync |> Option.map (fun middleware -> middleware(getInputContext))
executorMiddleware.ExecuteOperationAsync |> ValueOption.map (fun middleware -> middleware(getInputContext))
let! res = runMiddlewares executorMiddlewareFunc executionCtx executeOperation |> AsyncVal.toAsync
return prepareOutput res
with
Expand Down
7 changes: 2 additions & 5 deletions src/FSharp.Data.GraphQL.Server/Schema.fs
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,9 @@ type SchemaConfig =
Directives = [ IncludeDirective; SkipDirective; DeferDirective; streamDirective; LiveDirective ] }

/// GraphQL server schema. Defines the complete type system to be used by GraphQL queries.
type Schema<'Root> (query: ObjectDef<'Root>, [<Struct>] ?mutation: ObjectDef<'Root>, [<Struct>] ?subscription: SubscriptionObjectDef<'Root>, ?config: SchemaConfig) =
type Schema<'Root> (query: ObjectDef<'Root>, [<Struct>] ?mutation: ObjectDef<'Root>, [<Struct>] ?subscription: SubscriptionObjectDef<'Root>, [<Struct>] ?config: SchemaConfig) =

let schemaConfig =
match config with
| None -> SchemaConfig.Default
| Some c -> c
let schemaConfig = defaultValueArg config SchemaConfig.Default

let typeMap : TypeMap =

Expand Down
8 changes: 4 additions & 4 deletions tests/FSharp.Data.GraphQL.Tests/ExecutorMiddlewareTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ let executionMiddleware (inputContext : InputExecutionContextProvider) (ctx : Ex

let middleware =
{ new IExecutorMiddleware with
member _.CompileSchema = Some compileMiddleware
member _.PostCompileSchema = Some postCompileMiddleware
member _.PlanOperation = Some planningMiddleware
member _.ExecuteOperationAsync = Some executionMiddleware }
member _.CompileSchema = ValueSome compileMiddleware
member _.PostCompileSchema = ValueSome postCompileMiddleware
member _.PlanOperation = ValueSome planningMiddleware
member _.ExecuteOperationAsync = ValueSome executionMiddleware }

let executor = Executor(schema, [ middleware ])

Expand Down
Loading