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);
                }
            }
        }
Example #3
0
    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);
            }
        }
    }
Example #4
0
        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());
     }
 }
Example #6
0
        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);
            }
        }
Example #7
0
        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);
            }
        }
Example #12
0
 // 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);
        }
Example #18
0
        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);
                }
            }
        }
Example #19
0
 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());
 }
Example #22
0
 public void Apply(ActionModel action)
 {
     action.ApiExplorer.IsVisible = (action.Controller.ControllerType.Assembly == _testAppAssembly);
 }
Example #23
0
        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);
                }
            }
        }