2
2
3
3
namespace Asp . Versioning . ApiExplorer ;
4
4
5
+ using Asp . Versioning . ApiExplorer . Internal ;
5
6
using Microsoft . Extensions . Options ;
6
- using static Asp . Versioning . ApiVersionMapping ;
7
- using static System . Globalization . CultureInfo ;
8
7
9
8
/// <summary>
10
9
/// Represents the default implementation of an object that discovers and describes the API version information within an application.
11
10
/// </summary>
12
11
[ CLSCompliant ( false ) ]
13
12
public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvider
14
13
{
15
- private readonly ApiVersionDescriptionCollection collection ;
14
+ private readonly ApiVersionDescriptionCollection < GroupedApiVersionMetadata > collection ;
16
15
private readonly IOptions < ApiExplorerOptions > options ;
17
16
18
17
/// <summary>
@@ -28,7 +27,7 @@ public DefaultApiVersionDescriptionProvider(
28
27
ISunsetPolicyManager sunsetPolicyManager ,
29
28
IOptions < ApiExplorerOptions > apiExplorerOptions )
30
29
{
31
- collection = new ( this , providers ?? throw new ArgumentNullException ( nameof ( providers ) ) ) ;
30
+ collection = new ( Describe , providers ?? throw new ArgumentNullException ( nameof ( providers ) ) ) ;
32
31
SunsetPolicyManager = sunsetPolicyManager ;
33
32
options = apiExplorerOptions ;
34
33
}
@@ -58,133 +57,53 @@ protected virtual IReadOnlyList<ApiVersionDescription> Describe( IReadOnlyList<A
58
57
{
59
58
ArgumentNullException . ThrowIfNull ( metadata ) ;
60
59
61
- var descriptions = new List < ApiVersionDescription > ( capacity : metadata . Count ) ;
62
- var supported = new HashSet < ApiVersion > ( ) ;
63
- var deprecated = new HashSet < ApiVersion > ( ) ;
64
-
65
- BucketizeApiVersions ( metadata , supported , deprecated ) ;
66
- AppendDescriptions ( descriptions , supported , deprecated : false ) ;
67
- AppendDescriptions ( descriptions , deprecated , deprecated : true ) ;
68
-
69
- return descriptions . OrderBy ( d => d . ApiVersion ) . ToArray ( ) ;
70
- }
71
-
72
- private void BucketizeApiVersions ( IReadOnlyList < ApiVersionMetadata > metadata , HashSet < ApiVersion > supported , HashSet < ApiVersion > deprecated )
73
- {
74
- var declared = new HashSet < ApiVersion > ( ) ;
75
- var advertisedSupported = new HashSet < ApiVersion > ( ) ;
76
- var advertisedDeprecated = new HashSet < ApiVersion > ( ) ;
77
-
78
- for ( var i = 0 ; i < metadata . Count ; i ++ )
60
+ // TODO: consider refactoring and removing GroupedApiVersionDescriptionProvider as both implementations are now
61
+ // effectively the same. this cast is safe as an internal implementation detail. if this method is
62
+ // overridden, then this code doesn't even run
63
+ //
64
+ // REF: https://github.com/dotnet/aspnet-api-versioning/issues/1066
65
+ if ( metadata is GroupedApiVersionMetadata [ ] groupedMetadata )
79
66
{
80
- var model = metadata [ i ] . Map ( Explicit | Implicit ) ;
81
- var versions = model . DeclaredApiVersions ;
82
-
83
- for ( var j = 0 ; j < versions . Count ; j ++ )
84
- {
85
- declared . Add ( versions [ j ] ) ;
86
- }
87
-
88
- versions = model . SupportedApiVersions ;
89
-
90
- for ( var j = 0 ; j < versions . Count ; j ++ )
91
- {
92
- var version = versions [ j ] ;
93
- supported . Add ( version ) ;
94
- advertisedSupported . Add ( version ) ;
95
- }
96
-
97
- versions = model . DeprecatedApiVersions ;
98
-
99
- for ( var j = 0 ; j < versions . Count ; j ++ )
100
- {
101
- var version = versions [ j ] ;
102
- deprecated . Add ( version ) ;
103
- advertisedDeprecated . Add ( version ) ;
104
- }
67
+ return DescriptionProvider . Describe ( groupedMetadata , SunsetPolicyManager , Options ) ;
105
68
}
106
69
107
- advertisedSupported . ExceptWith ( declared ) ;
108
- advertisedDeprecated . ExceptWith ( declared ) ;
109
- supported . ExceptWith ( advertisedSupported ) ;
110
- deprecated . ExceptWith ( supported . Concat ( advertisedDeprecated ) ) ;
111
-
112
- if ( supported . Count == 0 && deprecated . Count == 0 )
113
- {
114
- supported . Add ( Options . DefaultApiVersion ) ;
115
- }
70
+ return Array . Empty < ApiVersionDescription > ( ) ;
116
71
}
117
72
118
- private void AppendDescriptions ( List < ApiVersionDescription > descriptions , IEnumerable < ApiVersion > versions , bool deprecated )
73
+ private sealed class GroupedApiVersionMetadata :
74
+ ApiVersionMetadata ,
75
+ IEquatable < GroupedApiVersionMetadata > ,
76
+ IGroupedApiVersionMetadata ,
77
+ IGroupedApiVersionMetadataFactory < GroupedApiVersionMetadata >
119
78
{
120
- foreach ( var version in versions )
121
- {
122
- var groupName = version . ToString ( Options . GroupNameFormat , CurrentCulture ) ;
123
- var sunsetPolicy = SunsetPolicyManager . TryGetPolicy ( version , out var policy ) ? policy : default ;
124
- descriptions . Add ( new ( version , groupName , deprecated , sunsetPolicy ) ) ;
125
- }
126
- }
79
+ private GroupedApiVersionMetadata ( string ? groupName , ApiVersionMetadata metadata )
80
+ : base ( metadata ) => GroupName = groupName ;
127
81
128
- private sealed class ApiVersionDescriptionCollection (
129
- DefaultApiVersionDescriptionProvider provider ,
130
- IEnumerable < IApiVersionMetadataCollationProvider > collators )
131
- {
132
- private readonly object syncRoot = new ( ) ;
133
- private readonly DefaultApiVersionDescriptionProvider provider = provider ;
134
- private readonly IApiVersionMetadataCollationProvider [ ] collators = collators . ToArray ( ) ;
135
- private IReadOnlyList < ApiVersionDescription > ? items ;
136
- private int version ;
137
-
138
- public IReadOnlyList < ApiVersionDescription > Items
139
- {
140
- get
141
- {
142
- if ( items is not null && version == ComputeVersion ( ) )
143
- {
144
- return items ;
145
- }
82
+ public string ? GroupName { get ; }
146
83
147
- lock ( syncRoot )
148
- {
149
- var currentVersion = ComputeVersion ( ) ;
84
+ static GroupedApiVersionMetadata IGroupedApiVersionMetadataFactory < GroupedApiVersionMetadata > . New (
85
+ string ? groupName ,
86
+ ApiVersionMetadata metadata ) => new ( groupName , metadata ) ;
150
87
151
- if ( items is not null && version == currentVersion )
152
- {
153
- return items ;
154
- }
88
+ public bool Equals ( GroupedApiVersionMetadata ? other ) =>
89
+ other is not null && other . GetHashCode ( ) == GetHashCode ( ) ;
155
90
156
- var context = new ApiVersionMetadataCollationContext ( ) ;
91
+ public override bool Equals ( object ? obj ) =>
92
+ obj is not null &&
93
+ GetType ( ) . Equals ( obj . GetType ( ) ) &&
94
+ GetHashCode ( ) == obj . GetHashCode ( ) ;
157
95
158
- for ( var i = 0 ; i < collators . Length ; i ++ )
159
- {
160
- collators [ i ] . Execute ( context ) ;
161
- }
162
-
163
- items = provider . Describe ( context . Results ) ;
164
- version = currentVersion ;
165
- }
166
-
167
- return items ;
168
- }
169
- }
170
-
171
- private int ComputeVersion ( ) =>
172
- collators . Length switch
173
- {
174
- 0 => 0 ,
175
- 1 => collators [ 0 ] . Version ,
176
- _ => ComputeVersion ( collators ) ,
177
- } ;
178
-
179
- private static int ComputeVersion ( IApiVersionMetadataCollationProvider [ ] providers )
96
+ public override int GetHashCode ( )
180
97
{
181
98
var hash = default ( HashCode ) ;
182
99
183
- for ( var i = 0 ; i < providers . Length ; i ++ )
100
+ if ( ! string . IsNullOrEmpty ( GroupName ) )
184
101
{
185
- hash . Add ( providers [ i ] . Version ) ;
102
+ hash . Add ( GroupName , StringComparer . Ordinal ) ;
186
103
}
187
104
105
+ hash . Add ( base . GetHashCode ( ) ) ;
106
+
188
107
return hash . ToHashCode ( ) ;
189
108
}
190
109
}
0 commit comments