Skip to content

Commit

Permalink
Added support for AggregateError to the execution pipeline. (#4014)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jul 30, 2021
1 parent 35e7295 commit ec4c539
Show file tree
Hide file tree
Showing 18 changed files with 324 additions and 95 deletions.
29 changes: 29 additions & 0 deletions src/HotChocolate/Core/src/Abstractions/AggregateError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using HotChocolate.Properties;

namespace HotChocolate
{
/// <summary>
/// An aggregate error allows to pass a collection of error in a single error object.
/// </summary>
public class AggregateError : Error
{
public AggregateError(IEnumerable<IError> errors)
: base(AbstractionResources.AggregateError_Message)
{
Errors = errors.ToArray();
}

public AggregateError(params IError[] errors)
: base(AbstractionResources.AggregateError_Message)
{
Errors = errors.ToArray();
}

/// <summary>
/// Gets the actual errors.
/// </summary>
public IReadOnlyList<IError> Errors { get; }
}
}
14 changes: 10 additions & 4 deletions src/HotChocolate/Core/src/Abstractions/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@

namespace HotChocolate
{
public sealed class Error : IError
/// <summary>
/// Represents a GraphQL execution error.
/// </summary>
public class Error : IError
{
private const string _codePropertyName = "code";

/// <summary>
/// Initializes a new instance of <see cref="Error"/>.
/// </summary>
public Error(
string message,
string? code = null,
Expand Down Expand Up @@ -77,8 +83,8 @@ public IError WithCode(string? code)
return RemoveCode();
}

var extensions = Extensions is null
? new OrderedDictionary<string, object?>() { [_codePropertyName] = code }
OrderedDictionary<string, object?> extensions = Extensions is null
? new OrderedDictionary<string, object?> { [_codePropertyName] = code }
: new OrderedDictionary<string, object?>(Extensions) { [_codePropertyName] = code };
return new Error(Message, code, Path, Locations, extensions, Exception);
}
Expand Down Expand Up @@ -157,7 +163,7 @@ public IError SetExtension(string key, object? value)
nameof(key));
}

var extensions = Extensions is { }
OrderedDictionary<string, object?> extensions = Extensions is { }
? new OrderedDictionary<string, object?>(Extensions)
: new OrderedDictionary<string, object?>();
extensions[key] = value;
Expand Down
36 changes: 29 additions & 7 deletions src/HotChocolate/Core/src/Abstractions/ErrorHandlerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,38 @@ public static IReadOnlyList<IError> Handle(
throw new ArgumentNullException(nameof(errors));
}

return HandleEnumerator(errorHandler, errors).ToList();
}
var result = new List<IError>();

private static IEnumerable<IError> HandleEnumerator(
IErrorHandler errorHandler,
IEnumerable<IError> errors)
{
foreach (IError error in errors)
{
yield return errorHandler.Handle(error);
if (error is AggregateError aggregateError)
{
foreach (var innerError in aggregateError.Errors)
{
AddProcessed(errorHandler.Handle(innerError));
}
}
else
{
AddProcessed(errorHandler.Handle(error));
}
}

return result;

void AddProcessed(IError error)
{
if (error is AggregateError aggregateError)
{
foreach (var innerError in aggregateError.Errors)
{
result.Add(innerError);
}
}
else
{
result.Add(error);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ public IQueryResultBuilder AddError(IError error)
throw new ArgumentNullException(nameof(error));
}

if (_errors is null)
{
_errors = new List<IError>();
}

_errors ??= new List<IError>();
_errors.Add(error);
return this;
}
Expand All @@ -48,11 +44,7 @@ public IQueryResultBuilder AddErrors(IEnumerable<IError> errors)
throw new ArgumentNullException(nameof(errors));
}

if (_errors is null)
{
_errors = new List<IError>();
}

_errors ??= new List<IError>();
_errors.AddRange(errors);
return this;
}
Expand All @@ -65,22 +57,14 @@ public IQueryResultBuilder ClearErrors()

public IQueryResultBuilder AddExtension(string key, object? data)
{
if (_extensionData is null)
{
_extensionData = new ExtensionData();
}

_extensionData ??= new ExtensionData();
_extensionData.Add(key, data);
return this;
}

public IQueryResultBuilder SetExtension(string key, object? data)
{
if (_extensionData is null)
{
_extensionData = new ExtensionData();
}

_extensionData ??= new ExtensionData();
_extensionData[key] = data;
return this;
}
Expand Down Expand Up @@ -109,22 +93,14 @@ public IQueryResultBuilder ClearExtensions()

public IQueryResultBuilder AddContextData(string key, object? data)
{
if (_contextData is null)
{
_contextData = new ExtensionData();
}

_contextData ??= new ExtensionData();
_contextData.Add(key, data);
return this;
}

public IQueryResultBuilder SetContextData(string key, object? data)
{
if (_contextData is null)
{
_contextData = new ExtensionData();
}

_contextData ??= new ExtensionData();
_contextData[key] = data;
return this;
}
Expand Down Expand Up @@ -157,23 +133,22 @@ public IQueryResult Create()
{
return new QueryResult(
_data,
_errors is { } && _errors.Count > 0 ? _errors : null,
_extensionData is { } && _extensionData.Count > 0 ? _extensionData : null,
_contextData is { } && _contextData.Count > 0 ? _contextData : null,
_errors is { Count: > 0 } ? _errors : null,
_extensionData is { Count: > 0 } ? _extensionData : null,
_contextData is { Count: > 0 } ? _contextData : null,
_label,
_path,
_hasNext,
_disposable);
}

public static QueryResultBuilder New() => new QueryResultBuilder();
public static QueryResultBuilder New() => new();

public static QueryResultBuilder FromResult(IQueryResult result)
{
var builder = new QueryResultBuilder();
builder._data = result.Data;
var builder = new QueryResultBuilder { _data = result.Data };

if (result.Errors is { })
if (result.Errors is not null)
{
builder._errors = new List<IError>(result.Errors);
}
Expand All @@ -182,7 +157,7 @@ public static QueryResultBuilder FromResult(IQueryResult result)
{
builder._extensionData = new ExtensionData(d);
}
else if (result.Extensions is { })
else if (result.Extensions is not null)
{
builder._extensionData = new ExtensionData(result.Extensions);
}
Expand All @@ -192,12 +167,15 @@ public static QueryResultBuilder FromResult(IQueryResult result)

public static IQueryResult CreateError(
IError error,
IReadOnlyDictionary<string, object?>? contextData = null) =>
new QueryResult(null, new List<IError> { error }, contextData: contextData);
IReadOnlyDictionary<string, object?>? contextData = null)
=> error is AggregateError aggregateError
? CreateError(aggregateError.Errors, contextData)
: new QueryResult(null, new List<IError> { error }, contextData: contextData);


public static IQueryResult CreateError(
IReadOnlyList<IError> errors,
IReadOnlyDictionary<string, object?>? contextData = null) =>
new QueryResult(null, errors, contextData: contextData);
IReadOnlyDictionary<string, object?>? contextData = null)
=> new QueryResult(null, errors, contextData: contextData);
}
}
2 changes: 1 addition & 1 deletion src/HotChocolate/Core/src/Abstractions/IError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace HotChocolate
{
/// <summary>
/// Represents a schema or query error.
/// Represents a GraphQL execution error.
/// </summary>
public interface IError
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,7 @@
<data name="FieldCoordinate_Parse_InvalidFormat" xml:space="preserve">
<value>The string has an invalid format.</value>
</data>
<data name="AggregateError_Message" xml:space="preserve">
<value>For error details look at the `Errors` property.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,6 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static partial class SchemaRequestExecutorBuilderExtensions
{
public static IRequestExecutorBuilder AddResolver(
this IRequestExecutorBuilder builder,
FieldResolver fieldResolver)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

if (fieldResolver is null)
{
throw new ArgumentNullException(nameof(fieldResolver));
}

// return builder.ConfigureSchema(b => b.AddResolver(fieldResolver));
throw new NotImplementedException();
}

/// <summary>
/// Adds a resolver delegate for a specific field.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,40 @@ public void ReportError(IError error)
throw new ArgumentNullException(nameof(error));
}

error = _operationContext.ErrorHandler.Handle(error);
_operationContext.Result.AddError(error, _selection.SyntaxNode);
_operationContext.DiagnosticEvents.ResolverError(this, error);
HasErrors = true;
if (error is AggregateError aggregateError)
{
foreach (var innerError in aggregateError.Errors)
{
ReportSingle(innerError);
}
}
else
{
ReportSingle(error);
}

void ReportSingle(IError singleError)
{
AddProcessedError(_operationContext.ErrorHandler.Handle(singleError));
HasErrors = true;
}

void AddProcessedError(IError processed)
{
if (processed is AggregateError ar)
{
foreach (var ie in ar.Errors)
{
_operationContext.Result.AddError(ie, _selection.SyntaxNode);
_operationContext.DiagnosticEvents.ResolverError(this, ie);
}
}
else
{
_operationContext.Result.AddError(processed, _selection.SyntaxNode);
_operationContext.DiagnosticEvents.ResolverError(this, processed);
}
}
}

public async ValueTask<T> ResolveAsync<T>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,40 @@ private void ReportError(IExecutionTask task, IError error)
}

AssertInitialized();
error = ErrorHandler.Handle(error);
Result.AddError(error);
DiagnosticEvents.TaskError(task, error);

if (error is AggregateError aggregateError)
{
foreach (var innerError in aggregateError.Errors)
{
ReportSingle(innerError);
}
}
else
{
ReportSingle(error);
}

void ReportSingle(IError singleError)
{
AddProcessedError(ErrorHandler.Handle(singleError));
}

void AddProcessedError(IError processed)
{
if (processed is AggregateError ar)
{
foreach (var ie in ar.Errors)
{
Result.AddError(ie);
DiagnosticEvents.TaskError(task, ie);
}
}
else
{
Result.AddError(processed);
DiagnosticEvents.TaskError(task, processed);
}
}
}

void IExecutionTaskContext.Completed(IExecutionTask task)
Expand Down
Loading

0 comments on commit ec4c539

Please sign in to comment.