diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs index 0afcda85c05..62316a8eb8c 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs @@ -193,11 +193,11 @@ public void ResolverError(IMiddlewareContext context, IError error) } } - public void ResolverError(IOperation operation, ISelection selection, IError error) + public void ResolverError(IRequestContext context, ISelection selection, IError error) { for (var i = 0; i < _listeners.Length; i++) { - _listeners[i].ResolverError(operation, selection, error); + _listeners[i].ResolverError(context, selection, error); } } diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs index fc39201baf3..7d7e35f6162 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs @@ -98,7 +98,7 @@ public virtual void ResolverError(IMiddlewareContext context, IError error) } /// - public virtual void ResolverError(IOperation operation, ISelection selection, IError error) + public virtual void ResolverError(IRequestContext context, ISelection selection, IError error) { } diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs index 19d4d9958d2..99490f285f8 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs @@ -213,8 +213,9 @@ public interface IExecutionDiagnosticEvents /// /// Called for field errors that do NOT occur within the resolver task. /// - /// - /// The operation that is being executed. + /// + /// The request context encapsulates all GraphQL-specific information about an + /// individual GraphQL request. /// /// /// The selection that is affected by the error. @@ -222,7 +223,11 @@ public interface IExecutionDiagnosticEvents /// /// The error object. /// - void ResolverError(IOperation operation, ISelection selection, IError error); + /// + /// Some field level errors are handled after the resolver was completed and this + /// are handled in the request scope. + /// + void ResolverError(IRequestContext context, ISelection selection, IError error); /// /// Called when starting to run an execution engine task. diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs index c8bec0f573c..498b3d445c9 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs @@ -56,7 +56,7 @@ public void ResolverError(IMiddlewareContext context, IError error) { } - public void ResolverError(IOperation operation, ISelection selection, IError error) + public void ResolverError(IRequestContext context, ISelection selection, IError error) { } diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs b/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs index 5213658f47e..e1b90703715 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs @@ -20,6 +20,7 @@ internal sealed partial class OperationContext private readonly DeferredWorkScheduler _deferredWorkScheduler; private readonly ResultBuilder _resultBuilder; private readonly PooledPathFactory _pathFactory; + private IRequestContext _requestContext = default!; private ISchema _schema = default!; private IErrorHandler _errorHandler = default!; private IActivator _activator = default!; @@ -60,6 +61,7 @@ internal sealed partial class OperationContext object? rootValue, Func resolveQueryRootValue) { + _requestContext = requestContext; _schema = requestContext.Schema; _errorHandler = requestContext.ErrorHandler; _activator = requestContext.Activator; @@ -78,11 +80,12 @@ internal sealed partial class OperationContext IncludeFlags = _operation.CreateIncludeFlags(variables); _workScheduler.Initialize(batchDispatcher); _deferredWorkScheduler.Initialize(this); - _resultBuilder.Initialize(_operation, _errorHandler, _diagnosticEvents); + _resultBuilder.Initialize(_requestContext, _diagnosticEvents); } public void InitializeFrom(OperationContext context) { + _requestContext = context._requestContext; _schema = context._schema; _errorHandler = context._errorHandler; _activator = context._activator; @@ -101,7 +104,7 @@ public void InitializeFrom(OperationContext context) IncludeFlags = _operation.CreateIncludeFlags(_variables); _workScheduler.Initialize(_batchDispatcher); _deferredWorkScheduler.InitializeFrom(this, context._deferredWorkScheduler); - _resultBuilder.Initialize(_operation, _errorHandler, _diagnosticEvents); + _resultBuilder.Initialize(_requestContext, _diagnosticEvents); } public void Clean() @@ -112,6 +115,7 @@ public void Clean() _workScheduler.Clear(); _resultBuilder.Clear(); _deferredWorkScheduler.Clear(); + _requestContext = default!; _schema = default!; _errorHandler = default!; _activator = default!; diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs b/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs index 8a70e7fe536..9ee0ebcf1f4 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs @@ -25,8 +25,8 @@ internal sealed partial class ResultBuilder if (!fieldErrors.Contains(violation.Selection)) { var error = NonNullOutputFieldViolation(path, violation.Selection.SyntaxNode); - error = _errorHandler.Handle(error); - _diagnosticEvents.ResolverError(_operation, violation.Selection, error); + error = _context.ErrorHandler.Handle(error); + _diagnosticEvents.ResolverError(_context, violation.Selection, error); errors.Add(error); } diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs b/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs index 989c8baaf80..fc050089fee 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs @@ -4,8 +4,7 @@ namespace HotChocolate.Execution.Processing; internal sealed partial class ResultBuilder { - private IOperation _operation = default!; - private IErrorHandler _errorHandler = default!; + private IRequestContext _context = default!; private IExecutionDiagnosticEvents _diagnosticEvents = default!; public ResultBuilder(ResultPool resultPool) @@ -15,12 +14,10 @@ public ResultBuilder(ResultPool resultPool) } public void Initialize( - IOperation operation, - IErrorHandler errorHandler, + IRequestContext context, IExecutionDiagnosticEvents diagnosticEvents) { - _operation = operation; - _errorHandler = errorHandler; + _context = context; _diagnosticEvents = diagnosticEvents; } @@ -37,8 +34,7 @@ public void Clear() InitializeResult(); - _operation = default!; - _errorHandler = default!; + _context = default!; _diagnosticEvents = default!; _data = null; _items = null; diff --git a/src/HotChocolate/Core/src/Types/Utilities/ListTypeConverter.cs b/src/HotChocolate/Core/src/Types/Utilities/ListTypeConverter.cs index afba7b0967b..baeed259ff2 100644 --- a/src/HotChocolate/Core/src/Types/Utilities/ListTypeConverter.cs +++ b/src/HotChocolate/Core/src/Types/Utilities/ListTypeConverter.cs @@ -154,7 +154,7 @@ internal sealed class ListTypeConverter : IChangeTypeProvider } var collection = (ICollection)Activator.CreateInstance(listType)!; - ChangeListType(input, (item, _) => collection.Add((T)elementConverter(item))); + ChangeListType(input, (item, _) => collection.Add((T)elementConverter(item)!)); return collection; } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs index 0b2c3c4eba6..daec6c69560 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs @@ -9,6 +9,7 @@ using GreenDonut; using HotChocolate.AspNetCore.Instrumentation; using HotChocolate.Execution; +using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.Language.Utilities; using HotChocolate.Resolvers; @@ -583,6 +584,13 @@ void BuildPath() Activity activity) => EnrichError(error, activity); + public virtual void EnrichResolverError( + IRequestContext context, + ISelection selection, + IError error, + Activity activity) + => EnrichError(error, activity); + public virtual void EnrichDataLoaderBatch( IDataLoader dataLoader, IReadOnlyList keys, diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs index db2e29e8716..89b45d5374b 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs @@ -335,4 +335,18 @@ public override void ResolverError(IMiddlewareContext context, IError error) activity.SetStatus(ActivityStatusCode.Error); } } + + public override void ResolverError(IRequestContext context, ISelection selection, IError error) + { + if (!_options.SkipResolveFieldValue && + context.ContextData.TryGetValue(RequestActivity, out var value)) + { + Debug.Assert(value is not null, "The activity mustn't be null!"); + + var activity = (Activity)value; + _enricher.EnrichResolverError(context, selection, error, activity); + activity.SetStatus(Status.Error); + activity.SetStatus(ActivityStatusCode.Error); + } + } }