From 6d8e2a8ba509c2b54d3555ee4c542933e28c905a Mon Sep 17 00:00:00 2001 From: XperiAndri Date: Sun, 27 Sep 2026 02:23:07 +0200 Subject: [PATCH] Used `[] ?` optional parameters and `voption` in executor middlewares and `Schema` * `IExecutorMiddleware` members return `voption` instead of `option` * `ExecutorMiddleware` and `Define.ExecutorMiddleware` take `[] ?` sub-middlewares * `Define.QueryWeightMiddleware`, `Define.ObjectListFilterMiddleware` and `Define.LiveQueryMiddleware` take `[] ?` arguments * `Schema` takes `[] ?config` * README middleware sample updated, including the missing `postCompile` Co-Authored-By: Claude Opus 5.5 --- README.md | 12 ++++++---- .../DefineExtensions.fs | 12 +++++----- .../MiddlewareDefinitions.fs | 24 +++++++++---------- .../DefineExtensions.fs | 2 +- src/FSharp.Data.GraphQL.Server/Executor.fs | 18 +++++++------- src/FSharp.Data.GraphQL.Server/Schema.fs | 7 ++---- .../ExecutorMiddlewareTests.fs | 8 +++---- 7 files changed, 41 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 1c5b35bf4..826a8178e 100644 --- a/README.md +++ b/README.md @@ -279,17 +279,19 @@ type OperationExecutionMiddleware = ExecutionContext -> (ExecutionContext -> AsyncVal) -> AsyncVal type IExecutorMiddleware = - abstract CompileSchema : SchemaCompileMiddleware option - abstract PlanOperation : OperationPlanningMiddleware option - abstract ExecuteOperationAsync : OperationExecutionMiddleware option + abstract CompileSchema : SchemaCompileMiddleware voption + abstract PostCompileSchema : SchemaPostCompileMiddleware voption + 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([] ?compile, [] ?postCompile, [] ?plan, [] ?execute) = interface IExecutorMiddleware with member _.CompileSchema = compile + member _.PostCompileSchema = postCompile member _.PlanOperation = plan member _.ExecuteOperationAsync = execute ``` @@ -297,7 +299,7 @@ type ExecutorMiddleware(?compile, ?plan, ?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) ``` diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/DefineExtensions.fs b/src/FSharp.Data.GraphQL.Server.Middleware/DefineExtensions.fs index eb2af4a63..b7f6c899e 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/DefineExtensions.fs +++ b/src/FSharp.Data.GraphQL.Server.Middleware/DefineExtensions.fs @@ -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. /// - static member QueryWeightMiddleware(threshold : float, ?reportToMetadata : bool) : IExecutorMiddleware = - let reportToMetadata = defaultArg reportToMetadata false + static member QueryWeightMiddleware(threshold : float, [] ?reportToMetadata : bool) : IExecutorMiddleware = + let reportToMetadata = defaultValueArg reportToMetadata false upcast QueryWeightMiddleware(threshold, reportToMetadata) /// @@ -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. /// - static member ObjectListFilterMiddleware<'ObjectType, 'ListType>(?reportToMetadata : bool) : IExecutorMiddleware = - let reportToMetadata = defaultArg reportToMetadata false + static member ObjectListFilterMiddleware<'ObjectType, 'ListType>([] ?reportToMetadata : bool) : IExecutorMiddleware = + let reportToMetadata = defaultValueArg reportToMetadata false upcast ObjectListFilterMiddleware<'ObjectType, 'ListType>(reportToMetadata) /// @@ -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. /// - static member LiveQueryMiddleware(?identityName : IdentityNameResolver) : IExecutorMiddleware = - let identityName = defaultArg identityName (fun _ -> "Id") + static member LiveQueryMiddleware([] ?identityName : IdentityNameResolver) : IExecutorMiddleware = + let identityName = defaultValueArg identityName (fun _ -> "Id") upcast LiveQueryMiddleware(identityName) diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs b/src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs index 6d0f182c2..4aae7f4a6 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs @@ -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) = @@ -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 @@ -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 diff --git a/src/FSharp.Data.GraphQL.Server/DefineExtensions.fs b/src/FSharp.Data.GraphQL.Server/DefineExtensions.fs index 621188c3a..f37623db5 100644 --- a/src/FSharp.Data.GraphQL.Server/DefineExtensions.fs +++ b/src/FSharp.Data.GraphQL.Server/DefineExtensions.fs @@ -14,7 +14,7 @@ module DefineExtensions = /// The schema post-compile sub-middleware function. /// The operation planning sub-middleware function. /// The operation execution sub-middleware function. - static member ExecutorMiddleware(?compile, ?postCompile, ?plan, ?execute) : IExecutorMiddleware = + static member ExecutorMiddleware([] ?compile, [] ?postCompile, [] ?plan, [] ?execute) : IExecutorMiddleware = { new IExecutorMiddleware with member _.CompileSchema = compile member _.PostCompileSchema = postCompile diff --git a/src/FSharp.Data.GraphQL.Server/Executor.fs b/src/FSharp.Data.GraphQL.Server/Executor.fs index 5fe48700b..eb54f0e40 100644 --- a/src/FSharp.Data.GraphQL.Server/Executor.fs +++ b/src/FSharp.Data.GraphQL.Server/Executor.fs @@ -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([] ?compile, [] ?postCompile, [] ?plan, [] ?execute) = interface IExecutorMiddleware with member _.CompileSchema = compile member _.PostCompileSchema = postCompile @@ -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 = @@ -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 @@ -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 diff --git a/src/FSharp.Data.GraphQL.Server/Schema.fs b/src/FSharp.Data.GraphQL.Server/Schema.fs index 93503639d..8f9c87d1c 100644 --- a/src/FSharp.Data.GraphQL.Server/Schema.fs +++ b/src/FSharp.Data.GraphQL.Server/Schema.fs @@ -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>, [] ?mutation: ObjectDef<'Root>, [] ?subscription: SubscriptionObjectDef<'Root>, ?config: SchemaConfig) = +type Schema<'Root> (query: ObjectDef<'Root>, [] ?mutation: ObjectDef<'Root>, [] ?subscription: SubscriptionObjectDef<'Root>, [] ?config: SchemaConfig) = - let schemaConfig = - match config with - | None -> SchemaConfig.Default - | Some c -> c + let schemaConfig = defaultValueArg config SchemaConfig.Default let typeMap : TypeMap = diff --git a/tests/FSharp.Data.GraphQL.Tests/ExecutorMiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/ExecutorMiddlewareTests.fs index 77aece82b..0bd6ea8f4 100644 --- a/tests/FSharp.Data.GraphQL.Tests/ExecutorMiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/ExecutorMiddlewareTests.fs @@ -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 ])