diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
index 5384c160..f3b2bf12 100644
--- a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs
@@ -50,25 +50,84 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is { } policy )
+ if ( description.DeprecationPolicy is { } deprecationPolicy )
{
- if ( policy.Date is { } when )
+ if ( deprecationPolicy.Date is { } when )
{
- text.Append( " The API will be sunset on " )
- .Append( when.Date.ToShortDateString() )
- .Append( '.' );
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
}
- if ( policy.HasLinks )
+ if ( deprecationPolicy.HasLinks )
{
text.AppendLine();
var rendered = false;
- for ( var i = 0; i < policy.Links.Count; i++ )
+ foreach ( var link in deprecationPolicy.Links )
{
- var link = policy.Links[i];
+ if ( link.Type == "text/html" )
+ {
+ if ( !rendered )
+ {
+ text.Append( "
Links
" );
+ }
+ }
+ }
+
+ if ( description.SunsetPolicy is { } sunsetPolicy )
+ {
+ if ( sunsetPolicy.Date is { } when )
+ {
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ }
+
+ if ( sunsetPolicy.HasLinks )
+ {
+ text.AppendLine();
+
+ var rendered = false;
+
+ foreach ( var link in sunsetPolicy.Links )
+ {
if ( link.Type == "text/html" )
{
if ( !rendered )
diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
index b725ab72..38a250af 100644
--- a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs
@@ -50,25 +50,84 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is { } policy )
+ if ( description.DeprecationPolicy is { } deprecationPolicy )
{
- if ( policy.Date is { } when )
+ if ( deprecationPolicy.Date is { } when )
{
- text.Append( " The API will be sunset on " )
- .Append( when.Date.ToShortDateString() )
- .Append( '.' );
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
}
- if ( policy.HasLinks )
+ if ( deprecationPolicy.HasLinks )
{
text.AppendLine();
var rendered = false;
- for ( var i = 0; i < policy.Links.Count; i++ )
+ foreach ( var link in deprecationPolicy.Links )
{
- var link = policy.Links[i];
+ if ( link.Type == "text/html" )
+ {
+ if ( !rendered )
+ {
+ text.Append( "Links
" );
+ }
+ }
+ }
+
+ if ( description.SunsetPolicy is { } sunsetPolicy )
+ {
+ if ( sunsetPolicy.Date is { } when )
+ {
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ }
+
+ if ( sunsetPolicy.HasLinks )
+ {
+ text.AppendLine();
+
+ var rendered = false;
+
+ foreach ( var link in sunsetPolicy.Links )
+ {
if ( link.Type == "text/html" )
{
if ( !rendered )
diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
index d531cea4..5a8cbf51 100644
--- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs
@@ -50,25 +50,84 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is { } policy )
+ if ( description.DeprecationPolicy is { } deprecationPolicy )
{
- if ( policy.Date is { } when )
+ if ( deprecationPolicy.Date is { } when )
{
- text.Append( " The API will be sunset on " )
- .Append( when.Date.ToShortDateString() )
- .Append( '.' );
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
}
- if ( policy.HasLinks )
+ if ( deprecationPolicy.HasLinks )
{
text.AppendLine();
var rendered = false;
- for ( var i = 0; i < policy.Links.Count; i++ )
+ foreach ( var link in deprecationPolicy.Links )
{
- var link = policy.Links[i];
+ if ( link.Type == "text/html" )
+ {
+ if ( !rendered )
+ {
+ text.Append( "Links
" );
+ }
+ }
+ }
+
+ if ( description.SunsetPolicy is { } sunsetPolicy )
+ {
+ if ( sunsetPolicy.Date is { } when )
+ {
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ }
+
+ if ( sunsetPolicy.HasLinks )
+ {
+ text.AppendLine();
+
+ var rendered = false;
+
+ foreach ( var link in sunsetPolicy.Links )
+ {
if ( link.Type == "text/html" )
{
if ( !rendered )
diff --git a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
index d531cea4..5a8cbf51 100644
--- a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
+++ b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs
@@ -50,25 +50,84 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri
text.Append( " This API version has been deprecated." );
}
- if ( description.SunsetPolicy is { } policy )
+ if ( description.DeprecationPolicy is { } deprecationPolicy )
{
- if ( policy.Date is { } when )
+ if ( deprecationPolicy.Date is { } when )
{
- text.Append( " The API will be sunset on " )
- .Append( when.Date.ToShortDateString() )
- .Append( '.' );
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be deprecated on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
}
- if ( policy.HasLinks )
+ if ( deprecationPolicy.HasLinks )
{
text.AppendLine();
var rendered = false;
- for ( var i = 0; i < policy.Links.Count; i++ )
+ foreach ( var link in deprecationPolicy.Links )
{
- var link = policy.Links[i];
+ if ( link.Type == "text/html" )
+ {
+ if ( !rendered )
+ {
+ text.Append( "Links
" );
+ }
+ }
+ }
+
+ if ( description.SunsetPolicy is { } sunsetPolicy )
+ {
+ if ( sunsetPolicy.Date is { } when )
+ {
+ if ( when < DateTime.Now )
+ {
+ text.Append( " The API has been sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ else
+ {
+ text.Append( " The API will be sunset on " )
+ .Append( when.Date.ToShortDateString() )
+ .Append( '.' );
+ }
+ }
+
+ if ( sunsetPolicy.HasLinks )
+ {
+ text.AppendLine();
+
+ var rendered = false;
+
+ foreach ( var link in sunsetPolicy.Links )
+ {
if ( link.Type == "text/html" )
{
if ( !rendered )
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/DeprecationPolicy.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/DeprecationPolicy.cs
new file mode 100644
index 00000000..eb6e349f
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/DeprecationPolicy.cs
@@ -0,0 +1,77 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Represents an API version deprecation policy.
+///
+public class DeprecationPolicy
+{
+ private readonly LinkList links;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DeprecationPolicy()
+ {
+ links = new DeprecationLinkList();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The date and time when the API version will be deprecated.
+ /// The optional link which provides information about the deprecation policy.
+ public DeprecationPolicy( DateTimeOffset date, LinkHeaderValue? link = default )
+ : this()
+ {
+ Date = date;
+
+ if ( link is not null )
+ {
+ links.Add( link );
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The link which provides information about the deprecation policy.
+ public DeprecationPolicy( LinkHeaderValue link )
+ : this()
+ {
+ links.Add( link );
+ }
+
+ ///
+ /// Gets the date and time when the API version will be deprecated.
+ ///
+ /// The date and time when the API version will be deprecated, if any.
+ public DateTimeOffset? Date { get; }
+
+ ///
+ /// Gets a value indicating whether the deprecation policy has any associated links.
+ ///
+ /// True if the deprecation policy has associated links; otherwise, false.
+ public bool HasLinks => links.Count > 0;
+
+ ///
+ /// Gets a read-only list of links that provide information about the deprecation policy.
+ ///
+ /// A read-only list of HTTP links.
+ /// If a link is provided, generally only one link is necessary; however, additional
+ /// links might be provided for different languages or different formats such as a HTML page
+ /// or a JSON file.
+ public IList Links => links;
+
+ internal sealed class DeprecationLinkList : LinkList
+ {
+ protected override void EnsureRelationType( LinkHeaderValue item )
+ {
+ if ( !item.RelationType.Equals( "deprecation", StringComparison.OrdinalIgnoreCase ) )
+ {
+ throw new ArgumentException( SR.InvalidDeprecationRelationType, nameof( item ) );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilder.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilder.cs
index 7a692d84..f400ec49 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilder.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilder.cs
@@ -23,4 +23,14 @@ public interface IApiVersioningPolicyBuilder
/// The and
/// parameters are both null.
ISunsetPolicyBuilder Sunset( string? name, ApiVersion? apiVersion );
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The optional name of the API the policy is for.
+ /// The optional API version the policy is for.
+ /// A new deprecation policy builder.
+ /// The and
+ /// parameters are both null.
+ public IDeprecationPolicyBuilder Deprecate( string? name, ApiVersion? apiVersion );
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
index 048d6d49..afdfd5b9 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersioningPolicyBuilderExtensions.cs
@@ -157,4 +157,151 @@ public static ISunsetPolicyBuilder Sunset(
ArgumentNullException.ThrowIfNull( builder );
return builder.Sunset( default, new ApiVersion( groupVersion, status ) );
}
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The name of the API the policy is for.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, string name )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( name, default );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The name of the API the policy is for.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate(
+ this IApiVersioningPolicyBuilder builder,
+ string name,
+ int majorVersion,
+ int? minorVersion = default,
+ string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( name, new ApiVersion( majorVersion, minorVersion, status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The name of the API the policy is for.
+ /// The version number.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, string name, double version, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( name, new ApiVersion( version, status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The name of the API the policy is for.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, string name, int year, int month, int day, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( name, new ApiVersion( new DateOnly( year, month, day ), status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The name of the API the policy is for.
+ /// The group version.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, string name, DateOnly groupVersion, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( name, new ApiVersion( groupVersion, status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The API version the policy is for.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, ApiVersion apiVersion )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( default, apiVersion );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The major version number.
+ /// The optional minor version number.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate(
+ this IApiVersioningPolicyBuilder builder,
+ int majorVersion,
+ int? minorVersion = default,
+ string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( default, new ApiVersion( majorVersion, minorVersion, status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The version number.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, double version, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( default, new ApiVersion( version, status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The version year.
+ /// The version month.
+ /// The version day.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, int year, int month, int day, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( default, new ApiVersion( new DateOnly( year, month, day ), status ) );
+ }
+
+ ///
+ /// Creates and returns a new deprecation policy builder.
+ ///
+ /// The extended API versioning policy builder.
+ /// The group version.
+ /// The optional version status.
+ /// A new deprecation policy builder.
+ public static IDeprecationPolicyBuilder Deprecate( this IApiVersioningPolicyBuilder builder, DateOnly groupVersion, string? status = default )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Deprecate( default, new ApiVersion( groupVersion, status ) );
+ }
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilder.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilder.cs
new file mode 100644
index 00000000..e7b73155
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilder.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Defines the behavior of a deprecation policy builder.
+///
+public interface IDeprecationPolicyBuilder : IPolicyBuilder
+{
+ ///
+ /// Creates and returns a new link builder.
+ ///
+ /// The link target URL.
+ /// A new link builder.
+ ILinkBuilder Link( Uri linkTarget );
+
+ ///
+ /// Indicates when a deprecation policy is applied.
+ ///
+ /// The date and time when a
+ /// deprecation policy is applied.
+ /// The current deprecation policy builder.
+ IDeprecationPolicyBuilder Effective( DateTimeOffset deprecationDate );
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilderExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilderExtensions.cs
new file mode 100644
index 00000000..fb685583
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyBuilderExtensions.cs
@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Provides extension methods for the interface.
+///
+public static class IDeprecationPolicyBuilderExtensions
+{
+ ///
+ /// Creates and returns a new link builder.
+ ///
+ /// The extended deprecation policy builder.
+ /// The link target URL.
+ /// A new link builder.
+ public static ILinkBuilder Link( this IDeprecationPolicyBuilder builder, string linkTarget )
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ return builder.Link( new Uri( linkTarget, UriKind.RelativeOrAbsolute ) );
+ }
+
+ ///
+ /// Indicates when a deprecation policy is applied.
+ ///
+ /// The type of deprecation policy builder.
+ /// The extended deprecation policy builder.
+ /// The year when the deprecation policy is applied.
+ /// The month when the deprecation policy is applied.
+ /// The day when the deprecation policy is applied.
+ /// The current deprecation policy builder.
+ public static TBuilder Effective( this TBuilder builder, int year, int month, int day )
+ where TBuilder : notnull, IDeprecationPolicyBuilder
+ {
+ ArgumentNullException.ThrowIfNull( builder );
+ builder.Effective( new DateTimeOffset( new DateTime( year, month, day ) ) );
+ return builder;
+ }
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyManager.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyManager.cs
new file mode 100644
index 00000000..1fe36e40
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IDeprecationPolicyManager.cs
@@ -0,0 +1,9 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Defines the behavior of an API version deprecation policy manager.
+///
+public interface IDeprecationPolicyManager : IPolicyManager
+{ }
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyBuilder.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyBuilder.cs
new file mode 100644
index 00000000..fde4520b
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyBuilder.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Defines the behavior of a policy builder which applies to a single API version.
+///
+/// The type of policy which is built by this builder.
+public interface IPolicyBuilder
+{
+ ///
+ /// Gets the policy name.
+ ///
+ /// The policy name, if any.
+ /// The name is typically of an API.
+ string? Name { get; }
+
+ ///
+ /// Gets the API version the policy is for.
+ ///
+ /// The specific policy API version, if any.
+ ApiVersion? ApiVersion { get; }
+
+ ///
+ /// Configures the builder per the specified .
+ ///
+ /// The applied policy.
+ void Per( TPolicy policy );
+
+ ///
+ /// Builds and returns a policy.
+ ///
+ /// A new policy.
+ TPolicy Build();
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManager.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManager.cs
new file mode 100644
index 00000000..43a5fd8d
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManager.cs
@@ -0,0 +1,23 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Defines the behavior of an API version policy manager.
+///
+/// The type of the policy.
+public interface IPolicyManager
+{
+ ///
+ /// Returns the policy for the specified API and version.
+ ///
+ /// The name of the API.
+ /// The API version to get the policy for.
+ /// The applicable policy, if any.
+ /// True if the policy was retrieved; otherwise, false.
+ /// If is null, it is assumed the caller intends to match any
+ /// policy for the specified API version. If
+ /// API version is null, it is assumed the caller intends to match
+ /// any policy for the specified .
+ bool TryGetPolicy( string? name, ApiVersion? apiVersion, [MaybeNullWhen( false )] out TPolicy policy );
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManagerExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManagerExtensions.cs
new file mode 100644
index 00000000..0c468f71
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IPolicyManagerExtensions.cs
@@ -0,0 +1,118 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Provides extension methods for the interface.
+///
+public static class IPolicyManagerExtensions
+{
+ ///
+ /// Returns the policy for the specified API and version.
+ ///
+ /// The extended policy manager.
+ /// The API version to get the policy for.
+ /// The applicable policy, if any.
+ /// The type of policy.
+ /// True if the policy was retrieved; otherwise, false.
+ public static bool TryGetPolicy(
+ this IPolicyManager policyManager,
+ ApiVersion apiVersion,
+ [MaybeNullWhen( false )] out TPolicy policy )
+ {
+ ArgumentNullException.ThrowIfNull( policyManager );
+ return policyManager.TryGetPolicy( default, apiVersion, out policy );
+ }
+
+ ///
+ /// Returns the policy for the specified API and version.
+ ///
+ /// The extended policy manager.
+ /// The name of the API.
+ /// The applicable policy, if any.
+ /// The type of policy.
+ /// True if the policy was retrieved; otherwise, false.
+ public static bool TryGetPolicy(
+ this IPolicyManager policyManager,
+ string name,
+ [MaybeNullWhen( false )] out TPolicy policy )
+ {
+ ArgumentNullException.ThrowIfNull( policyManager );
+ return policyManager.TryGetPolicy( name, default, out policy );
+ }
+
+ ///
+ /// Attempts to resolve a policy for the specified name and API version combination.
+ ///
+ /// The extended policy manager.
+ /// The name of the API.
+ /// The API version to get the policy for.
+ /// The type of policy.
+ /// The applicable policy, if any.
+ /// The resolution order is as follows:
+ ///
+ /// - and
+ /// - only
+ /// - only
+ ///
+ ///
+ public static TPolicy? ResolvePolicyOrDefault(
+ this IPolicyManager policyManager,
+ string? name,
+ ApiVersion? apiVersion )
+ {
+ ArgumentNullException.ThrowIfNull( policyManager );
+
+ if ( policyManager.TryResolvePolicy( name, apiVersion, out var policy ) )
+ {
+ return policy;
+ }
+
+ return default;
+ }
+
+ ///
+ /// Attempts to resolve a policy for the specified name and API version combination.
+ ///
+ /// The extended policy manager.
+ /// The name of the API.
+ /// The API version to get the policy for.
+ /// The applicable policy, if any.
+ /// The type of policy.
+ /// True if the policy was retrieved; otherwise, false.
+ /// The resolution order is as follows:
+ ///
+ /// - and
+ /// - only
+ /// - only
+ ///
+ ///
+ public static bool TryResolvePolicy(
+ this IPolicyManager policyManager,
+ string? name,
+ ApiVersion? apiVersion,
+ [MaybeNullWhen( false )] out TPolicy policy )
+ {
+ ArgumentNullException.ThrowIfNull( policyManager );
+
+ if ( !string.IsNullOrEmpty( name ) )
+ {
+ if ( apiVersion != null && policyManager.TryGetPolicy( name, apiVersion, out policy ) )
+ {
+ return true;
+ }
+ else if ( policyManager.TryGetPolicy( name!, out policy ) )
+ {
+ return true;
+ }
+ }
+
+ if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out policy ) )
+ {
+ return true;
+ }
+
+ policy = default!;
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilder.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilder.cs
index deca62dc..7523a54d 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilder.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyBuilder.cs
@@ -5,26 +5,14 @@ namespace Asp.Versioning;
///
/// Defines the behavior of a sunset policy builder.
///
-public interface ISunsetPolicyBuilder
+public interface ISunsetPolicyBuilder : IPolicyBuilder
{
///
- /// Gets the policy name.
- ///
- /// The policy name, if any.
- /// The name is typically of an API.
- string? Name { get; }
-
- ///
- /// Gets the API version the policy is for.
- ///
- /// The specific policy API version, if any.
- ApiVersion? ApiVersion { get; }
-
- ///
- /// Applies a sunset policy per the specified policy.
+ /// Creates and returns a new link builder.
///
- /// The applied sunset policy.
- void Per( SunsetPolicy policy );
+ /// The link target URL.
+ /// A new link builder.
+ ILinkBuilder Link( Uri linkTarget );
///
/// Indicates when a sunset policy is applied.
@@ -33,17 +21,4 @@ public interface ISunsetPolicyBuilder
/// sunset policy is applied.
/// The current sunset policy builder.
ISunsetPolicyBuilder Effective( DateTimeOffset sunsetDate );
-
- ///
- /// Creates and returns a new link builder.
- ///
- /// The link target URL.
- /// A new link builder.
- ILinkBuilder Link( Uri linkTarget );
-
- ///
- /// Builds and returns a sunset policy.
- ///
- /// A new sunset policy.
- SunsetPolicy Build();
}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
index de2dd77e..0baacf3e 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs
@@ -5,18 +5,5 @@ namespace Asp.Versioning;
///
/// Defines the behavior of an API version sunset policy manager.
///
-public interface ISunsetPolicyManager
-{
- ///
- /// Returns the sunset policy for the specified API and version.
- ///
- /// The name of the API.
- /// The API version to get the policy for.
- /// The applicable sunset policy, if any.
- /// True if the sunset policy was retrieved; otherwise, false.
- /// If is null, it is assumed the caller intends to match any sunset
- /// policy for the specified API version. If
- /// API version is null, it is assumed the caller intends to match
- /// any sunset policy for the specified .
- bool TryGetPolicy( string? name, ApiVersion? apiVersion, [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy );
-}
\ No newline at end of file
+public interface ISunsetPolicyManager : IPolicyManager
+{ }
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs
deleted file mode 100644
index f28f9cca..00000000
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-
-namespace Asp.Versioning;
-
-///
-/// Provides extension methods for the interface.
-///
-public static class ISunsetPolicyManagerExtensions
-{
- ///
- /// Returns the sunset policy for the specified API and version.
- ///
- /// The extended sunset policy manager.
- /// The API version to get the policy for.
- /// The applicable sunset policy, if any.
- /// True if the sunset policy was retrieved; otherwise, false.
- public static bool TryGetPolicy(
- this ISunsetPolicyManager policyManager,
- ApiVersion apiVersion,
- [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
- {
- ArgumentNullException.ThrowIfNull( policyManager );
- return policyManager.TryGetPolicy( default, apiVersion, out sunsetPolicy );
- }
-
- ///
- /// Returns the sunset policy for the specified API and version.
- ///
- /// The extended sunset policy manager.
- /// The name of the API.
- /// The applicable sunset policy, if any.
- /// True if the sunset policy was retrieved; otherwise, false.
- public static bool TryGetPolicy(
- this ISunsetPolicyManager policyManager,
- string name,
- [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
- {
- ArgumentNullException.ThrowIfNull( policyManager );
- return policyManager.TryGetPolicy( name, default, out sunsetPolicy );
- }
-
- ///
- /// Attempts to resolve a sunset policy for the specified name and API version combination.
- ///
- /// The extended sunset policy manager.
- /// The name of the API.
- /// The API version to get the policy for.
- /// The applicable sunset policy, if any.
- /// The resolution order is as follows:
- ///
- /// - and
- /// - only
- /// - only
- ///
- ///
- public static SunsetPolicy? ResolvePolicyOrDefault(
- this ISunsetPolicyManager policyManager,
- string? name,
- ApiVersion? apiVersion )
- {
- ArgumentNullException.ThrowIfNull( policyManager );
-
- if ( policyManager.TryResolvePolicy( name, apiVersion, out var policy ) )
- {
- return policy;
- }
-
- return default;
- }
-
- ///
- /// Attempts to resolve a sunset policy for the specified name and API version combination.
- ///
- /// The extended sunset policy manager.
- /// The name of the API.
- /// The API version to get the policy for.
- /// /// The applicable sunset policy, if any.
- /// True if the sunset policy was retrieved; otherwise, false.
- /// The resolution order is as follows:
- ///
- /// - and
- /// - only
- /// - only
- ///
- ///
- public static bool TryResolvePolicy(
- this ISunsetPolicyManager policyManager,
- string? name,
- ApiVersion? apiVersion,
- [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
- {
- ArgumentNullException.ThrowIfNull( policyManager );
-
- if ( !string.IsNullOrEmpty( name ) )
- {
- if ( apiVersion != null && policyManager.TryGetPolicy( name, apiVersion, out sunsetPolicy ) )
- {
- return true;
- }
- else if ( policyManager.TryGetPolicy( name!, out sunsetPolicy ) )
- {
- return true;
- }
- }
-
- if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out sunsetPolicy ) )
- {
- return true;
- }
-
- sunsetPolicy = default!;
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkList.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkList.cs
new file mode 100644
index 00000000..69d18f11
--- /dev/null
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkList.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using System.Collections.ObjectModel;
+
+internal abstract class LinkList : Collection
+{
+ public LinkList() { }
+
+ protected override void InsertItem( int index, LinkHeaderValue item )
+ {
+ EnsureRelationType( item );
+ base.InsertItem( index, item );
+ }
+
+ protected override void SetItem( int index, LinkHeaderValue item )
+ {
+ EnsureRelationType( item );
+ base.SetItem( index, item );
+ }
+
+ protected abstract void EnsureRelationType( LinkHeaderValue item );
+}
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/SR.Designer.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/SR.Designer.cs
index 9f45b67f..0e7b6c28 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/SR.Designer.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/SR.Designer.cs
@@ -141,5 +141,14 @@ internal static string InvalidSunsetRelationType {
return ResourceManager.GetString("InvalidSunsetRelationType", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to The relation type for a deprecation policy link must be "deprecation"..
+ ///
+ internal static string InvalidDeprecationRelationType {
+ get {
+ return ResourceManager.GetString("InvalidDeprecationRelationType", resourceCulture);
+ }
+ }
}
}
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/SR.resx b/src/Abstractions/src/Asp.Versioning.Abstractions/SR.resx
index ea5522a8..02f228a1 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/SR.resx
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/SR.resx
@@ -144,4 +144,7 @@
The relation type for a sunset policy link must be "sunset".
+
+ The relation type for a deprecation policy link must be "deprecation".
+
\ No newline at end of file
diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/SunsetPolicy.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/SunsetPolicy.cs
index e5e0f4be..412367ce 100644
--- a/src/Abstractions/src/Asp.Versioning.Abstractions/SunsetPolicy.cs
+++ b/src/Abstractions/src/Asp.Versioning.Abstractions/SunsetPolicy.cs
@@ -2,19 +2,20 @@
namespace Asp.Versioning;
-using System.Collections.ObjectModel;
-
///
/// Represents an API version sunset policy.
///
public class SunsetPolicy
{
- private SunsetLinkList? links;
+ private readonly LinkList links;
///
/// Initializes a new instance of the class.
///
- public SunsetPolicy() { }
+ public SunsetPolicy()
+ {
+ links = new SunsetLinkList();
+ }
///
/// Initializes a new instance of the class.
@@ -22,12 +23,13 @@ public SunsetPolicy() { }
/// The date and time when the API version will be sunset.
/// The optional link which provides information about the sunset policy.
public SunsetPolicy( DateTimeOffset date, LinkHeaderValue? link = default )
+ : this()
{
Date = date;
if ( link is not null )
{
- links = new() { link };
+ links.Add( link );
}
}
@@ -35,7 +37,11 @@ public SunsetPolicy() { }
/// Initializes a new instance of the class.
///
/// The link which provides information about the sunset policy.
- public SunsetPolicy( LinkHeaderValue link ) => links = new() { link };
+ public SunsetPolicy( LinkHeaderValue link )
+ : this()
+ {
+ links.Add( link );
+ }
///
/// Gets the date and time when the API version will be sunset.
@@ -47,7 +53,7 @@ public SunsetPolicy() { }
/// Gets a value indicating whether the sunset policy has any associated links.
///
/// True if the sunset policy has associated links; otherwise, false.
- public bool HasLinks => links is not null && links.Count > 0;
+ public bool HasLinks => links.Count > 0;
///
/// Gets a read-only list of links that provide information about the sunset policy.
@@ -56,23 +62,11 @@ public SunsetPolicy() { }
/// If a link is provided, generally only one link is necessary; however, additional
/// links might be provided for different languages or different formats such as a HTML page
/// or a JSON file.
- public IList Links => links ??= new();
+ public IList Links => links;
- private sealed class SunsetLinkList : Collection
+ internal sealed class SunsetLinkList : LinkList
{
- protected override void InsertItem( int index, LinkHeaderValue item )
- {
- base.InsertItem( index, item );
- EnsureRelationType( item );
- }
-
- protected override void SetItem( int index, LinkHeaderValue item )
- {
- base.SetItem( index, item );
- EnsureRelationType( item );
- }
-
- private static void EnsureRelationType( LinkHeaderValue item )
+ protected override void EnsureRelationType( LinkHeaderValue item )
{
if ( !item.RelationType.Equals( "sunset", StringComparison.OrdinalIgnoreCase ) )
{
diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
index bb3eed90..24399a61 100644
--- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
+++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs
@@ -566,6 +566,7 @@ private void PopulateActionDescriptions(
ApiVersion = apiVersion,
IsDeprecated = deprecated,
SunsetPolicy = SunsetPolicyManager.ResolvePolicyOrDefault( metadata.Name, apiVersion ),
+ DeprecationPolicy = DeprecationPolicyManager.ResolvePolicyOrDefault( metadata.Name, apiVersion ),
Properties = { [typeof( IEdmModel )] = routeBuilderContext.EdmModel },
};
diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
index c39e10b4..23aa1475 100644
--- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
+++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs
@@ -33,6 +33,9 @@ public async Task options_should_return_expected_headers()
resolver.AddService(
typeof( ISunsetPolicyManager ),
( sp, t ) => new SunsetPolicyManager( sp.GetRequiredService().GetApiVersioningOptions() ) );
+ resolver.AddService(
+ typeof( IDeprecationPolicyManager ),
+ ( sp, t ) => new DeprecationPolicyManager( sp.GetRequiredService().GetApiVersioningOptions() ) );
configuration.DependencyResolver = resolver;
configuration.AddApiVersioning(
options =>
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
index c7585844..ee868497 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs
@@ -33,6 +33,7 @@ public class VersionedApiExplorer : IApiExplorer
private readonly Lazy apiDescriptionsHolder;
private IDocumentationProvider? documentationProvider;
private ISunsetPolicyManager? sunsetPolicyManager;
+ private IDeprecationPolicyManager? deprecationPolicyManager;
///
/// Initializes a new instance of the class.
@@ -105,6 +106,16 @@ protected ISunsetPolicyManager SunsetPolicyManager
set => sunsetPolicyManager = value;
}
+ ///
+ /// Gets or sets the manager used to resolve deprecation policies for API descriptions.
+ ///
+ /// The configured deprecation policy manager.
+ protected IDeprecationPolicyManager DeprecationPolicyManager
+ {
+ get => deprecationPolicyManager ??= Configuration.GetDeprecationPolicyManager();
+ set => deprecationPolicyManager = value;
+ }
+
///
/// Gets a collection of HTTP methods supported by the action.
///
@@ -227,11 +238,13 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions()
}
var routes = FlattenRoutes( Configuration.Routes ).ToArray();
- var policyManager = Configuration.GetSunsetPolicyManager();
+ var sunsetPolicyManager = Configuration.GetSunsetPolicyManager();
+ var deprecationPolicyManager = Configuration.GetDeprecationPolicyManager();
foreach ( var apiVersion in FlattenApiVersions( controllerMappings ) )
{
- var sunsetPolicy = policyManager.TryGetPolicy( apiVersion, out var policy ) ? policy : default;
+ sunsetPolicyManager.TryGetPolicy( apiVersion, out var sunsetPolicy );
+ deprecationPolicyManager.TryGetPolicy( apiVersion, out var deprecationPolicy );
for ( var i = 0; i < routes.Length; i++ )
{
@@ -244,6 +257,7 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions()
ExploreRouteControllers( controllerMappings, route, apiVersion );
apiDescriptionGroup.SunsetPolicy = sunsetPolicy;
+ apiDescriptionGroup.DeprecationPolicy = deprecationPolicy;
// Remove ApiDescription that will lead to ambiguous action matching.
// E.g. a controller with Post() and PostComment(). When the route template is {controller}, it produces POST /controller and POST /controller.
@@ -878,6 +892,7 @@ private void PopulateActionDescriptions(
ApiVersion = apiVersion,
IsDeprecated = deprecated,
SunsetPolicy = SunsetPolicyManager.ResolvePolicyOrDefault( metadata.Name, apiVersion ),
+ DeprecationPolicy = DeprecationPolicyManager.ResolvePolicyOrDefault( metadata.Name, apiVersion ),
};
foreach ( var supportedResponseFormatter in supportedResponseFormatters )
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
index 469c44f5..a8ce4ccc 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs
@@ -51,6 +51,12 @@ public ApiDescriptionGroup( ApiVersion apiVersion, string name )
/// The defined sunset policy defined for the API, if any.
public SunsetPolicy? SunsetPolicy { get; set; }
+ ///
+ /// Gets or sets described API deprecation policy.
+ ///
+ /// The defined deprecation policy defined for the API, if any.
+ public DeprecationPolicy? DeprecationPolicy { get; set; }
+
///
/// Gets a collection of API descriptions for the current version.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
index da07e7d1..5087f286 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs
@@ -49,6 +49,12 @@ public ApiVersion ApiVersion
/// The defined sunset policy defined for the API, if any.
public SunsetPolicy? SunsetPolicy { get; set; }
+ ///
+ /// Gets or sets the described API deprecation policy.
+ ///
+ /// The defined deprecation policy defined for the API, if any.
+ public DeprecationPolicy? DeprecationPolicy { get; set; }
+
///
/// Gets or sets the response description.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs
index 7725393c..a57c55ac 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs
@@ -19,6 +19,7 @@ internal DefaultContainer()
container.AddService( typeof( IControllerNameConvention ), static ( sc, t ) => ControllerNameConvention.Default );
container.AddService( typeof( IProblemDetailsFactory ), static ( sc, t ) => new ProblemDetailsFactory() );
container.AddService( typeof( ISunsetPolicyManager ), NewSunsetPolicyManager );
+ container.AddService( typeof( IDeprecationPolicyManager ), NewDeprecationPolicyManager );
container.AddService( typeof( IReportApiVersions ), NewApiVersionReporter );
}
@@ -69,6 +70,9 @@ private static ApiVersioningOptions GetApiVersioningOptions( IServiceProvider se
private static ISunsetPolicyManager NewSunsetPolicyManager( IServiceProvider serviceProvider, Type type ) =>
new SunsetPolicyManager( GetApiVersioningOptions( serviceProvider ) );
+ private static IDeprecationPolicyManager NewDeprecationPolicyManager( IServiceProvider serviceProvider, Type type ) =>
+ new DeprecationPolicyManager( GetApiVersioningOptions( serviceProvider ) );
+
private static IReportApiVersions NewApiVersionReporter( IServiceProvider serviceProvider, Type type )
{
var options = GetApiVersioningOptions( serviceProvider );
@@ -76,7 +80,8 @@ private static IReportApiVersions NewApiVersionReporter( IServiceProvider servic
if ( options.ReportApiVersions )
{
var sunsetPolicyManager = (ISunsetPolicyManager) serviceProvider.GetService( typeof( ISunsetPolicyManager ) );
- return new DefaultApiVersionReporter( sunsetPolicyManager );
+ var deprecationPolicyManager = (IDeprecationPolicyManager) serviceProvider.GetService( typeof( IDeprecationPolicyManager ) );
+ return new DefaultApiVersionReporter( sunsetPolicyManager, deprecationPolicyManager );
}
return new DoNotReportApiVersions();
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
index 6cad7f56..2be9b722 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs
@@ -38,4 +38,8 @@ internal static IProblemDetailsFactory GetProblemDetailsFactory( this HttpConfig
internal static ISunsetPolicyManager GetSunsetPolicyManager( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService() ??
configuration.ApiVersioningServices().GetRequiredService();
+
+ internal static IDeprecationPolicyManager GetDeprecationPolicyManager( this HttpConfiguration configuration ) =>
+ configuration.DependencyResolver.GetService() ??
+ configuration.ApiVersioningServices().GetRequiredService();
}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DeprecationPolicyManager.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DeprecationPolicyManager.cs
new file mode 100644
index 00000000..2188db8c
--- /dev/null
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DeprecationPolicyManager.cs
@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Provides additional content specific to ASP.NET Web API.
+///
+public partial class DeprecationPolicyManager
+{
+ private readonly ApiVersioningOptions options;
+
+ ///
+ protected override ApiVersioningOptions Options => options;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The associated API versioning options.
+ public DeprecationPolicyManager( ApiVersioningOptions options ) => this.options = options;
+}
\ No newline at end of file
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
index 78af0cdf..c800e774 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs
@@ -9,6 +9,9 @@ public partial class SunsetPolicyManager
{
private readonly ApiVersioningOptions options;
+ ///
+ protected override ApiVersioningOptions Options => options;
+
///
/// Initializes a new instance of the class.
///
diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
index 2f9e1510..1f6ebc12 100644
--- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
+++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs
@@ -13,8 +13,11 @@ namespace System.Net.Http;
public static class HttpResponseMessageExtensions
{
private const string Sunset = nameof( Sunset );
+ private const string Deprecation = nameof( Deprecation );
private const string Link = nameof( Link );
+ private static readonly DateTime unixEpoch = new DateTime( 1970, 1, 1 );
+
///
/// Writes the sunset policy to the specified HTTP response.
///
@@ -35,6 +38,31 @@ public static void WriteSunsetPolicy( this HttpResponseMessage response, SunsetP
AddLinkHeaders( headers, sunsetPolicy.Links );
}
+ ///
+ /// Writes the sunset policy to the specified HTTP response.
+ ///
+ /// The HTTP response to write to.
+ /// The deprecation policy to write.
+ public static void WriteDeprecationPolicy( this HttpResponseMessage response, DeprecationPolicy deprecationPolicy )
+ {
+ ArgumentNullException.ThrowIfNull( response );
+ ArgumentNullException.ThrowIfNull( deprecationPolicy );
+
+ var headers = response.Headers;
+
+ if ( deprecationPolicy.Date.HasValue )
+ {
+ long unixTimestamp;
+ DateTimeOffset deprecationDate = deprecationPolicy.Date.Value;
+
+ unixTimestamp = (int) deprecationDate.Subtract( unixEpoch ).TotalSeconds;
+
+ headers.Add( Deprecation, $"@{unixTimestamp}" );
+ }
+
+ AddLinkHeaders( headers, deprecationPolicy.Links );
+ }
+
private static void AddLinkHeaders( HttpResponseHeaders headers, IList links )
{
var values = headers.TryGetValues( Link, out var existing )
diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
index 23e70e9e..c936feca 100644
--- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
+++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs
@@ -14,7 +14,8 @@ public void report_should_add_expected_headers()
{
// arrange
var sunsetDate = DateTimeOffset.Now;
- var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ) );
+ var deprecationDate = DateTimeOffset.Now;
+ var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ), new TestDeprecationPolicyManager( deprecationDate ) );
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
var response = new HttpResponseMessage( OK ) { RequestMessage = request };
@@ -50,16 +51,24 @@ public void report_should_add_expected_headers()
// assert
var headers = response.Headers;
+ long unixTimestamp = (int) deprecationDate.Subtract( new DateTime( 1970, 1, 1 ) ).TotalSeconds;
+
headers.GetValues( "api-supported-versions" ).Should().Equal( "1.0, 2.0" );
headers.GetValues( "api-deprecated-versions" ).Should().Equal( "0.9" );
headers.GetValues( "Sunset" )
.Single()
.Should()
.Be( sunsetDate.ToString( "r" ) );
- headers.GetValues( "Link" )
+ headers.GetValues( "Deprecation" )
.Single()
.Should()
- .Be( "; rel=\"sunset\"" );
+ .Be( $"@{unixTimestamp}" );
+ headers.GetValues( "Link" )
+ .Should()
+ .BeEquivalentTo( [
+ "; rel=\"sunset\"",
+ "; rel=\"deprecation\"",
+ ] );
}
private sealed class TestSunsetPolicyManager : ISunsetPolicyManager
@@ -73,7 +82,7 @@ public bool TryGetPolicy( string name, ApiVersion apiVersion, out SunsetPolicy s
{
if ( name == "Test" )
{
- var link = new LinkHeaderValue( new Uri( "http://docs.api.com/policy.html" ), "sunset" );
+ var link = new LinkHeaderValue( new Uri( "http://docs.api.com/sunset.html" ), "sunset" );
sunsetPolicy = new( sunsetDate, link );
return true;
}
@@ -82,4 +91,25 @@ public bool TryGetPolicy( string name, ApiVersion apiVersion, out SunsetPolicy s
return false;
}
}
+
+ private sealed class TestDeprecationPolicyManager : IDeprecationPolicyManager
+ {
+ private readonly DateTimeOffset deprecationDate;
+
+ public TestDeprecationPolicyManager( DateTimeOffset deprecationDate ) =>
+ this.deprecationDate = deprecationDate;
+
+ public bool TryGetPolicy( string name, ApiVersion apiVersion, out DeprecationPolicy deprecationPolicy )
+ {
+ if ( name == "Test" )
+ {
+ var link = new LinkHeaderValue( new Uri( "http://docs.api.com/deprecation.html" ), "deprecation" );
+ deprecationPolicy = new( deprecationDate, link );
+ return true;
+ }
+
+ deprecationPolicy = default;
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
index fdb4ff8d..18af2820 100644
--- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
+++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs
@@ -15,8 +15,8 @@ namespace Asp.Versioning.ApiExplorer;
using Microsoft.OData.Edm;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using static System.StringComparison;
using static ODataMetadataOptions;
+using static System.StringComparison;
using Opts = Microsoft.Extensions.Options.Options;
///
@@ -181,10 +181,11 @@ protected virtual void ExploreQueryOptions( IEnumerable apiDescr
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static int ApiVersioningOrder()
{
- var policyManager = new SunsetPolicyManager( Opts.Create( new ApiVersioningOptions() ) );
+ var sunsetPolicyManager = new SunsetPolicyManager( Opts.Create( new ApiVersioningOptions() ) );
+ var deprecationPolicyManager = new DeprecationPolicyManager( Opts.Create( new ApiVersioningOptions() ) );
var options = Opts.Create( new ApiExplorerOptions() );
var provider = new EmptyModelMetadataProvider();
- return new VersionedApiDescriptionProvider( policyManager, provider, options ).Order;
+ return new VersionedApiDescriptionProvider( sunsetPolicyManager, deprecationPolicyManager, provider, options ).Order;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionDescription.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionDescription.cs
index 897ed8f5..79d07314 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionDescription.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionDescription.cs
@@ -14,16 +14,19 @@ public class ApiVersionDescription
/// The group name for the API version.
/// Indicates whether the API version is deprecated.
/// The defined sunset policy, if any.
+ /// The defined deprecation policy, if any.
public ApiVersionDescription(
ApiVersion apiVersion,
string groupName,
bool deprecated = false,
- SunsetPolicy? sunsetPolicy = default )
+ SunsetPolicy? sunsetPolicy = default,
+ DeprecationPolicy? deprecationPolicy = default )
{
ApiVersion = apiVersion;
GroupName = groupName;
IsDeprecated = deprecated;
SunsetPolicy = sunsetPolicy;
+ DeprecationPolicy = deprecationPolicy;
}
///
@@ -54,4 +57,10 @@ public ApiVersionDescription(
///
/// The defined sunset policy defined for the API, if any.
public SunsetPolicy? SunsetPolicy { get; }
+
+ ///
+ /// Gets described API deprecation policy.
+ ///
+ /// The defined deprecation policy defined for the API, if any.
+ public DeprecationPolicy? DeprecationPolicy { get; }
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs
index f8948ab4..53bad7f3 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs
@@ -144,6 +144,7 @@ private static void AddApiVersioningServices( IServiceCollection services )
services.AddSingleton( static sp => sp.GetRequiredService>().Value.ApiVersionSelector );
services.TryAddSingleton();
services.TryAddSingleton();
+ services.TryAddSingleton();
services.TryAddEnumerable( Transient, ValidateApiVersioningOptions>() );
services.TryAddEnumerable( Transient, ApiVersioningRouteOptionsSetup>() );
services.TryAddEnumerable( Singleton() );
@@ -281,7 +282,7 @@ private static void TryAddErrorObjectJsonOptions( IServiceCollection services )
}
}
-// TEMP: this is a marker class to test whether Error Objects have been explicitly added. remove in 9.0+
+ // TEMP: this is a marker class to test whether Error Objects have been explicitly added. remove in 9.0+
#pragma warning disable CA1812 // Avoid uninstantiated internal classes
private sealed class ErrorObjectsAdded { }
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DeprecationPolicyManager.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DeprecationPolicyManager.cs
new file mode 100644
index 00000000..1778c21c
--- /dev/null
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DeprecationPolicyManager.cs
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using Microsoft.Extensions.Options;
+
+///
+/// Provides additional content specific to ASP.NET Core.
+///
+public partial class DeprecationPolicyManager
+{
+ private readonly IOptions options;
+
+ ///
+ protected override ApiVersioningOptions Options => options.Value;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The associated API versioning options.
+ public DeprecationPolicyManager( IOptions options ) => this.options = options;
+}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs
index 9b8ed20a..92fa841c 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs
@@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Http;
public static class HttpResponseExtensions
{
private const string Sunset = nameof( Sunset );
+ private const string Deprecation = nameof( Deprecation );
private const string Link = nameof( Link );
///
@@ -44,6 +45,40 @@ public static void WriteSunsetPolicy( this HttpResponse response, SunsetPolicy s
AddLinkHeaders( headers, sunsetPolicy.Links );
}
+ ///
+ /// Writes the deprecation policy to the specified HTTP response.
+ ///
+ /// The HTTP response to write to.
+ /// The deprecation policy to write.
+ [CLSCompliant( false )]
+ public static void WriteDeprecationPolicy( this HttpResponse response, DeprecationPolicy deprecationPolicy )
+ {
+ ArgumentNullException.ThrowIfNull( response );
+ ArgumentNullException.ThrowIfNull( deprecationPolicy );
+
+ var headers = response.Headers;
+
+ if ( headers.ContainsKey( Deprecation ) )
+ {
+ // the 'Deprecation' header is present, assume the headers have been written.
+ // this can happen when ApiVersioningOptions.ReportApiVersions = true
+ // and [ReportApiVersions] are both applied
+ return;
+ }
+
+ if ( deprecationPolicy.Date.HasValue )
+ {
+ long unixTimestamp;
+ DateTimeOffset deprecationDate = deprecationPolicy.Date.Value;
+
+ unixTimestamp = deprecationDate.ToUnixTimeSeconds();
+
+ headers[Deprecation] = $"@{unixTimestamp}";
+ }
+
+ AddLinkHeaders( headers, deprecationPolicy.Links );
+ }
+
private static void AddLinkHeaders( IHeaderDictionary headers, IList links )
{
var values = new string[links.Count];
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SunsetPolicyManager.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SunsetPolicyManager.cs
index aa16d930..ad3da6d2 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SunsetPolicyManager.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SunsetPolicyManager.cs
@@ -11,6 +11,9 @@ public partial class SunsetPolicyManager
{
private readonly IOptions options;
+ ///
+ protected override ApiVersioningOptions Options => options.Value;
+
///
/// Initializes a new instance of the class.
///
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs
index 30aaf0cd..7f17661f 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs
@@ -30,6 +30,13 @@ public static class ApiDescriptionExtensions
/// The defined sunset policy defined for the API or null.
public static SunsetPolicy? GetSunsetPolicy( this ApiDescription apiDescription ) => apiDescription.GetProperty();
+ ///
+ /// Gets the API deprecation policy associated with the API description, if any.
+ ///
+ /// The API description to get the deprecation policy for.
+ /// The defined deprecation policy defined for the API or null.
+ public static DeprecationPolicy? GetDeprecationPolicy( this ApiDescription apiDescription ) => apiDescription.GetProperty();
+
///
/// Gets a value indicating whether the associated API description is deprecated.
///
@@ -65,11 +72,20 @@ public static bool IsDeprecated( this ApiDescription apiDescription )
/// Sets the API sunset policy associated with the API description.
///
/// The API description to set the sunset policy for.
- /// The associated sunst policy.
+ /// The associated sunset policy.
/// This API is meant for infrastructure and should not be used by application code.
[EditorBrowsable( EditorBrowsableState.Never )]
public static void SetSunsetPolicy( this ApiDescription apiDescription, SunsetPolicy sunsetPolicy ) => apiDescription.SetProperty( sunsetPolicy );
+ ///
+ /// Sets the API deprecation policy associated with the API description.
+ ///
+ /// The API description to set the sunset policy for.
+ /// The associated deprecation policy.
+ /// This API is meant for infrastructure and should not be used by application code.
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static void SetDeprecationPolicy( this ApiDescription apiDescription, DeprecationPolicy deprecationPolicy ) => apiDescription.SetProperty( deprecationPolicy );
+
///
/// Attempts to update the relate path of the specified API description and remove the corresponding parameter according to the specified options.
///
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs
index cec78062..d8da7ef8 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs
@@ -12,17 +12,20 @@ namespace Microsoft.AspNetCore.Builder;
internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescriptionProviderFactory
{
private readonly ISunsetPolicyManager sunsetPolicyManager;
+ private readonly IDeprecationPolicyManager deprecationPolicyManager;
private readonly IApiVersionMetadataCollationProvider[] providers;
private readonly IEndpointInspector endpointInspector;
private readonly IOptions options;
public ApiVersionDescriptionProviderFactory(
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
IEnumerable providers,
IEndpointInspector endpointInspector,
IOptions options )
{
this.sunsetPolicyManager = sunsetPolicyManager;
+ this.deprecationPolicyManager = deprecationPolicyManager;
this.providers = providers.ToArray();
this.endpointInspector = endpointInspector;
this.options = options;
@@ -37,6 +40,6 @@ public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSou
collators.AddRange( providers );
- return new DefaultApiVersionDescriptionProvider( collators, sunsetPolicyManager, options );
+ return new DefaultApiVersionDescriptionProvider( collators, sunsetPolicyManager, deprecationPolicyManager, options );
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs
index 22151e11..1d264517 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs
@@ -20,15 +20,18 @@ public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvid
/// The sequence of
/// API version metadata collation providers..
/// The manager used to resolve sunset policies.
+ /// The manager used to resolve deprecation policies.
/// The container of configured
/// API explorer options.
public DefaultApiVersionDescriptionProvider(
IEnumerable providers,
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
IOptions apiExplorerOptions )
{
collection = new( Describe, providers ?? throw new ArgumentNullException( nameof( providers ) ) );
SunsetPolicyManager = sunsetPolicyManager;
+ DeprecationPolicyManager = deprecationPolicyManager;
options = apiExplorerOptions;
}
@@ -38,6 +41,12 @@ public DefaultApiVersionDescriptionProvider(
/// The associated sunset policy manager.
protected ISunsetPolicyManager SunsetPolicyManager { get; }
+ ///
+ /// Gets the manager used to resolve deprecation policies.
+ ///
+ /// The associated deprecation policy manager.
+ protected IDeprecationPolicyManager DeprecationPolicyManager { get; }
+
///
/// Gets the options associated with the API explorer.
///
@@ -64,7 +73,7 @@ protected virtual IReadOnlyList Describe( IReadOnlyList();
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
index c1a689fe..ee68d398 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs
@@ -61,6 +61,7 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder )
Transient(
static sp => new(
sp.GetRequiredService(),
+ sp.GetRequiredService(),
sp.GetRequiredService(),
sp.GetRequiredService(),
sp.GetRequiredService>() ) ) );
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs
index 294db52c..4b1be1c3 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs
@@ -20,15 +20,18 @@ public class GroupedApiVersionDescriptionProvider : IApiVersionDescriptionProvid
/// The sequence of
/// API version metadata collation providers..
/// The manager used to resolve sunset policies.
+ /// The manager used to resolve deprecation policies.
/// The container of configured
/// API explorer options.
public GroupedApiVersionDescriptionProvider(
IEnumerable providers,
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
IOptions apiExplorerOptions )
{
collection = new( Describe, providers ?? throw new ArgumentNullException( nameof( providers ) ) );
SunsetPolicyManager = sunsetPolicyManager;
+ DeprecationPolicyManager = deprecationPolicyManager;
options = apiExplorerOptions;
}
@@ -38,6 +41,12 @@ public GroupedApiVersionDescriptionProvider(
/// The associated sunset policy manager.
protected ISunsetPolicyManager SunsetPolicyManager { get; }
+ ///
+ /// Gets the manager used to resolve deprecation policies.
+ ///
+ /// The associated deprecation policy manager.
+ protected IDeprecationPolicyManager DeprecationPolicyManager { get; }
+
///
/// Gets the options associated with the API explorer.
///
@@ -57,7 +66,7 @@ public GroupedApiVersionDescriptionProvider(
protected virtual IReadOnlyList Describe( IReadOnlyList metadata )
{
ArgumentNullException.ThrowIfNull( metadata );
- return DescriptionProvider.Describe( metadata, SunsetPolicyManager, Options );
+ return DescriptionProvider.Describe( metadata, SunsetPolicyManager, DeprecationPolicyManager, Options );
}
///
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs
index ce3a0dbd..ce24ca77 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs
@@ -10,6 +10,7 @@ internal static class DescriptionProvider
internal static ApiVersionDescription[] Describe(
IReadOnlyList metadata,
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
ApiExplorerOptions options )
where T : IGroupedApiVersionMetadata, IEquatable
{
@@ -18,8 +19,8 @@ internal static ApiVersionDescription[] Describe(
var deprecated = new HashSet();
BucketizeApiVersions( metadata, supported, deprecated, options );
- AppendDescriptions( descriptions, supported, sunsetPolicyManager, options, deprecated: false );
- AppendDescriptions( descriptions, deprecated, sunsetPolicyManager, options, deprecated: true );
+ AppendDescriptions( descriptions, supported, sunsetPolicyManager, deprecationPolicyManager, options, deprecated: false );
+ AppendDescriptions( descriptions, deprecated, sunsetPolicyManager, deprecationPolicyManager, options, deprecated: true );
return [.. descriptions];
}
@@ -81,6 +82,7 @@ private static void AppendDescriptions(
SortedSet descriptions,
HashSet versions,
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
ApiExplorerOptions options,
bool deprecated )
{
@@ -100,8 +102,10 @@ private static void AppendDescriptions(
formattedGroupName = formatGroupName( formattedGroupName, version.ToString( format, CurrentCulture ) );
}
- var sunsetPolicy = sunsetPolicyManager.TryGetPolicy( version, out var policy ) ? policy : default;
- descriptions.Add( new( version, formattedGroupName, deprecated, sunsetPolicy ) );
+ sunsetPolicyManager.TryGetPolicy( version, out var sunsetPolicy );
+ deprecationPolicyManager.TryGetPolicy( version, out var deprecationPolicy );
+
+ descriptions.Add( new( version, formattedGroupName, deprecated, sunsetPolicy, deprecationPolicy ) );
}
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs
index e1f22177..e121ec85 100644
--- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs
+++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs
@@ -27,15 +27,18 @@ public class VersionedApiDescriptionProvider : IApiDescriptionProvider
/// Initializes a new instance of the class.
///
/// The manager used to resolve sunset policies.
+ /// The manager used to resolve deprecation policies.
/// The provider used to retrieve model metadata.
/// The container of configured
/// API explorer options.
public VersionedApiDescriptionProvider(
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
IModelMetadataProvider modelMetadataProvider,
IOptions options )
: this(
sunsetPolicyManager,
+ deprecationPolicyManager,
modelMetadataProvider,
new SimpleConstraintResolver( options ?? throw new ArgumentNullException( nameof( options ) ) ),
options )
@@ -46,11 +49,13 @@ public VersionedApiDescriptionProvider(
// BUG: https://github.com/dotnet/aspnetcore/issues/41773
internal VersionedApiDescriptionProvider(
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
IModelMetadataProvider modelMetadataProvider,
IInlineConstraintResolver constraintResolver,
IOptions options )
{
SunsetPolicyManager = sunsetPolicyManager;
+ DeprecationPolicyManager = deprecationPolicyManager;
ModelMetadataProvider = modelMetadataProvider;
this.constraintResolver = constraintResolver;
this.options = options;
@@ -68,6 +73,12 @@ internal VersionedApiDescriptionProvider(
/// The associated sunset policy manager.
protected ISunsetPolicyManager SunsetPolicyManager { get; }
+ ///
+ /// Gets the manager used to resolve deprecation policies.
+ ///
+ /// The associated deprecation policy manager.
+ protected IDeprecationPolicyManager DeprecationPolicyManager { get; }
+
///
/// Gets the options associated with the API explorer.
///
@@ -170,9 +181,14 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
groupResult.GroupName = formatGroupName( groupResult.GroupName, formattedVersion );
}
- if ( SunsetPolicyManager.TryResolvePolicy( metadata.Name, version, out var policy ) )
+ if ( SunsetPolicyManager.TryResolvePolicy( metadata.Name, version, out var sunsetPolicy ) )
+ {
+ groupResult.SetSunsetPolicy( sunsetPolicy );
+ }
+
+ if ( DeprecationPolicyManager.TryResolvePolicy( metadata.Name, version, out var deprecationPolicy ) )
{
- groupResult.SetSunsetPolicy( policy );
+ groupResult.SetDeprecationPolicy( deprecationPolicy );
}
groupResult.SetApiVersion( version );
diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs
index 9125c9ae..acec1184 100644
--- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs
+++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs
@@ -13,7 +13,8 @@ public void report_should_add_expected_headers()
{
// arrange
var sunsetDate = DateTimeOffset.Now;
- var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ) );
+ var deprecationDate = DateTimeOffset.Now;
+ var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ), new TestDeprecationPolicyManager( deprecationDate ) );
var httpContext = new Mock();
var features = new Mock();
var query = new Mock();
@@ -60,14 +61,21 @@ public void report_should_add_expected_headers()
reporter.Report( response.Object, model );
// assert
+ long unixTimestamp = (int) deprecationDate.Subtract( new DateTime( 1970, 1, 1 ) ).TotalSeconds;
+
headers["api-supported-versions"].Should().Equal( "1.0, 2.0" );
headers["api-deprecated-versions"].Should().Equal( "0.9" );
headers["Sunset"].Single()
.Should()
.Be( sunsetDate.ToString( "r" ) );
- headers["Link"].Single()
- .Should()
- .Be( "; rel=\"sunset\"" );
+ headers["Deprecation"].Single()
+ .Should()
+ .Be( $"@{unixTimestamp}" );
+ headers["Link"].Should()
+ .BeEquivalentTo( [
+ "; rel=\"sunset\"",
+ "; rel=\"deprecation\"",
+ ] );
}
private sealed class TestSunsetPolicyManager : ISunsetPolicyManager
@@ -81,7 +89,7 @@ public bool TryGetPolicy( string name, ApiVersion apiVersion, out SunsetPolicy s
{
if ( name == "Test" )
{
- var link = new LinkHeaderValue( new Uri( "http://docs.api.com/policy.html" ), "sunset" );
+ var link = new LinkHeaderValue( new Uri( "http://docs.api.com/sunset.html" ), "sunset" );
sunsetPolicy = new( sunsetDate, link );
return true;
}
@@ -90,4 +98,25 @@ public bool TryGetPolicy( string name, ApiVersion apiVersion, out SunsetPolicy s
return false;
}
}
+
+ private sealed class TestDeprecationPolicyManager : IDeprecationPolicyManager
+ {
+ private readonly DateTimeOffset deprecationDate;
+
+ public TestDeprecationPolicyManager( DateTimeOffset deprecationDate ) =>
+ this.deprecationDate = deprecationDate;
+
+ public bool TryGetPolicy( string name, ApiVersion apiVersion, out DeprecationPolicy deprecationPolicy )
+ {
+ if ( name == "Test" )
+ {
+ var link = new LinkHeaderValue( new Uri( "http://docs.api.com/deprecation.html" ), "deprecation" );
+ deprecationPolicy = new( deprecationDate, link );
+ return true;
+ }
+
+ deprecationPolicy = default;
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/DefaultApiVersionDescriptionProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/DefaultApiVersionDescriptionProviderTest.cs
index 24460974..28e55afc 100644
--- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/DefaultApiVersionDescriptionProviderTest.cs
+++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/DefaultApiVersionDescriptionProviderTest.cs
@@ -17,6 +17,7 @@ public void api_version_descriptions_should_collate_expected_versions()
new ActionApiVersionMetadataCollationProvider( new TestActionDescriptorCollectionProvider() ),
},
Mock.Of(),
+ Mock.Of(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
// act
@@ -50,6 +51,7 @@ public void api_version_descriptions_should_apply_sunset_policy()
new ActionApiVersionMetadataCollationProvider( new TestActionDescriptorCollectionProvider() ),
},
policyManager.Object,
+ Mock.Of(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
// act
diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs
index 9be0191f..f00bb8c4 100644
--- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs
+++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs
@@ -20,6 +20,7 @@ public void api_version_descriptions_should_collate_expected_versions()
new ActionApiVersionMetadataCollationProvider( new TestActionDescriptorCollectionProvider() ),
},
Mock.Of(),
+ Mock.Of(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
// act
@@ -56,6 +57,7 @@ public void api_version_descriptions_should_collate_expected_versions_with_custo
new ActionApiVersionMetadataCollationProvider( provider ),
},
Mock.Of(),
+ Mock.Of(),
Options.Create(
new ApiExplorerOptions()
{
@@ -94,6 +96,7 @@ public void api_version_descriptions_should_apply_sunset_policy()
new ActionApiVersionMetadataCollationProvider( new TestActionDescriptorCollectionProvider() ),
},
policyManager.Object,
+ Mock.Of(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
// act
diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs
index 8af044ae..5870c4f7 100644
--- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs
+++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs
@@ -18,6 +18,7 @@ public void versioned_api_explorer_should_group_and_order_descriptions_on_provid
var context = new ApiDescriptionProviderContext( actionProvider.ActionDescriptors.Items );
var apiExplorer = new VersionedApiDescriptionProvider(
Mock.Of(),
+ Mock.Of(),
NewModelMetadataProvider(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
@@ -73,6 +74,7 @@ public void versioned_api_explorer_should_apply_sunset_policy()
var apiExplorer = new VersionedApiDescriptionProvider(
policyManager.Object,
+ Mock.Of(),
NewModelMetadataProvider(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
@@ -103,6 +105,7 @@ public void versioned_api_explorer_should_preserve_group_name()
var context = new ApiDescriptionProviderContext( actionProvider.ActionDescriptors.Items );
var apiExplorer = new VersionedApiDescriptionProvider(
Mock.Of(),
+ Mock.Of(),
NewModelMetadataProvider(),
Options.Create( new ApiExplorerOptions() ) );
@@ -133,6 +136,7 @@ public void versioned_api_explorer_should_use_custom_group_name()
};
var apiExplorer = new VersionedApiDescriptionProvider(
Mock.Of(),
+ Mock.Of(),
NewModelMetadataProvider(),
Options.Create( options ) );
@@ -214,6 +218,7 @@ public void versioned_api_explorer_should_prefer_explicit_over_implicit_action_m
var apiExplorer = new VersionedApiDescriptionProvider(
Mock.Of(),
+ Mock.Of(),
NewModelMetadataProvider(),
Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) );
diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs
index e06a3e0c..627b593a 100644
--- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs
+++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs
@@ -84,7 +84,7 @@ private static ActionExecutingContext CreateContext(
var controller = default( object );
var endpoint = new Endpoint( c => Task.CompletedTask, new( new[] { metadata } ), "Test" );
var options = Options.Create( new ApiVersioningOptions() );
- var reporter = new DefaultApiVersionReporter( new SunsetPolicyManager( options ) );
+ var reporter = new DefaultApiVersionReporter( new SunsetPolicyManager( options ), new DeprecationPolicyManager( options ) );
endpointFeature.SetupProperty( f => f.Endpoint, endpoint );
versioningFeature.SetupProperty( f => f.RequestedApiVersion, new ApiVersion( 1.0 ) );
diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs
index cc75c6e5..53046eab 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs
@@ -15,16 +15,19 @@ public class ApiInformation
/// The supported read-only list of API versions.
/// The deprecated read-only list of API versions.
/// The API sunset policy.
+ /// The API deprecation policy.
/// The read-only mapping of API version to OpenAPI document URLs.
public ApiInformation(
IReadOnlyList supportedVersions,
IReadOnlyList deprecatedVersions,
SunsetPolicy sunsetPolicy,
+ DeprecationPolicy deprecationPolicy,
IReadOnlyDictionary openApiDocumentUrls )
{
SupportedApiVersions = supportedVersions ?? throw new System.ArgumentNullException( nameof( supportedVersions ) );
DeprecatedApiVersions = deprecatedVersions ?? throw new System.ArgumentNullException( nameof( deprecatedVersions ) );
SunsetPolicy = sunsetPolicy ?? throw new System.ArgumentNullException( nameof( sunsetPolicy ) );
+ DeprecationPolicy = deprecationPolicy ?? throw new System.ArgumentNullException( nameof( deprecationPolicy ) );
OpenApiDocumentUrls = openApiDocumentUrls ?? throw new System.ArgumentNullException( nameof( openApiDocumentUrls ) );
}
@@ -33,6 +36,7 @@ private ApiInformation()
SupportedApiVersions = Array.Empty();
DeprecatedApiVersions = Array.Empty();
SunsetPolicy = new();
+ DeprecationPolicy = new();
OpenApiDocumentUrls = new Dictionary( capacity: 0 );
}
@@ -62,6 +66,12 @@ private ApiInformation()
/// The sunset policy for the API.
public SunsetPolicy SunsetPolicy { get; }
+ ///
+ /// Gets the API deprecation policy.
+ ///
+ /// The deprecation policy for the API.
+ public DeprecationPolicy DeprecationPolicy { get; }
+
///
/// Gets the OpenAPI document URLs for each version.
///
diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs
index 449e249d..ab16c3f7 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs
@@ -8,6 +8,7 @@ namespace Asp.Versioning.Http;
public class ApiNotificationContext
{
private SunsetPolicy? sunsetPolicy;
+ private DeprecationPolicy? deprecationPolicy;
///
/// Initializes a new instance of the class.
@@ -37,4 +38,10 @@ public ApiNotificationContext( HttpResponseMessage response, ApiVersion apiVersi
///
/// The reported API sunset policy.
public SunsetPolicy SunsetPolicy => sunsetPolicy ??= Response.ReadSunsetPolicy();
+
+ ///
+ /// Gets the API deprecation policy reported in the response.
+ ///
+ /// The reported API deprecation policy.
+ public DeprecationPolicy DeprecationPolicy => deprecationPolicy ??= Response.ReadDeprecationPolicy();
}
\ No newline at end of file
diff --git a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs
index b42c2d28..974a5db0 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs
@@ -94,8 +94,9 @@ public static async Task GetApiInformationAsync(
var deprecated = versions.ToArray();
var sunsetPolicy = response.ReadSunsetPolicy();
+ var deprecationPolicy = response.ReadDeprecationPolicy();
var urls = response.GetOpenApiDocumentUrls( parser );
- return new( supported, deprecated, sunsetPolicy, urls );
+ return new( supported, deprecated, sunsetPolicy, deprecationPolicy, urls );
}
}
\ No newline at end of file
diff --git a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs
index 0a430a32..70be4409 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs
@@ -15,6 +15,7 @@ namespace System.Net.Http;
public static class HttpResponseMessageExtensions
{
private const string Sunset = nameof( Sunset );
+ private const string Deprecation = nameof( Deprecation );
private const string Link = nameof( Link );
///
@@ -69,6 +70,67 @@ public static SunsetPolicy ReadSunsetPolicy( this HttpResponseMessage response )
return policy;
}
+ ///
+ /// Gets an API deprecation policy from the HTTP response.
+ ///
+ /// The HTTP response to read from.
+ /// A new deprecation policy.
+ public static DeprecationPolicy ReadDeprecationPolicy( this HttpResponseMessage response )
+ {
+ ArgumentNullException.ThrowIfNull( response );
+
+ var headers = response.Headers;
+ var date = default( DateTimeOffset );
+ DeprecationPolicy policy;
+
+ if ( headers.TryGetValues( Deprecation, out var values ) )
+ {
+ var culture = CultureInfo.CurrentCulture;
+
+ foreach ( var value in values )
+ {
+ var split = value.Trim( '@' );
+ if ( long.TryParse( split, out var unixTimestamp ) )
+ {
+ DateTimeOffset parsed;
+#if NETSTANDARD
+ parsed = new DateTime(1970, 1, 1) + TimeSpan.FromSeconds(unixTimestamp);
+#else
+ parsed = DateTimeOffset.FromUnixTimeSeconds( unixTimestamp );
+#endif
+
+ if ( date == default || date < parsed )
+ {
+ date = parsed;
+ }
+ }
+ }
+
+ policy = date == default ? new() : new( date );
+ }
+ else
+ {
+ policy = new();
+ }
+
+ if ( headers.TryGetValues( Link, out values ) )
+ {
+ var baseUrl = response.RequestMessage?.RequestUri;
+ Func resolver = baseUrl is null ? url => url : url => new( baseUrl, url );
+
+ foreach ( var value in values )
+ {
+ if ( LinkHeaderValue.TryParse( value, resolver, out var link ) &&
+ link.RelationType.Equals( "deprecation", OrdinalIgnoreCase ) )
+ {
+ policy.Links.Add( link );
+ }
+ }
+ }
+
+ return policy;
+ }
+
///
/// Gets the OpenAPI document URLs from the HTTP response.
///
diff --git a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs
index a7535807..77085d1a 100644
--- a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs
+++ b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs
@@ -19,7 +19,7 @@ internal static void ApiVersionDeprecated(
SunsetPolicy sunsetPolicy )
{
var sunsetDate = FormatDate( sunsetPolicy.Date );
- var additionalInfo = FormatLinks( sunsetPolicy );
+ var additionalInfo = FormatLinks( sunsetPolicy.Links );
ApiVersionDeprecated(
logger,
@@ -46,7 +46,7 @@ internal static void NewApiVersionAvailable(
SunsetPolicy sunsetPolicy )
{
var sunsetDate = FormatDate( sunsetPolicy.Date );
- var additionalInfo = FormatLinks( sunsetPolicy );
+ var additionalInfo = FormatLinks( sunsetPolicy.Links );
NewApiVersionAvailable(
logger,
@@ -70,16 +70,10 @@ static partial void NewApiVersionAvailable(
private static string FormatDate( DateTimeOffset? date ) =>
date.HasValue ? date.Value.ToString( CultureInfo.CurrentCulture ) : "";
- private static string[] FormatLinks( SunsetPolicy sunsetPolicy )
+ private static string[] FormatLinks( IList links )
{
- if ( !sunsetPolicy.HasLinks )
- {
- return [];
- }
-
// ([,]):
var text = new StringBuilder();
- var links = sunsetPolicy.Links;
var additionalInfo = new string[links.Count];
for ( var i = 0; i < links.Count; i++ )
diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/System.Net.Http/HttpClientExtensionsTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/System.Net.Http/HttpClientExtensionsTest.cs
index 6ac1890a..b64a396e 100644
--- a/src/Client/test/Asp.Versioning.Http.Client.Tests/System.Net.Http/HttpClientExtensionsTest.cs
+++ b/src/Client/test/Asp.Versioning.Http.Client.Tests/System.Net.Http/HttpClientExtensionsTest.cs
@@ -41,6 +41,7 @@ public async Task get_api_information_async_should_return_expected_result()
{
Type = "text/html",
} ),
+ new(),
new Dictionary() { [new( 1.0 )] = new( "http://tempuri.org/swagger/v1/swagger.json" ) } ) );
}
}
\ No newline at end of file
diff --git a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs
index 2eb50a29..d9b2b531 100644
--- a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs
+++ b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs
@@ -10,13 +10,18 @@ namespace Asp.Versioning;
public class ApiVersioningPolicyBuilder : IApiVersioningPolicyBuilder
{
private Dictionary? sunsetPolicies;
+ private Dictionary? deprecationPolicies;
///
public virtual IReadOnlyList OfType() where T : notnull
{
if ( typeof( T ) == typeof( ISunsetPolicyBuilder ) && sunsetPolicies != null )
{
- return ( sunsetPolicies.Values.ToArray() as IReadOnlyList )!;
+ return sunsetPolicies.Values.Cast().ToArray();
+ }
+ else if ( typeof( T ) == typeof( IDeprecationPolicyBuilder ) && deprecationPolicies != null )
+ {
+ return deprecationPolicies.Values.Cast().ToArray();
}
return Array.Empty();
@@ -42,4 +47,25 @@ public virtual ISunsetPolicyBuilder Sunset( string? name, ApiVersion? apiVersion
return builder;
}
+
+ ///
+ public virtual IDeprecationPolicyBuilder Deprecate( string? name, ApiVersion? apiVersion )
+ {
+ if ( string.IsNullOrEmpty( name ) && apiVersion == null )
+ {
+ var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) );
+ throw new System.ArgumentException( message );
+ }
+
+ var key = new PolicyKey( name, apiVersion );
+
+ deprecationPolicies ??= [];
+
+ if ( !deprecationPolicies.TryGetValue( key, out var builder ) )
+ {
+ deprecationPolicies.Add( key, builder = new DeprecationPolicyBuilder( name, apiVersion ) );
+ }
+
+ return builder;
+ }
}
\ No newline at end of file
diff --git a/src/Common/src/Common/DefaultApiVersionReporter.cs b/src/Common/src/Common/DefaultApiVersionReporter.cs
index 72ed1408..b8486fb5 100644
--- a/src/Common/src/Common/DefaultApiVersionReporter.cs
+++ b/src/Common/src/Common/DefaultApiVersionReporter.cs
@@ -23,6 +23,7 @@ public sealed partial class DefaultApiVersionReporter : IReportApiVersions
private const string Sunset = nameof( Sunset );
private const string Link = nameof( Link );
private readonly ISunsetPolicyManager sunsetPolicyManager;
+ private readonly IDeprecationPolicyManager deprecationPolicyManager;
private readonly string apiSupportedVersionsName;
private readonly string apiDeprecatedVersionsName;
@@ -30,6 +31,7 @@ public sealed partial class DefaultApiVersionReporter : IReportApiVersions
/// Initializes a new instance of the class.
///
/// The manager used to resolve sunset policies.
+ /// The manager used to resolve deprecation policies.
/// The HTTP header name used for supported API versions.
/// The default value is "api-supported-versions".
/// THe HTTP header name used for deprecated API versions.
@@ -38,6 +40,7 @@ public sealed partial class DefaultApiVersionReporter : IReportApiVersions
/// and .
public DefaultApiVersionReporter(
ISunsetPolicyManager sunsetPolicyManager,
+ IDeprecationPolicyManager deprecationPolicyManager,
string supportedHeaderName = ApiSupportedVersions,
string deprecatedHeaderName = ApiDeprecatedVersions,
ApiVersionMapping mapping = Explicit | Implicit )
@@ -47,6 +50,7 @@ public DefaultApiVersionReporter(
ArgumentException.ThrowIfNullOrEmpty( deprecatedHeaderName );
this.sunsetPolicyManager = sunsetPolicyManager;
+ this.deprecationPolicyManager = deprecationPolicyManager;
apiSupportedVersionsName = supportedHeaderName;
apiDeprecatedVersionsName = deprecatedHeaderName;
Mapping = mapping;
@@ -91,9 +95,14 @@ public void Report( HttpResponse response, ApiVersionModel apiVersionModel )
#endif
var name = metadata.Name;
- if ( sunsetPolicyManager.TryResolvePolicy( name, version, out var policy ) )
+ if ( sunsetPolicyManager.TryResolvePolicy( name, version, out var sunsetPolicy ) )
{
- response.WriteSunsetPolicy( policy );
+ response.WriteSunsetPolicy( sunsetPolicy );
+ }
+
+ if ( deprecationPolicyManager.TryResolvePolicy( name, version, out var deprecationPolicy ) )
+ {
+ response.WriteDeprecationPolicy( deprecationPolicy );
}
}
}
\ No newline at end of file
diff --git a/src/Common/src/Common/DeprecationPolicyBuilder.cs b/src/Common/src/Common/DeprecationPolicyBuilder.cs
new file mode 100644
index 00000000..639042f5
--- /dev/null
+++ b/src/Common/src/Common/DeprecationPolicyBuilder.cs
@@ -0,0 +1,100 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Represents the default deprecation policy builder.
+///
+public class DeprecationPolicyBuilder : PolicyBuilder, IDeprecationPolicyBuilder
+{
+ private DateTimeOffset? date;
+ private DeprecationLinkBuilder? linkBuilder;
+ private Dictionary? linkBuilders;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the API the policy is for.
+ /// The API version the policy is for.
+ public DeprecationPolicyBuilder( string? name, ApiVersion? apiVersion )
+ : base( name, apiVersion ) { }
+
+ ///
+ public virtual IDeprecationPolicyBuilder Effective( DateTimeOffset deprecationDate )
+ {
+ date = deprecationDate;
+ return this;
+ }
+
+ ///
+ public virtual ILinkBuilder Link( Uri linkTarget )
+ {
+ DeprecationLinkBuilder newLinkBuilder;
+
+ if ( linkBuilder == null )
+ {
+ linkBuilder = newLinkBuilder = new( this, linkTarget );
+ }
+ else if ( linkBuilder.LinkTarget.Equals( linkTarget ) )
+ {
+ return linkBuilder;
+ }
+ else if ( linkBuilders == null )
+ {
+ linkBuilders = new()
+ {
+ [linkBuilder.LinkTarget] = linkBuilder,
+ [linkTarget] = newLinkBuilder = new( this, linkTarget ),
+ };
+ }
+ else if ( !linkBuilders.TryGetValue( linkTarget, out newLinkBuilder! ) )
+ {
+ linkBuilders.Add( linkTarget, newLinkBuilder = new( this, linkTarget ) );
+ }
+
+ return newLinkBuilder;
+ }
+
+ ///
+ public override DeprecationPolicy Build()
+ {
+ if ( Policy is not null )
+ {
+ return Policy;
+ }
+
+ DeprecationPolicy policy = date is null ? new() : new( date.Value );
+
+ if ( linkBuilders == null )
+ {
+ if ( linkBuilder != null )
+ {
+ policy.Links.Add( linkBuilder.Build() );
+ }
+ }
+ else
+ {
+ foreach ( var builder in linkBuilders.Values )
+ {
+ policy.Links.Add( builder.Build() );
+ }
+ }
+
+ return policy;
+ }
+
+ private sealed class DeprecationLinkBuilder : LinkBuilder, ILinkBuilder
+ {
+ protected override string RelationType => "deprecation";
+
+ private readonly DeprecationPolicyBuilder policyBuilder;
+
+ public DeprecationLinkBuilder( DeprecationPolicyBuilder policy, Uri linkTarget )
+ : base( linkTarget )
+ {
+ policyBuilder = policy;
+ }
+
+ public override ILinkBuilder Link( Uri linkTarget ) => policyBuilder.Link( linkTarget );
+ }
+}
\ No newline at end of file
diff --git a/src/Common/src/Common/DeprecationPolicyManager.cs b/src/Common/src/Common/DeprecationPolicyManager.cs
new file mode 100644
index 00000000..503b4608
--- /dev/null
+++ b/src/Common/src/Common/DeprecationPolicyManager.cs
@@ -0,0 +1,9 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+/// Represents the default API version sunset policy manager.
+///
+public partial class DeprecationPolicyManager : PolicyManager, IDeprecationPolicyManager
+{ }
\ No newline at end of file
diff --git a/src/Common/src/Common/SunsetLinkBuilder.cs b/src/Common/src/Common/LinkBuilder.cs
similarity index 80%
rename from src/Common/src/Common/SunsetLinkBuilder.cs
rename to src/Common/src/Common/LinkBuilder.cs
index 03f72175..16075de3 100644
--- a/src/Common/src/Common/SunsetLinkBuilder.cs
+++ b/src/Common/src/Common/LinkBuilder.cs
@@ -2,17 +2,16 @@
namespace Asp.Versioning;
-internal sealed class SunsetLinkBuilder : ILinkBuilder
+internal abstract class LinkBuilder : ILinkBuilder
{
- private readonly SunsetPolicyBuilder policy;
+ protected abstract string RelationType { get; }
private string? language;
private List? languages;
private string? title;
private string? type;
- public SunsetLinkBuilder( SunsetPolicyBuilder policy, Uri linkTarget )
+ public LinkBuilder( Uri linkTarget )
{
- this.policy = policy;
LinkTarget = linkTarget;
}
@@ -36,8 +35,6 @@ public ILinkBuilder Language( string value )
return this;
}
- public ILinkBuilder Link( Uri linkTarget ) => policy.Link( linkTarget );
-
public ILinkBuilder Title( string value )
{
title = value;
@@ -50,9 +47,11 @@ public ILinkBuilder Type( string value )
return this;
}
+ public abstract ILinkBuilder Link( Uri linkTarget );
+
public LinkHeaderValue Build()
{
- var link = new LinkHeaderValue( LinkTarget, "sunset" );
+ var link = new LinkHeaderValue( LinkTarget, RelationType );
if ( title != null )
{
diff --git a/src/Common/src/Common/PolicyBuilder.cs b/src/Common/src/Common/PolicyBuilder.cs
new file mode 100644
index 00000000..a493d906
--- /dev/null
+++ b/src/Common/src/Common/PolicyBuilder.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+using System.Globalization;
+
+///
+/// Represents the default policy builder.
+///
+/// The type of policy.
+public abstract class PolicyBuilder : IPolicyBuilder
+{
+ ///
+ /// Gets a pre-built policy.
+ ///
+ /// The pre-built policy, if it exists.
+ protected TPolicy? Policy { get; private set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the API the policy is for.
+ /// The API version the policy is for.
+ protected PolicyBuilder( string? name, ApiVersion? apiVersion )
+ {
+ if ( string.IsNullOrEmpty( name ) && apiVersion == null )
+ {
+ var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) );
+ throw new System.ArgumentException( message );
+ }
+
+ Name = name;
+ ApiVersion = apiVersion;
+ }
+
+ ///
+ public string? Name { get; }
+
+ ///
+ public ApiVersion? ApiVersion { get; }
+
+ ///
+ public virtual void Per( TPolicy policy ) =>
+ Policy = policy ?? throw new System.ArgumentNullException( nameof( policy ) );
+
+ ///
+ public abstract TPolicy Build();
+}
\ No newline at end of file
diff --git a/src/Common/src/Common/PolicyManager.cs b/src/Common/src/Common/PolicyManager.cs
new file mode 100644
index 00000000..ee06951f
--- /dev/null
+++ b/src/Common/src/Common/PolicyManager.cs
@@ -0,0 +1,52 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+
+namespace Asp.Versioning;
+
+///
+public abstract class PolicyManager : IPolicyManager
+ where TPolicyBuilder : IPolicyBuilder
+{
+ private Dictionary? policies;
+
+ ///
+ /// Gets the current api versioning options.
+ ///
+ /// The api versioning options.
+ protected abstract ApiVersioningOptions Options { get; }
+
+ ///
+ public virtual bool TryGetPolicy(
+ string? name,
+ ApiVersion? apiVersion,
+ [MaybeNullWhen( false )] out TPolicy policy )
+ {
+ if ( string.IsNullOrEmpty( name ) && apiVersion == null )
+ {
+ policy = default!;
+ return false;
+ }
+
+ policies ??= BuildPolicies( Options );
+
+ var key = new PolicyKey( name, apiVersion );
+
+ return policies.TryGetValue( key, out policy );
+ }
+
+ private static Dictionary BuildPolicies( ApiVersioningOptions options )
+ {
+ var builders = options.Policies.OfType();
+ var mapping = new Dictionary( capacity: builders.Count );
+
+ for ( var i = 0; i < builders.Count; i++ )
+ {
+ var builder = builders[i];
+ var policy = builder.Build();
+ var key = new PolicyKey( builder.Name, builder.ApiVersion );
+
+ mapping[key] = policy;
+ }
+
+ return mapping;
+ }
+}
\ No newline at end of file
diff --git a/src/Common/src/Common/SunsetPolicyBuilder.cs b/src/Common/src/Common/SunsetPolicyBuilder.cs
index a6450b49..71aaef9d 100644
--- a/src/Common/src/Common/SunsetPolicyBuilder.cs
+++ b/src/Common/src/Common/SunsetPolicyBuilder.cs
@@ -2,14 +2,11 @@
namespace Asp.Versioning;
-using System.Globalization;
-
///
/// Represents the default sunset policy builder.
///
-public class SunsetPolicyBuilder : ISunsetPolicyBuilder
+public class SunsetPolicyBuilder : PolicyBuilder, ISunsetPolicyBuilder
{
- private SunsetPolicy? sunsetPolicy;
private DateTimeOffset? date;
private SunsetLinkBuilder? linkBuilder;
private Dictionary? linkBuilders;
@@ -20,26 +17,7 @@ public class SunsetPolicyBuilder : ISunsetPolicyBuilder
/// The name of the API the policy is for.
/// The API version the policy is for.
public SunsetPolicyBuilder( string? name, ApiVersion? apiVersion )
- {
- if ( string.IsNullOrEmpty( name ) && apiVersion == null )
- {
- var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) );
- throw new System.ArgumentException( message );
- }
-
- Name = name;
- ApiVersion = apiVersion;
- }
-
- ///
- public string? Name { get; }
-
- ///
- public ApiVersion? ApiVersion { get; }
-
- ///
- public virtual void Per( SunsetPolicy policy ) =>
- sunsetPolicy = policy ?? throw new System.ArgumentNullException( nameof( policy ) );
+ : base( name, apiVersion ) { }
///
public virtual ISunsetPolicyBuilder Effective( DateTimeOffset sunsetDate )
@@ -78,11 +56,11 @@ public virtual ILinkBuilder Link( Uri linkTarget )
}
///
- public virtual SunsetPolicy Build()
+ public override SunsetPolicy Build()
{
- if ( sunsetPolicy is not null )
+ if ( Policy is not null )
{
- return sunsetPolicy;
+ return Policy;
}
SunsetPolicy policy = date is null ? new() : new( date.Value );
@@ -104,4 +82,19 @@ public virtual SunsetPolicy Build()
return policy;
}
+
+ private sealed class SunsetLinkBuilder : LinkBuilder, ILinkBuilder
+ {
+ protected override string RelationType => "sunset";
+
+ private readonly SunsetPolicyBuilder policyBuilder;
+
+ public SunsetLinkBuilder( SunsetPolicyBuilder policy, Uri linkTarget )
+ : base( linkTarget )
+ {
+ policyBuilder = policy;
+ }
+
+ public override ILinkBuilder Link( Uri linkTarget ) => policyBuilder.Link( linkTarget );
+ }
}
\ No newline at end of file
diff --git a/src/Common/src/Common/SunsetPolicyManager.cs b/src/Common/src/Common/SunsetPolicyManager.cs
index 50fdf115..cbd123eb 100644
--- a/src/Common/src/Common/SunsetPolicyManager.cs
+++ b/src/Common/src/Common/SunsetPolicyManager.cs
@@ -5,46 +5,5 @@ namespace Asp.Versioning;
///
/// Represents the default API version sunset policy manager.
///
-public partial class SunsetPolicyManager : ISunsetPolicyManager
-{
- private Dictionary? policies;
-
- ///
- public virtual bool TryGetPolicy(
- string? name,
- ApiVersion? apiVersion,
- [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy )
- {
- if ( string.IsNullOrEmpty( name ) && apiVersion == null )
- {
- sunsetPolicy = default!;
- return false;
- }
-
-#if NETFRAMEWORK
- policies ??= BuildPolicies( options );
-#else
- policies ??= BuildPolicies( options.Value );
-#endif
- var key = new PolicyKey( name, apiVersion );
-
- return policies.TryGetValue( key, out sunsetPolicy );
- }
-
- private static Dictionary BuildPolicies( ApiVersioningOptions options )
- {
- var builders = options.Policies.OfType();
- var mapping = new Dictionary( capacity: builders.Count );
-
- for ( var i = 0; i < builders.Count; i++ )
- {
- var builder = builders[i];
- var policy = builder.Build();
- var key = new PolicyKey( builder.Name, builder.ApiVersion );
-
- mapping[key] = policy;
- }
-
- return mapping;
- }
-}
\ No newline at end of file
+public partial class SunsetPolicyManager : PolicyManager, ISunsetPolicyManager
+{ }
\ No newline at end of file
diff --git a/src/Common/test/Common.Tests/ApiVersioningPolicyBuilderTest.cs b/src/Common/test/Common.Tests/ApiVersioningPolicyBuilderTest.cs
index 727c9060..82f0839e 100644
--- a/src/Common/test/Common.Tests/ApiVersioningPolicyBuilderTest.cs
+++ b/src/Common/test/Common.Tests/ApiVersioningPolicyBuilderTest.cs
@@ -36,6 +36,38 @@ public void sunset_should_return_same_policy_builder( string name, double? versi
result.Should().BeSameAs( expected );
}
+ [Fact]
+ public void deprecate_should_not_allow_empty_name_and_version()
+ {
+ // arrange
+ var builder = new ApiVersioningPolicyBuilder();
+
+ // act
+ Func deprecation = () => builder.Deprecate( default, default );
+
+ // assert
+ deprecation.Should().Throw().And
+ .Message.Should().Be( "'name' and 'apiVersion' cannot both be null." );
+ }
+
+ [Theory]
+ [InlineData( "Test", null )]
+ [InlineData( null, 1.1 )]
+ [InlineData( "Test", 1.1 )]
+ public void deprecate_should_return_same_policy_builder( string name, double? version )
+ {
+ // arrange
+ var apiVersion = version is null ? default : new ApiVersion( version.Value );
+ var builder = new ApiVersioningPolicyBuilder();
+ var expected = builder.Deprecate( name, apiVersion );
+
+ // act
+ var result = builder.Deprecate( name, apiVersion );
+
+ // assert
+ result.Should().BeSameAs( expected );
+ }
+
[Fact]
public void of_type_should_return_empty_list_for_unknown_type()
{
@@ -50,16 +82,34 @@ public void of_type_should_return_empty_list_for_unknown_type()
}
[Fact]
- public void of_type_should_return_filtered_builders()
+ public void of_type_sunset_should_return_filtered_builders()
{
// arrange
var builder = new ApiVersioningPolicyBuilder();
var expected = builder.Sunset( default, ApiVersion.Default );
+ var deprecation = builder.Deprecate( default, ApiVersion.Default );
// act
var list = builder.OfType();
// assert
list.Single().Should().BeSameAs( expected );
+ list.Single().Should().NotBeSameAs( deprecation );
+ }
+
+ [Fact]
+ public void of_type_deprecation_should_return_filtered_builders()
+ {
+ // arrange
+ var builder = new ApiVersioningPolicyBuilder();
+ var sunset = builder.Sunset( default, ApiVersion.Default );
+ var expected = builder.Deprecate( default, ApiVersion.Default );
+
+ // act
+ var list = builder.OfType();
+
+ // assert
+ list.Single().Should().BeSameAs( expected );
+ list.Single().Should().NotBeSameAs( sunset );
}
}
\ No newline at end of file