diff --git a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClient.cs b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClient.cs
index 0d0d8277807..6e8c13ae1a3 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClient.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClient.cs
@@ -31,6 +31,7 @@ public sealed class InMemorySourceSchemaClient : ISourceSchemaClient
private readonly RequestExecutorProxy _executor;
private readonly JsonResultFormatter _formatter;
+ private readonly ErrorHandlingMode? _onError;
private bool _disposed;
///
@@ -42,15 +43,20 @@ public sealed class InMemorySourceSchemaClient : ISourceSchemaClient
///
/// The JSON result formatter used to serialize execution results.
///
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
public InMemorySourceSchemaClient(
RequestExecutorProxy executor,
- JsonResultFormatter formatter)
+ JsonResultFormatter formatter,
+ ErrorHandlingMode? onError = null)
{
ArgumentNullException.ThrowIfNull(executor);
ArgumentNullException.ThrowIfNull(formatter);
_executor = executor;
_formatter = formatter;
+ _onError = onError;
}
///
@@ -68,7 +74,7 @@ public async ValueTask ExecuteAsync(
ObjectDisposedException.ThrowIf(_disposed, this);
ChunkedArrayWriter? buffer = null;
- var operationRequest = BuildOperationRequest(context, request, ref buffer);
+ var operationRequest = BuildOperationRequest(context, request, _onError, ref buffer);
try
{
@@ -103,7 +109,7 @@ public async IAsyncEnumerable ExecuteBatchStreamAsync(
{
for (var i = 0; i < requests.Length; i++)
{
- operationRequests[i] = BuildOperationRequest(context, requests[i], ref buffer);
+ operationRequests[i] = BuildOperationRequest(context, requests[i], _onError, ref buffer);
}
}
catch
@@ -221,6 +227,7 @@ public ValueTask DisposeAsync()
private static IOperationRequest BuildOperationRequest(
OperationPlanContext context,
SourceSchemaClientRequest request,
+ ErrorHandlingMode? onError,
ref ChunkedArrayWriter? buffer)
{
IFeatureCollection? features = null;
@@ -236,6 +243,7 @@ private static IOperationRequest BuildOperationRequest(
{
return OperationRequest.FromSourceText(
request.OperationSourceText,
+ errorHandlingMode: onError,
features: features);
}
@@ -247,6 +255,7 @@ private static IOperationRequest BuildOperationRequest(
{
return OperationRequest.FromSourceText(
request.OperationSourceText,
+ errorHandlingMode: onError,
variableValues: JsonDocument.Parse(sequence));
}
@@ -254,6 +263,7 @@ private static IOperationRequest BuildOperationRequest(
var cleanedJson = StripFileMarkers(buffer, sequence);
return OperationRequest.FromSourceText(
request.OperationSourceText,
+ errorHandlingMode: onError,
variableValues: JsonDocument.Parse(cleanedJson.AsSequence()),
features: features);
}
@@ -279,6 +289,7 @@ private static IOperationRequest BuildOperationRequest(
return VariableBatchRequest.FromSourceText(
request.OperationSourceText,
variableValues: JsonDocument.Parse(variableSequence),
+ errorHandlingMode: onError,
features: features);
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientConfiguration.cs b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientConfiguration.cs
index 05f1d1bab6e..f5f894792d7 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientConfiguration.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientConfiguration.cs
@@ -1,3 +1,5 @@
+using HotChocolate.Language;
+
namespace HotChocolate.Fusion.Execution.Clients;
///
@@ -11,14 +13,19 @@ public sealed class InMemorySourceSchemaClientConfiguration : ISourceSchemaClien
///
/// The name of the source schema.
/// The supported operation types.
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
public InMemorySourceSchemaClientConfiguration(
string name,
- SupportedOperationType supportedOperations = SupportedOperationType.All)
+ SupportedOperationType supportedOperations = SupportedOperationType.All,
+ ErrorHandlingMode? onError = null)
{
ArgumentException.ThrowIfNullOrEmpty(name);
Name = name;
SupportedOperations = supportedOperations;
+ OnError = onError;
}
///
@@ -26,4 +33,9 @@ public InMemorySourceSchemaClientConfiguration(
///
public SupportedOperationType SupportedOperations { get; }
+
+ ///
+ /// Gets the error handling mode requested by the source schema.
+ ///
+ public ErrorHandlingMode? OnError { get; }
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientFactory.cs b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientFactory.cs
index 0bb094e015f..aafe0d0826f 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientFactory.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Connectors.InMemory/InMemorySourceSchemaClientFactory.cs
@@ -39,6 +39,6 @@ protected override ISourceSchemaClient CreateClient(
InMemorySourceSchemaClientConfiguration configuration)
{
var proxy = new RequestExecutorProxy(_executorProvider, _executorEvents, configuration.Name);
- return new InMemorySourceSchemaClient(proxy, _formatter);
+ return new InMemorySourceSchemaClient(proxy, _formatter, configuration.OnError);
}
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Configuration/Parsers/HttpSourceSchemaClientConfigurationParser.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Configuration/Parsers/HttpSourceSchemaClientConfigurationParser.cs
index 6834861d732..b7f8b8f101e 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Configuration/Parsers/HttpSourceSchemaClientConfigurationParser.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Configuration/Parsers/HttpSourceSchemaClientConfigurationParser.cs
@@ -3,12 +3,13 @@
using System.Net.Http.Headers;
using System.Text.Json;
using HotChocolate.Fusion.Execution.Clients;
+using HotChocolate.Language;
namespace HotChocolate.Fusion.Configuration.Parsers;
///
/// Built-in parser that claims the http transport and produces a
-/// .
+/// .
///
internal sealed class HttpSourceSchemaClientConfigurationParser : ISourceSchemaClientConfigurationParser
{
@@ -27,16 +28,17 @@ public bool TryParse(
return false;
}
- private static SourceSchemaHttpClientConfiguration CreateHttpClientConfiguration(
+ private static HttpSourceSchemaClientConfiguration CreateHttpClientConfiguration(
string schemaName,
JsonElement http)
{
- var clientName = SourceSchemaHttpClientConfiguration.DefaultClientName;
+ var clientName = HttpSourceSchemaClientConfiguration.DefaultClientName;
var capabilities = SourceSchemaClientCapabilities.All;
var supportedOperations = SupportedOperationType.All;
ImmutableArray? defaultAcceptHeaderValues = null;
ImmutableArray? batchingAcceptHeaderValues = null;
ImmutableArray? subscriptionAcceptHeaderValues = null;
+ ErrorHandlingMode? onError = null;
if (http.TryGetProperty("clientName", out var clientNameProperty)
&& clientNameProperty.ValueKind is JsonValueKind.String
@@ -90,6 +92,14 @@ private static SourceSchemaHttpClientConfiguration CreateHttpClientConfiguration
}
}
+ if (capabilitiesElement.TryGetProperty("onError", out var onErrorElement)
+ && onErrorElement.ValueKind is JsonValueKind.String
+ && onErrorElement.GetString() is { } onErrorValue
+ && Enum.TryParse(onErrorValue, ignoreCase: true, out var parsedOnError))
+ {
+ onError = parsedOnError;
+ }
+
if (capabilitiesElement.TryGetProperty("subscriptions", out var subscriptionsElement))
{
if (subscriptionsElement.TryGetProperty("supported", out var supported)
@@ -112,12 +122,13 @@ private static SourceSchemaHttpClientConfiguration CreateHttpClientConfiguration
}
}
- return new SourceSchemaHttpClientConfiguration(
+ return new HttpSourceSchemaClientConfiguration(
name: schemaName,
httpClientName: clientName,
baseAddress: new Uri(http.GetProperty("url").GetString()!),
supportedOperations: supportedOperations,
capabilities: capabilities,
+ onError: onError,
defaultAcceptHeaderValues: defaultAcceptHeaderValues,
batchingAcceptHeaderValues: batchingAcceptHeaderValues,
subscriptionAcceptHeaderValues: subscriptionAcceptHeaderValues);
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.SourceSchemaClients.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.SourceSchemaClients.cs
index c8461909e44..4d7b0070c1b 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.SourceSchemaClients.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/DependencyInjection/CoreFusionGatewayBuilderExtensions.SourceSchemaClients.cs
@@ -4,6 +4,7 @@
using HotChocolate.Fusion.Execution;
using HotChocolate.Fusion.Execution.Clients;
using HotChocolate.Fusion.Execution.Nodes;
+using HotChocolate.Language;
namespace Microsoft.Extensions.DependencyInjection;
@@ -27,6 +28,9 @@ public static partial class CoreFusionGatewayBuilderExtensions
///
/// The client capabilities.
///
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
///
/// The Accept header values sent in case of a single, non-Subscription GraphQL request.
///
@@ -54,6 +58,7 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
Uri baseAddress,
SupportedOperationType supportedOperations = SupportedOperationType.All,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -67,6 +72,7 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
baseAddress,
supportedOperations,
capabilities,
+ onError,
defaultAcceptHeaderValues,
batchingAcceptHeaderValues,
subscriptionAcceptHeaderValues,
@@ -95,6 +101,9 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
///
/// The client capabilities.
///
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
///
/// The Accept header values sent in case of a single, non-Subscription GraphQL request.
///
@@ -123,6 +132,7 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
Uri baseAddress,
SupportedOperationType supportedOperations = SupportedOperationType.All,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -137,12 +147,13 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
return AddHttpClientConfiguration(
builder,
- new SourceSchemaHttpClientConfiguration(
+ new HttpSourceSchemaClientConfiguration(
name,
httpClientName,
baseAddress,
supportedOperations,
capabilities,
+ onError,
defaultAcceptHeaderValues,
batchingAcceptHeaderValues,
subscriptionAcceptHeaderValues,
@@ -165,7 +176,7 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
///
public static IFusionGatewayBuilder AddHttpClientConfiguration(
this IFusionGatewayBuilder builder,
- SourceSchemaHttpClientConfiguration configuration)
+ HttpSourceSchemaClientConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(configuration);
@@ -187,7 +198,7 @@ public static IFusionGatewayBuilder AddHttpClientConfiguration(
///
public static IFusionGatewayBuilder AddHttpClientConfiguration(
this IFusionGatewayBuilder builder,
- Func create)
+ Func create)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(create);
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClient.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClient.cs
similarity index 95%
rename from src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClient.cs
rename to src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClient.cs
index 5bf48cca33e..b891227bde3 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClient.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClient.cs
@@ -16,33 +16,35 @@ namespace HotChocolate.Fusion.Execution.Clients;
///
/// HTTP-based implementation of that sends GraphQL operations
/// to a downstream service over HTTP. Supports single requests, Apollo-style request batching,
-/// and variable batching depending on the configured .
+/// and variable batching depending on the configured .
///
-public sealed class SourceSchemaHttpClient : ISourceSchemaClient
+public sealed class HttpSourceSchemaClient : ISourceSchemaClient
{
private static readonly Uri s_unknownUri = new("http://unknown");
private static ReadOnlySpan VariableIndex => "variableIndex"u8;
private static ReadOnlySpan RequestIndex => "requestIndex"u8;
private readonly GraphQLHttpClient _client;
- private readonly SourceSchemaHttpClientConfiguration _configuration;
+ private readonly HttpSourceSchemaClientConfiguration _configuration;
+ private readonly ErrorHandlingMode? _onError;
private readonly bool _supportsVariableBatching;
private bool _disposed;
///
- /// Initializes a new instance of .
+ /// Initializes a new instance of .
///
/// The underlying HTTP client used to send requests.
/// The transport configuration for this source schema.
- public SourceSchemaHttpClient(
+ public HttpSourceSchemaClient(
GraphQLHttpClient client,
- SourceSchemaHttpClientConfiguration configuration)
+ HttpSourceSchemaClientConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(client);
ArgumentNullException.ThrowIfNull(configuration);
_client = client;
_configuration = configuration;
+ _onError = configuration.OnError;
var capabilities = configuration.Capabilities;
@@ -103,7 +105,7 @@ public IAsyncEnumerable ExecuteBatchStreamAsync(
if (ContainsSubscriptionRequest(requests))
{
throw new InvalidOperationException(
- FusionExecutionResources.SourceSchemaHttpClient_SubscriptionBatchNotSupported);
+ FusionExecutionResources.HttpSourceSchemaClient_SubscriptionBatchNotSupported);
}
var requiresFileUpload = requests[0].RequiresFileUpload;
@@ -244,7 +246,7 @@ private async IAsyncEnumerable ExecuteBatchStreamAsync(
result.Dispose();
throw new InvalidOperationException(
string.Format(
- FusionExecutionResources.SourceSchemaHttpClient_InvalidVariableIndex,
+ FusionExecutionResources.HttpSourceSchemaClient_InvalidVariableIndex,
variableIndex,
request.Node.Id));
}
@@ -297,7 +299,7 @@ private GraphQLHttpRequest CreateHttpRequest(
switch (originalRequest.Variables.Length)
{
case 0:
- httpRequest = new GraphQLHttpRequest(CreateSingleRequest(context, originalRequest, ref buffer))
+ httpRequest = new GraphQLHttpRequest(CreateSingleRequest(context, originalRequest, _onError, ref buffer))
{
Uri = _configuration.BaseAddress,
AcceptHeaderValue = defaultAcceptHeader
@@ -305,7 +307,7 @@ private GraphQLHttpRequest CreateHttpRequest(
break;
case 1:
- httpRequest = new GraphQLHttpRequest(CreateSingleRequest(context, originalRequest, ref buffer))
+ httpRequest = new GraphQLHttpRequest(CreateSingleRequest(context, originalRequest, _onError, ref buffer))
{
Uri = _configuration.BaseAddress,
AcceptHeaderValue = defaultAcceptHeader,
@@ -317,7 +319,7 @@ private GraphQLHttpRequest CreateHttpRequest(
if (!originalRequest.RequiresFileUpload && _supportsVariableBatching)
{
httpRequest =
- new GraphQLHttpRequest(CreateVariableBatchRequest(operationSourceText, originalRequest))
+ new GraphQLHttpRequest(CreateVariableBatchRequest(operationSourceText, originalRequest, _onError))
{
Uri = _configuration.BaseAddress,
AcceptHeaderValue = _configuration.BatchingAcceptHeaderValue
@@ -326,7 +328,7 @@ private GraphQLHttpRequest CreateHttpRequest(
else
{
httpRequest =
- new GraphQLHttpRequest(CreateOperationBatchRequest(context, originalRequest, ref buffer))
+ new GraphQLHttpRequest(CreateOperationBatchRequest(context, originalRequest, _onError, ref buffer))
{
Uri = _configuration.BaseAddress,
AcceptHeaderValue = _configuration.BatchingAcceptHeaderValue,
@@ -374,6 +376,7 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
CreateBatchUploadRequest(
sourceRequest,
VariableValues.Empty,
+ _onError,
buffer,
fileLookup,
fileEntries));
@@ -385,6 +388,7 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
CreateBatchUploadRequest(
sourceRequest,
sourceRequest.Variables[0],
+ _onError,
buffer,
fileLookup,
fileEntries,
@@ -399,6 +403,7 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
CreateBatchUploadRequest(
sourceRequest,
sourceRequest.Variables[j],
+ _onError,
buffer,
fileLookup,
fileEntries,
@@ -441,14 +446,14 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
switch (sourceRequest.Variables.Length)
{
case 0 or 1:
- batchRequests.Add(CreateSingleRequest(context, sourceRequest, ref buffer));
+ batchRequests.Add(CreateSingleRequest(context, sourceRequest, _onError, ref buffer));
break;
default:
if (_supportsVariableBatching)
{
batchRequests.Add(CreateVariableBatchRequest(
- sourceRequest.OperationSourceText, sourceRequest));
+ sourceRequest.OperationSourceText, sourceRequest, _onError));
}
else
{
@@ -458,7 +463,7 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
sourceRequest.OperationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: _onError,
variables: sourceRequest.Variables[j],
extensions: JsonSegment.Empty));
}
@@ -480,6 +485,7 @@ private GraphQLHttpRequest CreateHttpBatchRequest(
private static OperationRequest CreateSingleRequest(
OperationPlanContext context,
SourceSchemaClientRequest originalRequest,
+ ErrorHandlingMode? onError,
ref ChunkedArrayWriter? writer)
{
var variables = originalRequest.Variables.IsDefaultOrEmpty
@@ -496,7 +502,7 @@ private static OperationRequest CreateSingleRequest(
originalRequest.OperationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: onError,
variables: variables with { Values = cleanedJson },
extensions: JsonSegment.Empty,
fileMap: fileMap);
@@ -506,7 +512,7 @@ private static OperationRequest CreateSingleRequest(
originalRequest.OperationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: onError,
variables: variables,
extensions: JsonSegment.Empty);
}
@@ -514,6 +520,7 @@ private static OperationRequest CreateSingleRequest(
private static OperationRequest CreateBatchUploadRequest(
SourceSchemaClientRequest originalRequest,
VariableValues variables,
+ ErrorHandlingMode? onError,
ChunkedArrayWriter writer,
IFileLookup fileLookup,
ImmutableArray.Builder fileEntries,
@@ -525,7 +532,7 @@ private static OperationRequest CreateBatchUploadRequest(
originalRequest.OperationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: onError,
variables: variables with { Values = cleanedJson },
extensions: JsonSegment.Empty);
}
@@ -533,6 +540,7 @@ private static OperationRequest CreateBatchUploadRequest(
private static OperationBatchRequest CreateOperationBatchRequest(
OperationPlanContext context,
SourceSchemaClientRequest originalRequest,
+ ErrorHandlingMode? onError,
ref ChunkedArrayWriter? writer)
{
if (originalRequest.RequiresFileUpload
@@ -547,6 +555,7 @@ private static OperationBatchRequest CreateOperationBatchRequest(
requests[i] = CreateBatchUploadRequest(
originalRequest,
originalRequest.Variables[i],
+ onError,
writer,
fileLookup,
fileEntries,
@@ -567,7 +576,7 @@ private static OperationBatchRequest CreateOperationBatchRequest(
originalRequest.OperationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: onError,
variables: originalRequest.Variables[i],
extensions: JsonSegment.Empty);
}
@@ -578,13 +587,14 @@ private static OperationBatchRequest CreateOperationBatchRequest(
private static VariableBatchRequest CreateVariableBatchRequest(
string operationSourceText,
- SourceSchemaClientRequest originalRequest)
+ SourceSchemaClientRequest originalRequest,
+ ErrorHandlingMode? onError)
{
return new VariableBatchRequest(
operationSourceText,
id: null,
operationName: null,
- onError: null,
+ onError: onError,
variables: originalRequest.Variables,
extensions: JsonSegment.Empty);
}
@@ -706,8 +716,8 @@ public ValueTask DisposeAsync()
}
///
- /// Attaches and
- /// callbacks to
+ /// Attaches and
+ /// callbacks to
/// the HTTP request.
///
private void ConfigureCallbacks(
@@ -755,7 +765,7 @@ private static bool ContainsSubscriptionRequest(
///
private sealed class Response(
OperationPlanContext context,
- SourceSchemaHttpClientConfiguration configuration,
+ HttpSourceSchemaClientConfiguration configuration,
bool supportsVariableBatching,
ExecutionNode node,
OperationType operation,
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClientConfiguration.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientConfiguration.cs
similarity index 91%
rename from src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClientConfiguration.cs
rename to src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientConfiguration.cs
index 3e36b7f7442..a1a798345e7 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/SourceSchemaHttpClientConfiguration.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientConfiguration.cs
@@ -1,18 +1,19 @@
using System.Collections.Immutable;
using System.Net.Http.Headers;
using HotChocolate.Fusion.Execution.Nodes;
+using HotChocolate.Language;
namespace HotChocolate.Fusion.Execution.Clients;
///
/// Represents the configuration for fetching data from a source schema over HTTP.
///
-public class SourceSchemaHttpClientConfiguration : ISourceSchemaClientConfiguration
+public class HttpSourceSchemaClientConfiguration : ISourceSchemaClientConfiguration
{
public const string DefaultClientName = "fusion";
///
- /// Initializes a new instance of .
+ /// Initializes a new instance of .
///
///
/// The name of the source schema.
@@ -26,6 +27,9 @@ public class SourceSchemaHttpClientConfiguration : ISourceSchemaClientConfigurat
///
/// The client capabilities.
///
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
///
/// The Accept header values sent in case of a single, non-Subscription GraphQL request.
///
@@ -44,11 +48,12 @@ public class SourceSchemaHttpClientConfiguration : ISourceSchemaClientConfigurat
///
/// The action to call after a was materialized.
///
- public SourceSchemaHttpClientConfiguration(
+ public HttpSourceSchemaClientConfiguration(
string name,
Uri baseAddress,
SupportedOperationType supportedOperations = SupportedOperationType.All,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -61,6 +66,7 @@ public SourceSchemaHttpClientConfiguration(
baseAddress,
supportedOperations,
capabilities,
+ onError,
defaultAcceptHeaderValues,
batchingAcceptHeaderValues,
subscriptionAcceptHeaderValues,
@@ -71,7 +77,7 @@ public SourceSchemaHttpClientConfiguration(
}
///
- /// Initializes a new instance of .
+ /// Initializes a new instance of .
///
///
/// The name of the source schema.
@@ -88,6 +94,9 @@ public SourceSchemaHttpClientConfiguration(
///
/// The client capabilities.
///
+ ///
+ /// The error handling mode requested by the source schema.
+ ///
///
/// The Accept header values sent in case of a single, non-Subscription GraphQL request.
///
@@ -106,12 +115,13 @@ public SourceSchemaHttpClientConfiguration(
///
/// The action to call after a was materialized.
///
- public SourceSchemaHttpClientConfiguration(
+ public HttpSourceSchemaClientConfiguration(
string name,
string httpClientName,
Uri baseAddress,
SupportedOperationType supportedOperations = SupportedOperationType.All,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -128,6 +138,7 @@ public SourceSchemaHttpClientConfiguration(
BaseAddress = baseAddress;
SupportedOperations = supportedOperations;
Capabilities = capabilities;
+ OnError = onError;
DefaultAcceptHeaderValue = defaultAcceptHeaderValues is null
? AcceptContentTypes.DefaultHeader
@@ -171,6 +182,11 @@ public SourceSchemaHttpClientConfiguration(
///
public SourceSchemaClientCapabilities Capabilities { get; }
+ ///
+ /// Gets the error handling mode requested by the source schema.
+ ///
+ public ErrorHandlingMode? OnError { get; }
+
///
/// Gets a pre-formatted Accept header string for single, non-Subscription GraphQL requests.
///
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientFactory.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientFactory.cs
index 44e3bc3ed9e..714790180ec 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientFactory.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/HttpSourceSchemaClientFactory.cs
@@ -3,7 +3,7 @@
namespace HotChocolate.Fusion.Execution.Clients;
internal sealed class HttpSourceSchemaClientFactory
- : SourceSchemaClientFactory
+ : SourceSchemaClientFactory
{
private readonly IHttpClientFactory _httpClientFactory;
@@ -14,12 +14,12 @@ public HttpSourceSchemaClientFactory(IHttpClientFactory httpClientFactory)
}
protected override ISourceSchemaClient CreateClient(
- SourceSchemaHttpClientConfiguration configuration)
+ HttpSourceSchemaClientConfiguration configuration)
{
var httpClient = _httpClientFactory.CreateClient(configuration.HttpClientName);
httpClient.BaseAddress = configuration.BaseAddress;
- return new SourceSchemaHttpClient(
+ return new HttpSourceSchemaClient(
GraphQLHttpClient.Create(httpClient, disposeHttpClient: true),
configuration);
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/RequestCallbackState.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/RequestCallbackState.cs
index 7ee507da20f..0bcdfe88692 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/RequestCallbackState.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Clients/RequestCallbackState.cs
@@ -4,8 +4,8 @@ namespace HotChocolate.Fusion.Execution.Clients;
///
/// Carries the context needed by the transport-level request hooks
-/// ( and
-/// ).
+/// ( and
+/// ).
/// Stored on
/// so the static hook delegates can access it without capturing.
///
@@ -14,7 +14,7 @@ public readonly struct RequestCallbackState
public RequestCallbackState(
OperationPlanContext context,
ExecutionNode node,
- SourceSchemaHttpClientConfiguration configuration)
+ HttpSourceSchemaClientConfiguration configuration)
{
Context = context;
Node = node;
@@ -25,5 +25,5 @@ public RequestCallbackState(
public ExecutionNode Node { get; }
- public SourceSchemaHttpClientConfiguration Configuration { get; }
+ public HttpSourceSchemaClientConfiguration Configuration { get; }
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/ThrowHelper.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/ThrowHelper.cs
index 8f0caacece9..e14e23f204a 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/ThrowHelper.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Execution/ThrowHelper.cs
@@ -30,12 +30,12 @@ public static InvalidOperationException SingleOperationRequired()
public static InvalidOperationException RequestIndexOutOfRange(int requestIndex)
=> new(string.Format(
- FusionExecutionResources.SourceSchemaHttpClient_InvalidRequestIndex,
+ FusionExecutionResources.HttpSourceSchemaClient_InvalidRequestIndex,
requestIndex));
public static InvalidOperationException VariableIndexOutOfRange(int variableIndex)
=> new(string.Format(
- FusionExecutionResources.SourceSchemaHttpClient_VariableIndexOutOfRange,
+ FusionExecutionResources.HttpSourceSchemaClient_VariableIndexOutOfRange,
variableIndex));
public static ArgumentException InvalidClientConfiguration(Type expected, Type actual)
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.Designer.cs b/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.Designer.cs
index e6fecdecdd1..94d2c29afa8 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.Designer.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.Designer.cs
@@ -99,39 +99,39 @@ internal static string SourceSchemaRequestDispatcher_NodeNotRegisteredInGroup {
}
}
- internal static string SourceSchemaHttpClient_SubscriptionBatchNotSupported {
+ internal static string HttpSourceSchemaClient_SubscriptionBatchNotSupported {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_SubscriptionBatchNotSupported", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_SubscriptionBatchNotSupported", resourceCulture);
}
}
- internal static string SourceSchemaHttpClient_InvalidRequestIndex {
+ internal static string HttpSourceSchemaClient_InvalidRequestIndex {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_InvalidRequestIndex", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_InvalidRequestIndex", resourceCulture);
}
}
- internal static string SourceSchemaHttpClient_NoResponseChannelForNode {
+ internal static string HttpSourceSchemaClient_NoResponseChannelForNode {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_NoResponseChannelForNode", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_NoResponseChannelForNode", resourceCulture);
}
}
- internal static string SourceSchemaHttpClient_InvalidVariableIndex {
+ internal static string HttpSourceSchemaClient_InvalidVariableIndex {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_InvalidVariableIndex", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_InvalidVariableIndex", resourceCulture);
}
}
- internal static string SourceSchemaHttpClient_VariableIndexOutOfRange {
+ internal static string HttpSourceSchemaClient_VariableIndexOutOfRange {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_VariableIndexOutOfRange", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_VariableIndexOutOfRange", resourceCulture);
}
}
- internal static string SourceSchemaHttpClient_NoResultForNode {
+ internal static string HttpSourceSchemaClient_NoResultForNode {
get {
- return ResourceManager.GetString("SourceSchemaHttpClient_NoResultForNode", resourceCulture);
+ return ResourceManager.GetString("HttpSourceSchemaClient_NoResultForNode", resourceCulture);
}
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.resx b/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.resx
index b8857db71d4..4c51a8dd2b9 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.resx
+++ b/src/HotChocolate/Fusion/src/Fusion.Execution/Properties/FusionExecutionResources.resx
@@ -45,22 +45,22 @@
The execution node with id '{0}' is registered to be part of batching group '{1}', but no request was submitted for that node.
-
+
Subscription requests are not supported by batch execution.
-
+
The batch response contains an invalid requestIndex '{0}'.
-
+
No response channel exists for node '{0}'.
-
+
The batch response contains an invalid variableIndex '{0}' for node '{1}'.
-
+
The batch response contains an out-of-range variableIndex '{0}'.
-
+
The batch response does not contain any result for node '{0}'.
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.CreateSourceSchema.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.CreateSourceSchema.cs
index 97c3ecb5213..89d86275da2 100644
--- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.CreateSourceSchema.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.CreateSourceSchema.cs
@@ -4,6 +4,7 @@
using HotChocolate.Configuration;
using HotChocolate.Execution.Configuration;
using HotChocolate.Fusion.Execution.Clients;
+using HotChocolate.Language;
using HotChocolate.Types.Descriptors;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
@@ -23,6 +24,7 @@ protected TestServer CreateSourceSchema(
bool isOffline = false,
bool isTimingOut = false,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -54,6 +56,7 @@ protected TestServer CreateSourceSchema(
opt.ConfigureHttpClient = configureHttpClient;
opt.MockHttpResponse = mockHttpResponse;
opt.Capabilities = capabilities;
+ opt.OnError = onError;
opt.DefaultAcceptHeaderValues = defaultAcceptHeaderValues;
opt.BatchingAcceptHeaderValues = batchingAcceptHeaderValues;
opt.SubscriptionAcceptHeaderValues = subscriptionAcceptHeaderValues;
@@ -70,6 +73,7 @@ protected TestServer CreateSourceSchema(
Action? configureHttpClient = null,
HttpClient? httpClient = null,
SourceSchemaClientCapabilities capabilities = SourceSchemaClientCapabilities.All,
+ ErrorHandlingMode? onError = null,
ImmutableArray? defaultAcceptHeaderValues = null,
ImmutableArray? batchingAcceptHeaderValues = null,
ImmutableArray? subscriptionAcceptHeaderValues = null,
@@ -96,6 +100,7 @@ protected TestServer CreateSourceSchema(
opt.HttpClient = httpClient;
opt.MockHttpResponse = mockHttpResponse;
opt.Capabilities = capabilities;
+ opt.OnError = onError;
opt.DefaultAcceptHeaderValues = defaultAcceptHeaderValues;
opt.BatchingAcceptHeaderValues = batchingAcceptHeaderValues;
opt.SubscriptionAcceptHeaderValues = subscriptionAcceptHeaderValues;
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.MatchSnapshot.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.MatchSnapshot.cs
index 367cda5dccf..06dee9edcad 100644
--- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.MatchSnapshot.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.MatchSnapshot.cs
@@ -347,6 +347,8 @@ private static void WriteSourceSchema(
writer.Unindent();
}
+ TryWriteOnError(writer, item);
+
writer.Unindent();
}
@@ -354,6 +356,8 @@ private static void WriteSourceSchema(
}
else
{
+ TryWriteOnError(writer, jsonBody.RootElement);
+
writer.WriteLine("document: |");
writer.Indent();
@@ -523,6 +527,22 @@ private static void WriteOperationRequest(
}
}
+ private static void TryWriteOnError(CodeWriter writer, JsonElement requestElement)
+ {
+ if (requestElement.ValueKind is not JsonValueKind.Object
+ || !requestElement.TryGetProperty("onError", out var onErrorProperty)
+ || onErrorProperty.ValueKind is not JsonValueKind.String
+ || !Enum.TryParse(
+ onErrorProperty.GetString(),
+ ignoreCase: true,
+ out var onError))
+ {
+ return;
+ }
+
+ writer.WriteLine("onError: {0}", onError);
+ }
+
private static void WriteFormattedJson(CodeWriter writer, JsonElement json)
{
var memoryStream = new MemoryStream();
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs
index 2c7fc333973..d519e1ff63d 100644
--- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs
@@ -15,6 +15,7 @@
using HotChocolate.Fusion.Execution.Nodes;
using HotChocolate.Fusion.Logging;
using HotChocolate.Fusion.Options;
+using HotChocolate.Language;
using HotChocolate.Transport.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@@ -69,6 +70,7 @@ protected async Task CreateCompositeSchemaAsync(
name,
new Uri("http://localhost:5000/graphql"),
capabilities: sourceSchemaOptions.Capabilities,
+ onError: sourceSchemaOptions.OnError,
defaultAcceptHeaderValues: sourceSchemaOptions.DefaultAcceptHeaderValues,
batchingAcceptHeaderValues: sourceSchemaOptions.BatchingAcceptHeaderValues,
subscriptionAcceptHeaderValues: sourceSchemaOptions.SubscriptionAcceptHeaderValues,
@@ -336,6 +338,8 @@ private sealed class SourceSchemaOptions
public ImmutableArray? BatchingAcceptHeaderValues { get; set; }
public ImmutableArray? SubscriptionAcceptHeaderValues { get; set; }
+
+ public ErrorHandlingMode? OnError { get; set; }
}
private sealed class OperationPlanHttpRequestInterceptor : DefaultHttpRequestInterceptor
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs
index 4347a8df220..6ec46af7ccd 100644
--- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs
@@ -49,7 +49,7 @@ public async Task Error_On_Root_Field(ErrorHandlingMode onError)
}
[Fact]
- public async Task SchemaDefault_Null_AppliesWithoutPerRequestOverride()
+ public async Task OnError_SchemaDefault_Null_AppliesWithoutPerRequestOverride()
{
// arrange
using var server1 = CreateSourceSchema(
@@ -83,6 +83,40 @@ public async Task SchemaDefault_Null_AppliesWithoutPerRequestOverride()
await MatchSnapshotAsync(gateway, request, result);
}
+ [Fact]
+ public async Task OnError_Null_OnSourceSchema_Forwards_To_Subgraph_Request()
+ {
+ // arrange
+ using var server1 = CreateSourceSchema(
+ "A",
+ b => b.AddQueryType(),
+ onError: ErrorHandlingMode.Null);
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("A", server1)
+ ]);
+
+ // act
+ using var client = GraphQLHttpClient.Create(gateway.CreateClient());
+
+ var request = new OperationRequest(
+ """
+ {
+ productById(id: 1) {
+ name
+ }
+ }
+ """);
+
+ using var result = await client.PostAsync(
+ request,
+ new Uri("http://localhost:5000/graphql"));
+
+ // assert
+ await MatchSnapshotAsync(gateway, request, result);
+ }
+
[Theory]
[InlineData(ErrorHandlingMode.Propagate)]
[InlineData(ErrorHandlingMode.Null)]
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_Null_OnSourceSchema_Forwards_To_Subgraph_Request.yaml b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_Null_OnSourceSchema_Forwards_To_Subgraph_Request.yaml
new file mode 100644
index 00000000000..2b7ece205f6
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_Null_OnSourceSchema_Forwards_To_Subgraph_Request.yaml
@@ -0,0 +1,85 @@
+title: OnError_Null_OnSourceSchema_Forwards_To_Subgraph_Request
+request:
+ document: |
+ {
+ productById(id: 1) {
+ name
+ }
+ }
+response:
+ body: |
+ {
+ "data": {
+ "productById": null
+ },
+ "errors": [
+ {
+ "message": "Could not resolve Product",
+ "path": [
+ "productById"
+ ]
+ }
+ ]
+ }
+sourceSchemas:
+ - name: A
+ schema: |
+ schema {
+ query: Query
+ }
+
+ type Product {
+ name: String!
+ id: Int!
+ }
+
+ type Query {
+ productById(id: Int!): Product @lookup
+ }
+ interactions:
+ - request:
+ accept: application/graphql-response+json; charset=utf-8, application/json; charset=utf-8, application/jsonl; charset=utf-8, text/event-stream; charset=utf-8
+ onError: Null
+ document: |
+ query Op_97fd38fc_1 {
+ productById(id: 1) {
+ name
+ }
+ }
+ response:
+ results:
+ - |
+ {
+ "errors": [
+ {
+ "message": "Could not resolve Product",
+ "path": [
+ "productById"
+ ]
+ }
+ ],
+ "data": {
+ "productById": null
+ }
+ }
+operationPlan:
+ operation:
+ - document: |
+ {
+ productById(id: 1) {
+ name
+ }
+ }
+ hash: 97fd38fcea394bc6badd7ea22c6de660
+ searchSpace: 1
+ expandedNodes: 1
+ nodes:
+ - id: 1
+ type: Operation
+ schema: A
+ operation: |
+ query Op_97fd38fc_1 {
+ productById(id: 1) {
+ name
+ }
+ }
diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml
similarity index 96%
rename from src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml
rename to src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml
index 96c15b68ef7..674c7606273 100644
--- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml
+++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.OnError_SchemaDefault_Null_AppliesWithoutPerRequestOverride.yaml
@@ -1,4 +1,4 @@
-title: SchemaDefault_Null_AppliesWithoutPerRequestOverride
+title: OnError_SchemaDefault_Null_AppliesWithoutPerRequestOverride
request:
document: |
{
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap
index 26a84ad5d80..43f91265ce4 100644
--- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityExecutionDiagnosticListenerTests.Source_Schema_Transport_Error.snap
@@ -157,7 +157,7 @@
},
{
"Key": "exception.stacktrace",
- "Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamCoreAsync(CancellationToken cancellationToken)+MoveNext() in SourceSchemaHttpClient.cs\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.ReadAsResultStreamCoreAsync(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in SourceSchemaHttpClient.cs\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in SourceSchemaHttpClient.cs\n at HotChocolate.Fusion.Execution.Clients.SourceSchemaHttpClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
+ "Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.Response.ReadAsResultStreamCoreAsync(CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.Response.ReadAsResultStreamCoreAsync(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.Response.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
},
{
"Key": "exception.type",
diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/HttpSourceSchemaClientConfigurationParserTests.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/HttpSourceSchemaClientConfigurationParserTests.cs
new file mode 100644
index 00000000000..effa0f3599e
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/HttpSourceSchemaClientConfigurationParserTests.cs
@@ -0,0 +1,507 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json;
+using HotChocolate.Buffers;
+using HotChocolate.Features;
+using HotChocolate.Fusion.Configuration.Parsers;
+using HotChocolate.Fusion.Execution;
+using HotChocolate.Fusion.Execution.Clients;
+using HotChocolate.Language;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HotChocolate.Fusion.Configuration;
+
+public class HttpSourceSchemaClientConfigurationParserTests : FusionTestBase
+{
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Return_False_When_Http_Transport_Missing()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "a": {
+ "transports": {
+ "websockets": { "url": "ws://localhost:5000/graphql" }
+ }
+ }
+ }
+ }
+ """,
+ "a");
+ var transport = GetTransportProperty(sourceSchema, "websockets");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.False(claimed);
+ Assert.Null(configuration);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Produce_Configuration_When_Http_Transport_Present()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "clientName": "products-client"
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Summarize(http).MatchInlineSnapshot(
+ """
+ Name: products
+ HttpClientName: products-client
+ BaseAddress: http://localhost:5000/graphql
+ SupportedOperations: All
+ Capabilities: All
+ OnError:
+ """);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Disable_VariableBatching_Capability_When_Configured()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "batching": {
+ "variableBatching": false
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Equal(SourceSchemaClientCapabilities.RequestBatching, http.Capabilities);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Disable_Subscription_Operations_When_Configured()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "subscriptions": {
+ "supported": false
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Equal(
+ SupportedOperationType.Query | SupportedOperationType.Mutation,
+ http.SupportedOperations);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Set_OnError_To_Null_Mode_When_Configured_As_NULL()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "onError": "NULL"
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Equal(ErrorHandlingMode.Null, http.OnError);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Set_OnError_To_Propagate_When_Configured_As_PROPAGATE()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "onError": "PROPAGATE"
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Equal(ErrorHandlingMode.Propagate, http.OnError);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Leave_OnError_Null_When_Property_Missing()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {}
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Null(http.OnError);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Leave_OnError_Null_When_Value_Is_Json_Null()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "onError": null
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Null(http.OnError);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Leave_OnError_Null_When_Value_Is_Unknown_String()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql",
+ "capabilities": {
+ "onError": "BOGUS"
+ }
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Null(http.OnError);
+ }
+
+ [Fact]
+ public void HttpSourceSchemaClientConfigurationParser_Should_Default_ClientName_When_Not_Provided()
+ {
+ // arrange
+ var sourceSchema = GetSourceSchemaProperty(
+ """
+ {
+ "sourceSchemas": {
+ "products": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql"
+ }
+ }
+ }
+ }
+ }
+ """,
+ "products");
+ var transport = GetTransportProperty(sourceSchema, "http");
+ var parser = new HttpSourceSchemaClientConfigurationParser();
+
+ // act
+ var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
+
+ // assert
+ Assert.True(claimed);
+ var http = Assert.IsType(configuration);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, http.HttpClientName);
+ }
+
+ [Fact]
+ public async Task CreateClientConfigurations_Should_Throw_When_No_Parser_Claims_Schema()
+ {
+ // arrange
+ var config = CreateConfigurationWithSettings(
+ """
+ {
+ "sourceSchemas": {
+ "a": {
+ "transports": {
+ "xyz": { "url": "xyz://localhost" }
+ }
+ }
+ }
+ }
+ """);
+
+ var configProvider = new TestFusionConfigurationProvider(config);
+
+ var services =
+ new ServiceCollection()
+ .AddGraphQLGateway()
+ .AddConfigurationProvider(_ => configProvider)
+ .Services
+ .BuildServiceProvider();
+
+ var manager = services.GetRequiredService();
+
+ // act
+ async Task Act() => await manager.GetExecutorAsync();
+
+ // assert
+ var exception = await Assert.ThrowsAsync(Act);
+ Assert.Equal("No parser claimed any transport for source schema 'a'.", exception.Message);
+ }
+
+ [Fact]
+ public async Task CreateClientConfigurations_Should_Prefer_User_Parser_Over_Builtin()
+ {
+ // arrange
+ var config = CreateConfigurationWithSettings(
+ """
+ {
+ "sourceSchemas": {
+ "a": {
+ "transports": {
+ "http": {
+ "url": "http://localhost:5000/graphql"
+ }
+ }
+ }
+ }
+ }
+ """);
+
+ var configProvider = new TestFusionConfigurationProvider(config);
+ var userParser = new AlwaysClaimParser();
+
+ var builder =
+ new ServiceCollection()
+ .AddGraphQLGateway()
+ .AddConfigurationProvider(_ => configProvider);
+
+ FusionSetupUtilities.Configure(
+ builder,
+ setup => setup.SourceSchemaClientConfigurationParsers.Add(userParser));
+
+ var services = builder.Services.BuildServiceProvider();
+
+ var manager = services.GetRequiredService();
+
+ // act
+ var executor = await manager.GetExecutorAsync();
+
+ // assert
+ var clientConfigs = executor.Schema.Features.GetRequired();
+ Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
+ Assert.IsType(queryConfig);
+ }
+
+ private static JsonProperty GetSourceSchemaProperty(string settingsJson, string schemaName)
+ {
+ var document = JsonDocument.Parse(settingsJson);
+ var sourceSchemas = document.RootElement.GetProperty("sourceSchemas");
+
+ foreach (var candidate in sourceSchemas.EnumerateObject())
+ {
+ if (candidate.Name == schemaName)
+ {
+ return candidate;
+ }
+ }
+
+ throw new InvalidOperationException($"Source schema '{schemaName}' not found.");
+ }
+
+ private static JsonProperty GetTransportProperty(JsonProperty sourceSchema, string transportName)
+ {
+ var transports = sourceSchema.Value.GetProperty("transports");
+
+ foreach (var candidate in transports.EnumerateObject())
+ {
+ if (candidate.Name == transportName)
+ {
+ return candidate;
+ }
+ }
+
+ throw new InvalidOperationException($"Transport '{transportName}' not found.");
+ }
+
+ private static string Summarize(HttpSourceSchemaClientConfiguration configuration)
+ {
+ return $"""
+ Name: {configuration.Name}
+ HttpClientName: {configuration.HttpClientName}
+ BaseAddress: {configuration.BaseAddress}
+ SupportedOperations: {configuration.SupportedOperations}
+ Capabilities: {configuration.Capabilities}
+ OnError: {configuration.OnError?.ToString() ?? ""}
+ """;
+ }
+
+ private static FusionConfiguration CreateConfigurationWithSettings(string settingsJson)
+ {
+ var compositeSchema = ComposeSchemaDocument("type Query { foo: String }");
+ var settings = JsonDocument.Parse(settingsJson);
+
+ return new FusionConfiguration(
+ compositeSchema,
+ new JsonDocumentOwner(settings));
+ }
+
+ private sealed class AlwaysClaimParser : ISourceSchemaClientConfigurationParser
+ {
+ public bool TryParse(
+ JsonProperty sourceSchema,
+ JsonProperty transport,
+ [NotNullWhen(true)] out ISourceSchemaClientConfiguration? configuration)
+ {
+ configuration = new StubClientConfiguration(sourceSchema.Name);
+ return true;
+ }
+ }
+
+ private sealed class StubClientConfiguration(string name) : ISourceSchemaClientConfiguration
+ {
+ public string Name { get; } = name;
+
+ public SupportedOperationType SupportedOperations => SupportedOperationType.All;
+ }
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/ParsersTests.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/ParsersTests.cs
deleted file mode 100644
index 212c974c0c9..00000000000
--- a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Configuration/ParsersTests.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Text.Json;
-using HotChocolate.Buffers;
-using HotChocolate.Features;
-using HotChocolate.Fusion.Configuration.Parsers;
-using HotChocolate.Fusion.Execution;
-using HotChocolate.Fusion.Execution.Clients;
-using HotChocolate.Language;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace HotChocolate.Fusion.Configuration;
-
-public class ParsersTests : FusionTestBase
-{
- [Fact]
- public void HttpSourceSchemaClientConfigurationParser_Should_Return_False_When_Http_Transport_Missing()
- {
- // arrange
- var sourceSchema = GetSourceSchemaProperty(
- """
- {
- "sourceSchemas": {
- "a": {
- "transports": {
- "websockets": { "url": "ws://localhost:5000/graphql" }
- }
- }
- }
- }
- """,
- "a");
- var transport = GetTransportProperty(sourceSchema, "websockets");
- var parser = new HttpSourceSchemaClientConfigurationParser();
-
- // act
- var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
-
- // assert
- Assert.False(claimed);
- Assert.Null(configuration);
- }
-
- [Fact]
- public void HttpSourceSchemaClientConfigurationParser_Should_Produce_Configuration_When_Http_Transport_Present()
- {
- // arrange
- var sourceSchema = GetSourceSchemaProperty(
- """
- {
- "sourceSchemas": {
- "products": {
- "transports": {
- "http": {
- "url": "http://localhost:5000/graphql",
- "clientName": "products-client"
- }
- }
- }
- }
- }
- """,
- "products");
- var transport = GetTransportProperty(sourceSchema, "http");
- var parser = new HttpSourceSchemaClientConfigurationParser();
-
- // act
- var claimed = parser.TryParse(sourceSchema, transport, out var configuration);
-
- // assert
- Assert.True(claimed);
- var http = Assert.IsType(configuration);
- Summarize(http).MatchInlineSnapshot(
- """
- Name: products
- HttpClientName: products-client
- BaseAddress: http://localhost:5000/graphql
- SupportedOperations: All
- Capabilities: All
- """);
- }
-
- [Fact]
- public async Task CreateClientConfigurations_Should_Throw_When_No_Parser_Claims_Schema()
- {
- // arrange
- var config = CreateConfigurationWithSettings(
- """
- {
- "sourceSchemas": {
- "a": {
- "transports": {
- "xyz": { "url": "xyz://localhost" }
- }
- }
- }
- }
- """);
-
- var configProvider = new TestFusionConfigurationProvider(config);
-
- var services =
- new ServiceCollection()
- .AddGraphQLGateway()
- .AddConfigurationProvider(_ => configProvider)
- .Services
- .BuildServiceProvider();
-
- var manager = services.GetRequiredService();
-
- // act
- async Task Act() => await manager.GetExecutorAsync();
-
- // assert
- var exception = await Assert.ThrowsAsync(Act);
- Assert.Equal("No parser claimed any transport for source schema 'a'.", exception.Message);
- }
-
- [Fact]
- public async Task CreateClientConfigurations_Should_Prefer_User_Parser_Over_Builtin()
- {
- // arrange
- var config = CreateConfigurationWithSettings(
- """
- {
- "sourceSchemas": {
- "a": {
- "transports": {
- "http": {
- "url": "http://localhost:5000/graphql"
- }
- }
- }
- }
- }
- """);
-
- var configProvider = new TestFusionConfigurationProvider(config);
- var userParser = new AlwaysClaimParser();
-
- var builder =
- new ServiceCollection()
- .AddGraphQLGateway()
- .AddConfigurationProvider(_ => configProvider);
-
- FusionSetupUtilities.Configure(
- builder,
- setup => setup.SourceSchemaClientConfigurationParsers.Add(userParser));
-
- var services = builder.Services.BuildServiceProvider();
-
- var manager = services.GetRequiredService();
-
- // act
- var executor = await manager.GetExecutorAsync();
-
- // assert
- var clientConfigs = executor.Schema.Features.GetRequired();
- Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- Assert.IsType(queryConfig);
- }
-
- private static JsonProperty GetSourceSchemaProperty(string settingsJson, string schemaName)
- {
- var document = JsonDocument.Parse(settingsJson);
- var sourceSchemas = document.RootElement.GetProperty("sourceSchemas");
-
- foreach (var candidate in sourceSchemas.EnumerateObject())
- {
- if (candidate.Name == schemaName)
- {
- return candidate;
- }
- }
-
- throw new InvalidOperationException($"Source schema '{schemaName}' not found.");
- }
-
- private static JsonProperty GetTransportProperty(JsonProperty sourceSchema, string transportName)
- {
- var transports = sourceSchema.Value.GetProperty("transports");
-
- foreach (var candidate in transports.EnumerateObject())
- {
- if (candidate.Name == transportName)
- {
- return candidate;
- }
- }
-
- throw new InvalidOperationException($"Transport '{transportName}' not found.");
- }
-
- private static string Summarize(SourceSchemaHttpClientConfiguration configuration)
- {
- return $"""
- Name: {configuration.Name}
- HttpClientName: {configuration.HttpClientName}
- BaseAddress: {configuration.BaseAddress}
- SupportedOperations: {configuration.SupportedOperations}
- Capabilities: {configuration.Capabilities}
- """;
- }
-
- private static FusionConfiguration CreateConfigurationWithSettings(string settingsJson)
- {
- var compositeSchema = ComposeSchemaDocument("type Query { foo: String }");
- var settings = JsonDocument.Parse(settingsJson);
-
- return new FusionConfiguration(
- compositeSchema,
- new JsonDocumentOwner(settings));
- }
-
- private sealed class AlwaysClaimParser : ISourceSchemaClientConfigurationParser
- {
- public bool TryParse(
- JsonProperty sourceSchema,
- JsonProperty transport,
- [NotNullWhen(true)] out ISourceSchemaClientConfiguration? configuration)
- {
- configuration = new StubClientConfiguration(sourceSchema.Name);
- return true;
- }
- }
-
- private sealed class StubClientConfiguration(string name) : ISourceSchemaClientConfiguration
- {
- public string Name { get; } = name;
-
- public SupportedOperationType SupportedOperations => SupportedOperationType.All;
- }
-}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Execution/FusionRequestExecutorManagerTests.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Execution/FusionRequestExecutorManagerTests.cs
index 4f06ad8ad32..514c2c596a8 100644
--- a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Execution/FusionRequestExecutorManagerTests.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Execution/FusionRequestExecutorManagerTests.cs
@@ -379,9 +379,9 @@ public async Task CreateHttpClientConfiguration_Should_UseDefaults_When_NoCapabi
Assert.True(clientConfigs.TryGet("a", OperationType.Mutation, out _));
Assert.True(clientConfigs.TryGet("a", OperationType.Subscription, out _));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
- Assert.Equal(SourceSchemaHttpClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
Assert.Equal(SourceSchemaClientCapabilities.All, httpConfig.Capabilities);
Assert.Equal(SupportedOperationType.All, httpConfig.SupportedOperations);
@@ -428,7 +428,7 @@ public async Task CreateHttpClientConfiguration_Should_UseCustomClientName_When_
var clientConfigs = executor.Schema.Features.GetRequired();
Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
Assert.Equal("my-custom-client", httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
@@ -481,9 +481,9 @@ public async Task CreateHttpClientConfiguration_Should_DisableVariableBatching_W
var clientConfigs = executor.Schema.Features.GetRequired();
Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
- Assert.Equal(SourceSchemaHttpClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
Assert.Equal(SourceSchemaClientCapabilities.RequestBatching, httpConfig.Capabilities);
Assert.Equal(SupportedOperationType.All, httpConfig.SupportedOperations);
@@ -534,9 +534,9 @@ public async Task CreateHttpClientConfiguration_Should_DisableRequestBatching_Wh
var clientConfigs = executor.Schema.Features.GetRequired();
Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
- Assert.Equal(SourceSchemaHttpClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
Assert.Equal(SourceSchemaClientCapabilities.VariableBatching, httpConfig.Capabilities);
Assert.Equal(SupportedOperationType.All, httpConfig.SupportedOperations);
@@ -587,9 +587,9 @@ public async Task CreateHttpClientConfiguration_Should_DisableSubscriptions_When
var clientConfigs = executor.Schema.Features.GetRequired();
Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
- Assert.Equal(SourceSchemaHttpClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
Assert.Equal(SourceSchemaClientCapabilities.All, httpConfig.Capabilities);
Assert.Equal(SupportedOperationType.Query | SupportedOperationType.Mutation, httpConfig.SupportedOperations);
@@ -648,9 +648,9 @@ public async Task CreateHttpClientConfiguration_Should_UseCustomFormats_When_Spe
var clientConfigs = executor.Schema.Features.GetRequired();
Assert.True(clientConfigs.TryGet("a", OperationType.Query, out var queryConfig));
- var httpConfig = Assert.IsType(queryConfig);
+ var httpConfig = Assert.IsType(queryConfig);
Assert.Equal("a", httpConfig.Name);
- Assert.Equal(SourceSchemaHttpClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
+ Assert.Equal(HttpSourceSchemaClientConfiguration.DefaultClientName, httpConfig.HttpClientName);
Assert.Equal(new Uri("http://localhost:5000/graphql"), httpConfig.BaseAddress);
Assert.Equal(SourceSchemaClientCapabilities.All, httpConfig.Capabilities);
Assert.Equal(SupportedOperationType.All, httpConfig.SupportedOperations);