From e475f1531338287f643f02e05681fee421d02c2e Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Thu, 12 Sep 2024 15:13:06 +0300 Subject: [PATCH] Fixed ability to override `GraphQLRequestHandler<'Root>` (#495) --- .../GraphQLRequestHandler.fs | 19 ++- .../StartupExtensions.fs | 143 +++++++++++++++++- 2 files changed, 148 insertions(+), 14 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLRequestHandler.fs b/src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLRequestHandler.fs index 9f032d01b..2d820ed7a 100644 --- a/src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLRequestHandler.fs +++ b/src/FSharp.Data.GraphQL.Server.AspNetCore/GraphQLRequestHandler.fs @@ -14,11 +14,18 @@ open FsToolkit.ErrorHandling open FSharp.Data.GraphQL.Server +type DefaultGraphQLRequestHandler<'Root> ( + httpContextAccessor : IHttpContextAccessor, + options : IOptionsMonitor>, + logger : ILogger> +) = + inherit GraphQLRequestHandler<'Root> (httpContextAccessor, options, logger) + /// Provides logic to parse and execute GraphQL request -type GraphQLRequestHandler<'Root> ( +and [] GraphQLRequestHandler<'Root> ( httpContextAccessor : IHttpContextAccessor, options : IOptionsMonitor>, - logger : ILogger> + logger : ILogger ) = let ctx = httpContextAccessor.HttpContext @@ -213,10 +220,10 @@ type GraphQLRequestHandler<'Root> ( return! checkAnonymousFieldsOnly ctx } - abstract ExecuteOperation<'Root> : Executor<'Root> -> ParsedGQLQueryRequestContent -> Task + abstract ExecuteOperation<'Root> : executor:Executor<'Root> * content:ParsedGQLQueryRequestContent -> Task /// Execute the operation for given request - default _.ExecuteOperation<'Root> (executor: Executor<'Root>) content = task { + default _.ExecuteOperation<'Root> (executor: Executor<'Root>, content) = task { let operationName = content.OperationName |> Skippable.filter (not << isNull) |> Skippable.toOption let variables = content.Variables |> Skippable.filter (not << isNull) |> Skippable.toOption @@ -241,12 +248,12 @@ type GraphQLRequestHandler<'Root> ( return (TypedResults.Ok response) :> IResult } - member request.HandleAsync () : Task> = taskResult { + member handler.HandleAsync () : Task> = taskResult { if ctx.RequestAborted.IsCancellationRequested then return TypedResults.Empty else let executor = options.CurrentValue.SchemaExecutor match! checkOperationType () with | IntrospectionQuery optionalAstDocument -> return! executeIntrospectionQuery executor optionalAstDocument - | OperationQuery content -> return! request.ExecuteOperation executor content + | OperationQuery content -> return! handler.ExecuteOperation (executor, content) } diff --git a/src/FSharp.Data.GraphQL.Server.AspNetCore/StartupExtensions.fs b/src/FSharp.Data.GraphQL.Server.AspNetCore/StartupExtensions.fs index bf5fcf618..15076e74c 100644 --- a/src/FSharp.Data.GraphQL.Server.AspNetCore/StartupExtensions.fs +++ b/src/FSharp.Data.GraphQL.Server.AspNetCore/StartupExtensions.fs @@ -40,7 +40,7 @@ module ServiceCollectionExtensions = /// /// [] - member internal services.AddGraphQL<'Root> + member internal services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> ( executorFactory : Func>, rootFactory : HttpContext -> 'Root, @@ -84,7 +84,41 @@ module ServiceCollectionExtensions = } ) .AddHttpContextAccessor() - .AddScoped>() + .AddScoped, 'Handler>() + + /// + /// Adds GraphQL options and services to the service collection. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + [] + member internal services.AddGraphQL<'Root> + ( + executorFactory : Func>, + rootFactory : HttpContext -> 'Root, + [] additionalConverters : JsonConverter seq, + [] webSocketEndpointUrl : string, + [] configure : Func, GraphQLOptions<'Root>> + ) = + services.AddGraphQL<'Root, DefaultGraphQLRequestHandler<'Root>> (executorFactory, rootFactory, additionalConverters, webSocketEndpointUrl, configure) + + /// + /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + executor : Executor<'Root>, + rootFactory : HttpContext -> 'Root, + [] additionalConverters : JsonConverter seq + ) = + services.AddGraphQL<'Root, 'Handler> ((fun _ -> executor), rootFactory, additionalConverters, null, null) /// /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. @@ -100,7 +134,24 @@ module ServiceCollectionExtensions = rootFactory : HttpContext -> 'Root, [] additionalConverters : JsonConverter seq ) = - services.AddGraphQL ((fun _ -> executor), rootFactory, additionalConverters, null, null) + services.AddGraphQL<'Root> ((fun _ -> executor), rootFactory, additionalConverters, null, null) + + /// + /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + executor : Executor<'Root>, + rootFactory : HttpContext -> 'Root, + webSocketEndpointUrl : string, + [] additionalConverters : JsonConverter seq + ) = + services.AddGraphQL<'Root, 'Handler> ((fun _ -> executor), rootFactory, additionalConverters, webSocketEndpointUrl, null) /// /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. @@ -117,7 +168,24 @@ module ServiceCollectionExtensions = webSocketEndpointUrl : string, [] additionalConverters : JsonConverter seq ) = - services.AddGraphQL ((fun _ -> executor), rootFactory, additionalConverters, webSocketEndpointUrl, null) + services.AddGraphQL<'Root> ((fun _ -> executor), rootFactory, additionalConverters, webSocketEndpointUrl, null) + + /// + /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + executor : Executor<'Root>, + rootFactory : HttpContext -> 'Root, + configure : Func, GraphQLOptions<'Root>>, + [] additionalConverters : JsonConverter seq + ) = + services.AddGraphQL<'Root, 'Handler> ((fun _ -> executor), rootFactory, additionalConverters, null, configure) /// /// Adds GraphQL options and services to the service collection. Requires an executor instance to be provided. @@ -134,7 +202,26 @@ module ServiceCollectionExtensions = configure : Func, GraphQLOptions<'Root>>, [] additionalConverters : JsonConverter seq ) = - services.AddGraphQL ((fun _ -> executor), rootFactory, additionalConverters, null, configure) + services.AddGraphQL<'Root> ((fun _ -> executor), rootFactory, additionalConverters, null, configure) + + /// + /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + /// + /// The executor must be registered as a singleton service. + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + rootFactory : HttpContext -> 'Root, + [] additionalConverters : JsonConverter seq + ) = + let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() + services.AddGraphQL<'Root, 'Handler> (getExecutorService, rootFactory, additionalConverters, null, null) /// /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. @@ -153,7 +240,27 @@ module ServiceCollectionExtensions = [] additionalConverters : JsonConverter seq ) = let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() - services.AddGraphQL (getExecutorService, rootFactory, additionalConverters, null, null) + services.AddGraphQL<'Root> (getExecutorService, rootFactory, additionalConverters, null, null) + + /// + /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + /// + /// The executor must be registered as a singleton service. + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + rootFactory : HttpContext -> 'Root, + [] webSocketEndpointUrl : string, + [] additionalConverters : JsonConverter seq + ) = + let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() + services.AddGraphQL<'Root, 'Handler> (getExecutorService, rootFactory, additionalConverters, webSocketEndpointUrl, null) /// /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. @@ -173,7 +280,27 @@ module ServiceCollectionExtensions = [] additionalConverters : JsonConverter seq ) = let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() - services.AddGraphQL (getExecutorService, rootFactory, additionalConverters, webSocketEndpointUrl, null) + services.AddGraphQL<'Root> (getExecutorService, rootFactory, additionalConverters, webSocketEndpointUrl, null) + + /// + /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. + /// + /// It also adds converters to + /// to support serialization of GraphQL responses. + /// + /// + /// + /// The executor must be registered as a singleton service. + /// + [] + member services.AddGraphQL<'Root, 'Handler when 'Handler :> GraphQLRequestHandler<'Root> and 'Handler : not struct> + ( + rootFactory : HttpContext -> 'Root, + configure : Func, GraphQLOptions<'Root>>, + [] additionalConverters : JsonConverter seq + ) = + let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() + services.AddGraphQL<'Root, 'Handler> (getExecutorService, rootFactory, additionalConverters, null, configure) /// /// Adds GraphQL options and services to the service collection. It gets the executor from the service provider. @@ -193,7 +320,7 @@ module ServiceCollectionExtensions = [] additionalConverters : JsonConverter seq ) = let getExecutorService (sp : IServiceProvider) = sp.GetRequiredService>() - services.AddGraphQL (getExecutorService, rootFactory, additionalConverters, null, configure) + services.AddGraphQL<'Root> (getExecutorService, rootFactory, additionalConverters, null, configure) []