From c936ae80ca6b3df61e54e204444e088f5ced8051 Mon Sep 17 00:00:00 2001 From: SonjaKhan Date: Mon, 17 Nov 2014 10:52:33 -0800 Subject: [PATCH] Logging assemblies, controllers, and actions --- .../DefaultControllerModelBuilder.cs | 48 +++-- .../ControllerActionDescriptorProvider.cs | 27 ++- ...aultActionDescriptorsCollectionProvider.cs | 14 +- .../Logging/ActionConstraintValues.cs | 54 ++++++ .../Logging/ActionDescriptorValues.cs | 117 ++++++++++++ .../Logging/ActionModelValues.cs | 86 +++++++++ .../Logging/ApiExplorerModelValues.cs | 38 ++++ .../Logging/AssemblyValues.cs | 45 +++++ .../Logging/AttributeRouteInfoValues.cs | 42 +++++ .../Logging/AttributeRouteModelValues.cs | 51 +++++ .../Logging/ControllerModelValues.cs | 91 +++++++++ .../Logging/ControllerStatus.cs | 24 +++ .../Logging/FilterDescriptorValues.cs | 42 +++++ .../Logging/FilterValues.cs | 66 +++++++ .../Logging/IsControllerValues.cs | 36 ++++ .../Logging/LogFormatter.cs | 37 ---- .../Logging/ParameterDescriptorValues.cs | 42 +++++ .../Logging/ParameterModelValues.cs | 44 +++++ .../Logging/RouteConstraintAttributeValues.cs | 47 +++++ .../RouteDataActionConstraintValues.cs | 41 ++++ .../ActionConstraintValuesTest.cs | 84 +++++++++ .../ActionDescriptorValuesTest.cs | 23 +++ .../ActionModelValuesTest.cs | 24 +++ .../ApiExplorerModelValuesTest.cs | 20 ++ .../DefaultControllerModelBuilderTest.cs | 2 +- .../AttributeRouteInfoValuesTest.cs | 24 +++ .../AttributeRouteModelValuesTest.cs | 20 ++ ...ControllerActionDescriptorProviderTests.cs | 12 +- .../ControllerModelValuesTest.cs | 24 +++ ...DescriptorCollectionProviderLoggingTest.cs | 177 ++++++++++++++++++ .../DefaultActionSelectorTests.cs | 5 +- .../FilterDescriptorValuesTest.cs | 20 ++ .../FilterValuesTest.cs | 99 ++++++++++ .../KnownRouteValueConstraintTests.cs | 2 +- .../Logging/PropertiesAssert.cs | 33 ++++ .../RouteConstraintAttributeValuesTest.cs | 23 +++ .../RouteDataActionConstraintValuesTest.cs | 19 ++ .../StaticControllerModelBuilder.cs | 2 +- .../ApiControllerActionDiscoveryTest.cs | 6 +- 39 files changed, 1545 insertions(+), 66 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ActionConstraintValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ActionDescriptorValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ActionModelValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ApiExplorerModelValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/AssemblyValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteInfoValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteModelValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ControllerModelValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/FilterDescriptorValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/FilterValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs delete mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/LogFormatter.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ParameterDescriptorValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/ParameterModelValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/RouteConstraintAttributeValues.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Logging/RouteDataActionConstraintValues.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionConstraintValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionDescriptorValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionModelValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ApiExplorerModelValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteInfoValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteModelValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ControllerModelValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/FilterDescriptorValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/FilterValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/Logging/PropertiesAssert.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/RouteConstraintAttributeValuesTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/RouteDataActionConstraintValuesTest.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs index f58c3e1989..0e1dc06014 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs @@ -5,7 +5,9 @@ using System; using System.Linq; using System.Reflection; using Microsoft.AspNet.Mvc.Description; +using Microsoft.AspNet.Mvc.Logging; using Microsoft.AspNet.Mvc.Routing; +using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Mvc.ApplicationModels { @@ -15,14 +17,16 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public class DefaultControllerModelBuilder : IControllerModelBuilder { private readonly IActionModelBuilder _actionModelBuilder; + private readonly ILogger _logger; /// /// Creates a new . /// /// The used to create actions. - public DefaultControllerModelBuilder(IActionModelBuilder actionModelBuilder) + public DefaultControllerModelBuilder(IActionModelBuilder actionModelBuilder, ILoggerFactory loggerFactory) { _actionModelBuilder = actionModelBuilder; + _logger = loggerFactory.Create(); } /// @@ -61,24 +65,42 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels /// protected virtual bool IsController([NotNull] TypeInfo typeInfo) { - if (!typeInfo.IsClass || - typeInfo.IsAbstract || + var status = ControllerStatus.IsController; - // We only consider public top-level classes as controllers. IsPublic returns false for nested - // classes, regardless of visibility modifiers. - !typeInfo.IsPublic || - typeInfo.ContainsGenericParameters) + if (!typeInfo.IsClass) { - return false; + status |= ControllerStatus.IsNotAClass; + } + if (typeInfo.IsAbstract) + { + status |= ControllerStatus.IsAbstract; + } + // We only consider public top-level classes as controllers. IsPublic returns false for nested + // classes, regardless of visibility modifiers + if (!typeInfo.IsPublic) + { + status |= ControllerStatus.IsNotPublicOrTopLevel; + } + if (typeInfo.ContainsGenericParameters) + { + status |= ControllerStatus.ContainsGenericParameters; } - if (typeInfo.Name.Equals("Controller", StringComparison.OrdinalIgnoreCase)) { - return false; + status |= ControllerStatus.NameIsController; } - - return typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) || - typeof(Controller).GetTypeInfo().IsAssignableFrom(typeInfo); + if (!typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && + !typeof(Controller).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + status |= ControllerStatus.DoesNotEndWithControllerAndIsNotAssignable; + } + if (_logger.IsEnabled(LogLevel.Verbose)) + { + _logger.WriteVerbose(new IsControllerValues( + typeInfo.AsType(), + status)); + } + return status == ControllerStatus.IsController; } /// diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs index c5bffe2322..c727fea474 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.AspNet.Mvc.Logging; using Microsoft.AspNet.Mvc.Filters; +using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Mvc @@ -16,16 +18,19 @@ namespace Microsoft.AspNet.Mvc private readonly IAssemblyProvider _assemblyProvider; private readonly IReadOnlyList _globalFilters; private readonly IEnumerable _modelConventions; + private readonly ILogger _logger; - public ControllerActionDescriptorProvider(IAssemblyProvider assemblyProvider, - IControllerModelBuilder applicationModelBuilder, - IGlobalFilterProvider globalFilters, - IOptions optionsAccessor) + public ControllerActionDescriptorProvider([NotNull] IAssemblyProvider assemblyProvider, + [NotNull] IControllerModelBuilder applicationModelBuilder, + [NotNull] IGlobalFilterProvider globalFilters, + [NotNull] IOptions optionsAccessor, + [NotNull] ILoggerFactory loggerFactory) { _assemblyProvider = assemblyProvider; _applicationModelBuilder = applicationModelBuilder; _globalFilters = globalFilters.Filters; _modelConventions = optionsAccessor.Options.ApplicationModelConventions; + _logger = loggerFactory.Create(); } public int Order @@ -43,6 +48,13 @@ namespace Microsoft.AspNet.Mvc { var applicationModel = BuildModel(); ApplicationModelConventions.ApplyConventions(applicationModel, _modelConventions); + if (_logger.IsEnabled(LogLevel.Verbose)) + { + foreach (var controller in applicationModel.Controllers) + { + _logger.WriteVerbose(new ControllerModelValues(controller)); + } + } return ControllerActionDescriptorBuilder.Build(applicationModel); } @@ -53,6 +65,13 @@ namespace Microsoft.AspNet.Mvc var assemblies = _assemblyProvider.CandidateAssemblies; var types = assemblies.SelectMany(a => a.DefinedTypes); + if (_logger.IsEnabled(LogLevel.Verbose)) + { + foreach (var assembly in assemblies) + { + _logger.WriteVerbose(new AssemblyValues(assembly)); + } + } foreach (var type in types) { diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultActionDescriptorsCollectionProvider.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultActionDescriptorsCollectionProvider.cs index f48415b83c..0ab1a13760 100644 --- a/src/Microsoft.AspNet.Mvc.Core/DefaultActionDescriptorsCollectionProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/DefaultActionDescriptorsCollectionProvider.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNet.Mvc.Logging; using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Mvc { @@ -13,15 +15,17 @@ namespace Microsoft.AspNet.Mvc public class DefaultActionDescriptorsCollectionProvider : IActionDescriptorsCollectionProvider { private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; private ActionDescriptorsCollection _collection; /// /// Initializes a new instance of the class. /// /// The application IServiceProvider. - public DefaultActionDescriptorsCollectionProvider(IServiceProvider serviceProvider) + public DefaultActionDescriptorsCollectionProvider(IServiceProvider serviceProvider, ILoggerFactory factory) { _serviceProvider = serviceProvider; + _logger = factory.Create(); } /// @@ -48,6 +52,14 @@ namespace Microsoft.AspNet.Mvc actionDescriptorProvider.Invoke(actionDescriptorProviderContext); + if (_logger.IsEnabled(LogLevel.Verbose)) + { + foreach (var actionDescriptor in actionDescriptorProviderContext.Results) + { + _logger.WriteVerbose(new ActionDescriptorValues(actionDescriptor)); + } + } + return new ActionDescriptorsCollection(actionDescriptorProviderContext.Results, 0); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ActionConstraintValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionConstraintValues.cs new file mode 100644 index 0000000000..9b68e6729b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionConstraintValues.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of an . + /// + public class ActionConstraintValues : LoggerStructureBase + { + public ActionConstraintValues(IActionConstraintMetadata inner) + { + var constraint = inner as IActionConstraint; + if (constraint != null) + { + IsConstraint = true; + Order = constraint.Order; + } + if (inner is IActionConstraintFactory) + { + IsFactory = true; + } + ActionConstraintMetadataType = inner.GetType(); + } + + /// + /// The of this . + /// + public Type ActionConstraintMetadataType { get; } + + /// + /// The constraint order if this is an . See . + /// + public int Order { get; } + + /// + /// Whether the action constraint is an . + /// + public bool IsConstraint { get; } + + /// + /// Whether the action constraint is an . + /// + public bool IsFactory { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ActionDescriptorValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionDescriptorValues.cs new file mode 100644 index 0000000000..0e7c488ea2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionDescriptorValues.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of an or + /// . Logged during action discovery. + /// + public class ActionDescriptorValues : LoggerStructureBase + { + public ActionDescriptorValues([NotNull] ActionDescriptor inner) + { + Name = inner.Name; + DisplayName = inner.DisplayName; + Parameters = inner.Parameters.Select(p => new ParameterDescriptorValues(p)).ToList(); + FilterDescriptors = inner.FilterDescriptors.Select(f => new FilterDescriptorValues(f)).ToList(); + RouteConstraints = inner.RouteConstraints.Select(r => new RouteDataActionConstraintValues(r)).ToList(); + AttributeRouteInfo = new AttributeRouteInfoValues(inner.AttributeRouteInfo); + RouteValueDefaults = inner.RouteValueDefaults.ToDictionary(i => i.Key, i => i.Value.ToString()); + ActionConstraints = inner.ActionConstraints?.Select(a => new ActionConstraintValues(a))?.ToList(); + HttpMethods = inner.ActionConstraints?.OfType().SelectMany(c => c.HttpMethods).ToList(); + Properties = inner.Properties.ToDictionary(i => i.Key.ToString(), i => i.Value.GetType()); + var controllerActionDescriptor = inner as ControllerActionDescriptor; + if (controllerActionDescriptor != null) + { + MethodInfo = controllerActionDescriptor.MethodInfo; + ControllerName = controllerActionDescriptor.ControllerName; + ControllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo; + } + } + + /// + /// The name of the action. See . + /// + public string Name { get; } + + /// + /// A friendly name for the action. See . + /// + public string DisplayName { get; } + + /// + /// The parameters of the action as . + /// See . + /// + public List Parameters { get; } + + /// + /// The filters of the action as . + /// See . + /// + public List FilterDescriptors { get; } + + /// + /// The route constraints of the action as . + /// See + /// + public List RouteConstraints { get; } + + /// + /// The attribute route info of the action as . + /// See . + /// + public AttributeRouteInfoValues AttributeRouteInfo { get; } + + /// + /// See . + /// + public Dictionary RouteValueDefaults { get; } + + /// + /// The action constraints of the action as . + /// See . + /// + public List ActionConstraints { get; } + + /// + /// The http methods this action supports. + /// + public List HttpMethods { get; } + + /// + /// See . + /// + public Dictionary Properties { get; } + + /// + /// The method info of the action if this is a . + /// See . + /// + public MethodInfo MethodInfo { get; } + + /// + /// The name of the action's controller if this is a . + /// See . + /// + public string ControllerName { get; } + + /// + /// The type info of the action's controller if this is a . + /// See . + /// + public TypeInfo ControllerTypeInfo { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ActionModelValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionModelValues.cs new file mode 100644 index 0000000000..a4c5344f01 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ActionModelValues.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Represents the state of an . + /// Logged as a substructure of + /// + public class ActionModelValues : LoggerStructureBase + { + // note: omit the controller as this structure is nested inside the ControllerModelValues it belongs to + public ActionModelValues(ActionModel inner) + { + if (inner != null) + { + ActionName = inner.ActionName; + ActionMethod = inner.ActionMethod; + ApiExplorer = new ApiExplorerModelValues(inner.ApiExplorer); + Parameters = inner.Parameters.Select(p => new ParameterModelValues(p)).ToList(); + Filters = inner.Filters.Select(f => new FilterValues(f)).ToList(); + if (inner.AttributeRouteModel != null) + { + AttributeRouteModel = new AttributeRouteModelValues(inner.AttributeRouteModel); + } + HttpMethods = inner.HttpMethods; + ActionConstraints = inner.ActionConstraints?.Select(a => new ActionConstraintValues(a))?.ToList(); + } + } + + /// + /// The name of the action. See . + /// + public string ActionName { get; } + + /// + /// The method info of the action. See . + /// + public MethodInfo ActionMethod { get; } + + /// + /// See . + /// + public ApiExplorerModelValues ApiExplorer { get; } + + /// + /// The parameters of the action as . + /// See . + /// + public List Parameters { get; } + + /// + /// The filters of the action as . + /// See . + /// + public List Filters { get; } + + /// + /// The attribute route model of the action as . + /// See . + /// + public AttributeRouteModelValues AttributeRouteModel { get; } + + /// + /// The http methods this action supports. See . + /// + public List HttpMethods { get; } + + /// + /// The action constraints of the action as . + /// See . + /// + public List ActionConstraints { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ApiExplorerModelValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ApiExplorerModelValues.cs new file mode 100644 index 0000000000..8cf64ae7a1 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ApiExplorerModelValues.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of an . + /// + public class ApiExplorerModelValues : LoggerStructureBase + { + public ApiExplorerModelValues(ApiExplorerModel inner) + { + if (inner != null) + { + IsVisible = inner.IsVisible; + GroupName = inner.GroupName; + } + } + + /// + /// See . + /// + public bool? IsVisible { get; } + + /// + /// See . + /// + public string GroupName { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/AssemblyValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/AssemblyValues.cs new file mode 100644 index 0000000000..cc1dae48f7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/AssemblyValues.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of an . Logged during Assembly discovery in Startup. + /// + public class AssemblyValues : LoggerStructureBase + { + public AssemblyValues([NotNull] Assembly inner) + { + AssemblyName = inner.FullName; +#if ASPNET50 + Location = inner.Location; +#endif + IsDynamic = inner.IsDynamic; + } + + /// + /// The name of the assembly. See . + /// + public string AssemblyName { get; } + +#if ASPNET50 + /// + /// The location of the assembly. See . + /// + public string Location { get; } +#endif + + /// + /// Whether or not the assembly is dynamic. See . + /// + public bool IsDynamic { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteInfoValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteInfoValues.cs new file mode 100644 index 0000000000..ae983214ff --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteInfoValues.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.Routing; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of a . Logged as a substructure of + /// . + /// + public class AttributeRouteInfoValues : LoggerStructureBase + { + public AttributeRouteInfoValues(AttributeRouteInfo inner) + { + Template = inner?.Template; + Order = inner?.Order; + Name = inner?.Name; + } + + /// + /// The route template. See . + /// + public string Template { get; } + + /// + /// The order of the route. See . + /// + public int? Order { get; } + + /// + /// The name of the route. See . + /// + public string Name { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteModelValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteModelValues.cs new file mode 100644 index 0000000000..7a5167b4f7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/AttributeRouteModelValues.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Logging; +using Microsoft.AspNet.Mvc.ApplicationModels; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of a . Logged as a substructure of + /// . + /// + public class AttributeRouteModelValues : LoggerStructureBase + { + public AttributeRouteModelValues(AttributeRouteModel inner) + { + if (inner != null) + { + Template = inner.Template; + Order = inner.Order; + Name = inner.Name; + IsAbsoluteTemplate = inner.IsAbsoluteTemplate; + } + } + + /// + /// The template of the route. See . + /// + public string Template { get; } + + /// + /// The order of the route. See . + /// + public int? Order { get; } + + /// + /// The name of the route. See . + /// + public string Name { get; } + + /// + /// Whether or not the template is absolute. See . + /// + public bool IsAbsoluteTemplate { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerModelValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerModelValues.cs new file mode 100644 index 0000000000..7682934bb3 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerModelValues.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Framework.Logging; +using Microsoft.AspNet.Mvc.ApplicationModels; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of a . Logged during controller discovery. + /// + public class ControllerModelValues : LoggerStructureBase + { + public ControllerModelValues(ControllerModel inner) + { + if (inner != null) + { + ControllerName = inner.ControllerName; + ControllerType = inner.ControllerType.AsType(); + ApiExplorer = new ApiExplorerModelValues(inner.ApiExplorer); + Actions = inner.Actions.Select(a => new ActionModelValues(a)).ToList(); + Attributes = inner.Attributes.Select(a => a.GetType()).ToList(); + Filters = inner.Filters.Select(f => new FilterValues(f)).ToList(); + ActionConstraints = inner.ActionConstraints?.Select(a => new ActionConstraintValues(a))?.ToList(); + RouteConstraints = inner.RouteConstraints.Select( + r => new RouteConstraintAttributeValues(r)).ToList(); + AttributeRoutes = inner.AttributeRoutes.Select( + a => new AttributeRouteModelValues(a)).ToList(); + } + } + + /// + /// The name of the controller. See . + /// + public string ControllerName { get; } + + /// + /// The of the controller. See . + /// + public Type ControllerType { get; } + + /// + /// See . + /// + public ApiExplorerModelValues ApiExplorer { get; } + + /// + /// The actions of the controller as . + /// See . + /// + public List Actions { get; } + + /// + /// The s of the controller's attributes. + /// See . + /// + public List Attributes { get; } + + /// + /// The filters on the controller as . + /// See . + /// + public List Filters { get; } + + /// + /// The action constraints on the controller as . + /// See . + /// + public List ActionConstraints { get; } + + /// + /// The route constraints on the controller as . + /// See . + /// + public List RouteConstraints { get; set; } + + /// + /// The attribute routes on the controller as . + /// See . + /// + public List AttributeRoutes { get; set; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs new file mode 100644 index 0000000000..d64b8f8483 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Indicates the status of a class during controller discovery. + /// All values except 0 represent a reason why a type is not a controller. + /// + [Flags] + public enum ControllerStatus + { + IsController = 0, + IsNotAClass = 1, + IsNotPublicOrTopLevel = 2, + IsAbstract = 4, + ContainsGenericParameters = 8, + // The name of the controller class is "Controller" + NameIsController = 16, + DoesNotEndWithControllerAndIsNotAssignable = 32 + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/FilterDescriptorValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/FilterDescriptorValues.cs new file mode 100644 index 0000000000..53c3a95f18 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/FilterDescriptorValues.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of a . Logged as a substructure of + /// . + /// + public class FilterDescriptorValues : LoggerStructureBase + { + public FilterDescriptorValues([NotNull] FilterDescriptor inner) + { + Filter = new FilterValues(inner.Filter); + Order = inner.Order; + Scope = inner.Scope; + } + + /// + /// The instance of the filter descriptor as . + /// See . + /// + public FilterValues Filter { get; } + + /// + /// The filter order. See . + /// + public int Order { get; } + + /// + /// The filter scope. See . + /// + public int Scope { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/FilterValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/FilterValues.cs new file mode 100644 index 0000000000..60151950cb --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/FilterValues.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of an . Logged as a component of + /// , and as a substructure of + /// and . + /// + public class FilterValues : LoggerStructureBase + { + public FilterValues(IFilter inner) + { + FilterMetadataType = inner.GetType(); + if (inner is IFilterFactory) + { + IsFactory = true; + if (inner is ServiceFilterAttribute) + { + FilterType = ((ServiceFilterAttribute)inner).ServiceType; + } + else if (inner is TypeFilterAttribute) + { + FilterType = ((TypeFilterAttribute)inner).ImplementationType; + } + } + if (FilterType != null) + { + FilterInterfaces = FilterType.GetInterfaces().ToList(); + } + else + { + FilterInterfaces = FilterMetadataType.GetInterfaces().ToList(); + } + } + + /// + /// Whether or not the instance of is an . + /// + public bool IsFactory { get; } + + /// + /// The metadata type of the . + /// + public Type FilterMetadataType { get; } + + /// + /// The inner of the if it is a + /// or . + /// + public Type FilterType { get; } + + /// + /// A list of interfaces the implements. + /// + public List FilterInterfaces { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs new file mode 100644 index 0000000000..3d6ddb8b9e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logged to indicate the state of a class during controller discovery. Logs the type + /// of the controller as well as the . + /// + public class IsControllerValues : LoggerStructureBase + { + public IsControllerValues(Type type, ControllerStatus status) + { + Type = type; + Status = status; + } + + /// + /// The of the potential class. + /// + public Type Type { get; } + + /// + /// The of the . + /// + public ControllerStatus Status { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/LogFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/LogFormatter.cs deleted file mode 100644 index e9d1e7d0a1..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/Logging/LogFormatter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.AspNet.Mvc.Logging -{ - public static class LogFormatter - { - /// - /// A formatter for use with . - /// - public static string Formatter(object o, Exception e) - { - if (o != null && e != null) - { - return o + Environment.NewLine + e; - } - - if (o != null) - { - return o.ToString(); - } - - if (e != null) - { - return e.ToString(); - } - - return ""; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterDescriptorValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterDescriptorValues.cs new file mode 100644 index 0000000000..3a8cc407a0 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterDescriptorValues.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of a . Logged as a substructure of + /// . + /// + public class ParameterDescriptorValues : LoggerStructureBase + { + public ParameterDescriptorValues([NotNull] ParameterDescriptor inner) + { + ParameterName = inner.Name; + ParameterType = inner.ParameterType; + BinderMetadataType = inner.BinderMetadata?.GetType(); + } + + /// + /// The name of the parameter. See . + /// + public string ParameterName { get; } + + /// + /// The of the parameter. See . + /// + public Type ParameterType { get; } + + /// + /// The of the . + /// + public Type BinderMetadataType { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterModelValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterModelValues.cs new file mode 100644 index 0000000000..62b179ece5 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/ParameterModelValues.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of a . Logged as a substructure of + /// , this contains the name, type, and + /// binder metadata of the parameter. + /// + public class ParameterModelValues : LoggerStructureBase + { + public ParameterModelValues([NotNull] ParameterModel inner) + { + ParameterName = inner.ParameterName; + ParameterType = inner.ParameterInfo.ParameterType; + BinderMetadata = inner.BinderMetadata?.GetType(); + } + + /// + /// The name of the parameter. See . + /// + public string ParameterName { get; } + + /// + /// The of the parameter. + /// + public Type ParameterType { get; } + + /// + /// The of the . + /// + public Type BinderMetadata { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/RouteConstraintAttributeValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/RouteConstraintAttributeValues.cs new file mode 100644 index 0000000000..75638d97b8 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/RouteConstraintAttributeValues.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of a . Logged as a substructure of + /// + /// + public class RouteConstraintAttributeValues : LoggerStructureBase + { + public RouteConstraintAttributeValues([NotNull] RouteConstraintAttribute inner) + { + RouteKey = inner.RouteKey; + RouteValue = inner.RouteValue; + RouteKeyHandling = inner.RouteKeyHandling; + BlockNonAttributedActions = inner.BlockNonAttributedActions; + } + + /// + /// The route value key. See . + /// + public string RouteKey { get; } + + /// + /// The expected route value. See . + /// + public string RouteValue { get; } + + /// + /// The . See . + /// + public RouteKeyHandling RouteKeyHandling { get; } + + /// + /// See . + /// + public bool BlockNonAttributedActions { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/RouteDataActionConstraintValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/RouteDataActionConstraintValues.cs new file mode 100644 index 0000000000..e1e066d454 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Logging/RouteDataActionConstraintValues.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Mvc.Logging +{ + /// + /// Logging representation of the state of a . Logged as a substructure of + /// . + /// + public class RouteDataActionConstraintValues : LoggerStructureBase + { + public RouteDataActionConstraintValues([NotNull] RouteDataActionConstraint inner) + { + RouteKey = inner.RouteKey; + RouteValue = inner.RouteValue; + KeyHandling = inner.KeyHandling; + } + + /// + /// The route key. See . + /// + public string RouteKey { get; } + + /// + /// The route value. See . + /// + public string RouteValue { get; } + + /// + /// The . See . + /// + public RouteKeyHandling KeyHandling { get; } + + public override string Format() + { + return LogFormatter.FormatStructure(this); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionConstraintValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionConstraintValuesTest.cs new file mode 100644 index 0000000000..804673d333 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionConstraintValuesTest.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class ActionConstraintValuesTest + { + [Fact] + public void IActionConstraintMetadata_InitializesCorrectValues() + { + // Arrange + var constraint = new TestConstraintMetadata(); + + // Act + var constraintValues = new ActionConstraintValues(constraint); + + // Assert + Assert.False(constraintValues.IsConstraint); + Assert.False(constraintValues.IsFactory); + Assert.Equal(0, constraintValues.Order); + Assert.Equal(typeof(TestConstraintMetadata), constraintValues.ActionConstraintMetadataType); + } + + [Fact] + public void IActionConstraint_InitializesCorrectValues() + { + // Arrange + var constraint = new TestConstraint(); + + // Act + var constraintValues = new ActionConstraintValues(constraint); + + // Assert + Assert.True(constraintValues.IsConstraint); + Assert.False(constraintValues.IsFactory); + Assert.Equal(23, constraintValues.Order); + Assert.Equal(typeof(TestConstraint), constraintValues.ActionConstraintMetadataType); + } + + [Fact] + public void IActionConstraintFactory_InitializesCorrectValues() + { + // Arrange + var constraint = new TestFactory(); + + // Act + var constraintValues = new ActionConstraintValues(constraint); + + // Assert + Assert.False(constraintValues.IsConstraint); + Assert.True(constraintValues.IsFactory); + Assert.Equal(0, constraintValues.Order); + Assert.Equal(typeof(TestFactory), constraintValues.ActionConstraintMetadataType); + } + + private class TestConstraintMetadata : IActionConstraintMetadata + { + } + + private class TestConstraint : IActionConstraint + { + public int Order + { + get { return 23; } + } + + public bool Accept(ActionConstraintContext context) + { + return false; + } + } + + private class TestFactory : IActionConstraintFactory + { + public IActionConstraint CreateInstance(IServiceProvider services) + { + return new TestConstraint(); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionDescriptorValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionDescriptorValuesTest.cs new file mode 100644 index 0000000000..9ad84e2a34 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionDescriptorValuesTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class ActionDescriptorValuesTest + { + [Fact] + public void ActionDescriptorValues_IncludesAllProperties() + { + // Arrange + var include = new[] { "HttpMethods" }; + + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(ControllerActionDescriptor), + typeof(ActionDescriptorValues), + include: include); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionModelValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionModelValuesTest.cs new file mode 100644 index 0000000000..ec058efb7d --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionModelValuesTest.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.ApplicationModels; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class ActionModelValuesTest + { + [Fact] + public void ActionModelValues_IncludesAllProperties() + { + // Arrange + var exclude = new[] { "Controller", "Attributes", "IsActionNameMatchRequired" }; + + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(ActionModel), + typeof(ActionModelValues), + exclude); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApiExplorerModelValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApiExplorerModelValuesTest.cs new file mode 100644 index 0000000000..8ff5eb427a --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApiExplorerModelValuesTest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.ApplicationModels; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class ApiExplorerModelValuesTest + { + [Fact] + public void ApiExplorerModelValues_IncludesAllProperties() + { + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(ApiExplorerModel), + typeof(ApiExplorerModelValues)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs index e02d2431c6..87481ef450 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs @@ -166,7 +166,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels private class AccessibleControllerModelBuilder : DefaultControllerModelBuilder { public AccessibleControllerModelBuilder() - : base(new DefaultActionModelBuilder()) + : base(new DefaultActionModelBuilder(), new NullLoggerFactory()) { } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteInfoValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteInfoValuesTest.cs new file mode 100644 index 0000000000..f9da445d10 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteInfoValuesTest.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.ApplicationModels; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class AttributeRouteInfoValuesTest + { + [Fact] + public void AttributeRouteModelValues_IncludesAllProperties() + { + // Arrange + var exclude = new[] { "Attribute" }; + + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(AttributeRouteModel), + typeof(AttributeRouteModelValues), + exclude); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteModelValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteModelValuesTest.cs new file mode 100644 index 0000000000..07b45ef980 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/AttributeRouteModelValuesTest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.Routing; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class AttributeRouteModelValuesTest + { + [Fact] + public void AttributeRouteInfoValues_IncludesAllProperties() + { + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(AttributeRouteInfo), + typeof(AttributeRouteInfoValues)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs index 286f311e67..6e9034a301 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs @@ -1340,7 +1340,8 @@ namespace Microsoft.AspNet.Mvc.Test assemblyProvider.Object, modelBuilder, new TestGlobalFilterProvider(filters), - new MockMvcOptionsAccessor()); + new MockMvcOptionsAccessor(), + new NullLoggerFactory()); return provider; } @@ -1359,7 +1360,8 @@ namespace Microsoft.AspNet.Mvc.Test assemblyProvider.Object, modelBuilder, new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); + new MockMvcOptionsAccessor(), + new NullLoggerFactory()); return provider; } @@ -1379,7 +1381,8 @@ namespace Microsoft.AspNet.Mvc.Test assemblyProvider.Object, modelBuilder, new TestGlobalFilterProvider(), - options); + options, + new NullLoggerFactory()); } private IEnumerable GetDescriptors(params TypeInfo[] controllerTypeInfos) @@ -1395,7 +1398,8 @@ namespace Microsoft.AspNet.Mvc.Test assemblyProvider.Object, modelBuilder, new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); + new MockMvcOptionsAccessor(), + new NullLoggerFactory()); return provider.GetDescriptors(); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerModelValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerModelValuesTest.cs new file mode 100644 index 0000000000..b1b089943e --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerModelValuesTest.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.ApplicationModels; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class ControllerModelValuesTest + { + [Fact] + public void ControllerModelValues_IncludesAllProperties() + { + // Arrange + var exclude = new[] { "Application" }; + + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(ControllerModel), + typeof(ControllerModelValues), + exclude); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs new file mode 100644 index 0000000000..5e8761f81c --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.ComponentModel.Design; +using System.Linq; +using System.Reflection; +using Microsoft.AspNet.Mvc.ApplicationModels; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.NestedProviders; +using Microsoft.Framework.Logging; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class DefaultActionDescriptorCollectionProviderLoggingTest + { + [Fact] + public void SimpleController_AssemblyDiscovery() + { + // Arrange + var sink = new TestSink(); + var loggerFactory = new TestLoggerFactory(sink); + + // Act + var provider = GetProvider(loggerFactory, typeof(SimpleController).GetTypeInfo()); + provider.BuildModel(); + + // Assert + Assert.Single(sink.Writes); + + var assemblyValues = sink.Writes[0].State as AssemblyValues; + Assert.NotNull(assemblyValues); + Assert.True(assemblyValues.AssemblyName.Contains("Microsoft.AspNet.Mvc.Core.Test")); + } + + [Fact] + public void ControllerDiscovery() + { + // Arrange + var sink = new TestSink(); + var loggerFactory = new TestLoggerFactory(sink); + + // Act + var provider = GetProvider( + loggerFactory, + typeof(SimpleController).GetTypeInfo(), + typeof(BasicController).GetTypeInfo()); + provider.GetDescriptors(); + + // Assert + // 1 assembly, 2 controllers + Assert.Equal(3, sink.Writes.Count); + + var controllerModelValues = sink.Writes[1].State as ControllerModelValues; + Assert.NotNull(controllerModelValues); + Assert.Equal("Simple", controllerModelValues.ControllerName); + Assert.Equal(typeof(SimpleController), controllerModelValues.ControllerType); + Assert.Single(controllerModelValues.Actions); + Assert.Empty(controllerModelValues.AttributeRoutes); + Assert.Empty(controllerModelValues.RouteConstraints); + Assert.Empty(controllerModelValues.Attributes); + Assert.Empty(controllerModelValues.Filters); + + controllerModelValues = sink.Writes[2].State as ControllerModelValues; + Assert.NotNull(controllerModelValues); + Assert.Equal("Basic", controllerModelValues.ControllerName); + Assert.Equal(typeof(BasicController), controllerModelValues.ControllerType); + Assert.Equal(2, controllerModelValues.Actions.Count); + Assert.Equal("GET", controllerModelValues.Actions[0].HttpMethods.FirstOrDefault()); + Assert.Equal("POST", controllerModelValues.Actions[1].HttpMethods.FirstOrDefault()); + Assert.Empty(controllerModelValues.AttributeRoutes); + Assert.Empty(controllerModelValues.RouteConstraints); + Assert.NotEmpty(controllerModelValues.Attributes); + Assert.Single(controllerModelValues.Filters); + } + + [Fact] + public void ActionDiscovery() + { + // Arrange + var sink = new TestSink(); + var loggerFactory = new TestLoggerFactory(sink); + + // Act + CreateActionDescriptors(loggerFactory, + typeof(SimpleController).GetTypeInfo(), + typeof(BasicController).GetTypeInfo()); + + // Assert + // 1 assembly, 2 controllers, 3 actions + Assert.Equal(6, sink.Writes.Count); + + var actionDescriptorValues = sink.Writes[3].State as ActionDescriptorValues; + Assert.NotNull(actionDescriptorValues); + Assert.Equal("EmptyAction", actionDescriptorValues.Name); + Assert.Equal("Simple", actionDescriptorValues.ControllerName); + Assert.Equal(typeof(SimpleController), actionDescriptorValues.ControllerTypeInfo); + Assert.Null(actionDescriptorValues.AttributeRouteInfo.Name); + Assert.Null(actionDescriptorValues.ActionConstraints); + Assert.Empty(actionDescriptorValues.FilterDescriptors); + Assert.Empty(actionDescriptorValues.Parameters); + + actionDescriptorValues = sink.Writes[4].State as ActionDescriptorValues; + Assert.NotNull(actionDescriptorValues); + Assert.Equal("Basic", actionDescriptorValues.Name); + Assert.Equal("Basic", actionDescriptorValues.ControllerName); + Assert.Equal(typeof(BasicController), actionDescriptorValues.ControllerTypeInfo); + Assert.Null(actionDescriptorValues.AttributeRouteInfo.Name); + Assert.NotEmpty(actionDescriptorValues.ActionConstraints); + Assert.Equal(2, actionDescriptorValues.FilterDescriptors.Count); + Assert.Empty(actionDescriptorValues.Parameters); + + actionDescriptorValues = sink.Writes[5].State as ActionDescriptorValues; + Assert.NotNull(actionDescriptorValues); + Assert.Equal("Basic", actionDescriptorValues.Name); + Assert.Equal("Basic", actionDescriptorValues.ControllerName); + Assert.Equal(typeof(BasicController), actionDescriptorValues.ControllerTypeInfo); + Assert.Null(actionDescriptorValues.AttributeRouteInfo.Name); + Assert.NotEmpty(actionDescriptorValues.ActionConstraints); + Assert.Single(actionDescriptorValues.FilterDescriptors); + Assert.Single(actionDescriptorValues.RouteConstraints); + Assert.Single(actionDescriptorValues.Parameters); + } + + private void CreateActionDescriptors(ILoggerFactory loggerFactory, params TypeInfo[] controllerTypeInfo) + { + var actionDescriptorProvider = GetProvider(loggerFactory, controllerTypeInfo); + var descriptorProvider = + new NestedProviderManager(new[] { actionDescriptorProvider }); + + var serviceContainer = new ServiceContainer(); + serviceContainer.AddService(typeof(INestedProviderManager), + descriptorProvider); + + var actionCollectionDescriptorProvider = new DefaultActionDescriptorsCollectionProvider(serviceContainer, loggerFactory); + var descriptors = actionCollectionDescriptorProvider.ActionDescriptors; + } + + private ControllerActionDescriptorProvider GetProvider( + ILoggerFactory loggerFactory, params TypeInfo[] controllerTypeInfo) + { + var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfo); + + var assemblyProvider = new Mock(); + assemblyProvider + .SetupGet(ap => ap.CandidateAssemblies) + .Returns(new Assembly[] { controllerTypeInfo.First().Assembly }); + + var provider = new ControllerActionDescriptorProvider( + assemblyProvider.Object, + modelBuilder, + new TestGlobalFilterProvider(), + new MockMvcOptionsAccessor(), + loggerFactory); + + return provider; + } + + private class SimpleController + { + public void EmptyAction() { } + } + + [Authorize] + private class BasicController + { + [HttpGet] + [AllowAnonymous] + public void Basic() { } + + [HttpPost] + [Route("/Basic")] + public void Basic(int id) { } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs index c8b5b71b49..dddfeac1cc 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs @@ -739,7 +739,7 @@ namespace Microsoft.AspNet.Mvc serviceContainer.AddService(typeof(INestedProviderManager), descriptorProvider); - var actionCollectionDescriptorProvider = new DefaultActionDescriptorsCollectionProvider(serviceContainer); + var actionCollectionDescriptorProvider = new DefaultActionDescriptorsCollectionProvider(serviceContainer, new NullLoggerFactory()); var decisionTreeProvider = new ActionSelectorDecisionTreeProvider(actionCollectionDescriptorProvider); var actionConstraintProvider = new NestedProviderManager( @@ -771,7 +771,8 @@ namespace Microsoft.AspNet.Mvc assemblyProvider, modelBuilder, new TestGlobalFilterProvider(), - new MockMvcOptionsAccessor()); + new MockMvcOptionsAccessor(), + new NullLoggerFactory()); } private static HttpContext GetHttpContext(string httpMethod) diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/FilterDescriptorValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/FilterDescriptorValuesTest.cs new file mode 100644 index 0000000000..4850d4d5c8 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/FilterDescriptorValuesTest.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.Logging; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Test.Logging +{ + public class FilterDescriptorValuesTest + { + [Fact] + public void FilterDescriptorValues_IncludesAllProperties() + { + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(FilterDescriptor), + typeof(FilterDescriptorValues)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/FilterValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/FilterValuesTest.cs new file mode 100644 index 0000000000..446273d69e --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/FilterValuesTest.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class FilterValuesTest + { + [Fact] + public void IFilter_InitializesCorrectValues() + { + // Arrange + var filter = new TestFilter(); + + // Act + var filterValues = new FilterValues(filter); + + // Assert + Assert.False(filterValues.IsFactory); + Assert.Null(filterValues.FilterType); + Assert.Equal(typeof(TestFilter), filterValues.FilterMetadataType); + Assert.Equal( + new List() { typeof(IFilter), typeof(IExceptionFilter) }, + filterValues.FilterInterfaces); + } + + [Fact] + public void IFilterFactory_InitializesCorrectValues() + { + // Arrange + var filter = new TestFactory(); + + // Act + var filterValues = new FilterValues(filter); + + // Assert + Assert.True(filterValues.IsFactory); + Assert.Null(filterValues.FilterType); + Assert.Equal(typeof(TestFactory), filterValues.FilterMetadataType); + Assert.Equal( + new List() { typeof(IFilterFactory), typeof(IFilter) }, + filterValues.FilterInterfaces); + } + + [Fact] + public void ServiceFilterAttribute_InitializesCorrectValues() + { + // Arrange + var filter = new ServiceFilterAttribute(typeof(TestFilter)); + + // Act + var filterValues = new FilterValues(filter); + + // Assert + Assert.True(filterValues.IsFactory); + Assert.Equal(typeof(TestFilter), filterValues.FilterType); + Assert.Equal(typeof(ServiceFilterAttribute), filterValues.FilterMetadataType); + Assert.Equal( + new List() { typeof(IFilter), typeof(IExceptionFilter) }, + filterValues.FilterInterfaces); + } + + [Fact] + public void TypeFilterAttribute_InitializesCorrectValues() + { + // Arrange + var filter = new TypeFilterAttribute(typeof(TestFilter)); + + // Act + var filterValues = new FilterValues(filter); + + // Assert + Assert.True(filterValues.IsFactory); + Assert.Equal(typeof(TestFilter), filterValues.FilterType); + Assert.Equal(typeof(TypeFilterAttribute), filterValues.FilterMetadataType); + Assert.Equal( + new List() { typeof(IFilter), typeof(IExceptionFilter) }, + filterValues.FilterInterfaces); + } + + private class TestFilter : IFilter, IExceptionFilter + { + public void OnException(ExceptionContext context) + { + } + } + + private class TestFactory : IFilterFactory + { + public IFilter CreateInstance(IServiceProvider serviceProvider) + { + return new TestFilter(); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/KnownRouteValueConstraintTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/KnownRouteValueConstraintTests.cs index 9afcdac612..4cda51fa84 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/KnownRouteValueConstraintTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/KnownRouteValueConstraintTests.cs @@ -164,7 +164,7 @@ namespace Microsoft.AspNet.Routing.Tests .Returns(actionProvider.Object); context.Setup(o => o.RequestServices .GetService(typeof(IActionDescriptorsCollectionProvider))) - .Returns(new DefaultActionDescriptorsCollectionProvider(context.Object.RequestServices)); + .Returns(new DefaultActionDescriptorsCollectionProvider(context.Object.RequestServices, new NullLoggerFactory())); return context.Object; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Logging/PropertiesAssert.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Logging/PropertiesAssert.cs new file mode 100644 index 0000000000..851883accd --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Logging/PropertiesAssert.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public static class PropertiesAssert + { + /// + /// Given two types, compares their properties and asserts true if they have the same property names. + /// + /// The original type to compare against. + /// The shadow type whose properties will be compared against the original. + /// Properties that exist in the original type but not the shadow. + /// Properties that are in the shadow type but not in the original. + public static void PropertiesAreTheSame(Type original, Type shadow, string[] exclude = null, string[] include = null) + { + var originalProperties = original.GetProperties().Where(p => !exclude?.Contains(p.Name) ?? true) + .Select(p => p.Name); + if (include != null) + { + originalProperties = originalProperties.Concat(include.ToList()); + } + originalProperties = originalProperties.OrderBy(n => n); + + // Message is a property on all ILoggerStructures + var shadowProperties = shadow.GetProperties().Where(p => !string.Equals("Message", p.Name)) + .Select(p => p.Name).OrderBy(n => n); + + Assert.True(originalProperties.SequenceEqual(shadowProperties)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/RouteConstraintAttributeValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/RouteConstraintAttributeValuesTest.cs new file mode 100644 index 0000000000..a184379dcf --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/RouteConstraintAttributeValuesTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class RouteConstraintAttributeValuesTest + { + [Fact] + public void RouteConstraintAttributeValues_IncludesAllProperties() + { + // Arrange + var exclude = new[] { "TypeId" }; + + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(RouteConstraintAttribute), + typeof(RouteConstraintAttributeValues), + exclude); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/RouteDataActionConstraintValuesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/RouteDataActionConstraintValuesTest.cs new file mode 100644 index 0000000000..63b6d4311d --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/RouteDataActionConstraintValuesTest.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNet.Mvc.Logging +{ + public class RouteDataActionConstraintValuesTest + { + [Fact] + public void RouteDataActionConstraintValues_IncludesAllProperties() + { + // Assert + PropertiesAssert.PropertiesAreTheSame( + typeof(RouteDataActionConstraint), + typeof(RouteDataActionConstraintValues)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs b/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs index 7b8af20b09..1ea00f6030 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public class StaticControllerModelBuilder : DefaultControllerModelBuilder { public StaticControllerModelBuilder(params TypeInfo[] controllerTypes) - : base(new DefaultActionModelBuilder()) + : base(new DefaultActionModelBuilder(), new NullLoggerFactory()) { ControllerTypes = new List(controllerTypes ?? Enumerable.Empty()); } diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs index 190d4df449..4fd2013829 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs @@ -12,6 +12,7 @@ using Microsoft.AspNet.Mvc.Filters; using Microsoft.AspNet.Mvc.WebApiCompatShim; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection.NestedProviders; +using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -360,7 +361,8 @@ namespace System.Web.Http assemblyProvider.Object, new NamespaceLimitedActionDiscoveryConventions(), filterProvider.Object, - optionsAccessor.Object); + optionsAccessor.Object, + new NullLoggerFactory()); return new NestedProviderManager( new INestedProvider[] @@ -372,7 +374,7 @@ namespace System.Web.Http private class NamespaceLimitedActionDiscoveryConventions : DefaultControllerModelBuilder { public NamespaceLimitedActionDiscoveryConventions() - : base(new DefaultActionModelBuilder()) + : base(new DefaultActionModelBuilder(), new NullLoggerFactory()) { }