Skip to content

Commit

Permalink
Merge pull request #17 from rbeauchamp/feature/15
Browse files Browse the repository at this point in the history
Support both WebApi and OData endpoints
  • Loading branch information
Richard Beauchamp committed Dec 11, 2015
2 parents 712bc4e + f4adc04 commit b75a94d
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 38 deletions.
8 changes: 4 additions & 4 deletions Swashbuckle.OData.Nuget/Swashbuckle.OData.NuGet.nuproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
<Title>Swashbuckle.OData</Title>
<Authors>Richard Beauchamp</Authors>
<Owners>Richard Beauchamp</Owners>
<Summary>Extends Swashbuckle with WebApi OData v4 support!</Summary>
<Description>Extends Swashbuckle with WebApi OData v4 support!</Description>
<ReleaseNotes>Supports RESTier</ReleaseNotes>
<Summary>Extends Swashbuckle with OData v4 support!</Summary>
<Description>Extends Swashbuckle with OData v4 support!</Description>
<ReleaseNotes>Supports both WebApi and OData endpoints</ReleaseNotes>
<ProjectUrl>https://github.com/rbeauchamp/Swashbuckle.OData</ProjectUrl>
<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.3.0</Version>
<Version>2.4.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Swashbuckle.OData\Swashbuckle.OData.csproj" />
Expand Down
24 changes: 12 additions & 12 deletions Swashbuckle.OData.Sample/ApiControllers/ClientsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ namespace SwashbuckleODataSample.ApiControllers
{
public class ClientsController : ApiController
{
private readonly SwashbuckleODataContext db = new SwashbuckleODataContext();
private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext();

// GET: api/Clients
public IQueryable<Client> GetClients()
{
return db.Clients;
return _db.Clients;
}

// GET: api/Clients/5
[ResponseType(typeof (Client))]
public async Task<IHttpActionResult> GetClient(int id)
{
var client = await db.Clients.FindAsync(id);
var client = await _db.Clients.FindAsync(id);
if (client == null)
{
return NotFound();
Expand All @@ -47,11 +47,11 @@ public async Task<IHttpActionResult> PutClient(int id, Client client)
return BadRequest();
}

db.Entry(client).State = EntityState.Modified;
_db.Entry(client).State = EntityState.Modified;

try
{
await db.SaveChangesAsync();
await _db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
Expand All @@ -74,8 +74,8 @@ public async Task<IHttpActionResult> PostClient(Client client)
return BadRequest(ModelState);
}

db.Clients.Add(client);
await db.SaveChangesAsync();
_db.Clients.Add(client);
await _db.SaveChangesAsync();

return CreatedAtRoute("DefaultApi", new
{
Expand All @@ -87,14 +87,14 @@ public async Task<IHttpActionResult> PostClient(Client client)
[ResponseType(typeof (Client))]
public async Task<IHttpActionResult> DeleteClient(int id)
{
var client = await db.Clients.FindAsync(id);
var client = await _db.Clients.FindAsync(id);
if (client == null)
{
return NotFound();
}

db.Clients.Remove(client);
await db.SaveChangesAsync();
_db.Clients.Remove(client);
await _db.SaveChangesAsync();

return Ok(client);
}
Expand All @@ -103,14 +103,14 @@ protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
_db.Dispose();
}
base.Dispose(disposing);
}

private bool ClientExists(int id)
{
return db.Clients.Count(e => e.Id == id) > 0;
return _db.Clients.Count(e => e.Id == id) > 0;
}
}
}
24 changes: 12 additions & 12 deletions Swashbuckle.OData.Sample/ApiControllers/ProjectsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ namespace SwashbuckleODataSample.ApiControllers
{
public class ProjectsController : ApiController
{
private readonly SwashbuckleODataContext db = new SwashbuckleODataContext();
private readonly SwashbuckleODataContext _db = new SwashbuckleODataContext();

[Route("Projects/v1")]
public IQueryable<Project> GetProjects()
{
return db.Projects;
return _db.Projects;
}

[Route("Projects/v1/{id}")]
[ResponseType(typeof (Project))]
public async Task<IHttpActionResult> GetProject(int id)
{
var project = await db.Projects.FindAsync(id);
var project = await _db.Projects.FindAsync(id);
if (project == null)
{
return NotFound();
Expand All @@ -47,11 +47,11 @@ public async Task<IHttpActionResult> PutProject(int id, Project project)
return BadRequest();
}

db.Entry(project).State = EntityState.Modified;
_db.Entry(project).State = EntityState.Modified;

try
{
await db.SaveChangesAsync();
await _db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
Expand All @@ -74,8 +74,8 @@ public async Task<IHttpActionResult> PostProject(Project project)
return BadRequest(ModelState);
}

db.Projects.Add(project);
await db.SaveChangesAsync();
_db.Projects.Add(project);
await _db.SaveChangesAsync();

return CreatedAtRoute("DefaultApi", new
{
Expand All @@ -87,14 +87,14 @@ public async Task<IHttpActionResult> PostProject(Project project)
[ResponseType(typeof (Project))]
public async Task<IHttpActionResult> DeleteProject(int id)
{
var project = await db.Projects.FindAsync(id);
var project = await _db.Projects.FindAsync(id);
if (project == null)
{
return NotFound();
}

db.Projects.Remove(project);
await db.SaveChangesAsync();
_db.Projects.Remove(project);
await _db.SaveChangesAsync();

return Ok(project);
}
Expand All @@ -103,14 +103,14 @@ protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
_db.Dispose();
}
base.Dispose(disposing);
}

private bool ProjectExists(int id)
{
return db.Projects.Count(e => e.ProjectId == id) > 0;
return _db.Projects.Count(e => e.ProjectId == id) > 0;
}
}
}
26 changes: 26 additions & 0 deletions Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ public async Task It_explores_the_correct_controller()
}
}

[Test]
public async Task It_supports_both_webapi_and_odata_controllers()
{
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 clientsWebApi;
swaggerDocument.paths.TryGetValue("/api/Clients", out clientsWebApi);
clientsWebApi.Should().NotBeNull();
clientsWebApi.get.Should().NotBeNull();
clientsWebApi.patch.Should().BeNull();

PathItem clientWebApi;
swaggerDocument.paths.TryGetValue("/api/Clients/{id}", out clientWebApi);
clientWebApi.Should().NotBeNull();
clientWebApi.put.Should().NotBeNull();
clientWebApi.patch.Should().BeNull();
}
}

[Test]
public async Task It_generates_valid_swagger_2_0_json()
{
Expand Down
17 changes: 13 additions & 4 deletions Swashbuckle.OData/CollectionExtentions.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;

namespace Swashbuckle.OData
{
internal static class CollectionExtentions
{
/// <summary>
/// Adds the elements of the specified collection to the end of the <see cref="Collection{T}"/>.
/// Adds the elements of the specified collection to the end of the <see cref="Collection{T}" />.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source collection.</param>
/// <param name="collection">
/// The collection whose elements should be added to the end of the <see cref="Collection{T}"/>.
/// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
/// The collection whose elements should be added to the end of the <see cref="Collection{T}" />.
/// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
/// </param>
public static void AddRange<T>(this Collection<T> source, IEnumerable<T> collection)
{
Expand All @@ -27,7 +28,7 @@ public static void AddRange<T>(this Collection<T> source, IEnumerable<T> collect
}

/// <summary>
/// Adds the given item to the collection if the item is not null.
/// Adds the given item to the collection if the item is not null.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
Expand All @@ -41,5 +42,13 @@ public static void AddIfNotNull<T>(this Collection<T> source, T item)
source.Add(item);
}
}

public static IEnumerable<T> UnionEvenIfNull<T>(this IEnumerable<T> source, IEnumerable<T> other)
{
var nonNullSource = source ?? new List<T>();
var nonNullOther = other ?? new List<T>();

return nonNullSource.Union(nonNullOther);
}
}
}
33 changes: 28 additions & 5 deletions Swashbuckle.OData/ODataSwaggerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
var rootUri = new Uri(rootUrl);
var port = !rootUri.IsDefaultPort ? ":" + rootUri.Port : string.Empty;

var swaggerDoc = new SwaggerDocument
var odataSwaggerDoc = new SwaggerDocument
{
info = info,
host = rootUri.Host + port,
Expand All @@ -114,12 +114,35 @@ public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)

foreach (var filter in _options.DocumentFilters)
{
filter.Apply(swaggerDoc, schemaRegistry, _apiExplorer);
filter.Apply(odataSwaggerDoc, schemaRegistry, _apiExplorer);
}

return swaggerDoc.paths.Any()
? swaggerDoc
: _defaultProvider.GetSwagger(rootUrl, apiVersion);
return MergeODataAndWebApiSwaggerDocs(rootUrl, apiVersion, odataSwaggerDoc);
}

private SwaggerDocument MergeODataAndWebApiSwaggerDocs(string rootUrl, string apiVersion, SwaggerDocument odataSwaggerDoc)
{
var webApiSwaggerDoc = _defaultProvider.GetSwagger(rootUrl, apiVersion);

webApiSwaggerDoc.paths = webApiSwaggerDoc.paths.UnionEvenIfNull(odataSwaggerDoc.paths).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.definitions = webApiSwaggerDoc.definitions.UnionEvenIfNull(odataSwaggerDoc.definitions).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.parameters = webApiSwaggerDoc.parameters.UnionEvenIfNull(odataSwaggerDoc.parameters).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.responses = webApiSwaggerDoc.responses.UnionEvenIfNull(odataSwaggerDoc.responses).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.securityDefinitions = webApiSwaggerDoc.securityDefinitions.UnionEvenIfNull(odataSwaggerDoc.securityDefinitions).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.vendorExtensions = webApiSwaggerDoc.vendorExtensions.UnionEvenIfNull(odataSwaggerDoc.vendorExtensions).ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
webApiSwaggerDoc.tags = webApiSwaggerDoc.tags.UnionEvenIfNull(odataSwaggerDoc.tags).ToList();
webApiSwaggerDoc.consumes = webApiSwaggerDoc.consumes.UnionEvenIfNull(odataSwaggerDoc.consumes).ToList();
webApiSwaggerDoc.security = webApiSwaggerDoc.security.UnionEvenIfNull(odataSwaggerDoc.security).ToList();
webApiSwaggerDoc.produces = webApiSwaggerDoc.produces.UnionEvenIfNull(odataSwaggerDoc.produces).ToList();
webApiSwaggerDoc.schemes = webApiSwaggerDoc.schemes.UnionEvenIfNull(odataSwaggerDoc.schemes).ToList();

return webApiSwaggerDoc;
}

private SchemaRegistry GetSchemaRegistry()
Expand Down
2 changes: 1 addition & 1 deletion Swashbuckle.OData/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("2.3.0")]
[assembly: AssemblyInformationalVersion("2.4.0")]
51 changes: 51 additions & 0 deletions Swashbuckle.OData/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Swashbuckle.OData
Expand All @@ -18,5 +21,53 @@ internal static object GetInstanceField(this Type type, object instance, string
var field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}

//public static T MergeFields<T>(this T mergeTo, T mergeFrom)
//{
// var fields = mergeTo.GetType().GetFields();

// foreach (var fieldInfo in fields)
// {
// var fromValue = fieldInfo.GetValue(mergeFrom);

// if (fromValue != null)
// {
// Merge(mergeTo, fieldInfo, fromValue);
// }
// }

// return mergeTo;
//}

//private static void Merge<T>(T mergeTo, FieldInfo fieldInfo, object fromValue)
//{
// var fromListValue = fromValue as IList;
// if (fromListValue != null)
// {
// var toListValue = fieldInfo.GetValue(mergeTo) as IList;

// foreach (var fromItem in fromListValue)
// {
// toListValue.Add(fromItem);
// }
// }
// else
// {
// var fromDictionaryValue = fromValue as IDictionary;
// if (fromDictionaryValue != null)
// {
// var toDictionaryValue = fieldInfo.GetValue(mergeTo) as IDictionary;

// if (toDictionaryValue != null)
// {
// var mergedDictionary = new Dictionary<object, object>();
// foreach (DictionaryEntry keyValuePair in toDictionaryValue)
// {

// }
// }
// }
// }
//}
}
}

0 comments on commit b75a94d

Please sign in to comment.