diff --git a/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj b/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj index afbcab2..151e821 100644 --- a/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj +++ b/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj @@ -25,12 +25,12 @@ Richard Beauchamp Extends Swashbuckle with OData v4 support! Extends Swashbuckle with OData v4 support! - Provide single quotes around swagger path parameters of type string + Supports OData paths that contain parameters of type array https://github.com/rbeauchamp/Swashbuckle.OData https://github.com/rbeauchamp/Swashbuckle.OData/blob/master/License.txt Copyright 2015 Swashbuckle Swagger SwaggerUi OData Documentation Discovery Help WebApi AspNet AspNetWebApi Docs WebHost IIS - 2.7.1 + 2.7.2 diff --git a/Swashbuckle.OData.Sample/App_Start/ODataConfig.cs b/Swashbuckle.OData.Sample/App_Start/ODataConfig.cs index f25f8be..c0d2d7d 100644 --- a/Swashbuckle.OData.Sample/App_Start/ODataConfig.cs +++ b/Swashbuckle.OData.Sample/App_Start/ODataConfig.cs @@ -98,13 +98,13 @@ private static IEdmModel GetFunctionsEdmModel() var productType = builder.EntityType(); - // Function bound to a collection + // Function bound to an entity set // Returns the most expensive product, a single entity productType.Collection .Function("MostExpensive") .Returns(); - // Function bound to a collection + // Function bound to an entity set // Returns the top 10 product, a collection productType.Collection .Function("Top10") @@ -125,12 +125,12 @@ private static IEdmModel GetFunctionsEdmModel() .Returns() .Parameter("state"); - // Commented out because unbound functions don't - // play well in a config with mulitple OData routes - // Unbound Function - //builder.Function("GetSalesTaxRate") - // .Returns() - // .Parameter("state"); + // Function bound to an entity set + // Accepts an array as a parameter + productType.Collection + .Function("ProductsWithIds") + .ReturnsCollectionFromEntitySet("Products") + .CollectionParameter("Ids"); return builder.GetEdmModel(); } diff --git a/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs b/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs index bb1a0d0..cd39c57 100644 --- a/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs +++ b/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs @@ -5,7 +5,6 @@ using System.Web.Http; using System.Web.Http.Description; using System.Web.OData; -using System.Web.OData.Routing; using SwashbuckleODataSample.Models; using SwashbuckleODataSample.Repositories; diff --git a/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs b/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs index 077bc12..b966b4c 100644 --- a/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs +++ b/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs @@ -80,7 +80,7 @@ public IHttpActionResult GetPriceRank(int key) } /// - /// Get the sales tax for a product in a given state. This is a function which accepts a parameter. + /// Get the sales tax for a product in a given state. This function accepts a parameter. /// /// The product id /// The state @@ -99,17 +99,16 @@ public IHttpActionResult CalculateGeneralSalesTax(int key, string state) return NotFound(); } - ///// - ///// Get the sales tax rate for a state. This is an unbound function. - ///// - ///// The state - //[HttpGet] - //[ResponseType(typeof(double))] - //[ODataRoute("GetSalesTaxRate(state={state})")] - //public IHttpActionResult GetSalesTaxRate([FromODataUri] string state) - //{ - // return Ok(GetRate(state)); - //} + /// + /// Get products with the given Ids. This function accepts a parameter of type 'array'. + /// + /// The ids. + /// + [HttpGet] + public IQueryable ProductsWithIds([FromODataUri]int[] Ids) + { + return Data.Values.Where(p => Ids.Contains(p.Id)).AsQueryable(); + } private static double GetRate(string state) { diff --git a/Swashbuckle.OData.Tests/Containment/ContainmentTests.cs b/Swashbuckle.OData.Tests/Containment/ContainmentTests.cs index c565f14..f3c0b51 100644 --- a/Swashbuckle.OData.Tests/Containment/ContainmentTests.cs +++ b/Swashbuckle.OData.Tests/Containment/ContainmentTests.cs @@ -6,7 +6,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; namespace Swashbuckle.OData.Tests.Containment { @@ -19,7 +18,7 @@ public async Task It_supports_models_that_use_containment() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(AccountsController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -39,7 +38,7 @@ public async Task It_supports_odata_attribute_routing() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(AccountsController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/CustomRouteTests.cs b/Swashbuckle.OData.Tests/Fixtures/CustomRouteTests.cs index 7c96eb1..6104708 100644 --- a/Swashbuckle.OData.Tests/Fixtures/CustomRouteTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/CustomRouteTests.cs @@ -26,7 +26,7 @@ public async Task It_allows_definition_of_custom_routes() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, typeof(OrdersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/FromUriArrayTests.cs b/Swashbuckle.OData.Tests/Fixtures/FromUriArrayTests.cs new file mode 100644 index 0000000..9c83a4e --- /dev/null +++ b/Swashbuckle.OData.Tests/Fixtures/FromUriArrayTests.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Concurrent; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using System.Web.Http; +using System.Web.OData; +using System.Web.OData.Builder; +using System.Web.OData.Extensions; +using System.Web.OData.Routing; +using FluentAssertions; +using Microsoft.OData.Edm; +using Microsoft.Owin.Hosting; +using NUnit.Framework; +using Owin; +using Swashbuckle.Swagger; + +namespace Swashbuckle.OData.Tests +{ + [TestFixture] + public class FromUriArrayTests + { + [Test] + public async Task It_supports_uris_that_contain_arrays() + { + using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(Products2Controller)))) + { + // Arrange + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); + // Verify that the OData route is valid + var products = await httpClient.GetJsonAsync>("/odata/ProductsWithIds(Ids=[0,1])"); + products.Should().NotBeNull(); + products.Value.Count.Should().Be(2); + + // Act + var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); + + // Assert + PathItem pathItem; + swaggerDocument.paths.TryGetValue("/odata/ProductsWithIds(Ids=[{Ids}])", out pathItem); + pathItem.Should().NotBeNull(); + pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); + } + } + + private static void Configuration(IAppBuilder appBuilder, Type targetController) + { + var config = appBuilder.GetStandardHttpConfig(targetController); + + // Define a route to a controller class that contains functions + config.MapODataServiceRoute("FromUriArrayRoute", "odata", GetEdmModel()); + + config.EnsureInitialized(); + } + + private static IEdmModel GetEdmModel() + { + ODataModelBuilder builder = new ODataConventionModelBuilder(); + + builder.EntitySet("Products"); + + builder.Function("ProductsWithIds") + .ReturnsCollectionFromEntitySet("Products") + .CollectionParameter("Ids"); + + return builder.GetEdmModel(); + } + } + + public class Product2 + { + [Key] + public int Id { get; set; } + + public string Name { get; set; } + + public double Price { get; set; } + } + + public class Products2Controller : ODataController + { + private static readonly ConcurrentDictionary Data; + + static Products2Controller() + { + Data = new ConcurrentDictionary(); + var rand = new Random(); + + Enumerable.Range(0, 100).Select(i => new Product2 + { + Id = i, + Name = "Product " + i, + Price = rand.NextDouble() * 1000 + }).ToList().ForEach(p => Data.TryAdd(p.Id, p)); + } + + [HttpGet] + [ODataRoute("ProductsWithIds(Ids={Ids})")] + public IQueryable ProductsWithIds([FromODataUri]int[] Ids) + { + return Data.Values.Where(p => Ids.Contains(p.Id)).AsQueryable(); + } + } +} \ No newline at end of file diff --git a/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs b/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs index db10600..c6800d2 100644 --- a/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs @@ -16,7 +16,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; namespace Swashbuckle.OData.Tests @@ -30,7 +29,7 @@ public async Task It_supports_a_parameterless_function_bound_to_a_collection() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -40,6 +39,8 @@ public async Task It_supports_a_parameterless_function_bound_to_a_collection() swaggerDocument.paths.TryGetValue("/odata/v1/Products/Default.MostExpensive()", out pathItem); pathItem.Should().NotBeNull(); pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); } } @@ -49,7 +50,7 @@ public async Task It_supports_a_function_bound_to_an_entity_set_that_returns_a_c using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -59,6 +60,8 @@ public async Task It_supports_a_function_bound_to_an_entity_set_that_returns_a_c swaggerDocument.paths.TryGetValue("/odata/v1/Products/Default.Top10()", out pathItem); pathItem.Should().NotBeNull(); pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); } } @@ -68,7 +71,7 @@ public async Task It_supports_a_function_bound_to_an_entity() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -78,6 +81,8 @@ public async Task It_supports_a_function_bound_to_an_entity() swaggerDocument.paths.TryGetValue("/odata/v1/Products({Id})/Default.GetPriceRank()", out pathItem); pathItem.Should().NotBeNull(); pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); } } @@ -87,7 +92,7 @@ public async Task It_supports_a_function_that_accepts_a_string_parameter() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -97,6 +102,8 @@ public async Task It_supports_a_function_that_accepts_a_string_parameter() swaggerDocument.paths.TryGetValue("/odata/v1/Products({Id})/Default.CalculateGeneralSalesTax(state='{state}')", out pathItem); pathItem.Should().NotBeNull(); pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); } } @@ -106,7 +113,7 @@ public async Task It_supports_unbound_functions() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -116,6 +123,8 @@ public async Task It_supports_unbound_functions() swaggerDocument.paths.TryGetValue("/odata/v1/GetSalesTaxRate(state='{state}')", out pathItem); pathItem.Should().NotBeNull(); pathItem.get.Should().NotBeNull(); + + await ValidationUtils.ValidateSwaggerJson(); } } diff --git a/Swashbuckle.OData.Tests/Fixtures/GetTests.cs b/Swashbuckle.OData.Tests/Fixtures/GetTests.cs index 283f589..9914e44 100644 --- a/Swashbuckle.OData.Tests/Fixtures/GetTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/GetTests.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; using SwashbuckleODataSample.ODataControllers; @@ -24,7 +23,7 @@ public async Task It_includes_the_filter_parameter() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -49,7 +48,7 @@ public async Task It_has_all_optional_odata_query_parameters() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -69,7 +68,7 @@ public async Task It_has_a_parameter_with_a_name_equal_to_the_path_name() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -91,7 +90,7 @@ public async Task It_supports_types_with_a_guid_primary_key() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(OrdersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs index 2069476..9dc6c4a 100644 --- a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs @@ -25,7 +25,7 @@ public async Task It_applies_document_filters() { // Arrange Action config = c => c.DocumentFilter(); - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, unitTestConfigs: config))) { @@ -45,7 +45,7 @@ public async Task It_supports_multiple_odata_routes() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -63,7 +63,7 @@ public async Task It_supports_odata_routes_that_dont_map_to_a_controller() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -81,7 +81,7 @@ public async Task It_explores_the_correct_controller() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -102,7 +102,7 @@ public async Task It_explores_the_correct_versioned_controller() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, typeof(CustomersV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -123,7 +123,7 @@ public async Task It_supports_both_webapi_and_odata_controllers() using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, typeof(ClientsController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/PatchTests.cs b/Swashbuckle.OData.Tests/Fixtures/PatchTests.cs index a461c78..667a969 100644 --- a/Swashbuckle.OData.Tests/Fixtures/PatchTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/PatchTests.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; using SwashbuckleODataSample.ODataControllers; @@ -24,7 +23,7 @@ public async Task It_has_a_body_parameter_with_a_schema() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(OrdersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/PostTests.cs b/Swashbuckle.OData.Tests/Fixtures/PostTests.cs index a771998..b0d12a4 100644 --- a/Swashbuckle.OData.Tests/Fixtures/PostTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/PostTests.cs @@ -8,7 +8,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; using SwashbuckleODataSample.ODataControllers; @@ -23,7 +22,7 @@ public async Task It_has_a_summary() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/PutTests.cs b/Swashbuckle.OData.Tests/Fixtures/PutTests.cs index b7b542c..b67b916 100644 --- a/Swashbuckle.OData.Tests/Fixtures/PutTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/PutTests.cs @@ -8,7 +8,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; using SwashbuckleODataSample.ODataControllers; @@ -23,7 +22,7 @@ public async Task It_includes_a_put_operation() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -44,7 +43,7 @@ public async Task It_does_not_exist_if_not_in_the_controller() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(OrdersController)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/RestierTests.cs b/Swashbuckle.OData.Tests/Fixtures/RestierTests.cs index ab7281f..a712502 100644 --- a/Swashbuckle.OData.Tests/Fixtures/RestierTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/RestierTests.cs @@ -32,7 +32,7 @@ public async Task It_supports_restier() using (WebApp.Start(HttpClientUtils.BaseAddress, Configuration)) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -54,7 +54,7 @@ public async Task It_has_a_restier_get_with_all_optional_query_parameters() using (WebApp.Start(HttpClientUtils.BaseAddress, Configuration)) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -74,7 +74,7 @@ public async Task It_has_a_restier_response_with_the_correct_edm_model_type() using (WebApp.Start(HttpClientUtils.BaseAddress, Configuration)) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -96,7 +96,7 @@ public async Task It_groups_paths_by_entity_set() using (WebApp.Start(HttpClientUtils.BaseAddress, Configuration)) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); @@ -116,7 +116,7 @@ public async Task It_has_a_restier_get_users_response_of_type_array() using (WebApp.Start(HttpClientUtils.BaseAddress, Configuration)) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs b/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs index 8ad60ec..4c93637 100644 --- a/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using Owin; using Swashbuckle.Swagger; -using SwashbuckleODataSample; using SwashbuckleODataSample.Models; namespace Swashbuckle.OData.Tests @@ -23,7 +22,7 @@ public async Task It_wraps_string_type_url_params_with_single_quotes() using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(ProductsV1Controller)))) { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData.Tests/HttpClientUtils.cs b/Swashbuckle.OData.Tests/HttpClientUtils.cs index b8ba7e5..759ff91 100644 --- a/Swashbuckle.OData.Tests/HttpClientUtils.cs +++ b/Swashbuckle.OData.Tests/HttpClientUtils.cs @@ -1,7 +1,6 @@ using System; using System.Net.Http; using System.Net.Http.Headers; -using Flurl; namespace Swashbuckle.OData.Tests { @@ -9,11 +8,11 @@ public class HttpClientUtils { public const string BaseAddress = "http://localhost:8347/"; - public static HttpClient GetHttpClient(string baseAddress, string routePrefix = null) + public static HttpClient GetHttpClient(string baseAddress) { var client = new HttpClient { - BaseAddress = string.IsNullOrWhiteSpace(routePrefix) ? new Uri(baseAddress) : new Uri(baseAddress.AppendPathSegment(routePrefix)), + BaseAddress = new Uri(baseAddress), Timeout = TimeSpan.FromMilliseconds(5 * 60 * 1000) }; diff --git a/Swashbuckle.OData.Tests/ODataResponse.cs b/Swashbuckle.OData.Tests/ODataResponse.cs new file mode 100644 index 0000000..32da0ef --- /dev/null +++ b/Swashbuckle.OData.Tests/ODataResponse.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Swashbuckle.OData.Tests +{ + internal class ODataResponse + { + public List Value { get; set; } + } +} \ No newline at end of file diff --git a/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj b/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj index c97a0db..bfa506f 100644 --- a/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj +++ b/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj @@ -143,6 +143,7 @@ + @@ -165,6 +166,7 @@ + diff --git a/Swashbuckle.OData.Tests/ValidationUtils.cs b/Swashbuckle.OData.Tests/ValidationUtils.cs index f7e2a04..12ee1fe 100644 --- a/Swashbuckle.OData.Tests/ValidationUtils.cs +++ b/Swashbuckle.OData.Tests/ValidationUtils.cs @@ -5,7 +5,6 @@ using FluentAssertions; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; -using SwashbuckleODataSample; namespace Swashbuckle.OData.Tests { @@ -14,7 +13,7 @@ public static class ValidationUtils public static async Task ValidateSwaggerJson() { // Arrange - var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress, ODataConfig.ODataRoutePrefix); + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); // Act var response = await httpClient.GetAsync("swagger/docs/v1"); diff --git a/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs b/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs index bee9491..a55674f 100644 --- a/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs +++ b/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs @@ -1,25 +1,49 @@ -using System.Web.Http.Description; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Web.Http.Description; using Swashbuckle.Swagger; namespace Swashbuckle.OData.Descriptions { public static class ApiDescriptionExtensions { - public static string GetRelativePathWithQuotedStringParams(this ApiDescription apiDescription) + public static string GetRelativePathForSwagger(this ApiDescription apiDescription) { var parameters = apiDescription.ParameterDescriptions; var newRelativePathSansQueryString = apiDescription.RelativePathSansQueryString(); + newRelativePathSansQueryString = AdjustRelativePathForStringParams(parameters, newRelativePathSansQueryString); + newRelativePathSansQueryString = AdjustRelativePathForArrayParams(parameters, newRelativePathSansQueryString); + + return apiDescription.RelativePath.Replace(apiDescription.RelativePathSansQueryString(), newRelativePathSansQueryString); + } + + private static string AdjustRelativePathForStringParams(IEnumerable parameters, string newRelativePathSansQueryString) + { foreach (var parameter in parameters) { - if (newRelativePathSansQueryString.Contains("{" + parameter.Name + "}") && parameter.ParameterDescriptor.ParameterType == typeof(string)) + if (newRelativePathSansQueryString.Contains("{" + parameter.Name + "}") && parameter.ParameterDescriptor.ParameterType == typeof (string)) { newRelativePathSansQueryString = newRelativePathSansQueryString.Replace("{" + parameter.Name + "}", "\'{" + parameter.Name + "}\'"); } } + return newRelativePathSansQueryString; + } - return apiDescription.RelativePath.Replace(apiDescription.RelativePathSansQueryString(), newRelativePathSansQueryString); + private static string AdjustRelativePathForArrayParams(IEnumerable parameters, string newRelativePathSansQueryString) + { + foreach (var parameter in parameters) + { + if (newRelativePathSansQueryString.Contains("{" + parameter.Name + "}") + && typeof(IEnumerable).IsAssignableFrom(parameter.ParameterDescriptor.ParameterType) + && parameter.ParameterDescriptor.ParameterType != typeof(string)) + { + newRelativePathSansQueryString = newRelativePathSansQueryString.Replace("{" + parameter.Name + "}", "[{" + parameter.Name + "}]"); + } + } + return newRelativePathSansQueryString; } } } \ No newline at end of file diff --git a/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs b/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs index 92aa179..070d329 100644 --- a/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs +++ b/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs @@ -63,7 +63,7 @@ public IEnumerable Map(ODataActionDescriptor oDataActionDescript // Have to set ResponseDescription because it's internal!?? apiDescription.GetType().GetProperty("ResponseDescription").SetValue(apiDescription, responseDescription); - apiDescription.RelativePath = apiDescription.GetRelativePathWithQuotedStringParams(); + apiDescription.RelativePath = apiDescription.GetRelativePathForSwagger(); apiDescriptions.Add(apiDescription); } diff --git a/Swashbuckle.OData/Descriptions/ParameterExtensions.cs b/Swashbuckle.OData/Descriptions/ParameterExtensions.cs index 96e01a9..db0bcf2 100644 --- a/Swashbuckle.OData/Descriptions/ParameterExtensions.cs +++ b/Swashbuckle.OData/Descriptions/ParameterExtensions.cs @@ -89,6 +89,8 @@ public static string GenerateSamplePathParameterValue(this Parameter parameter) return "\'SampleString\'"; case "boolean": return "true"; + case "array": + return "[]"; default: throw new Exception($"Could not generate sample value for query parameter type {type} and format {"null"}"); } diff --git a/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs b/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs index f2a5335..3a0e960 100644 --- a/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs +++ b/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs @@ -67,7 +67,7 @@ public IEnumerable Map(ODataActionDescriptor oDataActionDescript // Have to set ResponseDescription because it's internal!?? apiDescription.GetType().GetProperty("ResponseDescription").SetValue(apiDescription, responseDescription); - apiDescription.RelativePath = apiDescription.GetRelativePathWithQuotedStringParams(); + apiDescription.RelativePath = apiDescription.GetRelativePathForSwagger(); apiDescriptions.Add(apiDescription); } diff --git a/Swashbuckle.OData/Properties/AssemblyInfo.cs b/Swashbuckle.OData/Properties/AssemblyInfo.cs index a0188dc..20e35af 100644 --- a/Swashbuckle.OData/Properties/AssemblyInfo.cs +++ b/Swashbuckle.OData/Properties/AssemblyInfo.cs @@ -37,4 +37,4 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("2.7.1")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("2.7.2")] \ No newline at end of file