Skip to content

Commit 665917d

Browse files
Chris Martinezcommonsensesoftware
authored andcommitted
Corrected version-neutral handling. Fixes #118.
1 parent 0e41ace commit 665917d

File tree

5 files changed

+74
-14
lines changed

5 files changed

+74
-14
lines changed

src/Microsoft.AspNet.WebApi.Versioning.ApiExplorer/Description/VersionedApiExplorer.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,18 @@ protected virtual bool ShouldExploreAction( string actionRouteParameterValue, Ht
133133

134134
if ( ( setting == null || !setting.IgnoreApi ) && MatchRegexConstraint( route, RouteValueKeys.Action, actionRouteParameterValue ) )
135135
{
136-
var versions = actionDescriptor.GetApiVersions();
136+
var model = actionDescriptor.GetApiVersionModel();
137137

138-
if ( versions.Contains( apiVersion ) )
138+
if ( model.IsApiVersionNeutral || model.DeclaredApiVersions.Contains( apiVersion ) )
139139
{
140140
return true;
141141
}
142142

143-
return versions.Count == 0 && actionDescriptor.ControllerDescriptor.GetDeclaredApiVersions().Contains( apiVersion );
143+
if ( model.DeclaredApiVersions.Count == 0 )
144+
{
145+
model = actionDescriptor.ControllerDescriptor.GetApiVersionModel();
146+
return model.IsApiVersionNeutral || model.DeclaredApiVersions.Contains( apiVersion );
147+
}
144148
}
145149

146150
return false;
@@ -164,7 +168,8 @@ protected virtual bool ShouldExploreController( string controllerRouteParameterV
164168

165169
if ( ( setting == null || !setting.IgnoreApi ) && MatchRegexConstraint( route, RouteValueKeys.Controller, controllerRouteParameterValue ) )
166170
{
167-
return controllerDescriptor.GetDeclaredApiVersions().Contains( apiVersion );
171+
var model = controllerDescriptor.GetApiVersionModel();
172+
return model.IsApiVersionNeutral || model.DeclaredApiVersions.Contains( apiVersion );
168173
}
169174

170175
return false;
@@ -478,8 +483,15 @@ IEnumerable<ApiVersion> FlattenApiVersions()
478483
advertisedDeprecated.ExceptWith( declared );
479484
supported.ExceptWith( advertisedSupported );
480485
deprecated.ExceptWith( supported.Concat( advertisedDeprecated ) );
486+
supported.UnionWith( deprecated );
487+
488+
if ( supported.Count == 0 )
489+
{
490+
supported.Add( options.DefaultApiVersion );
491+
return supported;
492+
}
481493

482-
return supported.Union( deprecated ).OrderBy( v => v );
494+
return supported.OrderBy( v => v );
483495
}
484496

485497
static HttpControllerDescriptor GetDirectRouteController( CandidateAction[] directRouteCandidates, ApiVersion apiVersion )

src/Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer/DefaultApiVersionDescriptionProvider.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.AspNetCore.Mvc.ApplicationModels;
55
using Microsoft.AspNetCore.Mvc.Infrastructure;
66
using Microsoft.AspNetCore.Mvc.Versioning;
7+
using Microsoft.Extensions.Options;
78
using System;
89
using System.Collections.Generic;
910
using System.Diagnostics.Contracts;
@@ -16,19 +17,26 @@
1617
public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvider
1718
{
1819
readonly Lazy<IReadOnlyList<ApiVersionDescription>> apiVersionDescriptions;
20+
readonly IOptions<ApiVersioningOptions> options;
1921

2022
/// <summary>
2123
/// Initializes a new instance of the <see cref="DefaultApiVersionDescriptionProvider"/> class.
2224
/// </summary>
2325
/// <param name="actionDescriptorCollectionProvider">The <see cref="IActionDescriptorCollectionProvider">provider</see> used to enumerate the actions within an application.</param>
2426
/// <param name="groupNameFormatter">The <see cref="IApiVersionGroupNameFormatter">formatter</see> used to get group names for API versions.</param>
25-
public DefaultApiVersionDescriptionProvider( IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, IApiVersionGroupNameFormatter groupNameFormatter )
27+
/// <param name="apiVersioningOptions">The <see cref="IOptions{TOptions}">container</see> of configured <see cref="ApiVersioningOptions">API versioning options</see>.</param>
28+
public DefaultApiVersionDescriptionProvider(
29+
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
30+
IApiVersionGroupNameFormatter groupNameFormatter,
31+
IOptions<ApiVersioningOptions> apiVersioningOptions )
2632
{
2733
Arg.NotNull( actionDescriptorCollectionProvider, nameof( actionDescriptorCollectionProvider ) );
2834
Arg.NotNull( groupNameFormatter, nameof( groupNameFormatter ) );
35+
Arg.NotNull( apiVersioningOptions, nameof( apiVersioningOptions ) );
2936

3037
apiVersionDescriptions = new Lazy<IReadOnlyList<ApiVersionDescription>>( () => EnumerateApiVersions( actionDescriptorCollectionProvider ) );
3138
GroupNameFormatter = groupNameFormatter;
39+
options = apiVersioningOptions;
3240
}
3341

3442
/// <summary>
@@ -37,6 +45,12 @@ public DefaultApiVersionDescriptionProvider( IActionDescriptorCollectionProvider
3745
/// <value>The <see cref="IApiVersionGroupNameFormatter">group name formatter</see> used to format group names.</value>
3846
protected IApiVersionGroupNameFormatter GroupNameFormatter { get; }
3947

48+
/// <summary>
49+
/// Gets the current API versioning options associated with the API explorer.
50+
/// </summary>
51+
/// <value>The current <see cref="ApiVersioningOptions">API versioning options</see>.</value>
52+
protected ApiVersioningOptions Options => options.Value;
53+
4054
/// <summary>
4155
/// Gets a read-only list of discovered API version descriptions.
4256
/// </summary>
@@ -88,7 +102,7 @@ protected virtual IReadOnlyList<ApiVersionDescription> EnumerateApiVersions( IAc
88102
return descriptions.OrderBy( d => d.ApiVersion ).ToArray();
89103
}
90104

91-
static void BucketizeApiVersions( IReadOnlyList<ActionDescriptor> actions, ISet<ApiVersion> supported, ISet<ApiVersion> deprecated )
105+
void BucketizeApiVersions( IReadOnlyList<ActionDescriptor> actions, ISet<ApiVersion> supported, ISet<ApiVersion> deprecated )
92106
{
93107
Contract.Requires( actions != null );
94108
Contract.Requires( supported != null );
@@ -125,6 +139,11 @@ static void BucketizeApiVersions( IReadOnlyList<ActionDescriptor> actions, ISet<
125139
advertisedDeprecated.ExceptWith( declared );
126140
supported.ExceptWith( advertisedSupported );
127141
deprecated.ExceptWith( supported.Concat( advertisedDeprecated ) );
142+
143+
if ( supported.Count == 0 && deprecated.Count == 0 )
144+
{
145+
supported.Add( Options.DefaultApiVersion );
146+
}
128147
}
129148

130149
void AppendDescriptions( ICollection<ApiVersionDescription> descriptions, IEnumerable<ApiVersion> versions, bool deprecated )

src/Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer/VersionedApiDescriptionProvider.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.AspNetCore.Mvc.Routing;
88
using Microsoft.AspNetCore.Mvc.Versioning;
99
using Microsoft.AspNetCore.Routing;
10+
using Microsoft.Extensions.Options;
1011
using System;
1112
using System.Collections.Generic;
1213
using System.Diagnostics.Contracts;
@@ -20,18 +21,26 @@
2021
[CLSCompliant( false )]
2122
public class VersionedApiDescriptionProvider : IApiDescriptionProvider
2223
{
24+
readonly IOptions<ApiVersioningOptions> options;
25+
2326
/// <summary>
2427
/// Initializes a new instance of <see cref="VersionedApiDescriptionProvider"/> class.
2528
/// </summary>
2629
/// <param name="groupNameFormatter">The <see cref="IApiVersionGroupNameFormatter">formatter</see> used to get group names for API versions.</param>
2730
/// <param name="metadadataProvider">The <see cref="IModelMetadataProvider">provider</see> used to retrieve model metadata.</param>
28-
public VersionedApiDescriptionProvider( IApiVersionGroupNameFormatter groupNameFormatter, IModelMetadataProvider metadadataProvider )
31+
/// <param name="apiVersioningOptions">The <see cref="IOptions{TOptions}">container</see> of configured <see cref="ApiVersioningOptions">API versioning options</see>.</param>
32+
public VersionedApiDescriptionProvider(
33+
IApiVersionGroupNameFormatter groupNameFormatter,
34+
IModelMetadataProvider metadadataProvider,
35+
IOptions<ApiVersioningOptions> apiVersioningOptions )
2936
{
3037
Arg.NotNull( groupNameFormatter, nameof( groupNameFormatter ) );
3138
Arg.NotNull( metadadataProvider, nameof( metadadataProvider ) );
39+
Arg.NotNull( apiVersioningOptions, nameof( apiVersioningOptions ) );
3240

3341
GroupNameFormatter = groupNameFormatter;
3442
MetadadataProvider = metadadataProvider;
43+
options = apiVersioningOptions;
3544
}
3645

3746
/// <summary>
@@ -46,6 +55,12 @@ public VersionedApiDescriptionProvider( IApiVersionGroupNameFormatter groupNameF
4655
/// <value>The <see cref="IModelMetadataProvider">provider</see> used to retrieve model metadata.</value>
4756
protected IModelMetadataProvider MetadadataProvider { get; }
4857

58+
/// <summary>
59+
/// Gets the current API versioning options associated with the API explorer.
60+
/// </summary>
61+
/// <value>The current <see cref="ApiVersioningOptions">API versioning options</see>.</value>
62+
protected ApiVersioningOptions Options => options.Value;
63+
4964
/// <summary>
5065
/// Gets the order prescendence of the current API description provider.
5166
/// </summary>
@@ -139,7 +154,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
139154
/// <remarks>The default implementation performs no operation.</remarks>
140155
public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context ) { }
141156

142-
static IEnumerable<ApiVersion> FlattenApiVersions( IEnumerable<ApiDescription> descriptions )
157+
IEnumerable<ApiVersion> FlattenApiVersions( IEnumerable<ApiDescription> descriptions )
143158
{
144159
Contract.Requires( descriptions != null );
145160
Contract.Ensures( Contract.Result<IEnumerable<ApiVersion>>() != null );
@@ -158,6 +173,12 @@ static IEnumerable<ApiVersion> FlattenApiVersions( IEnumerable<ApiDescription> d
158173
}
159174
}
160175

176+
if ( versions.Count == 0 )
177+
{
178+
versions.Add( Options.DefaultApiVersion );
179+
return versions;
180+
}
181+
161182
return versions.OrderBy( v => v );
162183
}
163184

test/Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests/DefaultApiVersionDescriptionProviderTest.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.AspNetCore.Mvc.ApplicationModels;
66
using Microsoft.AspNetCore.Mvc.Infrastructure;
77
using Microsoft.AspNetCore.Mvc.Versioning;
8+
using Microsoft.Extensions.Options;
89
using Moq;
910
using System.Reflection;
1011
using Xunit;
@@ -18,7 +19,8 @@ public void api_version_descriptions_should_collate_expected_versions()
1819
// arrange
1920
var actionProvider = new TestActionDescriptorCollectionProvider();
2021
var groupNameFormatter = new DefaultApiVersionGroupNameFormatter();
21-
var descriptionProvider = new DefaultApiVersionDescriptionProvider( actionProvider, groupNameFormatter );
22+
var apiVersioningOptions = new OptionsWrapper<ApiVersioningOptions>( new ApiVersioningOptions() );
23+
var descriptionProvider = new DefaultApiVersionDescriptionProvider( actionProvider, groupNameFormatter, apiVersioningOptions );
2224

2325
// act
2426
var descriptions = descriptionProvider.ApiVersionDescriptions;
@@ -40,7 +42,8 @@ public void is_deprecated_should_return_false_without_api_vesioning()
4042
// arrange
4143
var provider = new DefaultApiVersionDescriptionProvider(
4244
new Mock<IActionDescriptorCollectionProvider>().Object,
43-
new Mock<IApiVersionGroupNameFormatter>().Object );
45+
new Mock<IApiVersionGroupNameFormatter>().Object,
46+
new OptionsWrapper<ApiVersioningOptions>( new ApiVersioningOptions() ) );
4447
var action = new ActionDescriptor();
4548

4649
// act
@@ -56,7 +59,8 @@ public void is_deprecated_should_return_false_when_controller_is_versionX2Dneutr
5659
// arrange
5760
var provider = new DefaultApiVersionDescriptionProvider(
5861
new Mock<IActionDescriptorCollectionProvider>().Object,
59-
new Mock<IApiVersionGroupNameFormatter>().Object );
62+
new Mock<IApiVersionGroupNameFormatter>().Object,
63+
new OptionsWrapper<ApiVersioningOptions>( new ApiVersioningOptions() ) );
6064
var action = new ActionDescriptor();
6165
var controller = new ControllerModel( typeof( Controller ).GetTypeInfo(), new object[0] );
6266

@@ -78,7 +82,8 @@ public void is_deprecated_should_return_expected_result_for_deprecated_version(
7882
// arrange
7983
var provider = new DefaultApiVersionDescriptionProvider(
8084
new Mock<IActionDescriptorCollectionProvider>().Object,
81-
new Mock<IApiVersionGroupNameFormatter>().Object );
85+
new Mock<IApiVersionGroupNameFormatter>().Object,
86+
new OptionsWrapper<ApiVersioningOptions>( new ApiVersioningOptions() ) );
8287
var action = new ActionDescriptor();
8388
var controller = new ControllerModel( typeof( Controller ).GetTypeInfo(), new object[0] );
8489
var model = new ApiVersionModel(

test/Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using FluentAssertions;
44
using Microsoft.AspNetCore.Mvc.ModelBinding;
55
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
6+
using Microsoft.AspNetCore.Mvc.Versioning;
7+
using Microsoft.Extensions.Options;
68
using Moq;
79
using System.Collections.Generic;
810
using Xunit;
@@ -17,7 +19,8 @@ public void versioned_api_explorer_should_group_and_order_descriptions_on_provid
1719
var context = new ApiDescriptionProviderContext( actionProvider.ActionDescriptors.Items );
1820
var groupNameFormatter = new DefaultApiVersionGroupNameFormatter();
1921
var modelMetadataProvider = NewModelMetadataProvider();
20-
var apiExplorer = new VersionedApiDescriptionProvider( groupNameFormatter, modelMetadataProvider );
22+
var apiVersioningOptions = new OptionsWrapper<ApiVersioningOptions>( new ApiVersioningOptions() );
23+
var apiExplorer = new VersionedApiDescriptionProvider( groupNameFormatter, modelMetadataProvider, apiVersioningOptions );
2124

2225
foreach ( var action in context.Actions )
2326
{

0 commit comments

Comments
 (0)