diff --git a/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj b/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj
index b600711..afbcab2 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!
- Provides support for containment and ODataRoute attributes.
+ Provide single quotes around swagger path parameters of type string
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.0
+ 2.7.1
diff --git a/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs b/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs
index 1aa4d31..077bc12 100644
--- a/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs
+++ b/Swashbuckle.OData.Sample/ODataControllers/ProductsController.cs
@@ -27,6 +27,15 @@ static ProductsController()
}).ToList().ForEach(p => Data.TryAdd(p.Id, p));
}
+ ///
+ /// Query products
+ ///
+ [EnableQuery]
+ public IQueryable GetProducts()
+ {
+ return Data.Values.AsQueryable();
+ }
+
///
/// Get the most expensive product. This is a function bound to a collection.
///
diff --git a/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs b/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs
index c4b4ed1..db10600 100644
--- a/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs
+++ b/Swashbuckle.OData.Tests/Fixtures/FunctionTests.cs
@@ -94,7 +94,7 @@ public async Task It_supports_a_function_that_accepts_a_string_parameter()
// Assert
PathItem pathItem;
- swaggerDocument.paths.TryGetValue("/odata/v1/Products({Id})/Default.CalculateGeneralSalesTax(state={state})", out pathItem);
+ swaggerDocument.paths.TryGetValue("/odata/v1/Products({Id})/Default.CalculateGeneralSalesTax(state='{state}')", out pathItem);
pathItem.Should().NotBeNull();
pathItem.get.Should().NotBeNull();
}
@@ -113,7 +113,7 @@ public async Task It_supports_unbound_functions()
// Assert
PathItem pathItem;
- swaggerDocument.paths.TryGetValue("/odata/v1/GetSalesTaxRate(state={state})", out pathItem);
+ swaggerDocument.paths.TryGetValue("/odata/v1/GetSalesTaxRate(state='{state}')", out pathItem);
pathItem.Should().NotBeNull();
pathItem.get.Should().NotBeNull();
}
diff --git a/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs b/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs
new file mode 100644
index 0000000..8ad60ec
--- /dev/null
+++ b/Swashbuckle.OData.Tests/Fixtures/StringTypeUrlParamTests.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Threading.Tasks;
+using System.Web.Http.Dispatcher;
+using System.Web.OData.Builder;
+using System.Web.OData.Extensions;
+using FluentAssertions;
+using Microsoft.OData.Edm;
+using Microsoft.Owin.Hosting;
+using NUnit.Framework;
+using Owin;
+using Swashbuckle.Swagger;
+using SwashbuckleODataSample;
+using SwashbuckleODataSample.Models;
+
+namespace Swashbuckle.OData.Tests
+{
+ [TestFixture]
+ public class StringTypeUrlParamTests
+ {
+ [Test]
+ 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);
+
+ // Act
+ var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1");
+
+ // Assert
+ PathItem pathItem;
+ swaggerDocument.paths.TryGetValue("/odata/v1/Products({Id})/Default.CalculateGeneralSalesTax(state='{state}')", out pathItem);
+ pathItem.Should().NotBeNull();
+ pathItem.get.Should().NotBeNull();
+ }
+ }
+
+ private static void Configuration(IAppBuilder appBuilder, Type targetController)
+ {
+ var config = appBuilder.GetStandardHttpConfig(targetController);
+
+ var controllerSelector = new UnitTestODataVersionControllerSelector(config, targetController);
+ config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
+
+ // Define a route to a controller class that contains functions
+ config.MapODataServiceRoute("FunctionsODataRoute", "odata/v1", GetFunctionsEdmModel());
+ controllerSelector.RouteVersionSuffixMapping.Add("FunctionsODataRoute", "V1");
+
+ config.EnsureInitialized();
+ }
+
+ private static IEdmModel GetFunctionsEdmModel()
+ {
+ ODataModelBuilder builder = new ODataConventionModelBuilder();
+
+ builder.EntitySet("Products");
+
+ var productType = builder.EntityType();
+
+ // Function bound to a collection
+ // Returns the most expensive product, a single entity
+ productType.Collection
+ .Function("MostExpensive")
+ .Returns();
+
+ // Function bound to a collection
+ // Returns the top 10 product, a collection
+ productType.Collection
+ .Function("Top10")
+ .ReturnsCollectionFromEntitySet("Products");
+
+ // Function bound to a single entity
+ // Returns the instance's price rank among all products
+ productType
+ .Function("GetPriceRank")
+ .Returns();
+
+ // Function bound to a single entity
+ // Accept a string as parameter and return a double
+ // This function calculate the general sales tax base on the
+ // state
+ productType
+ .Function("CalculateGeneralSalesTax")
+ .Returns()
+ .Parameter("state");
+
+ // Unbound Function
+ builder.Function("GetSalesTaxRate")
+ .Returns()
+ .Parameter("state");
+
+ return builder.GetEdmModel();
+ }
+ }
+}
\ 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 67a1b0d..c97a0db 100644
--- a/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj
+++ b/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj
@@ -150,6 +150,7 @@
+
diff --git a/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs b/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs
new file mode 100644
index 0000000..bee9491
--- /dev/null
+++ b/Swashbuckle.OData/Descriptions/ApiDescriptionExtensions.cs
@@ -0,0 +1,25 @@
+using System.Web.Http.Description;
+using Swashbuckle.Swagger;
+
+namespace Swashbuckle.OData.Descriptions
+{
+ public static class ApiDescriptionExtensions
+ {
+ public static string GetRelativePathWithQuotedStringParams(this ApiDescription apiDescription)
+ {
+ var parameters = apiDescription.ParameterDescriptions;
+
+ var newRelativePathSansQueryString = apiDescription.RelativePathSansQueryString();
+
+ foreach (var parameter in parameters)
+ {
+ if (newRelativePathSansQueryString.Contains("{" + parameter.Name + "}") && parameter.ParameterDescriptor.ParameterType == typeof(string))
+ {
+ newRelativePathSansQueryString = newRelativePathSansQueryString.Replace("{" + parameter.Name + "}", "\'{" + parameter.Name + "}\'");
+ }
+ }
+
+ return apiDescription.RelativePath.Replace(apiDescription.RelativePathSansQueryString(), newRelativePathSansQueryString);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs b/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs
index 7193def..92aa179 100644
--- a/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs
+++ b/Swashbuckle.OData/Descriptions/ODataActionDescriptorMapper.cs
@@ -63,6 +63,8 @@ public IEnumerable Map(ODataActionDescriptor oDataActionDescript
// Have to set ResponseDescription because it's internal!??
apiDescription.GetType().GetProperty("ResponseDescription").SetValue(apiDescription, responseDescription);
+ apiDescription.RelativePath = apiDescription.GetRelativePathWithQuotedStringParams();
+
apiDescriptions.Add(apiDescription);
}
return apiDescriptions;
diff --git a/Swashbuckle.OData/Descriptions/ODataSwaggerUtilities.cs b/Swashbuckle.OData/Descriptions/ODataSwaggerUtilities.cs
index c2e1cfe..23c6f88 100644
--- a/Swashbuckle.OData/Descriptions/ODataSwaggerUtilities.cs
+++ b/Swashbuckle.OData/Descriptions/ODataSwaggerUtilities.cs
@@ -326,15 +326,7 @@ public static string GetPathForEntity(string routePrefix, IEdmNavigationSource n
private static string AppendSingleColumnKeyTemplate(IEdmEntitySet entitySet, string singleEntityPath)
{
var key = entitySet.EntityType().Key().Single();
- //if (key.Type.Definition.TypeKind == EdmTypeKind.Primitive && ((IEdmPrimitiveType) key.Type.Definition).PrimitiveKind == EdmPrimitiveTypeKind.String)
- //{
- // singleEntityPath += "'{" + key.Name + "}', ";
- //}
- //else
- //{
-
singleEntityPath += "{" + key.Name + "}, ";
- //}
return singleEntityPath;
}
@@ -342,14 +334,7 @@ private static string AppendMultiColumnKeyTemplate(IEdmEntitySet entitySet, stri
{
foreach (var key in entitySet.EntityType().Key())
{
- //if (key.Type.Definition.TypeKind == EdmTypeKind.Primitive && ((IEdmPrimitiveType)key.Type.Definition).PrimitiveKind == EdmPrimitiveTypeKind.String)
- //{
- // singleEntityPath += key.Name + "='{" + key.Name + "}', ";
- //}
- //else
- //{
- singleEntityPath += key.Name + "={" + key.Name + "}, ";
- //}
+ singleEntityPath += key.Name + "={" + key.Name + "}, ";
}
return singleEntityPath;
}
@@ -403,14 +388,7 @@ public static string GetPathForOperationOfEntitySet(IEdmOperation operation, IEd
{
foreach (var parameter in operation.Parameters.Skip(1))
{
- //if (parameter.Type.Definition.TypeKind == EdmTypeKind.Primitive && ((IEdmPrimitiveType) parameter.Type.Definition).PrimitiveKind == EdmPrimitiveTypeKind.String)
- //{
- // swaggerOperationPath += parameter.Name + "=" + "'{" + parameter.Name + "}',";
- //}
- //else
- //{
- swaggerOperationPath += parameter.Name + "=" + "{" + parameter.Name + "},";
- //}
+ swaggerOperationPath += parameter.Name + "=" + "{" + parameter.Name + "},";
}
}
if (swaggerOperationPath.EndsWith(",", StringComparison.Ordinal))
@@ -444,14 +422,7 @@ public static string GetPathForOperationOfEntity(string routePrefix, IEdmOperati
{
foreach (var parameter in operation.Parameters.Skip(1))
{
- //if (parameter.Type.Definition.TypeKind == EdmTypeKind.Primitive && ((IEdmPrimitiveType) parameter.Type.Definition).PrimitiveKind == EdmPrimitiveTypeKind.String)
- //{
- // swaggerOperationPath += parameter.Name + "=" + "{" + parameter.Name + "},";
- //}
- //else
- //{
- swaggerOperationPath += parameter.Name + "=" + "{" + parameter.Name + "},";
- //}
+ swaggerOperationPath += parameter.Name + "=" + "{" + parameter.Name + "},";
}
}
if (swaggerOperationPath.EndsWith(",", StringComparison.Ordinal))
diff --git a/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs b/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs
index 7eeac6d..f2a5335 100644
--- a/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs
+++ b/Swashbuckle.OData/Descriptions/SwaggerOperationMapper.cs
@@ -67,6 +67,8 @@ public IEnumerable Map(ODataActionDescriptor oDataActionDescript
// Have to set ResponseDescription because it's internal!??
apiDescription.GetType().GetProperty("ResponseDescription").SetValue(apiDescription, responseDescription);
+ apiDescription.RelativePath = apiDescription.GetRelativePathWithQuotedStringParams();
+
apiDescriptions.Add(apiDescription);
}
diff --git a/Swashbuckle.OData/Properties/AssemblyInfo.cs b/Swashbuckle.OData/Properties/AssemblyInfo.cs
index 033618c..a0188dc 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.0")]
\ No newline at end of file
+[assembly: AssemblyInformationalVersion("2.7.1")]
\ No newline at end of file
diff --git a/Swashbuckle.OData/Swashbuckle.OData.csproj b/Swashbuckle.OData/Swashbuckle.OData.csproj
index 123db67..6607388 100644
--- a/Swashbuckle.OData/Swashbuckle.OData.csproj
+++ b/Swashbuckle.OData/Swashbuckle.OData.csproj
@@ -172,6 +172,7 @@
+