public void Build_WithControllerPropertiesSet_AddsPropertiesWithBinderMetadataSet() { // Arrange var applicationModel = new ApplicationModel(); var controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>() { }); controller.ControllerProperties.Add( new PropertyModel( controller.ControllerType.GetProperty("BoundProperty"), new List<object>() { }) { BindingInfo = BindingInfo.GetBindingInfo(new object[] { new FromQueryAttribute() }), PropertyName = "BoundProperty" }); controller.ControllerProperties.Add( new PropertyModel(controller.ControllerType.GetProperty("UnboundProperty"), new List<object>() { })); controller.Application = applicationModel; applicationModel.Controllers.Add(controller); var methodInfo = typeof(TestController).GetMethod("SomeAction"); var actionModel = new ActionModel(methodInfo, new List<object>() { }); actionModel.Controller = controller; controller.Actions.Add(actionModel); // Act var descriptors = ControllerActionDescriptorBuilder.Build(applicationModel); // Assert var property = Assert.Single(descriptors.Single().BoundProperties); Assert.Equal("BoundProperty", property.Name); Assert.Equal(typeof(string), property.ParameterType); Assert.Equal(BindingSource.Query, property.BindingInfo.BindingSource); }
public void Apply(ControllerModel controller) { if (IsConventionApplicable(controller)) { var newActions = new List<ActionModel>(); foreach (var action in controller.Actions) { SetHttpMethodFromConvention(action); // Action Name doesn't really come into play with attribute routed actions. However for a // non-attribute-routed action we need to create a 'named' version and an 'unnamed' version. if (!IsActionAttributeRouted(action)) { var namedAction = action; var unnamedAction = new ActionModel(namedAction); unnamedAction.RouteConstraints.Add(new UnnamedActionRouteConstraint()); newActions.Add(unnamedAction); } } foreach (var action in newActions) { controller.Actions.Add(action); } } }
public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { var newActions = new List<ActionModel>(); foreach (var action in controller.Actions) { var localizedRouteAttributes = action.Attributes.OfType<LocalizedRouteAttribute>().ToArray(); if (localizedRouteAttributes.Any()) { foreach (var localizedRouteAttribute in localizedRouteAttributes) { var localizedVersions = GetLocalizedVersionsForARoute(localizedRouteAttribute.Name); foreach (var localizedVersion in localizedVersions) { var newAction = new ActionModel(action) { AttributeRouteModel = localizedVersion, }; newAction.Properties["culture"] = new CultureInfo(((LocalizedRouteAttribute) localizedVersion.Attribute).Culture ?? "en-US"); newAction.Filters.Add(new LocalizedRouteFilter()); newActions.Add(newAction); } } } } foreach (var newAction in newActions) { controller.Actions.Add(newAction); } } }
public void CopyConstructor_DoesDeepCopyOfOtherModels() { // Arrange var action = new ActionModel(typeof(TestController).GetMethod("Edit"), new List<object>()); var parameter = new ParameterModel(action.ActionMethod.GetParameters()[0], new List<object>()); parameter.Action = action; action.Parameters.Add(parameter); 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); }
public void Apply(ActionModel action) { if (IsConventionApplicable(action.Controller)) { action.ActionConstraints.Add(new OverloadActionConstraint()); } }
public ActionModel(ActionModel other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } ActionMethod = other.ActionMethod; ActionName = other.ActionName; // Not making a deep copy of the controller, this action still belongs to the same controller. Controller = other.Controller; // These are just metadata, safe to create new collections ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints); Attributes = new List<object>(other.Attributes); Filters = new List<IFilterMetadata>(other.Filters); HttpMethods = new List<string>(other.HttpMethods); Properties = new Dictionary<object, object>(other.Properties); // Make a deep copy of other 'model' types. ApiExplorer = new ApiExplorerModel(other.ApiExplorer); Parameters = new List<ParameterModel>(other.Parameters.Select(p => new ParameterModel(p))); RouteConstraints = new List<IRouteConstraintProvider>(other.RouteConstraints); if (other.AttributeRouteModel != null) { AttributeRouteModel = new AttributeRouteModel(other.AttributeRouteModel); } }
public void CopyConstructor_DoesDeepCopyOfOtherModels() { // Arrange var controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>()); var action = new ActionModel(typeof(TestController).GetMethod("Edit"), new List<object>()); controller.Actions.Add(action); action.Controller = controller; 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); Assert.NotSame(controller.Attributes, controller2.Attributes); Assert.NotSame(controller.Filters, controller2.Filters); Assert.NotSame(controller.RouteConstraints, controller2.RouteConstraints); }
private bool IsActionAttributeRouted(ActionModel action) { if (action.Controller.AttributeRoutes.Count > 0) { return true; } return action.AttributeRouteModel?.Template != null; }
public void Apply(ActionModel action) { foreach (var param in action.Parameters) { if (param.Attributes.Any(p => p.GetType() == typeof(FromHeaderAttribute))) { param.Action.Properties["source"] = "From Header"; } } }
public void Apply(ActionModel action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } if (IsConventionApplicable(action.Controller)) { action.ActionConstraints.Add(new OverloadActionConstraint()); } }
public void Apply(ActionModel action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } if (IsConventionApplicable(action.Controller)) { var optionalParameters = new HashSet<string>(); var uriBindingSource = (new FromUriAttribute()).BindingSource; foreach (var parameter in action.Parameters) { // Some IBindingSourceMetadata attributes like ModelBinder attribute return null // as their binding source. Special case to ensure we do not ignore them. if (parameter.BindingInfo?.BindingSource != null || parameter.Attributes.OfType<IBindingSourceMetadata>().Any()) { // This has a binding behavior configured, just leave it alone. } else if (CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // Simple types are by-default from the URI. parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = uriBindingSource; } else { // Complex types are by-default from the body. parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = BindingSource.Body; } // For all non IOptionalBinderMetadata, which are not URL source (like FromQuery etc.) do not // participate in overload selection and hence are added to the hashset so that they can be // ignored in OverloadActionConstraint. var optionalMetadata = parameter.Attributes.OfType<IOptionalBinderMetadata>().SingleOrDefault(); if (parameter.ParameterInfo.HasDefaultValue && parameter.BindingInfo.BindingSource == uriBindingSource || optionalMetadata != null && optionalMetadata.IsOptional || optionalMetadata == null && parameter.BindingInfo.BindingSource != uriBindingSource) { optionalParameters.Add(parameter.ParameterName); } } action.Properties.Add("OptionalParameters", optionalParameters); } }
// 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(); RouteConstraints = inner.RouteConstraints.Select( r => new RouteConstraintProviderValues(r)).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(); Properties = new Dictionary<object, object>(inner.Properties); } }
private void SetHttpMethodFromConvention(ActionModel action) { if (action.HttpMethods.Count > 0) { // If the HttpMethods are set from attributes, don't override it with the convention return; } // The Method name is used to infer verb constraints. Changing the action name has not impact. foreach (var verb in SupportedHttpMethodConventions) { if (action.ActionMethod.Name.StartsWith(verb, StringComparison.OrdinalIgnoreCase)) { action.HttpMethods.Add(verb); return; } } // If no convention matches, then assume POST action.HttpMethods.Add("POST"); }
public void Build_WithPropertiesSet_FromApplicationModel() { // Arrange var applicationModel = new ApplicationModel(); applicationModel.Properties["test"] = "application"; var controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>() { }); controller.Application = applicationModel; applicationModel.Controllers.Add(controller); var methodInfo = typeof(TestController).GetMethod("SomeAction"); var actionModel = new ActionModel(methodInfo, new List<object>() { }); actionModel.Controller = controller; controller.Actions.Add(actionModel); // Act var descriptors = ControllerActionDescriptorBuilder.Build(applicationModel); // Assert Assert.Equal("application", descriptors.Single().Properties["test"]); }
public void Apply(ActionModel action) { action.ActionName = "ChangedAction"; }
public void Apply(ActionModel model) { model.Properties["description"] = _value; }
public void ApplyConventions_RunsInOrderOfDecreasingScope() { // Arrange var sequence = 0; var applicationConvention = new Mock<IApplicationModelConvention>(); applicationConvention .Setup(c => c.Apply(It.IsAny<ApplicationModel>())) .Callback(() => { Assert.Equal(0, sequence++); }); var controllerConvention = new Mock<IControllerModelConvention>(); controllerConvention .Setup(c => c.Apply(It.IsAny<ControllerModel>())) .Callback(() => { Assert.Equal(1, sequence++); }); var actionConvention = new Mock<IActionModelConvention>(); actionConvention .Setup(c => c.Apply(It.IsAny<ActionModel>())) .Callback(() => { Assert.Equal(2, sequence++); }); var parameterConvention = new Mock<IParameterModelConvention>(); parameterConvention .Setup(c => c.Apply(It.IsAny<ParameterModel>())) .Callback(() => { Assert.Equal(3, sequence++); }); var options = new TestOptionsManager<MvcOptions>(); options.Value.Conventions.Add(applicationConvention.Object); var applicationModel = new ApplicationModel(); var controller = new ControllerModel(typeof(ConventionsController).GetTypeInfo(), new List<object>() { controllerConvention.Object }); controller.Application = applicationModel; applicationModel.Controllers.Add(controller); var methodInfo = typeof(ConventionsController).GetMethod("Create"); var actionModel = new ActionModel(methodInfo, new List<object>() { actionConvention.Object }); actionModel.Controller = controller; controller.Actions.Add(actionModel); var parameterInfo = actionModel.ActionMethod.GetParameters().Single(); var parameterModel = new ParameterModel(parameterInfo, new List<object>() { parameterConvention.Object }); parameterModel.Action = actionModel; actionModel.Parameters.Add(parameterModel); // Act ApplicationModelConventions.ApplyConventions(applicationModel, options.Value.Conventions); // Assert Assert.Equal(4, sequence); }
public void CopyConstructor_CopiesAllProperties() { // Arrange var action = new ActionModel( typeof(TestController).GetMethod("Edit"), new List<object>() { new HttpGetAttribute(), new MyFilterAttribute(), }); action.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" })); action.ActionName = "Edit"; action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>()); action.Filters.Add(new MyFilterAttribute()); action.HttpMethods.Add("GET"); action.RouteConstraints.Add(new MyRouteConstraintAttribute()); action.Properties.Add(new KeyValuePair<object, object>("test key", "test value")); // Act var action2 = new ActionModel(action); // Assert foreach (var property in typeof(ActionModel).GetProperties()) { // Reflection is used to make sure the test fails when a new property is added. 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; } var value1 = property.GetValue(action); var value2 = property.GetValue(action2); if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType)) { Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2); // Ensure non-default value Assert.NotEmpty((IEnumerable<object>)value1); } else if (typeof(IDictionary<object, object>).IsAssignableFrom(property.PropertyType)) { Assert.Equal(value1, value2); // Ensure non-default value Assert.NotEmpty((IDictionary<object, object>)value1); } else if (property.PropertyType.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null) { Assert.Equal(value1, value2); // Ensure non-default value Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType)); } else { Assert.Same(value1, value2); // Ensure non-default value Assert.NotNull(value1); } } }
public void Apply(ActionModel model) { model.ActionName = _actionName; }
public void Apply(ActionModel action) { action.Properties.Add("TestProperty", "TestValue"); }
private void AddProperties(ActionModel action, IEnumerable<object> controllerAttributes) { action.Properties.Add("ControllerAttributes", controllerAttributes.ToArray()); action.Properties.Add("ActionAttributes", action.Attributes.ToArray()); action.Properties.Add("IsObsolete", action.Attributes.OfType<ObsoleteAttribute>().Any()); }
public void Apply(ActionModel action) { action.ApiExplorer.IsVisible = (action.Controller.ControllerType.Assembly == _testAppAssembly); }
public void CopyConstructor_CopiesAllProperties() { // Arrange var action = new ActionModel(typeof(TestController).GetMethod("Edit"), new List <object>() { new HttpGetAttribute(), new AuthorizeAttribute() }); action.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" })); action.ActionName = "Edit"; action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List <object>()); action.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireClaim("whatever").Build())); action.HttpMethods.Add("GET"); action.RouteConstraints.Add(new AreaAttribute("Admin")); action.Properties.Add(new KeyValuePair <object, object>("test key", "test value")); // Act var action2 = new ActionModel(action); // Assert foreach (var property in typeof(ActionModel).GetProperties()) { // Reflection is used to make sure the test fails when a new property is added. 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; } var value1 = property.GetValue(action); var value2 = property.GetValue(action2); if (typeof(IEnumerable <object>).IsAssignableFrom(property.PropertyType)) { Assert.Equal <object>((IEnumerable <object>)value1, (IEnumerable <object>)value2); // Ensure non-default value Assert.NotEmpty((IEnumerable <object>)value1); } else if (typeof(IDictionary <object, object>).IsAssignableFrom(property.PropertyType)) { Assert.Equal(value1, value2); // Ensure non-default value Assert.NotEmpty((IDictionary <object, object>)value1); } else if (property.PropertyType.IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null) { Assert.Equal(value1, value2); // Ensure non-default value Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType)); } else { Assert.Same(value1, value2); // Ensure non-default value Assert.NotNull(value1); } } }