From 7d4258b18616579773836d89da25f039b6dae7f1 Mon Sep 17 00:00:00 2001 From: Alessio Franceschelli Date: Sun, 23 Jun 2019 23:25:48 +0100 Subject: [PATCH] Api Explorer: multiple complex properties --- .../src/DefaultApiDescriptionProvider.cs | 1 + .../test/DefaultApiDescriptionProviderTest.cs | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs index 10029a5c44..d44d801bd9 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs @@ -585,6 +585,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer if (Visited.Add(key)) { Visit(propertyContext, source ?? ambientSource, newContainerName); + Visited.Remove(key); } else { diff --git a/src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs index eb32865767..3d6d97d7a2 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs @@ -1485,6 +1485,66 @@ namespace Microsoft.AspNetCore.Mvc.Description Assert.Equal(typeof(decimal), productPrice.Type); } + [Fact] + public void GetApiDescription_ParameterDescription_DuplicatePropertiesWithChildren_ExpandBoth() + { + // Arrange + var action = CreateActionDescriptor(nameof(AcceptsMultipleProperties)); + var parameterDescriptor = action.Parameters.Single(); + + // Act + var descriptions = GetApiDescriptions(action); + + // Assert + var description = Assert.Single(descriptions); + Assert.Equal(4, description.ParameterDescriptions.Count); + + var parentNames = new[] { "Parent1", "Parent2" }; + + foreach (var parentName in parentNames) + { + var id = Assert.Single(description.ParameterDescriptions, p => p.Name == $"{parentName}.Child.Id"); + Assert.Same(BindingSource.Query, id.Source); + Assert.Equal(typeof(int), id.Type); + + var name = Assert.Single(description.ParameterDescriptions, p => p.Name == $"{parentName}.Child.Name"); + Assert.Same(BindingSource.Query, name.Source); + Assert.Equal(typeof(string), name.Type); + } + } + + [Fact] + public void GetApiDescription_ParameterDescription_DuplicatePropertiesWithChildren_Nested_ExpandAll() + { + // Arrange + var action = CreateActionDescriptor(nameof(AcceptsMultiplePropertiesNested)); + var parameterDescriptor = action.Parameters.Single(); + + // Act + var descriptions = GetApiDescriptions(action); + + // Assert + var description = Assert.Single(descriptions); + Assert.Equal(8, description.ParameterDescriptions.Count); + + var groupNames = new[] { "Group1", "Group2" }; + var parentNames = new[] { "Parent1", "Parent2" }; + + foreach (var groupName in groupNames) + { + foreach (var parentName in parentNames) + { + var id = Assert.Single(description.ParameterDescriptions, p => p.Name == $"{groupName}.{parentName}.Child.Id"); + Assert.Same(BindingSource.Query, id.Source); + Assert.Equal(typeof(int), id.Type); + + var name = Assert.Single(description.ParameterDescriptions, p => p.Name == $"{groupName}.{parentName}.Child.Name"); + Assert.Same(BindingSource.Query, name.Source); + Assert.Equal(typeof(string), name.Type); + } + } + } + [Fact] public void GetApiDescription_ParameterDescription_BreaksCycles() { @@ -2081,6 +2141,14 @@ namespace Microsoft.AspNetCore.Mvc.Description { } + private void AcceptsMultipleProperties([FromQuery]MultipleProperties model) + { + } + + private void AcceptsMultiplePropertiesNested([FromQuery]MultiplePropertiesContainer model) + { + } + private void ParameterDefaultValue(int value = 10) { } private class TestController @@ -2237,6 +2305,23 @@ namespace Microsoft.AspNetCore.Mvc.Description public int Id { get; set; } } + private class MultiplePropertiesContainer + { + public MultipleProperties Group1 { get; set; } + public MultipleProperties Group2 { get; set; } + } + + private class MultipleProperties + { + public Parent Parent1 { get; set; } + public Parent Parent2 { get; set; } + } + + private class Parent + { + public Child Child { get; set; } + } + private class MockInputFormatter : TextInputFormatter { public List SupportedTypes { get; } = new List();