Skip to content

Commit

Permalink
#10: Support entity types with a guid primary key
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard Beauchamp committed Dec 8, 2015
1 parent be6c536 commit 5ba0642
Show file tree
Hide file tree
Showing 29 changed files with 218 additions and 140 deletions.
2 changes: 1 addition & 1 deletion Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<LicenseUrl>https://github.com/rbeauchamp/Swashbuckle.OData/blob/master/License.txt</LicenseUrl>
<Copyright>Copyright 2015</Copyright>
<Tags>Swashbuckle Swagger SwaggerUi OData Documentation Discovery Help WebApi AspNet AspNetWebApi Docs WebHost IIS</Tags>
<Version>2.1.0</Version>
<Version>2.1.1</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Swashbuckle.OData\Swashbuckle.OData.csproj" />
Expand Down
1 change: 0 additions & 1 deletion Swashbuckle.OData.Sample/App_Start/FormatterConfig.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Web.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;

namespace SwashbuckleODataSample
{
Expand Down
1 change: 0 additions & 1 deletion Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Web.Http;
using System.Web.Http.Description;
using Swashbuckle.Application;
using Swashbuckle.OData;
using SwashbuckleODataSample;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -14,21 +13,18 @@ public class CustomersController : ODataController
{
private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext();

// GET: odata/Customers
[EnableQuery]
public IQueryable<Customer> GetCustomers()
{
return _db.Customers;
}

// GET: odata/Customers(5)
[EnableQuery]
public SingleResult<Customer> GetCustomer([FromODataUri] int key)
{
return SingleResult.Create(_db.Customers.Where(customer => customer.Id == key));
}

// PUT: odata/Customers(5)
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<Customer> patch)
{
Expand Down Expand Up @@ -63,7 +59,6 @@ public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<Customer>
return Updated(customer);
}

// POST: odata/Customers
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> Post(Customer customer)
{
Expand All @@ -78,7 +73,6 @@ public async Task<IHttpActionResult> Post(Customer customer)
return Created(customer);
}

// PATCH: odata/Customers(5)
[ResponseType(typeof(void))]
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Customer> patch)
Expand Down Expand Up @@ -114,7 +108,6 @@ public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Custome
return Updated(customer);
}

// DELETE: odata/Customers(5)
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
Expand All @@ -130,7 +123,6 @@ public async Task<IHttpActionResult> Delete([FromODataUri] int key)
return StatusCode(HttpStatusCode.NoContent);
}

// GET: odata/Customers(5)/Orders
[EnableQuery]
public IQueryable<Order> GetOrders([FromODataUri] int key)
{
Expand Down
5 changes: 3 additions & 2 deletions Swashbuckle.OData.Sample/ODataControllers/Order.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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; }

Expand Down
21 changes: 9 additions & 12 deletions Swashbuckle.OData.Sample/ODataControllers/OrdersController.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,24 +14,23 @@ public class OrdersController : ODataController
{
private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext();

// GET: odata/Orders
[EnableQuery]
public IQueryable<Order> GetOrders()
{
return _db.Orders;
}

// GET: odata/Orders(5)
[EnableQuery]
public SingleResult<Order> GetOrder([FromODataUri] int key)
public SingleResult<Order> GetOrder([FromODataUri] Guid key)
{
return SingleResult.Create(_db.Orders.Where(order => order.OrderId == key));
}

// POST: odata/Orders
[ResponseType(typeof(Order))]
public async Task<IHttpActionResult> Post(Order order)
{
order.OrderId = SequentialGuidGenerator.Generate(SequentialGuidType.SequentialAtEnd);

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
Expand All @@ -42,10 +42,9 @@ public async Task<IHttpActionResult> Post(Order order)
return Created(order);
}

// PATCH: odata/Orders(5)
[ResponseType(typeof(void))]
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Order> patch)
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<Order> patch)
{
Validate(patch.GetEntity());

Expand Down Expand Up @@ -78,9 +77,8 @@ public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Order>
return Updated(order);
}

// DELETE: odata/Orders(5)
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
var order = await _db.Orders.FindAsync(key);
if (order == null)
Expand All @@ -94,9 +92,8 @@ public async Task<IHttpActionResult> Delete([FromODataUri] int key)
return StatusCode(HttpStatusCode.NoContent);
}

// GET: odata/Orders(5)/Customer
[EnableQuery]
public SingleResult<Customer> GetCustomer([FromODataUri] int key)
public SingleResult<Customer> GetCustomer([FromODataUri] Guid key)
{
return SingleResult.Create(_db.Orders.Where(m => m.OrderId == key)
.Select(m => m.Customer));
Expand All @@ -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;
}
Expand Down
83 changes: 83 additions & 0 deletions Swashbuckle.OData.Sample/SequentialGuidGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Security.Cryptography;

namespace SwashbuckleODataSample
{
/// <summary>
/// Generates <see cref="System.Guid" /> values using strategy from Jeremy Todd.
/// <see cref="http://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-under-multiple-database" />
/// </summary>
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);
}
}

/// <remarks>
/// Database GUID Column SequentialGuidType
/// Microsoft SQL Server uniqueidentifier SequentialAtEnd
/// MySQL char(36) SequentialAsString
/// Oracle raw(16) SequentialAsBinary
/// PostgreSQL uuid SequentialAsString
/// SQLite varies varies
/// </remarks>
public enum SequentialGuidType
{
/// <summary>
/// Use for MySQL char(36)
/// Use for PostgreSQL uuid
/// </summary>
SequentialAsString,

/// <summary>
/// Use for Oracle raw(16)
/// </summary>
SequentialAsBinary,

/// <summary>
/// Use for Microsoft SQL Server uniqueidentifier
/// </summary>
SequentialAtEnd
}
}
1 change: 1 addition & 0 deletions Swashbuckle.OData.Sample/Swashbuckle.OData.Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
</Compile>
<Compile Include="ODataControllers\SwashbuckleODataContext.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SequentialGuidGenerator.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />
Expand Down
21 changes: 19 additions & 2 deletions Swashbuckle.OData.Tests/Fixtures/GetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SwaggerDocument>("swagger/docs/v1");

// Assert
PathItem pathItem;
swaggerDocument.paths.TryGetValue("/Orders({OrderId})", out pathItem);
pathItem.Should().NotBeNull();
pathItem.get.Should().NotBeNull();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions Swashbuckle.OData.Tests/Fixtures/PutTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Owin.Hosting;
using NUnit.Framework;
Expand Down
4 changes: 2 additions & 2 deletions Swashbuckle.OData.Tests/HttpExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.Contracts;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -249,8 +250,7 @@ public static Task<HttpResponseMessage> PatchAsync<T>(this HttpClient client, st
/// <typeparam name="T">The type of object to serialize.</typeparam>
public static Task<HttpResponseMessage> PatchAsync<T>(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<T>(value, formatter, mediaType);
Expand Down
1 change: 0 additions & 1 deletion Swashbuckle.OData.Tests/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions Swashbuckle.OData.Tests/Swashbuckle.OData.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;CONTRACTS_FULL</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
Expand All @@ -29,7 +29,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;CONTRACTS_FULL</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
Expand Down
2 changes: 1 addition & 1 deletion Swashbuckle.OData/CollectionExtentions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Swashbuckle.OData
{
public static class CollectionExtentions
internal static class CollectionExtentions
{
/// <summary>
/// Adds the elements of the specified collection to the end of the <see cref="Collection{T}"/>.
Expand Down
Loading

0 comments on commit 5ba0642

Please sign in to comment.