From 5ba06422ba57881dd34ccb4869bfec6ab6195c0e Mon Sep 17 00:00:00 2001 From: Richard Beauchamp Date: Tue, 8 Dec 2015 12:18:19 -0800 Subject: [PATCH] #10: Support entity types with a guid primary key --- .../Swashbuckle.OData.NuGet.nuproj | 2 +- .../App_Start/FormatterConfig.cs | 1 - .../App_Start/SwaggerConfig.cs | 1 - .../ODataControllers/CustomersController.cs | 8 -- .../ODataControllers/Order.cs | 5 +- .../ODataControllers/OrdersController.cs | 21 ++--- .../SequentialGuidGenerator.cs | 83 +++++++++++++++++++ .../Swashbuckle.OData.Sample.csproj | 1 + Swashbuckle.OData.Tests/Fixtures/GetTests.cs | 21 ++++- .../Fixtures/HttpConfigurationRoutesTests.cs | 5 +- .../Fixtures/ODataSwaggerProviderTests.cs | 1 - Swashbuckle.OData.Tests/Fixtures/PutTests.cs | 3 +- Swashbuckle.OData.Tests/HttpExtensions.cs | 4 +- .../Properties/AssemblyInfo.cs | 1 - .../Swashbuckle.OData.Tests.csproj | 4 +- Swashbuckle.OData/CollectionExtentions.cs | 2 +- .../Descriptions/IParameterMapper.cs | 20 +++-- .../Descriptions/ODataApiExplorer.cs | 10 ++- .../SwaggerToApiExplorerMapper.cs | 2 +- .../HttpConfigurationExtensions.cs | 2 +- Swashbuckle.OData/ODataSwaggerConverter.cs | 69 +++++---------- Swashbuckle.OData/ODataSwaggerProvider.cs | 22 +++-- Swashbuckle.OData/ODataSwaggerUtilities.cs | 19 +++-- Swashbuckle.OData/ReflectionExtensions.cs | 2 +- Swashbuckle.OData/SchemaRegistryExtensions.cs | 5 +- .../SwaggerApiParameterDescription.cs | 2 +- .../SwaggerApiParameterSource.cs | 2 +- Swashbuckle.OData/Swashbuckle.OData.csproj | 16 ++-- Swashbuckle.OData/packages.config | 24 +++--- 29 files changed, 218 insertions(+), 140 deletions(-) create mode 100644 Swashbuckle.OData.Sample/SequentialGuidGenerator.cs diff --git a/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj b/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj index 5b5ae16..8fdec03 100644 --- a/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj +++ b/Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj @@ -30,7 +30,7 @@ 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.1.0 + 2.1.1 diff --git a/Swashbuckle.OData.Sample/App_Start/FormatterConfig.cs b/Swashbuckle.OData.Sample/App_Start/FormatterConfig.cs index 8236a01..08a5f8f 100644 --- a/Swashbuckle.OData.Sample/App_Start/FormatterConfig.cs +++ b/Swashbuckle.OData.Sample/App_Start/FormatterConfig.cs @@ -1,7 +1,6 @@ using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; namespace SwashbuckleODataSample { diff --git a/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs b/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs index 4674416..2a33550 100644 --- a/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs +++ b/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs @@ -1,5 +1,4 @@ using System.Web.Http; -using System.Web.Http.Description; using Swashbuckle.Application; using Swashbuckle.OData; using SwashbuckleODataSample; diff --git a/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs b/Swashbuckle.OData.Sample/ODataControllers/CustomersController.cs index d7f55db..a79f564 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.Results; using SwashbuckleODataSample.Models; namespace SwashbuckleODataSample.Controllers @@ -14,21 +13,18 @@ public class CustomersController : ODataController { private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext(); - // GET: odata/Customers [EnableQuery] public IQueryable GetCustomers() { return _db.Customers; } - // GET: odata/Customers(5) [EnableQuery] public SingleResult GetCustomer([FromODataUri] int key) { return SingleResult.Create(_db.Customers.Where(customer => customer.Id == key)); } - // PUT: odata/Customers(5) [ResponseType(typeof(void))] public async Task Put([FromODataUri] int key, Delta patch) { @@ -63,7 +59,6 @@ public async Task Put([FromODataUri] int key, Delta return Updated(customer); } - // POST: odata/Customers [ResponseType(typeof(Customer))] public async Task Post(Customer customer) { @@ -78,7 +73,6 @@ public async Task Post(Customer customer) return Created(customer); } - // PATCH: odata/Customers(5) [ResponseType(typeof(void))] [AcceptVerbs("PATCH", "MERGE")] public async Task Patch([FromODataUri] int key, Delta patch) @@ -114,7 +108,6 @@ public async Task Patch([FromODataUri] int key, Delta Delete([FromODataUri] int key) { @@ -130,7 +123,6 @@ public async Task Delete([FromODataUri] int key) return StatusCode(HttpStatusCode.NoContent); } - // GET: odata/Customers(5)/Orders [EnableQuery] public IQueryable GetOrders([FromODataUri] int key) { diff --git a/Swashbuckle.OData.Sample/ODataControllers/Order.cs b/Swashbuckle.OData.Sample/ODataControllers/Order.cs index cdd5aaa..2b7884f 100644 --- a/Swashbuckle.OData.Sample/ODataControllers/Order.cs +++ b/Swashbuckle.OData.Sample/ODataControllers/Order.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Web.OData.Builder; using Microsoft.OData.Edm; @@ -8,7 +9,7 @@ namespace SwashbuckleODataSample.Models public class Order { [Key] - public int OrderId { get; set; } + public Guid OrderId { get; set; } public string OrderName { get; set; } diff --git a/Swashbuckle.OData.Sample/ODataControllers/OrdersController.cs b/Swashbuckle.OData.Sample/ODataControllers/OrdersController.cs index ed27cb2..af08e08 100644 --- a/Swashbuckle.OData.Sample/ODataControllers/OrdersController.cs +++ b/Swashbuckle.OData.Sample/ODataControllers/OrdersController.cs @@ -1,4 +1,5 @@ -using System.Data.Entity.Infrastructure; +using System; +using System.Data.Entity.Infrastructure; using System.Linq; using System.Net; using System.Threading.Tasks; @@ -13,24 +14,23 @@ public class OrdersController : ODataController { private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext(); - // GET: odata/Orders [EnableQuery] public IQueryable GetOrders() { return _db.Orders; } - // GET: odata/Orders(5) [EnableQuery] - public SingleResult GetOrder([FromODataUri] int key) + public SingleResult GetOrder([FromODataUri] Guid key) { return SingleResult.Create(_db.Orders.Where(order => order.OrderId == key)); } - // POST: odata/Orders [ResponseType(typeof(Order))] public async Task Post(Order order) { + order.OrderId = SequentialGuidGenerator.Generate(SequentialGuidType.SequentialAtEnd); + if (!ModelState.IsValid) { return BadRequest(ModelState); @@ -42,10 +42,9 @@ public async Task Post(Order order) return Created(order); } - // PATCH: odata/Orders(5) [ResponseType(typeof(void))] [AcceptVerbs("PATCH", "MERGE")] - public async Task Patch([FromODataUri] int key, Delta patch) + public async Task Patch([FromODataUri] Guid key, Delta patch) { Validate(patch.GetEntity()); @@ -78,9 +77,8 @@ public async Task Patch([FromODataUri] int key, Delta return Updated(order); } - // DELETE: odata/Orders(5) [ResponseType(typeof(void))] - public async Task Delete([FromODataUri] int key) + public async Task Delete([FromODataUri] Guid key) { var order = await _db.Orders.FindAsync(key); if (order == null) @@ -94,9 +92,8 @@ public async Task Delete([FromODataUri] int key) return StatusCode(HttpStatusCode.NoContent); } - // GET: odata/Orders(5)/Customer [EnableQuery] - public SingleResult GetCustomer([FromODataUri] int key) + public SingleResult GetCustomer([FromODataUri] Guid key) { return SingleResult.Create(_db.Orders.Where(m => m.OrderId == key) .Select(m => m.Customer)); @@ -111,7 +108,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - private bool OrderExists(int key) + private bool OrderExists(Guid key) { return _db.Orders.Count(e => e.OrderId == key) > 0; } diff --git a/Swashbuckle.OData.Sample/SequentialGuidGenerator.cs b/Swashbuckle.OData.Sample/SequentialGuidGenerator.cs new file mode 100644 index 0000000..3332bfb --- /dev/null +++ b/Swashbuckle.OData.Sample/SequentialGuidGenerator.cs @@ -0,0 +1,83 @@ +using System; +using System.Security.Cryptography; + +namespace SwashbuckleODataSample +{ + /// + /// Generates values using strategy from Jeremy Todd. + /// + /// + public static class SequentialGuidGenerator + { + private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider(); + + public static Guid Generate(SequentialGuidType sequentialGuidType) + { + var randomBytes = new byte[10]; + Rng.GetBytes(randomBytes); + + var timestamp = DateTime.UtcNow.Ticks / 10000L; + var timestampBytes = BitConverter.GetBytes(timestamp); + + if (BitConverter.IsLittleEndian) + { + Array.Reverse(timestampBytes); + } + + var guidBytes = new byte[16]; + + switch (sequentialGuidType) + { + case SequentialGuidType.SequentialAsString: + case SequentialGuidType.SequentialAsBinary: + Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6); + Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 10); + + // If formatting as a string, we have to reverse the order + // of the Data1 and Data2 blocks on little-endian systems. + if (sequentialGuidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian) + { + Array.Reverse(guidBytes, 0, 4); + Array.Reverse(guidBytes, 4, 2); + } + break; + case SequentialGuidType.SequentialAtEnd: + + Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10); + Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6); + break; + default: + throw new ArgumentOutOfRangeException(nameof(sequentialGuidType), sequentialGuidType, null); + } + + return new Guid(guidBytes); + } + } + + /// + /// Database GUID Column SequentialGuidType + /// Microsoft SQL Server uniqueidentifier SequentialAtEnd + /// MySQL char(36) SequentialAsString + /// Oracle raw(16) SequentialAsBinary + /// PostgreSQL uuid SequentialAsString + /// SQLite varies varies + /// + public enum SequentialGuidType + { + /// + /// Use for MySQL char(36) + /// Use for PostgreSQL uuid + /// + SequentialAsString, + + /// + /// Use for Oracle raw(16) + /// + SequentialAsBinary, + + /// + /// Use for Microsoft SQL Server uniqueidentifier + /// + SequentialAtEnd + } +} \ No newline at end of file diff --git a/Swashbuckle.OData.Sample/Swashbuckle.OData.Sample.csproj b/Swashbuckle.OData.Sample/Swashbuckle.OData.Sample.csproj index 2994e4d..cb8c619 100644 --- a/Swashbuckle.OData.Sample/Swashbuckle.OData.Sample.csproj +++ b/Swashbuckle.OData.Sample/Swashbuckle.OData.Sample.csproj @@ -135,6 +135,7 @@ + diff --git a/Swashbuckle.OData.Tests/Fixtures/GetTests.cs b/Swashbuckle.OData.Tests/Fixtures/GetTests.cs index 0cbd72d..d0e3511 100644 --- a/Swashbuckle.OData.Tests/Fixtures/GetTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/GetTests.cs @@ -2,8 +2,6 @@ using System.Threading.Tasks; using FluentAssertions; using Microsoft.Owin.Hosting; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using NUnit.Framework; using Swashbuckle.OData.Tests.WebHost; using Swashbuckle.Swagger; @@ -73,5 +71,24 @@ public async Task It_has_a_parameter_with_a_name_equal_to_the_path_name() pathItem.get.parameters.Should().Contain(parameter => parameter.name == "Id"); } } + + [Test] + public async Task It_supports_types_with_a_guid_primary_key() + { + using (WebApp.Start(TestWebApiStartup.BaseAddress, appBuilder => new TestWebApiStartup().Configuration(appBuilder))) + { + // Arrange + var httpClient = HttpClientUtils.GetHttpClient(); + + // Act + var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); + + // Assert + PathItem pathItem; + swaggerDocument.paths.TryGetValue("/Orders({OrderId})", out pathItem); + pathItem.Should().NotBeNull(); + pathItem.get.Should().NotBeNull(); + } + } } } \ No newline at end of file diff --git a/Swashbuckle.OData.Tests/Fixtures/HttpConfigurationRoutesTests.cs b/Swashbuckle.OData.Tests/Fixtures/HttpConfigurationRoutesTests.cs index 289c9b1..7a84586 100644 --- a/Swashbuckle.OData.Tests/Fixtures/HttpConfigurationRoutesTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/HttpConfigurationRoutesTests.cs @@ -1,11 +1,8 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; +using System.Threading.Tasks; using FluentAssertions; using Microsoft.Owin.Hosting; using NUnit.Framework; using Swashbuckle.OData.Tests.WebHost; -using Flurl; using Swashbuckle.Swagger; using SwashbuckleODataSample; diff --git a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs index 1c03b1d..6cb20c1 100644 --- a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using FluentAssertions; using Microsoft.Owin.Hosting; -using Newtonsoft.Json.Linq; using NUnit.Framework; using Swashbuckle.Application; using Swashbuckle.OData.Tests.WebHost; diff --git a/Swashbuckle.OData.Tests/Fixtures/PutTests.cs b/Swashbuckle.OData.Tests/Fixtures/PutTests.cs index 62ee9d4..b70ca0f 100644 --- a/Swashbuckle.OData.Tests/Fixtures/PutTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/PutTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using FluentAssertions; using Microsoft.Owin.Hosting; using NUnit.Framework; diff --git a/Swashbuckle.OData.Tests/HttpExtensions.cs b/Swashbuckle.OData.Tests/HttpExtensions.cs index e8a2f08..e2c518e 100644 --- a/Swashbuckle.OData.Tests/HttpExtensions.cs +++ b/Swashbuckle.OData.Tests/HttpExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; @@ -249,8 +250,7 @@ public static Task PatchAsync(this HttpClient client, st /// The type of object to serialize. public static Task PatchAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken) { - if (client == null) - throw new ArgumentNullException("client"); + Contract.Requires(client != null); var method = new HttpMethod("PATCH"); var content = new ObjectContent(value, formatter, mediaType); diff --git a/Swashbuckle.OData.Tests/Properties/AssemblyInfo.cs b/Swashbuckle.OData.Tests/Properties/AssemblyInfo.cs index 19d0e81..cade12f 100644 --- a/Swashbuckle.OData.Tests/Properties/AssemblyInfo.cs +++ b/Swashbuckle.OData.Tests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj b/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj index 07bb38e..7d2dacb 100644 --- a/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj +++ b/Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj @@ -20,7 +20,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;CONTRACTS_FULL prompt 4 false @@ -29,7 +29,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;CONTRACTS_FULL prompt 4 false diff --git a/Swashbuckle.OData/CollectionExtentions.cs b/Swashbuckle.OData/CollectionExtentions.cs index 39a9fe3..771cd33 100644 --- a/Swashbuckle.OData/CollectionExtentions.cs +++ b/Swashbuckle.OData/CollectionExtentions.cs @@ -4,7 +4,7 @@ namespace Swashbuckle.OData { - public static class CollectionExtentions + internal static class CollectionExtentions { /// /// Adds the elements of the specified collection to the end of the . diff --git a/Swashbuckle.OData/Descriptions/IParameterMapper.cs b/Swashbuckle.OData/Descriptions/IParameterMapper.cs index cf750fb..b7a0fa6 100644 --- a/Swashbuckle.OData/Descriptions/IParameterMapper.cs +++ b/Swashbuckle.OData/Descriptions/IParameterMapper.cs @@ -5,12 +5,12 @@ namespace Swashbuckle.OData { - public interface IParameterMapper + internal interface IParameterMapper { HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDescriptor actionDescriptor); } - public class MapByParameterName : IParameterMapper + internal class MapByParameterName : IParameterMapper { public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDescriptor actionDescriptor) { @@ -19,7 +19,7 @@ public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDes } } - public class MapByDescription : IParameterMapper + internal class MapByDescription : IParameterMapper { public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDescriptor actionDescriptor) { @@ -42,7 +42,7 @@ public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDes } } - public class MapByIndex : IParameterMapper + internal class MapByIndex : IParameterMapper { public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDescriptor actionDescriptor) { @@ -64,7 +64,7 @@ public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDes } } - public class MapToDefault : IParameterMapper + internal class MapToDefault : IParameterMapper { public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDescriptor actionDescriptor) { @@ -75,7 +75,7 @@ public HttpParameterDescriptor Map(Parameter parameter, int index, HttpActionDes }; } - private static Type GetType(Parameter queryParameter) + private static Type GetType(PartialSchema queryParameter) { var type = queryParameter.type; var format = queryParameter.format; @@ -90,7 +90,7 @@ private static Type GetType(Parameter queryParameter) case "boolean": return typeof(bool); default: - throw new Exception(string.Format("Could not determine .NET type for parameter type {0} and format {1}", type, "null")); + throw new Exception($"Could not determine .NET type for parameter type {type} and format 'null'"); } case "int32": return typeof(int); @@ -106,8 +106,12 @@ private static Type GetType(Parameter queryParameter) return typeof(double); case "float": return typeof(float); + case "guid": + return typeof(Guid); + case "binary": + return typeof(byte[]); default: - throw new Exception(string.Format("Could not determine .NET type for parameter type {0} and format {1}", type, format)); + throw new Exception($"Could not determine .NET type for parameter type {type} and format {format}"); } } } diff --git a/Swashbuckle.OData/Descriptions/ODataApiExplorer.cs b/Swashbuckle.OData/Descriptions/ODataApiExplorer.cs index 5c0e78f..a87902a 100644 --- a/Swashbuckle.OData/Descriptions/ODataApiExplorer.cs +++ b/Swashbuckle.OData/Descriptions/ODataApiExplorer.cs @@ -16,7 +16,7 @@ namespace Swashbuckle.OData { - public class ODataApiExplorer : IApiExplorer + internal class ODataApiExplorer : IApiExplorer { private const string ServiceRoot = "http://any/"; private readonly Lazy> _apiDescriptions; @@ -296,7 +296,7 @@ private static string GenerateSampleQueryParameterValue(Parameter queryParameter case "boolean": return "true"; default: - throw new Exception(string.Format("Could not generate sample value for query parameter type {0} and format {1}", type, "null")); + throw new Exception($"Could not generate sample value for query parameter type {type} and format {"null"}"); } case "int32": case "int64": @@ -311,8 +311,12 @@ private static string GenerateSampleQueryParameterValue(Parameter queryParameter return "2.34d"; case "float": return "2.0f"; + case "guid": + return Guid.NewGuid().ToString(); + case "binary": + return Convert.ToBase64String(new byte[]{ 130, 200, 234, 23 }); default: - throw new Exception(string.Format("Could not generate sample value for query parameter type {0} and format {1}", type, format)); + throw new Exception($"Could not generate sample value for query parameter type {type} and format {format}"); } } diff --git a/Swashbuckle.OData/Descriptions/SwaggerToApiExplorerMapper.cs b/Swashbuckle.OData/Descriptions/SwaggerToApiExplorerMapper.cs index 443e754..e0b6b4f 100644 --- a/Swashbuckle.OData/Descriptions/SwaggerToApiExplorerMapper.cs +++ b/Swashbuckle.OData/Descriptions/SwaggerToApiExplorerMapper.cs @@ -6,7 +6,7 @@ namespace Swashbuckle.OData { - public class SwaggerToApiExplorerMapper + internal class SwaggerToApiExplorerMapper { private readonly IEnumerable _parameterMappers; diff --git a/Swashbuckle.OData/HttpConfigurationExtensions.cs b/Swashbuckle.OData/HttpConfigurationExtensions.cs index 91fecbb..454ed6a 100644 --- a/Swashbuckle.OData/HttpConfigurationExtensions.cs +++ b/Swashbuckle.OData/HttpConfigurationExtensions.cs @@ -3,7 +3,7 @@ namespace Swashbuckle.OData { - public static class HttpConfigurationExtensions + internal static class HttpConfigurationExtensions { internal static JsonSerializerSettings SerializerSettingsOrDefault(this HttpConfiguration httpConfig) { diff --git a/Swashbuckle.OData/ODataSwaggerConverter.cs b/Swashbuckle.OData/ODataSwaggerConverter.cs index 93eae11..1b5a10a 100644 --- a/Swashbuckle.OData/ODataSwaggerConverter.cs +++ b/Swashbuckle.OData/ODataSwaggerConverter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using Microsoft.OData.Edm; @@ -14,7 +13,7 @@ namespace Swashbuckle.OData /// /// Represents an used to converter an Edm model to Swagger model. /// - public class ODataSwaggerConverter + internal class ODataSwaggerConverter { private const string DefaultHost = "default"; private const string DefaultbasePath = "/odata"; @@ -26,10 +25,7 @@ public class ODataSwaggerConverter /// The Edm model. public ODataSwaggerConverter(IEdmModel model) { - if (model == null) - { - throw new ArgumentNullException("model"); - } + Contract.Requires(model != null); EdmModel = model; MetadataUri = DefaultMetadataUri; @@ -57,47 +53,28 @@ public ODataSwaggerConverter(IEdmModel model) /// public IEdmModel EdmModel { get; } - /// - /// Gets the Swagger model. - /// - public virtual SwaggerDocument SwaggerModel - { - get - { - if (SwaggerDoc == null) - { - ConvertToSwaggerModel(); - } - - Contract.Assert(SwaggerDoc != null); - return SwaggerDoc; - } - } - /// /// Gets the document in the Swagger. /// - [SuppressMessage("Microsoft.Usage", "CA2227:EnableSetterForProperty", Justification = "Enable setter for virtual property")] - protected virtual SwaggerDocument SwaggerDoc { get; set; } + private SwaggerDocument SwaggerDoc { get; set; } /// /// Gets the paths in the Swagger. /// - [SuppressMessage("Microsoft.Usage", "CA2227:EnableSetterForProperty", Justification = "Enable setter for virtual property")] - protected virtual IDictionary SwaggerPaths { get; set; } + private IDictionary SwaggerPaths { get; set; } /// /// Gets the definitions in the Swagger. /// - [SuppressMessage("Microsoft.Usage", "CA2227:EnableSetterForProperty", Justification = "Enable setter for virtual property")] - protected virtual IDictionary SwaggerDefinitions { get; set; } + private IDictionary SwaggerDefinitions { get; set; } /// - /// Converts the Edm model to Swagger model. + /// Converts the Edm model to Swagger model. /// - /// The represents the Swagger model. - public virtual SwaggerDocument ConvertToSwaggerModel() + public SwaggerDocument ConvertToSwaggerModel() { + Contract.Ensures(Contract.Result() != null); + if (SwaggerDoc != null) { return SwaggerDoc; @@ -116,7 +93,7 @@ public virtual SwaggerDocument ConvertToSwaggerModel() /// /// Start to initialize the Swagger model. /// - protected virtual void InitializeStart() + private void InitializeStart() { SwaggerDoc = null; SwaggerPaths = null; @@ -126,7 +103,7 @@ protected virtual void InitializeStart() /// /// Initialize the document of Swagger model. /// - protected virtual void InitializeDocument() + private void InitializeDocument() { SwaggerDoc = new SwaggerDocument { @@ -156,10 +133,10 @@ protected virtual void InitializeDocument() /// /// Initialize the entity container to Swagger model. /// - protected virtual void InitializeContainer() + private void InitializeContainer() { - Contract.Assert(SwaggerDoc != null); - Contract.Assert(EdmModel != null); + Contract.Requires(SwaggerDoc != null); + Contract.Requires(EdmModel != null); SwaggerPaths = new Dictionary(); @@ -186,10 +163,10 @@ protected virtual void InitializeContainer() /// /// Initialize the type definitions to Swagger model. /// - protected virtual void InitializeTypeDefinitions() + private void InitializeTypeDefinitions() { - Contract.Assert(SwaggerDoc != null); - Contract.Assert(EdmModel != null); + Contract.Requires(SwaggerDoc != null); + Contract.Requires(EdmModel != null); SwaggerDefinitions = new Dictionary(); SwaggerDoc.definitions = SwaggerDefinitions; @@ -203,11 +180,11 @@ protected virtual void InitializeTypeDefinitions() /// /// Initialize the operations to Swagger model. /// - protected virtual void InitializeOperations() + private void InitializeOperations() { - Contract.Assert(SwaggerDoc != null); - Contract.Assert(EdmModel != null); - Contract.Assert(SwaggerPaths != null); + Contract.Requires(SwaggerDoc != null); + Contract.Requires(EdmModel != null); + Contract.Requires(SwaggerPaths != null); if (EdmModel.EntityContainer == null) { @@ -255,9 +232,9 @@ protected virtual void InitializeOperations() /// /// End to initialize the Swagger model. /// - protected virtual void InitializeEnd() + private void InitializeEnd() { - Contract.Assert(SwaggerDefinitions != null); + Contract.Requires(SwaggerDefinitions != null); SwaggerDefinitions.Add("_Error", new Schema { diff --git a/Swashbuckle.OData/ODataSwaggerProvider.cs b/Swashbuckle.OData/ODataSwaggerProvider.cs index a325100..d22257d 100644 --- a/Swashbuckle.OData/ODataSwaggerProvider.cs +++ b/Swashbuckle.OData/ODataSwaggerProvider.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Web.Http; using System.Web.Http.Description; -using System.Web.OData; using System.Web.OData.Routing; using Swashbuckle.Application; using Swashbuckle.Swagger; @@ -61,10 +60,12 @@ public ODataSwaggerProvider(ISwaggerProvider defaultProvider, SwaggerDocsConfig /// private static IDictionary GetApiVersions(ISwaggerProvider defaultProvider) { - var swaggerGenerator = defaultProvider as SwaggerGenerator; - Contract.Assert(swaggerGenerator != null, "The ODataSwaggerProvider currently requires a defaultProvider of type SwaggerGenerator"); + Contract.Requires(defaultProvider is SwaggerGenerator, "The ODataSwaggerProvider currently requires a defaultProvider of type SwaggerGenerator"); + + var swaggerGenerator = (SwaggerGenerator)defaultProvider; + var apiVersions = typeof(SwaggerGenerator).GetInstanceField(swaggerGenerator, "_apiVersions") as IDictionary; - Contract.Assert(apiVersions != null, "The ODataSwaggerProvider currently requires that the SwaggerGenerator has a non-null field '_apiVersions' of type SwaggerGeneratorOptions"); + Contract.Assume(apiVersions != null, "The ODataSwaggerProvider currently requires that the SwaggerGenerator has a non-null field '_apiVersions' of type SwaggerGeneratorOptions"); return apiVersions; } @@ -74,10 +75,12 @@ private static IDictionary GetApiVersions(ISwaggerProvider default /// The default provider. private static SwaggerGeneratorOptions GetSwaggerGeneratorOptions(ISwaggerProvider defaultProvider) { - var swaggerGenerator = defaultProvider as SwaggerGenerator; - Contract.Assert(swaggerGenerator != null, "The ODataSwaggerProvider currently requires a defaultProvider of type SwaggerGenerator"); + Contract.Requires(defaultProvider is SwaggerGenerator, "The ODataSwaggerProvider currently requires a defaultProvider of type SwaggerGenerator"); + + var swaggerGenerator = (SwaggerGenerator) defaultProvider; + var options = typeof (SwaggerGenerator).GetInstanceField(swaggerGenerator, "_options") as SwaggerGeneratorOptions; - Contract.Assert(options != null, "The ODataSwaggerProvider currently requires that the SwaggerGenerator has a non-null field '_options' of type SwaggerGeneratorOptions"); + Contract.Assume(options != null, "The ODataSwaggerProvider currently requires that the SwaggerGenerator has a non-null field '_options' of type SwaggerGeneratorOptions"); return options; } @@ -151,7 +154,7 @@ private PathItem CreatePathItem(IEnumerable apiDescriptions, Sch { var httpMethod = group.Key; - var apiDescription = (group.Count() == 1) + var apiDescription = group.Count() == 1 ? group.First() : _options.ConflictingActionsResolver(group); @@ -186,6 +189,9 @@ private PathItem CreatePathItem(IEnumerable apiDescriptions, Sch private Operation CreateOperation(ApiDescription apiDescription, SchemaRegistry schemaRegistry) { + Contract.Requires(apiDescription != null); + Contract.Requires(schemaRegistry != null); + var parameters = apiDescription.ParameterDescriptions .Select(paramDesc => { diff --git a/Swashbuckle.OData/ODataSwaggerUtilities.cs b/Swashbuckle.OData/ODataSwaggerUtilities.cs index 1f1bf93..9b87e78 100644 --- a/Swashbuckle.OData/ODataSwaggerUtilities.cs +++ b/Swashbuckle.OData/ODataSwaggerUtilities.cs @@ -17,7 +17,7 @@ namespace Swashbuckle.OData /// Utility methods used to convert the Swagger model. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "o", Justification = "Utils is spelled correctly.")] - public static class ODataSwaggerUtilities + internal static class ODataSwaggerUtilities { /// /// Create the Swagger path for the Edm entity set. @@ -434,8 +434,8 @@ public static Schema CreateSwaggerDefinitionForStructureType(IEdmStructuredType private static void SetSwaggerType(Parameter obj, IEdmType edmType) { - Contract.Assert(obj != null); - Contract.Assert(edmType != null); + Contract.Requires(obj != null); + Contract.Requires(edmType != null); switch (edmType.TypeKind) { @@ -475,8 +475,8 @@ private static void SetSwaggerType(Parameter obj, IEdmType edmType) private static void SetSwaggerType(Schema obj, IEdmType edmType) { - Contract.Assert(obj != null); - Contract.Assert(edmType != null); + Contract.Requires(obj != null); + Contract.Requires(edmType != null); switch (edmType.TypeKind) { @@ -516,12 +516,13 @@ private static void SetSwaggerType(Schema obj, IEdmType edmType) private static string GetPrimitiveTypeAndFormat(IEdmPrimitiveType primitiveType, out string format) { - Contract.Assert(primitiveType != null); + Contract.Requires(primitiveType != null); format = null; switch (primitiveType.PrimitiveKind) { case EdmPrimitiveTypeKind.String: + case EdmPrimitiveTypeKind.None: return "string"; case EdmPrimitiveTypeKind.Int16: case EdmPrimitiveTypeKind.Int32: @@ -547,6 +548,12 @@ private static string GetPrimitiveTypeAndFormat(IEdmPrimitiveType primitiveType, case EdmPrimitiveTypeKind.Single: format = "float"; return "number"; + case EdmPrimitiveTypeKind.Guid: + format = "guid"; + return "string"; + case EdmPrimitiveTypeKind.Binary: + format = "binary"; + return "string"; default: return "string"; } diff --git a/Swashbuckle.OData/ReflectionExtensions.cs b/Swashbuckle.OData/ReflectionExtensions.cs index d49f371..854980c 100644 --- a/Swashbuckle.OData/ReflectionExtensions.cs +++ b/Swashbuckle.OData/ReflectionExtensions.cs @@ -3,7 +3,7 @@ namespace Swashbuckle.OData { - public static class ReflectionExtensions + internal static class ReflectionExtensions { /// /// Uses reflection to get the field value from an object. diff --git a/Swashbuckle.OData/SchemaRegistryExtensions.cs b/Swashbuckle.OData/SchemaRegistryExtensions.cs index d912116..a3bc9ea 100644 --- a/Swashbuckle.OData/SchemaRegistryExtensions.cs +++ b/Swashbuckle.OData/SchemaRegistryExtensions.cs @@ -1,14 +1,17 @@ using System; +using System.Diagnostics.Contracts; using System.Web.Http; using System.Web.OData; using Swashbuckle.Swagger; namespace Swashbuckle.OData { - public static class SchemaRegistryExtensions + internal static class SchemaRegistryExtensions { public static Schema GetOrRegisterODataType(this SchemaRegistry registry, Type type) { + Contract.Requires(type != null); + var isAGenericODataType = IsAGenericODataType(type); return isAGenericODataType ? registry.GetOrRegister(type.GetGenericArguments()[0]) diff --git a/Swashbuckle.OData/SwaggerApiParameterDescription.cs b/Swashbuckle.OData/SwaggerApiParameterDescription.cs index ec27704..ad460e7 100644 --- a/Swashbuckle.OData/SwaggerApiParameterDescription.cs +++ b/Swashbuckle.OData/SwaggerApiParameterDescription.cs @@ -2,7 +2,7 @@ namespace Swashbuckle.OData { - public class SwaggerApiParameterDescription : ApiParameterDescription + internal class SwaggerApiParameterDescription : ApiParameterDescription { public SwaggerApiParameterSource SwaggerSource { get; set; } } diff --git a/Swashbuckle.OData/SwaggerApiParameterSource.cs b/Swashbuckle.OData/SwaggerApiParameterSource.cs index 7bd8075..c88f184 100644 --- a/Swashbuckle.OData/SwaggerApiParameterSource.cs +++ b/Swashbuckle.OData/SwaggerApiParameterSource.cs @@ -1,6 +1,6 @@ namespace Swashbuckle.OData { - public enum SwaggerApiParameterSource + internal enum SwaggerApiParameterSource { Query, Header, diff --git a/Swashbuckle.OData/Swashbuckle.OData.csproj b/Swashbuckle.OData/Swashbuckle.OData.csproj index 7a6bb69..dbe3f14 100644 --- a/Swashbuckle.OData/Swashbuckle.OData.csproj +++ b/Swashbuckle.OData/Swashbuckle.OData.csproj @@ -21,7 +21,7 @@ full false bin\Debug\ - TRACE;DEBUG;CONTRACTS_FULL;ASPNETWEBAPI + TRACE;DEBUG;CONTRACTS_FULL prompt 4 True @@ -29,7 +29,7 @@ True False False - False + True True True True @@ -52,7 +52,7 @@ True True False - False + True @@ -73,7 +73,7 @@ pdbonly true bin\Release\ - TRACE;CONTRACTS_FULL;ASPNETWEBAPI + TRACE;CONTRACTS_FULL prompt 4 True @@ -81,7 +81,7 @@ True False False - False + True True True True @@ -104,7 +104,7 @@ True True False - False + True @@ -122,10 +122,6 @@ false - - ..\packages\Flurl.1.0.10\lib\portable-net40+sl50+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Flurl.dll - True - ..\packages\Microsoft.OData.Core.6.13.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.OData.Core.dll True diff --git a/Swashbuckle.OData/packages.config b/Swashbuckle.OData/packages.config index d7a9d59..1ff8fd3 100644 --- a/Swashbuckle.OData/packages.config +++ b/Swashbuckle.OData/packages.config @@ -1,16 +1,14 @@  - - - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file