Skip to content

Commit

Permalink
Fixed the reporting of API versions when conventions are used and add…
Browse files Browse the repository at this point in the history
…ed additional test coverage. Fixes #47. (#50)
  • Loading branch information
commonsensesoftware committed Nov 7, 2016
1 parent 664d76a commit 44811d4
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ private void MergeAttributesWithConventions( HttpControllerDescriptor controller

var providers = controllerDescriptor.GetCustomAttributes<IApiVersionProvider>().ToArray();

if ( providers.Length == 0 )
{
return;
}

supportedVersions.UnionWith( from provider in providers
where !provider.AdvertiseOnly && !provider.Deprecated
from version in provider.Versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private ControllerVersionInfo ApplyControllerConventions( ControllerModel contro

controllerModel.SetProperty( model );

return new ControllerVersionInfo( supportedVersions, deprecatedAdvertisedVersions, advertisedVersions, deprecatedAdvertisedVersions );
return new ControllerVersionInfo( supportedVersions, deprecatedVersions, advertisedVersions, deprecatedAdvertisedVersions );
}

private void MergeAttributesWithConventions( ControllerModel controllerModel )
Expand All @@ -57,6 +57,11 @@ private void MergeAttributesWithConventions( ControllerModel controllerModel )

var providers = controllerModel.Attributes.OfType<IApiVersionProvider>().ToArray();

if ( providers.Length == 0 )
{
return;
}

supportedVersions.UnionWith( from provider in providers
where !provider.AdvertiseOnly && !provider.Deprecated
from version in provider.Versions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Microsoft.Web.Http.Conventions.Controllers
{
using System.Web.Http;

[RoutePrefix( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : ApiController
{
[Route]
public IHttpActionResult Get() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );

[Route( "{id:int}" )]
public IHttpActionResult Get( int id ) => Ok( new { controller = GetType().Name, id = id, version = Request.GetRequestedApiVersion().ToString() } );

[Route]
public IHttpActionResult GetV3() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );

[Route( "{id:int}" )]
public IHttpActionResult GetV3( int id ) => Ok( new { controller = GetType().Name, id = id, version = Request.GetRequestedApiVersion().ToString() } );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protected ConventionsAcceptanceTest()
FilteredControllerTypes.Add( typeof( ValuesController ) );
FilteredControllerTypes.Add( typeof( Values2Controller ) );
FilteredControllerTypes.Add( typeof( HelloWorldController ) );
FilteredControllerTypes.Add( typeof( HelloWorld2Controller ) );
Configuration.AddApiVersioning(
options =>
{
Expand All @@ -28,10 +29,13 @@ protected ConventionsAcceptanceTest()
.HasApiVersion( 3, 0 )
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
options.Conventions.Controller<HelloWorldController>()
.HasApiVersion( 1, 0 )
options.Conventions.Controller<HelloWorldController>().HasDeprecatedApiVersion( 1, 0 );
options.Conventions.Controller<HelloWorld2Controller>()
.HasApiVersion( 2, 0 )
.AdvertisesApiVersion( 3, 0 );
.HasApiVersion( 3, 0 )
.AdvertisesApiVersion( 4, 0 )
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
} );
Configuration.MapHttpAttributeRoutes( constraintResolver );
Configuration.EnsureInitialized();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,41 @@
public class _a_url_versioned_ApiController_using_conventions : ConventionsAcceptanceTest
{
[Theory]
[InlineData( "api/v1/helloworld", "1", null )]
[InlineData( "api/v2/helloworld", "2", null )]
[InlineData( "api/v1/helloworld/42", "1", "42" )]
[InlineData( "api/v2/helloworld/42", "2", "42" )]
public async Task _get_should_return_200( string requestUrl, string apiVersion, string id )
[InlineData( "api/v1/helloworld", nameof( HelloWorldController ), "1" )]
[InlineData( "api/v2/helloworld", nameof( HelloWorld2Controller ), "2" )]
[InlineData( "api/v3/helloworld", nameof( HelloWorld2Controller ), "3" )]
public async Task _get_should_return_200( string requestUrl, string controllerName, string apiVersion )
{
// arrange
var body = new Dictionary<string, string>()
{
["controller"] = nameof( HelloWorldController ),
["version"] = apiVersion
};
var example = new { controller = "", version = "" };

if ( !string.IsNullOrEmpty( id ) )
{
body["id"] = id;
}
// act
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsExampleAsync( example );

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
content.ShouldBeEquivalentTo( new { controller = controllerName, version = apiVersion } );
}

[Theory]
[InlineData( "api/v1/helloworld/42", nameof( HelloWorldController ), "1" )]
[InlineData( "api/v2/helloworld/42", nameof( HelloWorld2Controller ), "2" )]
[InlineData( "api/v3/helloworld/42", nameof( HelloWorld2Controller ), "3" )]
public async Task _get_with_id_should_return_200( string requestUrl, string controllerName, string apiVersion )
{
// arrange
var example = new { controller = "", version = "", id = "" };

// act
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();
var content = await response.Content.ReadAsExampleAsync( example );

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0" );
content.ShouldBeEquivalentTo( body );
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
content.ShouldBeEquivalentTo( new { controller = controllerName, version = apiVersion, id = "42" } );
}

[Fact]
Expand All @@ -48,7 +58,7 @@ public async Task _get_should_return_400_when_version_is_unsupported()


// act
var response = await GetAsync( "api/v3/helloworld" );
var response = await GetAsync( "api/v4/helloworld" );
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();

// assert
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Microsoft.AspNetCore.Mvc.Conventions.Controllers
{
using AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
using System;

[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : Controller
{
[HttpGet]
public IActionResult Get() => Ok( new { Controller = nameof( HelloWorld2Controller ), Version = HttpContext.GetRequestedApiVersion().ToString() } );

[HttpGet( "{id:int}" )]
public IActionResult Get( int id ) => Ok( new { Controller = nameof( HelloWorld2Controller ), Id = id, Version = HttpContext.GetRequestedApiVersion().ToString() } );

[HttpGet]
public IActionResult GetV3() => Ok( new { Controller = nameof( HelloWorld2Controller ), Version = HttpContext.GetRequestedApiVersion().ToString() } );

[HttpGet( "{id:int}" )]
public IActionResult GetV3( int id ) => Ok( new { Controller = nameof( HelloWorld2Controller ), Id = id, Version = HttpContext.GetRequestedApiVersion().ToString() } );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ protected ConventionsAcceptanceTest()
FilteredControllerTypes.Add( typeof( ValuesController ).GetTypeInfo() );
FilteredControllerTypes.Add( typeof( Values2Controller ).GetTypeInfo() );
FilteredControllerTypes.Add( typeof( HelloWorldController ).GetTypeInfo() );
FilteredControllerTypes.Add( typeof( HelloWorld2Controller ).GetTypeInfo() );
}

protected override void OnAddApiVersioning( ApiVersioningOptions options )
Expand All @@ -23,10 +24,13 @@ protected override void OnAddApiVersioning( ApiVersioningOptions options )
.HasApiVersion( 3, 0 )
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
options.Conventions.Controller<HelloWorldController>()
.HasApiVersion( 1, 0 )
options.Conventions.Controller<HelloWorldController>().HasDeprecatedApiVersion( 1, 0 );
options.Conventions.Controller<HelloWorld2Controller>()
.HasApiVersion( 2, 0 )
.AdvertisesApiVersion( 3, 0 );
.HasApiVersion( 3, 0 )
.AdvertisesApiVersion( 4, 0 )
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,63 @@

public class _a_url_versioned_Controller_using_conventions : ConventionsAcceptanceTest
{
[Theory]
[InlineData( "api/v1/helloworld", "1", null )]
[InlineData( "api/v2/helloworld", "2", null )]
[InlineData( "api/v1/helloworld/42", "1", "42" )]
[InlineData( "api/v2/helloworld/42", "2", "42" )]
public async Task _get_should_return_200( string requestUrl, string apiVersion, string id )
[Fact]
public async Task _get_should_return_200()
{
// REMARKS: this test should be a theory, but when it is, it becomes flaky. any failure succeeds when run again.
// the exact cause is unknown, but seems to be related to some form of caching. running a loop in a single test
// case seems to resolve the problem.

// arrange
var body = new Dictionary<string, string>()
var iterations = new[]
{
["controller"] = nameof( HelloWorldController ),
["version"] = apiVersion
new { RequestUrl = "api/v1/helloworld", ControllerName = nameof( HelloWorldController ), ApiVersion = "1" },
new { RequestUrl = "api/v2/helloworld", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "2" },
new { RequestUrl = "api/v3/helloworld", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "3" },
};
var example = new { controller = "", version = "" };

if ( !string.IsNullOrEmpty( id ) )
foreach ( var iteration in iterations )
{
body["id"] = id;
// act
var response = await GetAsync( iteration.RequestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsExampleAsync( example );

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
content.ShouldBeEquivalentTo( new { controller = iteration.ControllerName, version = iteration.ApiVersion } );
}
}

// act
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();
[Fact]
public async Task _get_with_id_should_return_200()
{
// REMARKS: this test should be a theory, but when it is, it becomes flaky. any failure succeeds when run again.
// the exact cause is unknown, but seems to be related to some form of caching. running a loop in a single test
// case seems to resolve the problem.

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0" );
content.ShouldBeEquivalentTo( body );
// arrange
var iterations = new[]
{
new { RequestUrl = "api/v1/helloworld/42", ControllerName = nameof( HelloWorldController ), ApiVersion = "1" },
new { RequestUrl = "api/v2/helloworld/42", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "2" },
new { RequestUrl = "api/v3/helloworld/42", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "3" },
};

var example = new { controller = "", version = "", id = "" };

foreach ( var iteration in iterations )
{
// act
var response = await GetAsync( iteration.RequestUrl ).EnsureSuccessStatusCode();
var content = await response.Content.ReadAsExampleAsync( example );

// assert
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
content.ShouldBeEquivalentTo( new { controller = iteration.ControllerName, version = iteration.ApiVersion, id = "42" } );
}
}

[Fact]
Expand All @@ -48,7 +79,7 @@ public async Task _get_should_return_400_when_version_is_unsupported()


// act
var response = await GetAsync( "api/v3/helloworld" );
var response = await GetAsync( "api/v4/helloworld" );
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();

// assert
Expand Down

0 comments on commit 44811d4

Please sign in to comment.