Example #1
0
 private void ExposeNewsfeedExceptionMessages(UnhandledExceptionContext context)
 {
     if (context.Exception is NewsfeedDataException)
     {
         context.ErrorMessage = context.Exception.Message;
     }
 }
Example #2
0
        public async Task <ExecutionResult> ExecuteAsync(ExecutionOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.Schema == null)
            {
                throw new InvalidOperationException("Cannot execute request if no schema is specified");
            }
            if (options.Query == null)
            {
                throw new InvalidOperationException("Cannot execute request if no query is specified");
            }
            if (options.FieldMiddleware == null)
            {
                throw new InvalidOperationException("Cannot execute request if no middleware builder specified");
            }

            var metrics = new Metrics(options.EnableMetrics).Start(options.OperationName);

            options.Schema.NameConverter = options.NameConverter;
            options.Schema.Filter        = options.SchemaFilter;

            ExecutionResult  result  = null;
            ExecutionContext context = null;

            try
            {
                if (!options.Schema.Initialized)
                {
                    using (metrics.Subject("schema", "Initializing schema"))
                    {
                        options.FieldMiddleware.ApplyTo(options.Schema);
                        options.Schema.Initialize();
                    }
                }

                var document = options.Document;
                using (metrics.Subject("document", "Building document"))
                {
                    if (document == null)
                    {
                        document = _documentBuilder.Build(options.Query);
                    }
                }

                if (document.Operations.Count == 0)
                {
                    throw new NoOperationError();
                }

                var operation = GetOperation(options.OperationName, document);
                metrics.SetOperationName(operation?.Name);

                if (operation == null)
                {
                    throw new InvalidOperationException($"Query does not contain operation '{options.OperationName}'.");
                }

                IValidationResult validationResult;
                using (metrics.Subject("document", "Validating document"))
                {
                    validationResult = await _documentValidator.ValidateAsync(
                        options.Query,
                        options.Schema,
                        document,
                        options.ValidationRules,
                        options.UserContext,
                        options.Inputs);
                }

                if (options.ComplexityConfiguration != null && validationResult.IsValid)
                {
                    using (metrics.Subject("document", "Analyzing complexity"))
                        _complexityAnalyzer.Validate(document, options.ComplexityConfiguration);
                }

                context = BuildExecutionContext(
                    options.Schema,
                    options.Root,
                    document,
                    operation,
                    options.Inputs,
                    options.UserContext,
                    options.CancellationToken,
                    metrics,
                    options.Listeners,
                    options.ThrowOnUnhandledException,
                    options.UnhandledExceptionDelegate,
                    options.MaxParallelExecutionCount,
                    options.RequestServices);

                foreach (var listener in options.Listeners)
                {
                    await listener.AfterValidationAsync(context, validationResult)
                    .ConfigureAwait(false);
                }

                if (!validationResult.IsValid)
                {
                    return(new ExecutionResult
                    {
                        Errors = validationResult.Errors,
                        Perf = metrics.Finish()
                    });
                }

                if (context.Errors.Count > 0)
                {
                    return(new ExecutionResult
                    {
                        Errors = context.Errors,
                        Perf = metrics.Finish()
                    });
                }

                using (metrics.Subject("execution", "Executing operation"))
                {
                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    IExecutionStrategy executionStrategy = SelectExecutionStrategy(context);

                    if (executionStrategy == null)
                    {
                        throw new InvalidOperationException("Invalid ExecutionStrategy!");
                    }

                    var task = executionStrategy.ExecuteAsync(context)
                               .ConfigureAwait(false);

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAwaitedAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    result = await task;

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.AfterExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }
                }

                if (context.Errors.Count > 0)
                {
                    result.Errors = context.Errors;
                }
            }
            catch (OperationCanceledException) when(options.CancellationToken.IsCancellationRequested)
            {
                throw;
            }
            catch (ExecutionError ex)
            {
                result = new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        ex
                    }
                };
            }
            catch (Exception ex)
            {
                if (options.ThrowOnUnhandledException)
                {
                    throw;
                }

                UnhandledExceptionContext exceptionContext = null;
                if (options.UnhandledExceptionDelegate != null)
                {
                    exceptionContext = new UnhandledExceptionContext(context, null, ex);
                    options.UnhandledExceptionDelegate(exceptionContext);
                    ex = exceptionContext.Exception;
                }

                result = new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        ex is ExecutionError executionError ? executionError : new UnhandledError(exceptionContext?.ErrorMessage ?? "Error executing document.", ex)
                    }
                };
            }
            finally
            {
                result ??= new ExecutionResult();
                result.Perf = metrics.Finish();
            }

            return(result);
        }
        private async Task <ExecutionResult> CoreExecuteAsync(ExecutionOptions options)
        {
            if (options.Schema == null)
            {
                throw new InvalidOperationException("Cannot execute request if no schema is specified");
            }
            if (options.Query == null && options.Document == null)
            {
                return new ExecutionResult {
                           Errors = new ExecutionErrors {
                               new QueryMissingError()
                           }
                }
            }
            ;

            var metrics = (options.EnableMetrics ? new Metrics() : Metrics.None).Start(options.OperationName);

            ExecutionResult? result            = null;
            ExecutionContext?context           = null;
            bool             executionOccurred = false;

            try
            {
                if (!options.Schema.Initialized)
                {
                    using (metrics.Subject("schema", "Initializing schema"))
                    {
                        options.Schema.Initialize();
                    }
                }

                var document = options.Document;

                bool saveInCache       = false;
                bool analyzeComplexity = true;
                var  validationRules   = options.ValidationRules;
                using (metrics.Subject("document", "Building document"))
                {
                    if (document == null && (document = await _documentCache.GetAsync(options.Query !).ConfigureAwait(false)) != null)
                    {
                        // none of the default validation rules yet are dependent on the inputs, and the
                        // operation name is not passed to the document validator, so any successfully cached
                        // document should not need any validation rules run on it
                        validationRules   = options.CachedDocumentValidationRules ?? Array.Empty <IValidationRule>();
                        analyzeComplexity = false;
                    }
                    if (document == null)
                    {
                        document    = _documentBuilder.Build(options.Query !);
                        saveInCache = true;
                    }
                }

                if (document.OperationsCount() == 0)
                {
                    throw new NoOperationError();
                }

                var operation = GetOperation(options.OperationName, document);
                if (operation == null)
                {
                    throw new InvalidOperationError($"Query does not contain operation '{options.OperationName}'.");
                }
                metrics.SetOperationName(operation.Name);

                IValidationResult validationResult;
                Variables         variables;
                using (metrics.Subject("document", "Validating document"))
                {
                    (validationResult, variables) = await _documentValidator.ValidateAsync(
                        new ValidationOptions
                    {
                        Document          = document,
                        Rules             = validationRules,
                        Operation         = operation,
                        UserContext       = options.UserContext,
                        RequestServices   = options.RequestServices,
                        CancellationToken = options.CancellationToken,
                        Schema            = options.Schema,
                        Variables         = options.Variables ?? Inputs.Empty,
                        Extensions        = options.Extensions ?? Inputs.Empty,
                    }).ConfigureAwait(false);
                }

                if (options.ComplexityConfiguration != null && validationResult.IsValid && analyzeComplexity)
                {
                    using (metrics.Subject("document", "Analyzing complexity"))
                        _complexityAnalyzer.Validate(document, options.ComplexityConfiguration);
                }

                if (saveInCache && validationResult.IsValid)
                {
                    await _documentCache.SetAsync(options.Query !, document).ConfigureAwait(false);
                }

                context = BuildExecutionContext(options, document, operation, variables, metrics);

                foreach (var listener in options.Listeners)
                {
                    await listener.AfterValidationAsync(context, validationResult) // TODO: remove ExecutionContext or make different type ?
                    .ConfigureAwait(false);
                }

                if (!validationResult.IsValid)
                {
                    return(new ExecutionResult
                    {
                        Errors = validationResult.Errors,
                        Perf = metrics.Finish()
                    });
                }

                if (context.Errors.Count > 0)
                {
                    return(new ExecutionResult
                    {
                        Errors = context.Errors,
                        Perf = metrics.Finish()
                    });
                }

                executionOccurred = true;

                using (metrics.Subject("execution", "Executing operation"))
                {
                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    var task = (context.ExecutionStrategy ?? throw new InvalidOperationException("Execution strategy not specified")).ExecuteAsync(context)
                               .ConfigureAwait(false);

                    result = await task;

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.AfterExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }
                }

                result.AddErrors(context.Errors);
            }
            catch (OperationCanceledException) when(options.CancellationToken.IsCancellationRequested)
            {
                throw;
            }
            catch (ExecutionError ex)
            {
                (result ??= new()).AddError(ex);
            }
            catch (Exception ex)
            {
                if (options.ThrowOnUnhandledException)
                {
                    throw;
                }

                UnhandledExceptionContext?exceptionContext = null;
                if (options.UnhandledExceptionDelegate != null)
                {
                    exceptionContext = new UnhandledExceptionContext(context, null, ex);
                    await options.UnhandledExceptionDelegate(exceptionContext).ConfigureAwait(false);

                    ex = exceptionContext.Exception;
                }

                (result ??= new()).AddError(ex is ExecutionError executionError ? executionError : new UnhandledError(exceptionContext?.ErrorMessage ?? "Error executing document.", ex));
            }
            finally
            {
                result ??= new();
                result.Perf = metrics.Finish();
                if (executionOccurred)
                {
                    result.Executed = true;
                }
                context?.Dispose();
            }

            return(result);
        }
Example #4
0
        /// <inheritdoc/>
        public async Task <ExecutionResult> ExecuteAsync(ExecutionOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.Schema == null)
            {
                throw new InvalidOperationException("Cannot execute request if no schema is specified");
            }
            if (options.Query == null)
            {
                throw new InvalidOperationException("Cannot execute request if no query is specified");
            }
            if (options.FieldMiddleware == null)
            {
                throw new InvalidOperationException("Cannot execute request if no middleware builder specified");
            }

            var metrics = new Metrics(options.EnableMetrics).Start(options.OperationName);

            options.Schema.NameConverter = options.NameConverter;
            options.Schema.Filter        = options.SchemaFilter;

            ExecutionResult  result  = null;
            ExecutionContext context = null;

            try
            {
                if (!options.Schema.Initialized)
                {
                    using (metrics.Subject("schema", "Initializing schema"))
                    {
                        lock (options.Schema)
                        {
                            if (!options.Schema.Initialized)
                            {
                                options.FieldMiddleware.ApplyTo(options.Schema);
                                options.Schema.Initialize();
                            }
                        }
                    }
                }

                var document = options.Document;
                using (metrics.Subject("document", "Building document"))
                {
                    if (document == null)
                    {
                        document = _documentBuilder.Build(options.Query);
                    }
                }

                if (document.Operations.Count == 0)
                {
                    throw new NoOperationError();
                }

                var operation = GetOperation(options.OperationName, document);
                metrics.SetOperationName(operation?.Name);

                if (operation == null)
                {
                    throw new InvalidOperationException($"Query does not contain operation '{options.OperationName}'.");
                }

                IValidationResult validationResult;
                using (metrics.Subject("document", "Validating document"))
                {
                    validationResult = await _documentValidator.ValidateAsync(
                        options.Query,
                        options.Schema,
                        document,
                        options.ValidationRules,
                        options.UserContext,
                        options.Inputs);
                }

                if (options.ComplexityConfiguration != null && validationResult.IsValid)
                {
                    using (metrics.Subject("document", "Analyzing complexity"))
                        _complexityAnalyzer.Validate(document, options.ComplexityConfiguration);
                }

                try
                {
                    context = BuildExecutionContext(
                        options.Schema,
                        options.Root,
                        document,
                        operation,
                        options.Inputs ?? Inputs.Empty,
                        options.UserContext,
                        options.CancellationToken,
                        metrics,
                        options.Listeners,
                        options.ThrowOnUnhandledException,
                        options.UnhandledExceptionDelegate,
                        options.MaxParallelExecutionCount,
                        options.RequestServices);
                }
                catch (InvalidVariableError)
                {
                    // error parsing variables
                    // attempt to run AfterValidationAsync with null for the 'ExecutionContext.Variables' property

                    context = BuildExecutionContext(
                        options.Schema,
                        options.Root,
                        document,
                        operation,
                        null,
                        options.UserContext,
                        options.CancellationToken,
                        metrics,
                        options.Listeners,
                        options.ThrowOnUnhandledException,
                        options.UnhandledExceptionDelegate,
                        options.MaxParallelExecutionCount,
                        options.RequestServices);

                    try
                    {
                        foreach (var listener in options.Listeners)
                        {
                            await listener.AfterValidationAsync(context, validationResult)
                            .ConfigureAwait(false);
                        }

                        // if there was a validation error, return that, and ignore the variable parsing error
                        if (!validationResult.IsValid)
                        {
                            return(new ExecutionResult
                            {
                                Errors = validationResult.Errors,
                                Perf = metrics.Finish()
                            });
                        }
                    }
                    catch
                    {
                        // if there was an error within AfterValidationAsync (such as a NullReferenceException
                        // due to ExecutionContext.Variables being null), skip this step and throw the variable parsing error
                    }

                    // if there was no validation errors returned, throw the variable parsing error
                    throw;
                }

                foreach (var listener in options.Listeners)
                {
                    await listener.AfterValidationAsync(context, validationResult)
                    .ConfigureAwait(false);
                }

                if (!validationResult.IsValid)
                {
                    return(new ExecutionResult
                    {
                        Errors = validationResult.Errors,
                        Perf = metrics.Finish()
                    });
                }

                if (context.Errors.Count > 0)
                {
                    return(new ExecutionResult
                    {
                        Errors = context.Errors,
                        Perf = metrics.Finish()
                    });
                }

                using (metrics.Subject("execution", "Executing operation"))
                {
                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    IExecutionStrategy executionStrategy = SelectExecutionStrategy(context);

                    if (executionStrategy == null)
                    {
                        throw new InvalidOperationException("Invalid ExecutionStrategy!");
                    }

                    var task = executionStrategy.ExecuteAsync(context)
                               .ConfigureAwait(false);

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
#pragma warning disable CS0612 // Type or member is obsolete
                            await listener.BeforeExecutionAwaitedAsync(context)
#pragma warning restore CS0612 // Type or member is obsolete
                            .ConfigureAwait(false);
                        }
                    }

                    result = await task;

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.AfterExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }
                }

                if (context.Errors.Count > 0)
                {
                    result.Errors = context.Errors;
                }
            }
            catch (OperationCanceledException) when(options.CancellationToken.IsCancellationRequested)
            {
                throw;
            }
            catch (ExecutionError ex)
            {
                result = new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        ex
                    }
                };
            }
            catch (Exception ex)
            {
                if (options.ThrowOnUnhandledException)
                {
                    throw;
                }

                UnhandledExceptionContext exceptionContext = null;
                if (options.UnhandledExceptionDelegate != null)
                {
                    exceptionContext = new UnhandledExceptionContext(context, null, ex);
                    options.UnhandledExceptionDelegate(exceptionContext);
                    ex = exceptionContext.Exception;
                }

                result = new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        ex is ExecutionError executionError ? executionError : new UnhandledError(exceptionContext?.ErrorMessage ?? "Error executing document.", ex)
                    }
                };
            }
            finally
            {
                result ??= new ExecutionResult();
                result.Perf = metrics.Finish();
            }

            return(result);
        }
        /// <inheritdoc/>
        public virtual async Task <ExecutionResult> ExecuteAsync(ExecutionOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.Schema == null)
            {
                throw new InvalidOperationException("Cannot execute request if no schema is specified");
            }
            if (options.Query == null)
            {
                throw new InvalidOperationException("Cannot execute request if no query is specified");
            }

            var metrics = (options.EnableMetrics ? new Metrics() : Metrics.None).Start(options.OperationName);

            ExecutionResult  result            = null;
            ExecutionContext context           = null;
            bool             executionOccurred = false;

            try
            {
                if (!options.Schema.Initialized)
                {
                    using (metrics.Subject("schema", "Initializing schema"))
                    {
                        options.Schema.Initialize();
                    }
                }

                var  document          = options.Document;
                bool saveInCache       = false;
                bool analyzeComplexity = true;
                var  validationRules   = options.ValidationRules;
                using (metrics.Subject("document", "Building document"))
                {
                    if (document == null && (document = _documentCache[options.Query]) != null)
                    {
                        // none of the default validation rules yet are dependent on the inputs, and the
                        // operation name is not passed to the document validator, so any successfully cached
                        // document should not need any validation rules run on it
                        validationRules   = options.CachedDocumentValidationRules ?? Array.Empty <IValidationRule>();
                        analyzeComplexity = false;
                    }
                    if (document == null)
                    {
                        document    = _documentBuilder.Build(options.Query);
                        saveInCache = true;
                    }
                }

                if (document.Operations.Count == 0)
                {
                    throw new NoOperationError();
                }

                var operation = GetOperation(options.OperationName, document);
                metrics.SetOperationName(operation?.Name);

                if (operation == null)
                {
                    throw new InvalidOperationException($"Query does not contain operation '{options.OperationName}'.");
                }

                IValidationResult validationResult;
                using (metrics.Subject("document", "Validating document"))
                {
                    validationResult = await _documentValidator.ValidateAsync(
                        options.Query,
                        options.Schema,
                        document,
                        validationRules,
                        options.UserContext,
                        options.Inputs);
                }

                if (options.ComplexityConfiguration != null && validationResult.IsValid && analyzeComplexity)
                {
                    using (metrics.Subject("document", "Analyzing complexity"))
                        _complexityAnalyzer.Validate(document, options.ComplexityConfiguration);
                }

                if (saveInCache && validationResult.IsValid)
                {
                    _documentCache[options.Query] = document;
                }

                try
                {
                    context = BuildExecutionContext(
                        options.Schema,
                        options.Root,
                        document,
                        operation,
                        options.Inputs ?? Inputs.Empty,
                        options.UserContext,
                        options.CancellationToken,
                        metrics,
                        options.Listeners,
                        options.ThrowOnUnhandledException,
                        options.UnhandledExceptionDelegate,
                        options.MaxParallelExecutionCount,
                        options.RequestServices);
                }
                catch (InvalidVariableError)
                {
                    // error parsing variables
                    // attempt to run AfterValidationAsync with null for the 'ExecutionContext.Variables' property

                    context = BuildExecutionContext(
                        options.Schema,
                        options.Root,
                        document,
                        operation,
                        null,
                        options.UserContext,
                        options.CancellationToken,
                        metrics,
                        options.Listeners,
                        options.ThrowOnUnhandledException,
                        options.UnhandledExceptionDelegate,
                        options.MaxParallelExecutionCount,
                        options.RequestServices);

                    try
                    {
                        foreach (var listener in options.Listeners)
                        {
                            await listener.AfterValidationAsync(context, validationResult)
                            .ConfigureAwait(false);
                        }

                        // if there was a validation error, return that, and ignore the variable parsing error
                        if (!validationResult.IsValid)
                        {
                            return(new ExecutionResult
                            {
                                Errors = validationResult.Errors,
                                Perf = metrics.Finish()
                            });
                        }
                    }
                    catch
                    {
                        // if there was an error within AfterValidationAsync (such as a NullReferenceException
                        // due to ExecutionContext.Variables being null), skip this step and throw the variable parsing error
                    }

                    // if there was no validation errors returned, throw the variable parsing error
                    throw;
                }

                foreach (var listener in options.Listeners)
                {
                    await listener.AfterValidationAsync(context, validationResult)
                    .ConfigureAwait(false);
                }

                if (!validationResult.IsValid)
                {
                    return(new ExecutionResult
                    {
                        Errors = validationResult.Errors,
                        Perf = metrics.Finish()
                    });
                }

                if (context.Errors.Count > 0)
                {
                    return(new ExecutionResult
                    {
                        Errors = context.Errors,
                        Perf = metrics.Finish()
                    });
                }

                executionOccurred = true;

                using (metrics.Subject("execution", "Executing operation"))
                {
                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.BeforeExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }

                    IExecutionStrategy executionStrategy = SelectExecutionStrategy(context);

                    if (executionStrategy == null)
                    {
                        throw new InvalidOperationException("Invalid ExecutionStrategy!");
                    }

                    var task = executionStrategy.ExecuteAsync(context)
                               .ConfigureAwait(false);

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
#pragma warning disable CS0612 // Type or member is obsolete
                            await listener.BeforeExecutionAwaitedAsync(context)
#pragma warning restore CS0612 // Type or member is obsolete
                            .ConfigureAwait(false);
                        }
                    }

                    result = await task;

                    if (context.Listeners != null)
                    {
                        foreach (var listener in context.Listeners)
                        {
                            await listener.AfterExecutionAsync(context)
                            .ConfigureAwait(false);
                        }
                    }
                }

                result.AddErrors(context.Errors);
            }
            catch (OperationCanceledException) when(options.CancellationToken.IsCancellationRequested)
            {
                throw;
            }
            catch (ExecutionError ex)
            {
                (result ??= new ExecutionResult()).AddError(ex);
            }
            catch (Exception ex)
            {
                if (options.ThrowOnUnhandledException)
                {
                    throw;
                }

                UnhandledExceptionContext exceptionContext = null;
                if (options.UnhandledExceptionDelegate != null)
                {
                    exceptionContext = new UnhandledExceptionContext(context, null, ex);
                    options.UnhandledExceptionDelegate(exceptionContext);
                    ex = exceptionContext.Exception;
                }

                (result ??= new ExecutionResult()).AddError(ex is ExecutionError executionError ? executionError : new UnhandledError(exceptionContext?.ErrorMessage ?? "Error executing document.", ex));
            }
            finally
            {
                result ??= new ExecutionResult();
                result.Perf = metrics.Finish();
                if (executionOccurred)
                {
                    result.Executed = true;
                }
                context?.Dispose();
            }

            return(result);
        }
        public async Task <ExecutionResult> ExecuteAsync(ExecutionOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.Schema == null)
            {
                throw new InvalidOperationException("Cannot execute request if no schema is specified");
            }

            var metrics = new Metrics(options.EnableMetrics).Start(options.OperationName);

            options.Schema.FieldNameConverter = options.FieldNameConverter;
            options.Schema.Filter             = options.SchemaFilter;

            ExecutionResult  result  = null;
            ExecutionContext context = null;

            try
            {
                ValidateOptions(options);

                if (!options.Schema.Initialized)
                {
                    using (metrics.Subject("schema", "Initializing schema"))
                    {
                        options.FieldMiddleware.ApplyTo(options.Schema);
                        options.Schema.Initialize();
                    }
                }

                var document = options.Document;
                using (metrics.Subject("document", "Building document"))
                {
                    if (document == null)
                    {
                        document = _documentBuilder.Build(options.Query);
                    }
                }

                var operation = GetOperation(options.OperationName, document);
                metrics.SetOperationName(operation?.Name);

                if (operation == null)
                {
                    throw new ExecutionError("Unable to determine operation from query.");
                }

                IValidationResult validationResult;
                using (metrics.Subject("document", "Validating document"))
                {
                    validationResult = _documentValidator.Validate(
                        options.Query,
                        options.Schema,
                        document,
                        options.ValidationRules,
                        options.UserContext,
                        options.Inputs);
                }

                if (options.ComplexityConfiguration != null && validationResult.IsValid)
                {
                    using (metrics.Subject("document", "Analyzing complexity"))
                        _complexityAnalyzer.Validate(document, options.ComplexityConfiguration);
                }

                foreach (var listener in options.Listeners)
                {
                    await listener.AfterValidationAsync(
                        options.UserContext,
                        validationResult,
                        options.CancellationToken)
                    .ConfigureAwait(false);
                }

                if (!validationResult.IsValid)
                {
                    return(new ExecutionResult
                    {
                        Errors = validationResult.Errors,
                        ExposeExceptions = options.ExposeExceptions,
                        Perf = metrics.Finish()?.ToArray()
                    });
                }

                context = BuildExecutionContext(
                    options.Schema,
                    options.Root,
                    document,
                    operation,
                    options.Inputs,
                    options.UserContext,
                    options.CancellationToken,
                    metrics,
                    options.Listeners,
                    options.ThrowOnUnhandledException,
                    options.UnhandledExceptionDelegate);

                if (context.Errors.Any())
                {
                    return(new ExecutionResult
                    {
                        Errors = context.Errors,
                        ExposeExceptions = options.ExposeExceptions,
                        Perf = metrics.Finish()?.ToArray()
                    });
                }

                using (metrics.Subject("execution", "Executing operation"))
                {
                    foreach (var listener in context.Listeners)
                    {
                        await listener.BeforeExecutionAsync(context.UserContext, context.CancellationToken)
                        .ConfigureAwait(false);
                    }

                    IExecutionStrategy executionStrategy = SelectExecutionStrategy(context);

                    if (executionStrategy == null)
                    {
                        throw new InvalidOperationException("Invalid ExecutionStrategy!");
                    }

                    var task = executionStrategy.ExecuteAsync(context)
                               .ConfigureAwait(false);

                    foreach (var listener in context.Listeners)
                    {
                        await listener.BeforeExecutionAwaitedAsync(context.UserContext, context.CancellationToken)
                        .ConfigureAwait(false);
                    }

                    result = await task;

                    foreach (var listener in context.Listeners)
                    {
                        await listener.AfterExecutionAsync(context.UserContext, context.CancellationToken)
                        .ConfigureAwait(false);
                    }
                }

                if (context.Errors.Any())
                {
                    result.Errors = context.Errors;
                }
            }
            catch (Exception ex)
            {
                if (options.ThrowOnUnhandledException)
                {
                    throw;
                }

                if (options.UnhandledExceptionDelegate != null)
                {
                    var exceptionContext = new UnhandledExceptionContext(context, null, ex);
                    options.UnhandledExceptionDelegate(exceptionContext);
                    ex = exceptionContext.Exception;
                }

                result = new ExecutionResult
                {
                    Errors = new ExecutionErrors
                    {
                        new ExecutionError(ex.Message, ex)
                    }
                };
            }
            finally
            {
                result = result ?? new ExecutionResult();
                result.ExposeExceptions = options.ExposeExceptions;
                result.Perf             = metrics.Finish()?.ToArray();
            }

            return(result);
        }
Example #7
0
 private static void GraphQL_UnhandledException(UnhandledExceptionContext e) => ExceptionOut.WriteException("[GraphQLUnhandledException]" + e.OriginalException.GetType().FullName, e.OriginalException, null, true);