/// <summary> /// Initializes a new instance of the <see cref="ActionMethodModelStateValidatedLogEntry" /> class. /// </summary> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> /// <param name="modelState">the model dictionary created by the controller.</param> public ActionMethodModelStateValidatedLogEntry( IGraphMethod method, IDataRequest request, InputModelStateDictionary modelState) : base(LogEventIds.ControllerModelValidated) { this.PipelineRequestId = request.Id; this.ControllerName = method.Parent.ObjectType?.FriendlyName(true) ?? method.Parent.Name; this.ActionName = method.Name; this.FieldPath = method.Route.Path; this.ModelDataIsValid = modelState.IsValid; _shortControllerName = method.Parent.ObjectType?.FriendlyName() ?? method.Parent.Name; this.ModelItems = null; if (modelState.Values != null && modelState.Values.Any()) { var entries = new List <ModelStateEntryLogItem>(); foreach (var item in modelState.Values) { if (item.ValidationState == InputModelValidationState.Invalid) { entries.Add(new ModelStateEntryLogItem(item)); } } this.ModelItems = entries; } }
/// <summary> /// Initializes a new instance of the <see cref="FieldContextBuilder" /> class. /// </summary> /// <param name="serviceProvider">The service provider.</param> /// <param name="user">The user.</param> /// <param name="graphField">The graph field.</param> /// <param name="schema">The schema.</param> /// <param name="graphMethod">The metadata describing the method/functon to be invoked by a resolver.</param> public FieldContextBuilder( IServiceProvider serviceProvider, ClaimsPrincipal user, IGraphField graphField, ISchema schema, IGraphMethod graphMethod) { _schema = Validation.ThrowIfNullOrReturn(schema, nameof(schema)); _graphField = Validation.ThrowIfNullOrReturn(graphField, nameof(graphField)); _user = Validation.ThrowIfNullOrReturn(user, nameof(user)); _messageCollection = new GraphMessageCollection(); this.ServiceProvider = Validation.ThrowIfNullOrReturn(serviceProvider, nameof(serviceProvider)); Type expectedInputType = null; if (!Validation.IsCastable <GraphDirective>(graphMethod.Parent.ObjectType) && !Validation.IsCastable <GraphController>(graphMethod.Parent.ObjectType)) { expectedInputType = graphMethod.Parent.ObjectType; } var metaData = new MetaDataCollection(); _mockRequest = new Mock <IGraphFieldRequest>(); _mockInvocationContext = new Mock <IGraphFieldInvocationContext>(); // fake the request for the field data (normally generated by the primary query exeuction context) var id = Guid.NewGuid().ToString("N"); _mockRequest.Setup(x => x.Id).Returns(id); _mockRequest.Setup(x => x.Origin).Returns(SourceOrigin.None); _mockRequest.Setup(x => x.Items).Returns(metaData); _mockRequest.Setup(x => x.Field).Returns(_graphField); _mockRequest.Setup(x => x.InvocationContext).Returns(_mockInvocationContext.Object); _mockInvocationContext.Setup(x => x.ExpectedSourceType).Returns(expectedInputType); _mockInvocationContext.Setup(x => x.Field).Returns(_graphField); _mockInvocationContext.Setup(x => x.Arguments).Returns(_arguments); _mockInvocationContext.Setup(x => x.Name).Returns(_graphField.Name); _mockInvocationContext.Setup(x => x.Directives).Returns(new List <IDirectiveInvocationContext>()); _mockInvocationContext.Setup(x => x.ChildContexts).Returns(new FieldInvocationContextCollection()); _mockInvocationContext.Setup(x => x.Origin).Returns(SourceOrigin.None); this.GraphMethod = new Mock <IGraphMethod>(); this.GraphMethod.Setup(x => x.Parent).Returns(graphMethod.Parent); this.GraphMethod.Setup(x => x.ObjectType).Returns(graphMethod.ObjectType); this.GraphMethod.Setup(x => x.ExpectedReturnType).Returns(graphMethod.ExpectedReturnType); this.GraphMethod.Setup(x => x.Method).Returns(graphMethod.Method); this.GraphMethod.Setup(x => x.IsAsyncField).Returns(graphMethod.IsAsyncField); this.GraphMethod.Setup(x => x.Name).Returns(graphMethod.Name); this.GraphMethod.Setup(x => x.InternalFullName).Returns(graphMethod.InternalFullName); this.GraphMethod.Setup(x => x.InternalName).Returns(graphMethod.InternalName); this.GraphMethod.Setup(x => x.Route).Returns(graphMethod.Route); this.GraphMethod.Setup(x => x.Arguments).Returns(graphMethod.Arguments); }
private void ValidateModelDictionaryToLogEntry( IGraphMethod graphMethod, IGraphFieldRequest fieldRequest, InputModelStateDictionary dictionary, ActionMethodModelStateValidatedLogEntry logEntry) { Assert.AreEqual(fieldRequest.Id, logEntry.PipelineRequestId); Assert.AreEqual(graphMethod.Parent.ObjectType.FriendlyName(true), logEntry.ControllerName); Assert.AreEqual(graphMethod.Name, logEntry.ActionName); Assert.AreEqual(dictionary.IsValid, logEntry.ModelDataIsValid); foreach (var kvp in dictionary) { var itemResult = kvp.Value; if (itemResult.ValidationState == InputModelValidationState.Invalid) { var entryResult = logEntry.ModelItems.Cast <ModelStateEntryLogItem>().First(x => itemResult.Name == x.Name); Assert.AreEqual(itemResult.Name, entryResult.Name); Assert.AreEqual(itemResult.ValidationState.ToString(), entryResult.ValidationState); var itemErrors = itemResult.Errors; var entryErrors = entryResult.Errors; if (itemErrors.Count == 0) { // log entry should skip the property if // no errors are recorded Assert.IsTrue(entryErrors == null); continue; } Assert.AreEqual(itemErrors.Count, entryErrors.Count); for (var i = 0; i < itemErrors.Count; i++) { var itemError = itemErrors[i]; var entryError = entryErrors[i] as ModelStateErrorLogItem; Assert.AreEqual(itemError.ErrorMessage, entryError.ErrorMessage); var itemException = itemError.Exception; var entryException = entryError.Exception as ExceptionLogItem; Assert.AreEqual(itemException == null, entryException == null); if (itemException != null) { Assert.AreEqual(itemException.Message, entryException.ExceptionMessage); Assert.AreEqual(itemException.StackTrace, entryException.StackTrace); Assert.AreEqual(itemException.GetType().FriendlyName(true), entryException.TypeName); } } } } }
/// <summary> /// Initializes a new instance of the <see cref="ActionMethodInvocationCompletedLogEntry" /> class. /// </summary> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> /// <param name="result">The result that was generated.</param> public ActionMethodInvocationCompletedLogEntry(IGraphMethod method, IDataRequest request, object result) : base(LogEventIds.ControllerInvocationCompleted) { this.PipelineRequestId = request.Id; this.ControllerName = method.Parent.InternalFullName; this.ActionName = method.InternalName; this.FieldPath = method.Route.Path; this.ResultTypeName = result?.GetType().FriendlyName(true); _shortControllerName = method.Parent.InternalName; }
/// <inheritdoc /> public virtual void ActionMethodInvocationCompleted(IGraphMethod action, IDataRequest request, object result) { if (!this.IsEnabled(LogLevel.Trace)) { return; } var entry = new ActionMethodInvocationCompletedLogEntry(action, request, result); this.LogEvent(LogLevel.Trace, entry); }
/// <inheritdoc /> public virtual void ActionMethodUnhandledException(IGraphMethod action, IDataRequest request, Exception exception) { if (!this.IsEnabled(LogLevel.Error)) { return; } var entry = new ActionMethodUnhandledExceptionLogEntry(action, request, exception); this.LogEvent(LogLevel.Error, entry); }
/// <inheritdoc /> public virtual void ActionMethodModelStateValidated(IGraphMethod action, IDataRequest request, InputModelStateDictionary modelState) { if (!this.IsEnabled(LogLevel.Trace)) { return; } var entry = new ActionMethodModelStateValidatedLogEntry(action, request, modelState); this.LogEvent(LogLevel.Trace, entry); }
/// <summary> /// Initializes a new instance of the <see cref="ActionMethodInvocationExceptionLogEntry" /> class. /// </summary> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> /// <param name="exception">The exception that was thrown.</param> public ActionMethodInvocationExceptionLogEntry( IGraphMethod method, IDataRequest request, Exception exception) : base( LogEventIds.ControllerInvocationException, method, request, exception) { }
/// <summary> /// Initializes a new instance of the <see cref="ActionMethodUnhandledExceptionLogEntry" /> class. /// </summary> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> /// <param name="exception">The exception that was thrown.</param> public ActionMethodUnhandledExceptionLogEntry( IGraphMethod method, IDataRequest request, Exception exception) : base( LogEventIds.ControllerUnhandledException, method, request, exception) { }
/// <summary> /// Initializes a new instance of the <see cref="ActionMethodInvocationStartedLogEntry" /> class. /// </summary> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> public ActionMethodInvocationStartedLogEntry(IGraphMethod method, IDataRequest request) : base(LogEventIds.ControllerInvocationStarted) { this.PipelineRequestId = request.Id; this.ControllerName = method.Parent.InternalFullName; this.ActionName = method.Name; this.FieldPath = method.Route.Path; this.SourceObjectType = method.ObjectType?.ToString(); this.IsAsync = method.IsAsyncField; _shortControllerName = method.Parent.InternalName; }
/// <summary> /// Initializes a new instance of the <see cref="BaseActionMethodExceptionLogEntry" /> class. /// </summary> /// <param name="eventId">The event identifier.</param> /// <param name="method">The method being invoked.</param> /// <param name="request">The request being executed on the method.</param> /// <param name="exception">The exception that was thrown.</param> protected BaseActionMethodExceptionLogEntry( EventId eventId, IGraphMethod method, IDataRequest request, Exception exception) : base(eventId) { this.PipelineRequestId = request.Id; this.ControllerTypeName = method.Parent.InternalFullName; this.ActionName = method.Name; this.Exception = new ExceptionLogItem(exception); }
/// <summary> /// Prepares this colleciton of arguments for use in the provided method invocation generating /// a set of values that can be used to invoke the method. /// </summary> /// <param name="graphMethod">The graph method.</param> /// <returns>System.Object[].</returns> public object[] PrepareArguments(IGraphMethod graphMethod) { var paramSet = new List <object>(); foreach (var argTemplate in graphMethod.Arguments) { object passedValue = this.ResolveParameterFromArgumentTemplate(argTemplate); if (passedValue == null && !argTemplate.TypeExpression.IsNullable) { // technically shouldn't be throwable given the validation routines // but captured here as a saftey net for users // doing custom extensions or implementations throw new GraphExecutionException( $"The parameter '{argTemplate.Name}' for field '{graphMethod.Route.Path}' could not be resolved from the query document " + $"or variable collection and no default value was found."); } paramSet.Add(passedValue); } return(paramSet.ToArray()); }
/// <summary> /// Initializes a new instance of the <see cref="InternalServerErrorGraphActionResult" /> class. /// </summary> /// <param name="action">The action that was invoked to cause this internal error, if any.</param> /// <param name="exception">The exception, if any, that was thrown. Useful for logging or other intermediate actions.</param> public InternalServerErrorGraphActionResult(IGraphMethod action, Exception exception) { _action = action; _exception = exception; }
/// <summary> /// Initializes a new instance of the <see cref="RouteNotFoundGraphActionResult" /> class. /// </summary> /// <param name="invokedAction">The invoked action at the route location.</param> /// <param name="thrownException">The thrown exception that occured when invoking the action, if any.</param> public RouteNotFoundGraphActionResult(IGraphMethod invokedAction, Exception thrownException = null) { _invokeDef = invokedAction; _thrownException = thrownException; }
/// <summary> /// Initializes a new instance of the <see cref="GraphObjectMethodResolver" /> class. /// </summary> /// <param name="graphMethod">The graph method.</param> public GraphObjectMethodResolver(IGraphMethod graphMethod) { _graphMethod = Validation.ThrowIfNullOrReturn(graphMethod, nameof(graphMethod)); _methodInfo = _graphMethod.Method; }
/// <summary> /// Initializes a new instance of the <see cref="GraphControllerActionResolver"/> class. /// </summary> /// <param name="actionMethod">The action method that this resolver will invoke.</param> public GraphControllerActionResolver(IGraphMethod actionMethod) { _actionMethod = Validation.ThrowIfNullOrReturn(actionMethod, nameof(actionMethod)); }
/// <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<System.Object>.</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; } } }
/// <summary> /// Initializes a new instance of the <see cref="GraphObjectPropertyResolver" /> class. /// </summary> /// <param name="propertyGetMethod">The property method.</param> public GraphObjectPropertyResolver(IGraphMethod propertyGetMethod) { _graphMethod = Validation.ThrowIfNullOrReturn(propertyGetMethod, nameof(propertyGetMethod)); }