Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 95bf9be

Browse filesBrowse files
Refactor to support API version metadata collation via DI. Fixes #922
1 parent 4e1e8f8 commit 95bf9be
Copy full SHA for 95bf9be
Expand file treeCollapse file tree

17 files changed

+506
-420
lines changed

‎src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Asp.Versioning.Routing;
44

55
using Asp.Versioning;
6+
using Asp.Versioning.ApiExplorer;
67
using Microsoft.AspNetCore.Http;
78
using Microsoft.AspNetCore.OData.Routing;
89
using Microsoft.AspNetCore.OData.Routing.Template;
@@ -159,6 +160,7 @@ public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList<Policy
159160
private static int ApiVersioningPolicy() =>
160161
new ApiVersionMatcherPolicy(
161162
ApiVersionParser.Default,
163+
Enumerable.Empty<IApiVersionMetadataCollationProvider>(),
162164
Options.Create( new ApiVersioningOptions() ),
163165
new NullLogger<ApiVersionMatcherPolicy>() ).Order;
164166

+135Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.ApiExplorer;
4+
5+
using System.Collections;
6+
7+
/// <summary>
8+
/// Represents a collection of collated API version metadata.
9+
/// </summary>
10+
public class ApiVersionMetadataCollationCollection : IList<ApiVersionMetadata>, IReadOnlyList<ApiVersionMetadata>
11+
{
12+
private readonly List<ApiVersionMetadata> items;
13+
private readonly List<string?> groups;
14+
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="ApiVersionMetadataCollationCollection"/> class.
17+
/// </summary>
18+
public ApiVersionMetadataCollationCollection()
19+
{
20+
items = new();
21+
groups = new();
22+
}
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="ApiVersionMetadataCollationCollection"/> class.
26+
/// </summary>
27+
/// <param name="capacity">The initial capacity of the collection.</param>
28+
public ApiVersionMetadataCollationCollection( int capacity )
29+
{
30+
items = new( capacity );
31+
groups = new( capacity );
32+
}
33+
34+
/// <summary>
35+
/// Gets the item in the list at the specified index.
36+
/// </summary>
37+
/// <param name="index">The zero-based index of the item to retrieve.</param>
38+
/// <returns>The item at the specified index.</returns>
39+
public ApiVersionMetadata this[int index] => items[index];
40+
41+
ApiVersionMetadata IList<ApiVersionMetadata>.this[int index]
42+
{
43+
get => items[index];
44+
set => throw new NotSupportedException();
45+
}
46+
47+
/// <inheritdoc />
48+
public int Count => items.Count;
49+
50+
#pragma warning disable CA1033 // Interface methods should be callable by child types
51+
bool ICollection<ApiVersionMetadata>.IsReadOnly => ( (ICollection<ApiVersionMetadata>) items ).IsReadOnly;
52+
#pragma warning restore CA1033 // Interface methods should be callable by child types
53+
54+
/// <inheritdoc />
55+
public void Add( ApiVersionMetadata item ) => Insert( Count, item, default );
56+
57+
/// <summary>
58+
/// Adds an item to the collection.
59+
/// </summary>
60+
/// <param name="item">The item to add.</param>
61+
/// <param name="groupName">The associated group name, if any.</param>
62+
public void Add( ApiVersionMetadata item, string? groupName ) => Insert( Count, item, groupName );
63+
64+
/// <inheritdoc />
65+
public void Clear()
66+
{
67+
items.Clear();
68+
groups.Clear();
69+
}
70+
71+
/// <inheritdoc />
72+
public bool Contains( ApiVersionMetadata item ) => item != null && items.Contains( item );
73+
74+
/// <inheritdoc />
75+
public void CopyTo( ApiVersionMetadata[] array, int arrayIndex ) => items.CopyTo( array, arrayIndex );
76+
77+
/// <inheritdoc />
78+
public IEnumerator<ApiVersionMetadata> GetEnumerator() => items.GetEnumerator();
79+
80+
/// <inheritdoc />
81+
public int IndexOf( ApiVersionMetadata item ) => item == null ? -1 : items.IndexOf( item );
82+
83+
/// <inheritdoc />
84+
public void Insert( int index, ApiVersionMetadata item ) => Insert( index, item, default );
85+
86+
/// <summary>
87+
/// Inserts an item into the collection.
88+
/// </summary>
89+
/// <param name="index">The zero-based index where insertion takes place.</param>
90+
/// <param name="item">The item to insert.</param>
91+
/// <param name="groupName">The associated group name, if any.</param>
92+
public void Insert( int index, ApiVersionMetadata item, string? groupName )
93+
{
94+
items.Insert( index, item ?? throw new ArgumentNullException( nameof( item ) ) );
95+
groups.Insert( index, groupName );
96+
}
97+
98+
/// <inheritdoc />
99+
public bool Remove( ApiVersionMetadata item )
100+
{
101+
if ( item == null )
102+
{
103+
return false;
104+
}
105+
106+
var index = items.IndexOf( item );
107+
108+
if ( index < 0 )
109+
{
110+
return false;
111+
}
112+
113+
RemoveAt( index );
114+
return true;
115+
}
116+
117+
/// <inheritdoc />
118+
public void RemoveAt( int index )
119+
{
120+
items.RemoveAt( index );
121+
groups.RemoveAt( index );
122+
}
123+
124+
IEnumerator IEnumerable.GetEnumerator() => ( (IEnumerable) items ).GetEnumerator();
125+
126+
/// <summary>
127+
/// Gets the group name for the item at the specified index.
128+
/// </summary>
129+
/// <param name="index">The zero-based index of the item to get the group name for.</param>
130+
/// <returns>The associated group name or <c>null</c>.</returns>
131+
/// <remarks>If the specified <paramref name="index"/> is out of range, <c>null</c>
132+
/// is returned.</remarks>
133+
public string? GroupName( int index ) =>
134+
index < 0 || index >= groups.Count ? default : groups[index];
135+
}
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.ApiExplorer;
4+
5+
/// <summary>
6+
/// Represents the context used during API version metadata collation.
7+
/// </summary>
8+
public class ApiVersionMetadataCollationContext
9+
{
10+
/// <summary>
11+
/// Gets the read-only list of collation results.
12+
/// </summary>
13+
/// <value>The <see cref="IReadOnlyList{T}">read-only list</see> of collation results.</value>
14+
public ApiVersionMetadataCollationCollection Results { get; } = new();
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.ApiExplorer;
4+
5+
using Microsoft.AspNetCore.Routing;
6+
using Microsoft.Extensions.Primitives;
7+
8+
/// <summary>
9+
/// Represents the API version metadata collection provider for endpoints.
10+
/// </summary>
11+
[CLSCompliant( false )]
12+
public sealed class EndpointApiVersionMetadataCollationProvider : IApiVersionMetadataCollationProvider
13+
{
14+
private readonly EndpointDataSource endpointDataSource;
15+
private int version;
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="EndpointApiVersionMetadataCollationProvider"/> class.
19+
/// </summary>
20+
/// <param name="endpointDataSource">The underlying <see cref="endpointDataSource">endpoint data source</see>.</param>
21+
public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointDataSource )
22+
{
23+
this.endpointDataSource = endpointDataSource ?? throw new ArgumentNullException( nameof( endpointDataSource ) );
24+
ChangeToken.OnChange( endpointDataSource.GetChangeToken, () => ++version );
25+
}
26+
27+
/// <inheritdoc />
28+
public int Version => version;
29+
30+
/// <inheritdoc />
31+
public void Execute( ApiVersionMetadataCollationContext context )
32+
{
33+
if ( context == null )
34+
{
35+
throw new ArgumentNullException( nameof( context ) );
36+
}
37+
38+
var endpoints = endpointDataSource.Endpoints;
39+
40+
for ( var i = 0; i < endpoints.Count; i++ )
41+
{
42+
var endpoint = endpoints[i];
43+
44+
if ( endpoint.Metadata.GetMetadata<ApiVersionMetadata>() is not ApiVersionMetadata item )
45+
{
46+
continue;
47+
}
48+
49+
var groupName = endpoint.Metadata.OfType<IEndpointGroupNameMetadata>().LastOrDefault()?.EndpointGroupName;
50+
context.Results.Add( item, groupName );
51+
}
52+
}
53+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
3+
namespace Asp.Versioning.ApiExplorer;
4+
5+
/// <summary>
6+
/// Defines the behavior of an API version metadata collation provider.
7+
/// </summary>
8+
public interface IApiVersionMetadataCollationProvider
9+
{
10+
/// <summary>
11+
/// Gets version of the underlying provider results.
12+
/// </summary>
13+
/// <value>The version of the provider results. This can be used to detect changes.</value>
14+
int Version { get; }
15+
16+
/// <summary>
17+
/// Executes the provider using the given context.
18+
/// </summary>
19+
/// <param name="context">The collation context.</param>
20+
void Execute( ApiVersionMetadataCollationContext context );
21+
}

‎src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs

Copy file name to clipboardExpand all lines: src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Microsoft.Extensions.DependencyInjection;
44

55
using Asp.Versioning;
6-
using Asp.Versioning.Builder;
6+
using Asp.Versioning.ApiExplorer;
77
using Asp.Versioning.Routing;
88
using Microsoft.AspNetCore.Http;
99
using Microsoft.AspNetCore.Routing;
@@ -92,6 +92,7 @@ private static void AddApiVersioningServices( IServiceCollection services )
9292
services.TryAddSingleton<ISunsetPolicyManager, SunsetPolicyManager>();
9393
services.TryAddEnumerable( Transient<IPostConfigureOptions<RouteOptions>, ApiVersioningRouteOptionsSetup>() );
9494
services.TryAddEnumerable( Singleton<MatcherPolicy, ApiVersionMatcherPolicy>() );
95+
services.TryAddEnumerable( Singleton<IApiVersionMetadataCollationProvider, EndpointApiVersionMetadataCollationProvider>() );
9596
services.Replace( WithLinkGeneratorDecorator( services ) );
9697
TryAddProblemDetailsRfc7231Compliance( services );
9798
}
@@ -157,6 +158,8 @@ LinkGenerator NewFactory( IServiceProvider serviceProvider )
157158
}
158159
}
159160

161+
// TODO: Remove in .NET 8.0
162+
// REF: https://github.com/dotnet/aspnetcore/issues/45051
160163
private static void TryAddProblemDetailsRfc7231Compliance( IServiceCollection services )
161164
{
162165
var descriptor = services.FirstOrDefault( IsDefaultProblemDetailsWriter );

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.