Skip to content

Commit

Permalink
Integrated Gateway into Request Pipeline (#5298)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Aug 12, 2022
1 parent 337af7a commit 9f44720
Show file tree
Hide file tree
Showing 26 changed files with 392 additions and 294 deletions.
Expand Up @@ -50,6 +50,7 @@ internal static class InternalServiceCollectionExtensions
services.TryAddSingleton(_ => new ObjectResultPool(maximumRetained, maximumArrayCapacity));
services.TryAddSingleton(_ => new ListResultPool(maximumRetained, maximumArrayCapacity));
services.TryAddSingleton<ResultPool>();
services.TryAddTransient<ResultBuilder>();
return services;
}

Expand Down Expand Up @@ -129,18 +130,12 @@ internal static class InternalServiceCollectionExtensions
{
var pool = sp.GetRequiredService<ObjectPool<DeferredWorkState>>();
var state = pool.Get();
return new DeferredWorkStateOwner(state, pool);
});

services.TryAddTransient<DeferredWorkScheduler>();

services.TryAddScoped<IFactory<DeferredWorkStateOwner>>(
sp => new ServiceFactory<DeferredWorkStateOwner>(sp));

services.TryAddScoped<IFactory<DeferredWorkScheduler>>(
sp => new ServiceFactory<DeferredWorkScheduler>(sp));

return services;
}

Expand Down
Expand Up @@ -2,26 +2,19 @@
using System.Threading;
using HotChocolate.Execution.DependencyInjection;
using HotChocolate.Execution.Instrumentation;
using Microsoft.Extensions.DependencyInjection;
using static HotChocolate.Execution.QueryResultBuilder;

namespace HotChocolate.Execution.Processing;

internal sealed class DeferredWorkScheduler : IDeferredWorkScheduler
{
private readonly IFactory<OperationContextOwner> _operationContextFactory;
private readonly IFactory<DeferredWorkStateOwner> _deferredWorkStateFactory;
private readonly object _stateSync = new();
private IFactory<OperationContextOwner> _operationContextFactory = default!;
private IFactory<DeferredWorkStateOwner> _deferredWorkStateFactory = default!;
private OperationContext _parentContext = default!;
private DeferredWorkStateOwner? _stateOwner;

public DeferredWorkScheduler(
IFactory<OperationContextOwner> operationContextFactory,
IFactory<DeferredWorkStateOwner> deferredWorkStateFactory)
{
_operationContextFactory = operationContextFactory;
_deferredWorkStateFactory = deferredWorkStateFactory;
}

private DeferredWorkStateOwner StateOwner
{
get
Expand All @@ -45,7 +38,11 @@ private DeferredWorkStateOwner StateOwner

public void Initialize(OperationContext operationContext)
{
var services = operationContext.Services;

_parentContext = operationContext;
_operationContextFactory = services.GetRequiredService<IFactory<OperationContextOwner>>();
_deferredWorkStateFactory = services.GetRequiredService<IFactory<DeferredWorkStateOwner>>();
}

public void InitializeFrom(OperationContext operationContext, DeferredWorkScheduler scheduler)
Expand Down
Expand Up @@ -39,7 +39,7 @@ public ResultBuilder Result
get
{
AssertInitialized();
return _resultHelper;
return _resultBuilder;
}
}

Expand Down
Expand Up @@ -16,7 +16,7 @@ internal sealed partial class OperationContext
private readonly IFactory<ResolverTask> _resolverTaskFactory;
private readonly WorkScheduler _workScheduler;
private readonly DeferredWorkScheduler _deferredWorkScheduler;
private readonly ResultBuilder _resultHelper;
private readonly ResultBuilder _resultBuilder;
private readonly PooledPathFactory _pathFactory;
private ISchema _schema = default!;
private IErrorHandler _errorHandler = default!;
Expand All @@ -35,15 +35,14 @@ internal sealed partial class OperationContext
public OperationContext(
IFactory<ResolverTask> resolverTaskFactory,
PooledPathFactory pathFactory,
ResultPool resultPool,
ITypeConverter typeConverter,
DeferredWorkScheduler deferredWorkScheduler)
ResultBuilder resultBuilder,
ITypeConverter typeConverter)
{
_resolverTaskFactory = resolverTaskFactory;
_pathFactory = pathFactory;
_workScheduler = new(this);
_deferredWorkScheduler = deferredWorkScheduler;
_resultHelper = new(resultPool);
_deferredWorkScheduler = new();
_resultBuilder = resultBuilder;
Converter = typeConverter;
}

Expand Down Expand Up @@ -75,7 +74,7 @@ internal sealed partial class OperationContext
IncludeFlags = _operation.CreateIncludeFlags(variables);
_workScheduler.Initialize(batchDispatcher);
_deferredWorkScheduler.Initialize(this);
_resultHelper.Initialize(_operation, _errorHandler, _diagnosticEvents);
_resultBuilder.Initialize(_operation, _errorHandler, _diagnosticEvents);
}

public void InitializeFrom(OperationContext context)
Expand All @@ -97,7 +96,7 @@ public void InitializeFrom(OperationContext context)
IncludeFlags = _operation.CreateIncludeFlags(_variables);
_workScheduler.Initialize(_batchDispatcher);
_deferredWorkScheduler.InitializeFrom(this, context._deferredWorkScheduler);
_resultHelper.Initialize(_operation, _errorHandler, _diagnosticEvents);
_resultBuilder.Initialize(_operation, _errorHandler, _diagnosticEvents);
}

public void Clean()
Expand All @@ -106,7 +105,7 @@ public void Clean()
{
_pathFactory.Clear();
_workScheduler.Clear();
_resultHelper.Clear();
_resultBuilder.Clear();
_schema = default!;
_errorHandler = default!;
_activator = default!;
Expand Down
Expand Up @@ -221,5 +221,3 @@ public int Compare(IError? x, IError? y)
public static readonly ErrorComparer Default = new();
}
}


14 changes: 14 additions & 0 deletions src/HotChocolate/Fusion/src/Core/Clients/GraphQLClientFactory.cs
@@ -0,0 +1,14 @@
namespace HotChocolate.Fusion.Clients;

internal sealed class GraphQLClientFactory
{
private readonly Dictionary<string, IGraphQLClient> _executors;

public GraphQLClientFactory(IEnumerable<IGraphQLClient> executors)
{
_executors = executors.ToDictionary(t => t.SchemaName);
}

public IGraphQLClient Create(string schemaName)
=> _executors[schemaName];
}
Expand Up @@ -2,24 +2,25 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using HotChocolate.Fusion.Utilities;
using HotChocolate.Utilities;

namespace HotChocolate.Fusion.Execution;
namespace HotChocolate.Fusion.Clients;

public sealed class HttpRequestExecutor : IRemoteRequestExecutor
public sealed class GraphQLHttpClient : IGraphQLClient
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly JsonRequestFormatter _formatter = new();

public HttpRequestExecutor(string schemaName, IHttpClientFactory httpClientFactory)
public GraphQLHttpClient(string schemaName, IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
SchemaName = schemaName;
}

public string SchemaName { get; }

public async Task<Response> ExecuteAsync(Request request, CancellationToken cancellationToken)
public async Task<GraphQLResponse> ExecuteAsync(GraphQLRequest request, CancellationToken cancellationToken)
{
// todo : this is just a naive dummy implementation
using var writer = new ArrayWriter();
Expand All @@ -41,10 +42,24 @@ public async Task<Response> ExecuteAsync(Request request, CancellationToken canc
}

var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
return new Response(document);
return new GraphQLResponse(document);
}

private HttpRequestMessage CreateRequestMessage(ArrayWriter writer, Request request)
public Task<IAsyncEnumerable<GraphQLResponse>> ExecuteBatchAsync(
IReadOnlyList<GraphQLRequest> requests,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public Task<IAsyncEnumerable<GraphQLResponse>> SubscribeAsync(
GraphQLRequest graphQLRequests,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

private HttpRequestMessage CreateRequestMessage(ArrayWriter writer, GraphQLRequest request)
{
_formatter.Write(writer, request);

Expand Down
@@ -1,10 +1,10 @@
using HotChocolate.Language;

namespace HotChocolate.Fusion.Execution;
namespace HotChocolate.Fusion.Clients;

public readonly struct Request
public readonly struct GraphQLRequest
{
public Request(
public GraphQLRequest(
string schemaName,
DocumentNode document,
ObjectValueNode? variableValues,
Expand Down
42 changes: 42 additions & 0 deletions src/HotChocolate/Fusion/src/Core/Clients/GraphQLResponse.cs
@@ -0,0 +1,42 @@
using System.Text.Json;

namespace HotChocolate.Fusion.Clients;

public sealed class GraphQLResponse : IDisposable
{
private readonly JsonDocument? _document;

public GraphQLResponse(JsonDocument? document)
{
_document = document;

if (_document is not null)
{
if (_document.RootElement.TryGetProperty(ResponseProperties.Data, out var value))
{
Data = value;
}

if (_document.RootElement.TryGetProperty(ResponseProperties.Errors, out value))
{
Errors = value;
}

if (_document.RootElement.TryGetProperty(ResponseProperties.Extensions, out value))
{
Extensions = value;
}
}
}

public JsonElement Data { get; }

public JsonElement Errors { get; }

public JsonElement Extensions { get; }

public void Dispose()
{
_document?.Dispose();
}
}
18 changes: 18 additions & 0 deletions src/HotChocolate/Fusion/src/Core/Clients/IGraphQLClient.cs
@@ -0,0 +1,18 @@
namespace HotChocolate.Fusion.Clients;

public interface IGraphQLClient
{
string SchemaName { get; }

Task<GraphQLResponse> ExecuteAsync(
GraphQLRequest request,
CancellationToken cancellationToken);

Task<IAsyncEnumerable<GraphQLResponse>> ExecuteBatchAsync(
IReadOnlyList<GraphQLRequest> requests,
CancellationToken cancellationToken);

Task<IAsyncEnumerable<GraphQLResponse>> SubscribeAsync(
GraphQLRequest graphQLRequests,
CancellationToken cancellationToken);
}
@@ -0,0 +1,8 @@
namespace HotChocolate.Fusion.Clients;

internal static class ResponseProperties
{
public const string Data = "data";
public const string Errors = "errors";
public const string Extensions = "extensions";
}
@@ -0,0 +1,62 @@
using HotChocolate.Execution.Configuration;
using HotChocolate.Fusion.Clients;
using HotChocolate.Fusion.Execution;
using HotChocolate.Fusion.Metadata;
using HotChocolate.Fusion.Pipeline;
using HotChocolate.Fusion.Planning;
using Microsoft.Extensions.DependencyInjection.Extensions;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;

public static class RequestExecutorBuilderExtensions
{
public static IRequestExecutorBuilder AddGraphQLGateway(
this IRequestExecutorBuilder builder,
string serviceConfig,
string sdl)
{
var configuration = ServiceConfiguration.Load(serviceConfig);

return builder
.AddDocumentFromString(sdl)
.UseField(next => next)
.UseDefaultGatewayPipeline()
.ConfigureSchemaServices(
sc =>
{
foreach (var schemaName in configuration.Bindings)
{
sc.AddSingleton<IGraphQLClient>(
sp => new GraphQLHttpClient(
schemaName,
sp.GetApplicationService<IHttpClientFactory>()));
}
sc.TryAddSingleton(configuration);
sc.TryAddSingleton<RequestPlaner>();
sc.TryAddSingleton<RequirementsPlaner>();
sc.TryAddSingleton<ExecutionPlanBuilder>();
sc.TryAddSingleton<GraphQLClientFactory>();
sc.TryAddSingleton<FederatedQueryExecutor>();
});
}

public static IRequestExecutorBuilder UseDefaultGatewayPipeline(
this IRequestExecutorBuilder builder)
{
return builder
.UseInstrumentations()
.UseExceptions()
.UseTimeout()
.UseDocumentCache()
.UseDocumentParser()
.UseDocumentValidation()
.UseOperationCache()
.UseOperationComplexityAnalyzer()
.UseOperationResolver()
.UseOperationVariableCoercion()
.UseRequest<QueryPlanMiddleware>()
.UseRequest<OperationExecutionMiddleware>();
}
}

0 comments on commit 9f44720

Please sign in to comment.