Fix for #1442 - Find better names for ApiExplorerProperties

Taking the suggestion here to move these to a sub-object. This is future
proof in the event that we need to capture more data for ApiExplorer, and
reads better.
This commit is contained in:
Ryan Nowak 2014-10-24 13:50:35 -07:00
parent 46897037e9
commit 305dd87b9b
10 changed files with 90 additions and 47 deletions

View File

@ -13,6 +13,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
ActionMethod = actionMethod;
ApiExplorer = new ApiExplorerModel();
Attributes = new List<object>();
ActionConstraints = new List<IActionConstraintMetadata>();
Filters = new List<IFilter>();
@ -24,8 +25,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
ActionMethod = other.ActionMethod;
ActionName = other.ActionName;
ApiExplorerGroupName = other.ApiExplorerGroupName;
ApiExplorerIsVisible = other.ApiExplorerIsVisible;
IsActionNameMatchRequired = other.IsActionNameMatchRequired;
// Not making a deep copy of the controller, this action still belongs to the same controller.
@ -38,6 +37,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
HttpMethods = new List<string>(other.HttpMethods);
// Make a deep copy of other 'model' types.
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
Parameters = new List<ParameterModel>(other.Parameters.Select(p => new ParameterModel(p)));
if (other.AttributeRouteModel != null)
@ -51,6 +51,15 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public string ActionName { get; set; }
/// <summary>
/// Gets or sets the <see cref="ApiExplorerModel"/> for this action.
/// </summary>
/// <remarks>
/// Setting the value of any properties on <see cref="ActionModel.ApiExplorer"/> will override any
/// values set on the associated <see cref="ControllerModel.ApiExplorer"/>.
/// </remarks>
public ApiExplorerModel ApiExplorer { get; set; }
public List<object> Attributes { get; private set; }
public ControllerModel Controller { get; set; }
@ -64,18 +73,5 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public List<ParameterModel> Parameters { get; private set; }
public AttributeRouteModel AttributeRouteModel { get; set; }
/// <summary>
/// If <c>true</c>, <see cref="Description.ApiDescription"/> objects will be created for this action.
/// If <c>null</c> then the value of <see cref="ControllerModel.ApiExplorerIsVisible"/> will be used.
/// </summary>
public bool? ApiExplorerIsVisible { get; set; }
/// <summary>
/// The value for <see cref="Description.ApiDescription.GroupName"/> of
/// <see cref="Description.ApiDescription"/> objects created for actions defined by this controller.
/// If <c>null</c> then the value of <see cref="ControllerModel.ApiExplorerGroupName"/> will be used.
/// </summary>
public string ApiExplorerGroupName { get; set; }
}
}

View File

@ -0,0 +1,43 @@
// 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.
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
/// <summary>
/// A model for ApiExplorer properties associated with a controller or action.
/// </summary>
public class ApiExplorerModel
{
/// <summary>
/// Creates a new <see cref="ApiExplorerModel"/>.
/// </summary>
public ApiExplorerModel()
{
}
/// <summary>
/// Creates a new <see cref="ApiExplorerModel"/> with properties copied from <paramref name="other"/>.
/// </summary>
/// <param name="other">The <see cref="ApiExplorerModel"/> to copy.</param>
public ApiExplorerModel([NotNull] ApiExplorerModel other)
{
GroupName = other.GroupName;
IsVisible = other.IsVisible;
}
/// <summary>
/// If <c>true</c>, <see cref="Description.ApiDescription"/> objects will be created for the associated
/// controller or action.
/// </summary>
/// <remarks>
/// Set this value to configure whether or not the associated controller or action will appear in ApiExplorer.
/// </remarks>
public bool? IsVisible { get; set; }
/// <summary>
/// The value for <see cref="Description.ApiDescription.GroupName"/> of
/// <see cref="Description.ApiDescription"/> objects created for the associated controller or action.
/// </summary>
public string GroupName { get; set; }
}
}

View File

@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
ControllerType = controllerType;
Actions = new List<ActionModel>();
ApiExplorer = new ApiExplorerModel();
Attributes = new List<object>();
AttributeRoutes = new List<AttributeRouteModel>();
ActionConstraints = new List<IActionConstraintMetadata>();
@ -23,8 +24,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public ControllerModel([NotNull] ControllerModel other)
{
ApiExplorerGroupName = other.ApiExplorerGroupName;
ApiExplorerIsVisible = other.ApiExplorerIsVisible;
ControllerName = other.ControllerName;
ControllerType = other.ControllerType;
@ -39,6 +38,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Make a deep copy of other 'model' types.
Actions = new List<ActionModel>(other.Actions.Select(a => new ActionModel(a)));
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
AttributeRoutes = new List<AttributeRouteModel>(
other.AttributeRoutes.Select(a => new AttributeRouteModel(a)));
}
@ -47,8 +47,15 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public List<ActionModel> Actions { get; private set; }
/// <summary>
/// Gets or sets the <see cref="ApiExplorerModel"/> for this controller.
/// </summary>
public ApiExplorerModel ApiExplorer { get; set; }
public ApplicationModel Application { get; set; }
public List<AttributeRouteModel> AttributeRoutes { get; private set; }
public List<object> Attributes { get; private set; }
public string ControllerName { get; set; }
@ -58,19 +65,5 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public List<IFilter> Filters { get; private set; }
public List<RouteConstraintAttribute> RouteConstraints { get; private set; }
public List<AttributeRouteModel> AttributeRoutes { get; private set; }
/// <summary>
/// If <c>true</c>, <see cref="Description.ApiDescription"/> objects will be created for actions defined by
/// this controller.
/// </summary>
public bool? ApiExplorerIsVisible { get; set; }
/// <summary>
/// The value for <see cref="Description.ApiDescription.GroupName"/> of
/// <see cref="Description.ApiDescription"/> objects created for actions defined by this controller.
/// </summary>
public string ApiExplorerGroupName { get; set; }
}
}

View File

@ -194,13 +194,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
if (apiVisibility != null)
{
actionModel.ApiExplorerIsVisible = !apiVisibility.IgnoreApi;
actionModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
}
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().FirstOrDefault();
if (apiGroupName != null)
{
actionModel.ApiExplorerGroupName = apiGroupName.GroupName;
actionModel.ApiExplorer.GroupName = apiGroupName.GroupName;
}
var httpMethods = attributes.OfType<IActionHttpMethodProvider>();

View File

@ -110,13 +110,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
if (apiVisibility != null)
{
controllerModel.ApiExplorerIsVisible = !apiVisibility.IgnoreApi;
controllerModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
}
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().FirstOrDefault();
if (apiGroupName != null)
{
controllerModel.ApiExplorerGroupName = apiGroupName.GroupName;
controllerModel.ApiExplorer.GroupName = apiGroupName.GroupName;
}
return controllerModel;

View File

@ -297,12 +297,12 @@ namespace Microsoft.AspNet.Mvc
ActionModel action,
ControllerModel controller)
{
var apiExplorerIsVisible = action.ApiExplorerIsVisible ?? controller.ApiExplorerIsVisible ?? false;
var apiExplorerIsVisible = action.ApiExplorer?.IsVisible ?? controller.ApiExplorer?.IsVisible ?? false;
if (apiExplorerIsVisible)
{
var apiExplorerActionData = new ApiDescriptionActionData()
{
GroupName = action.ApiExplorerGroupName ?? controller.ApiExplorerGroupName,
GroupName = action.ApiExplorer?.GroupName ?? controller.ApiExplorer?.GroupName,
};
actionDescriptor.SetProperty(apiExplorerActionData);

View File

@ -23,11 +23,16 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
action.AttributeRouteModel = route;
var apiExplorer = action.ApiExplorer;
apiExplorer.IsVisible = false;
apiExplorer.GroupName = "group1";
// Act
var action2 = new ActionModel(action);
// Assert
Assert.NotSame(action, action2.Parameters[0]);
Assert.NotSame(apiExplorer, action2.ApiExplorer);
Assert.NotSame(route, action2.AttributeRouteModel);
}
@ -39,8 +44,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
action.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
action.ActionName = "Edit";
action.ApiExplorerGroupName = "group";
action.ApiExplorerIsVisible = true;
action.Attributes.Add(new HttpGetAttribute());
action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo());
action.Filters.Add(new AuthorizeAttribute());
@ -53,7 +57,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Assert
foreach (var property in typeof(ActionModel).GetProperties())
{
if (property.Name.Equals("Parameters") || property.Name.Equals("AttributeRouteModel"))
if (property.Name.Equals("ApiExplorer") ||
property.Name.Equals("AttributeRouteModel") ||
property.Name.Equals("Parameters"))
{
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
continue;

View File

@ -23,12 +23,17 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
controller.AttributeRoutes.Add(route);
var apiExplorer = controller.ApiExplorer;
controller.ApiExplorer.GroupName = "group";
controller.ApiExplorer.IsVisible = true;
// Act
var controller2 = new ControllerModel(controller);
// Assert
Assert.NotSame(action, controller2.Actions[0]);
Assert.NotSame(route, controller2.AttributeRoutes[0]);
Assert.NotSame(apiExplorer, controller2.ApiExplorer);
Assert.NotSame(controller.ActionConstraints, controller2.ActionConstraints);
Assert.NotSame(controller.Actions, controller2.Actions);
@ -44,8 +49,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
var controller = new ControllerModel(typeof(TestController).GetTypeInfo());
controller.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
controller.ApiExplorerGroupName = "group";
controller.ApiExplorerIsVisible = true;
controller.Application = new ApplicationModel();
controller.Attributes.Add(new HttpGetAttribute());
controller.ControllerName = "cool";
@ -58,7 +61,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Assert
foreach (var property in typeof(ControllerModel).GetProperties())
{
if (property.Name.Equals("Actions") || property.Name.Equals("AttributeRoutes"))
if (property.Name.Equals("Actions") ||
property.Name.Equals("AttributeRoutes") ||
property.Name.Equals("ApiExplorer"))
{
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
continue;

View File

@ -25,7 +25,7 @@ namespace ApiExplorer
{
if (controller.ControllerType == _type)
{
controller.ApiExplorerIsVisible = false;
controller.ApiExplorer.IsVisible = false;
}
}
}

View File

@ -14,10 +14,10 @@ namespace ApiExplorer
{
foreach (var controller in application.Controllers)
{
if (controller.ApiExplorerIsVisible == null)
if (controller.ApiExplorer.IsVisible == null)
{
controller.ApiExplorerIsVisible = true;
controller.ApiExplorerGroupName = controller.ControllerName;
controller.ApiExplorer.IsVisible = true;
controller.ApiExplorer.GroupName = controller.ControllerName;
}
}
}