Beispiel #1
0
        /// <summary>
        /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="resolutionContext">The resolution context containing the request and the
        /// runtime information needed to resolve it.</param>
        /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
        /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
        public Task Resolve(FieldResolutionContext resolutionContext, CancellationToken cancelToken = default)
        {
            var sourceData = resolutionContext.Arguments.SourceData as IntrospectedType;

            if (sourceData == null)
            {
                resolutionContext.Result = Enumerable.Empty <IntrospectedEnumValue>();
            }
            else
            {
                var includedDeprecated = resolutionContext.Arguments.ContainsKey(Constants.ReservedNames.DEPRECATED_ARGUMENT_NAME) &&
                                         (bool)resolutionContext.Arguments[Constants.ReservedNames.DEPRECATED_ARGUMENT_NAME].Value;

                if (includedDeprecated)
                {
                    resolutionContext.Result = sourceData.EnumValues;
                }
                else
                {
                    resolutionContext.Result = sourceData.EnumValues?.Where(x => !x.IsDeprecated).ToList();
                }
            }

            return(Task.CompletedTask);
        }
Beispiel #2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FieldResolutionStartedLogEntry" /> class.
 /// </summary>
 /// <param name="context">The field context containing the necessary data to resolve
 /// the field and produce a reslt.</param>
 public FieldResolutionStartedLogEntry(FieldResolutionContext context)
     : base(LogEventIds.FieldResolutionStarted)
 {
     this.PipelineRequestId  = context.Request.Id;
     this.FieldExecutionMode = context.Request.Field.Mode.ToString();
     this.FieldPath          = context.Request.Field.Route.Path;
 }
Beispiel #3
0
        /// <summary>
        /// Invoke the action item as an asyncronous operation.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="next">The next.</param>
        /// <param name="cancelToken">The cancel token.</param>
        /// <returns>Task.</returns>
        public async Task InvokeAsync(GraphFieldExecutionContext context, GraphMiddlewareInvocationDelegate <GraphFieldExecutionContext> next, CancellationToken cancelToken = default)
        {
            // create a set of validation contexts for every incoming source graph item
            // to capture and validate every item regardless of it being successfully resolved or failed
            var validationContexts = context.Request.DataSource.Items.Select(
                x => new FieldValidationContext(_schema, x, context.Messages));

            // begin profiling of this single field of data
            context.Metrics?.BeginFieldResolution(context);
            bool fieldShouldBeCanceled = false;

            if (context.IsValid)
            {
                // build a collection of invokable parameters from the supplied context
                var executionArguments = context
                                         .InvocationContext
                                         .Arguments
                                         .Merge(context.VariableData)
                                         .WithSourceData(context.Request.DataSource.Value);

                // resolve the field
                var resolutionContext = new FieldResolutionContext(context, context.Request, executionArguments);

                context.Logger?.FieldResolutionStarted(resolutionContext);

                var task = context.Field?.Resolver?.Resolve(resolutionContext, cancelToken);

                await task.ConfigureAwait(false);

                context.Messages.AddRange(resolutionContext.Messages);

                this.AssignResults(context, resolutionContext);
                fieldShouldBeCanceled = resolutionContext.IsCancelled;

                context.Logger?.FieldResolutionCompleted(resolutionContext);
            }

            if (fieldShouldBeCanceled)
            {
                context.Cancel();
                context.Request.DataSource.Items.ForEach(x => x.Cancel());
            }

            // validate the resolution of the field in whatever manner that means for its current state
            var completionProcessor = new FieldCompletionRuleProcessor();

            completionProcessor.Execute(validationContexts);

            // end profiling of this single field of data
            context.Metrics?.EndFieldResolution(context);

            await next(context, cancelToken).ConfigureAwait(false);

            // validate the final result after all downstream middleware execute
            // in the standard pipeline this generally means all child fields have resolved
            var validationProcessor = new FieldValidationRuleProcessor();

            validationProcessor.Execute(validationContexts);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="FieldResolutionCompletedLogEntry" /> class.
 /// </summary>
 /// <param name="context">The context.</param>
 public FieldResolutionCompletedLogEntry(FieldResolutionContext context)
     : base(LogEventIds.FieldResolutionCompleted)
 {
     this.PipelineRequestId = context.Request.Id;
     this.FieldPath         = context.Request.InvocationContext.Field.Route.Path;
     this.TypeExpression    = context.Request.InvocationContext.Field.TypeExpression.ToString();
     this.HasData           = context.Result != null;
     this.ResultIsValid     = context.Messages.IsSucessful;
 }
Beispiel #5
0
        /// <inheritdoc />
        public virtual void FieldResolutionCompleted(FieldResolutionContext context)
        {
            if (!this.IsEnabled(LogLevel.Trace))
            {
                return;
            }

            var entry = new FieldResolutionCompletedLogEntry(context);

            this.LogEvent(LogLevel.Trace, entry);
        }
Beispiel #6
0
        /// <summary>
        /// Assigns the results of resolving the field to the items on the execution context.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        /// <param name="resolutionContext">The resolution context.</param>
        private void AssignResults(GraphFieldExecutionContext executionContext, FieldResolutionContext resolutionContext)
        {
            // transfer the result to the execution context
            // then deteremine what (if any) data items can be updated from its value
            executionContext.Result = resolutionContext.Result;

            if (executionContext.Field.Mode == FieldResolutionMode.PerSourceItem)
            {
                if (executionContext.Request.DataSource.Items.Count == 1)
                {
                    var item = executionContext.Request.DataSource.Items[0];
                    executionContext.ResolvedSourceItems.Add(item);
                    item.AssignResult(resolutionContext.Result);
                    return;
                }

                throw new GraphExecutionException(
                          $"When attempting to resolve the field '{executionContext.Field.Route.Path}' an unexpected error occured and the request was teriminated.",
                          executionContext.Request.Origin,
                          new InvalidOperationException(
                              $"The field '{executionContext.Field.Route.Parent}' has a resolution mode of '{nameof(FieldResolutionMode.PerSourceItem)}' " +
                              $"but the execution context contains {executionContext.Request.DataSource.Items.Count} source items. The runtime is unable to determine which " +
                              "item to assign the resultant value to."));
            }
            else if (executionContext.Field.Mode == FieldResolutionMode.Batch)
            {
                var batchProcessor = new BatchResultProcessor(
                    executionContext.Field,
                    executionContext.Request.DataSource.Items,
                    executionContext.Request.Origin);

                var itemsWithAssignedData = batchProcessor.Resolve(executionContext.Result);
                executionContext.ResolvedSourceItems.AddRange(itemsWithAssignedData);
                executionContext.Messages.AddRange(batchProcessor.Messages);
                return;
            }

            throw new ArgumentOutOfRangeException(
                      nameof(executionContext.Field.Mode),
                      $"The execution mode for field '{executionContext.Field.Route.Path}' cannot be resolved " +
                      $"by {nameof(InvokeFieldResolverMiddleware<TSchema>)}. (Mode: {executionContext.Field.Mode.ToString()})");
        }
Beispiel #7
0
        /// <summary>
        /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="resolutionContext">The resolution context containing the request and the
        /// runtime information needed to resolve it.</param>
        /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
        /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
        public Task Resolve(FieldResolutionContext resolutionContext, CancellationToken cancelToken = default)
        {
            if (!resolutionContext.Arguments.TryGetArgument <string>("name", out var name))
            {
                resolutionContext.Messages.Critical("Required Argument 'name' not found.");
            }
            else
            {
                var type = _schema.FindIntrospectedType(name);
                if (type == null || !type.Publish)
                {
                    resolutionContext.Messages.Info($"Unknown graph type. The type '{name}' does not exist on the '{_schema.Name}' schema");
                }
                else
                {
                    resolutionContext.Result = type;
                }
            }

            return(Task.CompletedTask);
        }
Beispiel #8
0
        public async Task Resolve(FieldResolutionContext context, CancellationToken cancelToken = default)
        {
            IGraphActionResult result;

            try
            {
                // create a scoped controller instance for this invocation
                var controller = context
                                 .ServiceProvider?
                                 .GetService(_actionMethod.Parent.ObjectType) as GraphController;

                if (controller == null)
                {
                    result = new RouteNotFoundGraphActionResult(
                        $"The controller assigned to process the field '{context.Request.InvocationContext.Field.Route.Path}' " +
                        "was not found.");
                }
                else
                {
                    // invoke the right action method and set a result.
                    var task         = controller.InvokeActionAsync(_actionMethod, context);
                    var returnedItem = await task.ConfigureAwait(false);

                    result = this.EnsureGraphActionResult(returnedItem);
                }
            }
            catch (Exception ex)
            {
                // :(
                result = new InternalServerErrorGraphActionResult("Operation failed.", ex);
            }

            // resolve the final graph action output using the provided field context
            // in what ever manner is appropriate for the result itself
            await result.Complete(context).ConfigureAwait(false);
        }
Beispiel #9
0
        /// <summary>
        /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
        /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
        public virtual async Task Resolve(FieldResolutionContext context, CancellationToken cancelToken = default)
        {
            var sourceData = context.Arguments?.SourceData;

            if (sourceData == null)
            {
                context.Messages.Critical(
                    "No source data was provided to the field resolver " +
                    $"for '{_graphMethod.Route.Path}'. Unable to complete the request.",
                    Constants.ErrorCodes.INVALID_OBJECT,
                    context.Request.Origin);

                return;
            }

            var typeCheck = _methodInfo.ReflectedType ?? _methodInfo.DeclaringType;

            if (context.Arguments.SourceData.GetType() != typeCheck)
            {
                context.Messages.Critical(
                    "The source data provided to the field resolver " +
                    $"for '{_graphMethod.Route.Path}' could not be coerced into the expected source graph type. See exception for details.",
                    Constants.ErrorCodes.INVALID_OBJECT,
                    context.Request.Origin,
                    new GraphExecutionException(
                        $"The method '{_graphMethod.InternalFullName}' expected source data of type " +
                        $"'{_graphMethod.Parent.ObjectType.FriendlyName()}' but received '{sourceData.GetType().FriendlyName()}' " +
                        "which is not compatible."));

                return;
            }

            try
            {
                object data = null;

                var paramSet     = context.Arguments.PrepareArguments(_graphMethod);
                var invoker      = InstanceFactory.CreateInstanceMethodInvoker(_graphMethod.Method);
                var invokeReturn = invoker(context.Arguments.SourceData, paramSet);
                if (_graphMethod.IsAsyncField)
                {
                    if (invokeReturn is Task task)
                    {
                        await task.ConfigureAwait(false);

                        data = task.ResultOrDefault();
                    }
                }
                else
                {
                    data = invokeReturn;
                }

                context.Result = data;
            }
            catch (GraphExecutionException gee)
            {
                context.Messages.Critical(
                    gee.Message,
                    Constants.ErrorCodes.EXECUTION_ERROR,
                    context.Request.Origin);
            }
            catch (Exception ex)
            {
                context.Messages.Critical(
                    $"An unknown error occured atttempting to resolve the field '{_graphMethod.Route.Path}'. " +
                    $"See exception for details.",
                    Constants.ErrorCodes.UNHANDLED_EXCEPTION,
                    context.Request.Origin,
                    ex);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="context">The field context containing the necessary data to resolve
        /// the field and produce a reslt.</param>
        /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
        /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
        public async Task Resolve(FieldResolutionContext context, CancellationToken cancelToken = default)
        {
            var data = await _func(context?.Arguments.SourceData as TSource).ConfigureAwait(false);

            context.Result = data;
        }
        /// <summary>
        /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="context">The field context containing the necessary data to resolve
        /// the field and produce a reslt.</param>
        /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
        /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
        public async Task Resolve(FieldResolutionContext context, CancellationToken cancelToken = default)
        {
            var sourceData = context.Arguments.SourceData;

            if (sourceData == null)
            {
                context.Messages.Critical(
                    "No source data was provided to the field resolver " +
                    $"for '{_graphMethod.Name}'. Unable to complete the request.",
                    Constants.ErrorCodes.INVALID_OBJECT,
                    context.Request.Origin);

                return;
            }

            // valdidate the incoming source data to ensure its process-able by this property
            // resolver. If the data is being resolved through an interface or object reference
            // ensure the provided
            // source data can be converted otherwise ensure the types match exactly.
            if (_graphMethod.Parent.ObjectType.IsInterface || _graphMethod.Parent.ObjectType.IsClass)
            {
                if (!Validation.IsCastable(sourceData.GetType(), _graphMethod.Parent.ObjectType))
                {
                    context.Messages.Critical(
                        "The source data provided to the field resolver " +
                        $"for '{_graphMethod.Route.Path}' could not be coerced into the expected source graph type. See exception for details.",
                        Constants.ErrorCodes.INVALID_OBJECT,
                        context.Request.Origin,
                        new GraphExecutionException(
                            $"The property '{_graphMethod.InternalFullName}' expected source data that implements the interface " +
                            $"'{_graphMethod.Parent.ObjectType.FriendlyName()}' but received '{sourceData.GetType().FriendlyName()}' which " +
                            "is not compatible."));

                    return;
                }
            }
            else if (sourceData.GetType() != _graphMethod.Parent.ObjectType)
            {
                context.Messages.Critical(
                    "The source data provided to the field resolver " +
                    $"for '{_graphMethod.Route.Path}' could not be coerced into the expected source graph type. See exception for details.",
                    Constants.ErrorCodes.INVALID_OBJECT,
                    context.Request.Origin,
                    new GraphExecutionException(
                        $"The property '{_graphMethod.InternalFullName}' expected source data of type " +
                        $"'{_graphMethod.Parent.ObjectType.FriendlyName()}' but received '{sourceData.GetType().FriendlyName()}' " +
                        "which is not compatible."));

                return;
            }

            try
            {
                var invoker      = InstanceFactory.CreateInstanceMethodInvoker(_graphMethod.Method);
                var invokeReturn = invoker(sourceData, new object[0]);
                if (_graphMethod.IsAsyncField)
                {
                    if (invokeReturn is Task task)
                    {
                        await task.ConfigureAwait(false);

                        if (task.IsFaulted)
                        {
                            throw task.UnwrapException();
                        }

                        invokeReturn = task.ResultOfTypeOrNull(_graphMethod.ExpectedReturnType);
                    }
                    else
                    {
                        context.Messages.Critical(
                            "The source data provided to the field resolver " +
                            $"for '{_graphMethod.Route.Path}' could not be coerced into the expected source graph type. See exception for details.",
                            Constants.ErrorCodes.INVALID_OBJECT,
                            context.Request.Origin,
                            new GraphExecutionException(
                                $"The method '{_graphMethod.Route.Path}' is defined " +
                                $"as asyncronous but it did not return a {typeof(Task)}."));
                        invokeReturn = null;
                    }
                }

                context.Result = invokeReturn;
            }
            catch (Exception ex)
            {
                context.Messages.Critical(
                    $"An unknown error occured atttempting to resolve the field '{_graphMethod.Route.Path}'. See exception for details.",
                    Constants.ErrorCodes.UNHANDLED_EXCEPTION,
                    context.Request.Origin,
                    ex);
            }
        }
 /// <summary>
 /// Processes the given <see cref="IGraphFieldRequest" /> against this instance
 /// performing the operation as defined by this entity and generating a response.
 /// </summary>
 /// <param name="context">The field context containing the necessary data to resolve
 /// the field and produce a reslt.</param>
 /// <param name="cancelToken">The cancel token monitoring the execution of a graph request.</param>
 /// <returns>Task&lt;IGraphPipelineResponse&gt;.</returns>
 public Task Resolve(FieldResolutionContext context, CancellationToken cancelToken = default)
 {
     context.Result = _dataObject ?? new object();
     return(Task.CompletedTask);
 }