public async Task InternalServerError_WithMessage_AndException_FriendlyErrorMessage()
        {
            var exception    = new Exception("Fail");
            var actionResult = new InternalServerErrorGraphActionResult("big fail", exception);

            var context = this.CreateResolutionContext();
            await actionResult.Complete(context);

            Assert.IsTrue(context.IsCancelled);
            Assert.AreEqual(1, context.Messages.Count);
            Assert.AreEqual(Constants.ErrorCodes.UNHANDLED_EXCEPTION, context.Messages[0].Code);
            Assert.AreEqual("big fail", context.Messages[0].Message);
            Assert.AreEqual(exception, context.Messages[0].Exception);
        }
        public async Task InternalServerError_WithAction_AndException_FriendlyErrorMessage()
        {
            var action = TemplateHelper.CreateFieldTemplate <ActionableController>(nameof(ActionableController.DoStuff)) as IGraphMethod;

            var exception    = new Exception("Fail");
            var actionResult = new InternalServerErrorGraphActionResult(action, exception);

            var context = this.CreateResolutionContext();
            await actionResult.Complete(context);

            Assert.IsTrue(context.IsCancelled);
            Assert.AreEqual(1, context.Messages.Count);
            Assert.AreEqual(Constants.ErrorCodes.UNHANDLED_EXCEPTION, context.Messages[0].Code);
            Assert.IsTrue(context.Messages[0].Message.Contains("An unhandled exception was thrown during"));
            Assert.AreEqual(exception, context.Messages[0].Exception);
        }
        /// <summary>
        /// Processes the given <see cref="IGraphDirectiveRequest" /> against this instance
        /// performing the operation as defined by this entity and generating a response.
        /// </summary>
        /// <param name="context">The  context containing the necessary data to resolve
        /// the directive and produce a result.</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(DirectiveResolutionContext context, CancellationToken cancelToken = default)
        {
            var action = _directiveTemplate.FindMethod(context.Request.LifeCycle);

            // if no action is found skip processing of this directive
            if (action == null)
            {
                return;
            }

            IGraphActionResult result;

            try
            {
                // create a scoped controller instance for this invocation
                var directive = context
                                .ServiceProvider?
                                .GetService(_directiveTemplate.ObjectType) as GraphDirective;

                if (directive == null)
                {
                    result = new RouteNotFoundGraphActionResult(
                        $"The directive '{_directiveTemplate.InternalFullName}' " +
                        "was not found in the scoped service provider.");
                }
                else
                {
                    // invoke the right action method and set a result.
                    var task = directive.InvokeActionAsync(action, 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);
        }
Example #4
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);
        }
Example #5
0
        /// <summary>
        /// Invoke the specified action method as an asynchronous operation.
        /// </summary>
        /// <param name="actionToInvoke">The action to invoke.</param>
        /// <param name="context">The context.</param>
        /// <returns>Task&lt;System.Object&gt;.</returns>
        public async Task <object> InvokeActionAsync(
            IGraphMethod actionToInvoke,
            BaseResolutionContext <TRequest> context)
        {
            // deconstruct the context for processing
            _action = actionToInvoke;

            var fieldRequest = context.Request;

            this.Request = Validation.ThrowIfNullOrReturn(fieldRequest, nameof(fieldRequest));
            _context     = context;
            _logger      = context?.Logger;
            _logger?.ActionMethodInvocationRequestStarted(_action, this.Request);

            if (_action?.Method == null)
            {
                return(new InternalServerErrorGraphActionResult(
                           $"The definition for field '{this.GetType().Name}' defined no graph action to execute. Operation failed."));
            }

            try
            {
                var modelGenerator = new ModelStateGenerator(context.ServiceProvider);
                this.ModelState = modelGenerator.CreateStateDictionary(context.Arguments);

                _logger?.ActionMethodModelStateValidated(_action, this.Request, this.ModelState);

                if (_action.Method.DeclaringType != this.GetType())
                {
                    throw new TargetException($"Unable to invoke action '{_action.Route.Path}' on controller '{this.GetType().FriendlyName()}'. The controller " +
                                              "does not own the method.");
                }

                var invoker = InstanceFactory.CreateInstanceMethodInvoker(_action.Method);
                var invocationParameters = context.Arguments.PrepareArguments(_action);

                var invokeReturn = invoker(this, invocationParameters);
                if (_action.IsAsyncField)
                {
                    if (invokeReturn is Task task)
                    {
                        await task.ConfigureAwait(false);

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

                        invokeReturn = task.ResultOrDefault();
                    }
                    else
                    {
                        // given all the checking and parsing this should be imnpossible, but just in case
                        invokeReturn = new InternalServerErrorGraphActionResult($"The action '{_action.Route.Path}' is defined " +
                                                                                $"as asyncronous but it did not return a {typeof(Task)}.");
                    }
                }

                _logger?.ActionMethodInvocationCompleted(_action, this.Request, invokeReturn);
                return(invokeReturn);
            }
            catch (TargetInvocationException ti)
            {
                var innerException = ti.InnerException ?? ti;
                _logger?.ActionMethodInvocationException(_action, this.Request, innerException);

                return(new InternalServerErrorGraphActionResult(_action, innerException));
            }
            catch (Exception ex)
            {
                switch (ex)
                {
                // catch any other invocation exceptions and treat them as an invalid route
                // might happen if a method was declared differently than the actual call signature
                case TargetException _:
                case TargetParameterCountException _:
                    _logger?.ActionMethodInvocationException(_action, this.Request, ex);
                    return(new RouteNotFoundGraphActionResult(_action, ex));

                default:
                    // total failure by the user's action code.
                    // record and bubble
                    _logger?.ActionMethodUnhandledException(_action, this.Request, ex);
                    throw;
                }
            }
        }