diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 6124173..d29e994 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,4 +3,4 @@ ## Package Constraints - **FluentAssertions**: Do not upgrade beyond major version 7.x due to a licensing change in version 8+. -- **Swashbuckle.AspNetCore**: Do not upgrade beyond version 6.x due to breaking changes in Microsoft.OpenApi v2. + diff --git a/Directory.Packages.props b/Directory.Packages.props index 2e8c89d..36e7c45 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + diff --git a/sample/MinApi/Program.cs b/sample/MinApi/Program.cs index f58e5de..6a50fe1 100644 --- a/sample/MinApi/Program.cs +++ b/sample/MinApi/Program.cs @@ -1,6 +1,7 @@ using Azure.Core; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; +using Scalar.AspNetCore; using SampleMinimalApiSli; using ServiceLevelIndicators; @@ -13,14 +14,7 @@ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(options => -{ - - var fileName = typeof(Program).Assembly.GetName().Name + ".xml"; - var filePath = Path.Combine(AppContext.BaseDirectory, fileName); - options.IncludeXmlComments(filePath); -}); +builder.Services.AddOpenApi(); // Build a resource configuration action to set service information. @@ -65,8 +59,8 @@ .AddServiceLevelIndicator("background_work"); app.UseUserRoute(); -app.UseSwagger(); -app.UseSwaggerUI(); +app.MapOpenApi(); +app.MapScalarApiReference(); app.UseHttpsRedirection(); app.UseServiceLevelIndicator(); app.Run(); diff --git a/sample/MinApi/Properties/launchSettings.json b/sample/MinApi/Properties/launchSettings.json index 46e4f27..a940696 100644 --- a/sample/MinApi/Properties/launchSettings.json +++ b/sample/MinApi/Properties/launchSettings.json @@ -3,7 +3,7 @@ "SampleMinimalApiSli": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "scalar/v1", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/sample/MinApi/SampleMinimalApiSli.csproj b/sample/MinApi/SampleMinimalApiSli.csproj index ad17a2b..8469fa6 100644 --- a/sample/MinApi/SampleMinimalApiSli.csproj +++ b/sample/MinApi/SampleMinimalApiSli.csproj @@ -7,7 +7,8 @@ - + + diff --git a/sample/WebApi/Program.cs b/sample/WebApi/Program.cs index 7eb2fdc..6e54c23 100644 --- a/sample/WebApi/Program.cs +++ b/sample/WebApi/Program.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Options; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; +using Scalar.AspNetCore; using SampleWebApplicationSLI; using ServiceLevelIndicators; @@ -13,13 +14,7 @@ builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(options => -{ - - var fileName = typeof(Program).Assembly.GetName().Name + ".xml"; - var filePath = Path.Combine(AppContext.BaseDirectory, fileName); - options.IncludeXmlComments(filePath); -}); +builder.Services.AddOpenApi(); builder.Services.AddProblemDetails(); // Build a resource configuration action to set service information. @@ -47,8 +42,8 @@ var app = builder.Build(); -app.UseSwagger(); -app.UseSwaggerUI(); +app.MapOpenApi(); +app.MapScalarApiReference(); app.UseHttpsRedirection(); app.UseServiceLevelIndicator(); app.UseAuthorization(); diff --git a/sample/WebApi/Properties/launchSettings.json b/sample/WebApi/Properties/launchSettings.json index 51932bc..0649eac 100644 --- a/sample/WebApi/Properties/launchSettings.json +++ b/sample/WebApi/Properties/launchSettings.json @@ -3,7 +3,7 @@ "SampleWebApplicationSLI": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "scalar/v1", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/sample/WebApi/SampleWebApplicationSLI.csproj b/sample/WebApi/SampleWebApplicationSLI.csproj index ad17a2b..8469fa6 100644 --- a/sample/WebApi/SampleWebApplicationSLI.csproj +++ b/sample/WebApi/SampleWebApplicationSLI.csproj @@ -7,7 +7,8 @@ - + + diff --git a/sample/WebApiVersioned/AddApiVersionMetadata.cs b/sample/WebApiVersioned/AddApiVersionMetadata.cs deleted file mode 100644 index f8e7a7d..0000000 --- a/sample/WebApiVersioned/AddApiVersionMetadata.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace SampleVersionedWebApplicationSLI; - -using System.Globalization; -using System.Text.Json; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -/// -/// Represents the OpenAPI/Swashbuckle operation filter used to document information provided, but not used. -/// -/// This is only required due to bugs in the . -/// Once they are fixed and published, this class can be removed. -public class AddApiVersionMetadata : IOperationFilter -{ - /// - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var apiDescription = context.ApiDescription; - - operation.Deprecated |= apiDescription.IsDeprecated; - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077 - foreach (var responseType in context.ApiDescription.SupportedResponseTypes) - { - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387 - var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString(CultureInfo.InvariantCulture); - var response = operation.Responses[responseKey]; - - foreach (var contentType in response.Content.Keys) - { - if (!responseType.ApiResponseFormats.Any(x => x.MediaType == contentType)) - { - response.Content.Remove(contentType); - } - } - } - - if (operation.Parameters == null) - { - return; - } - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } - - if (parameter.Schema.Default == null && - description.DefaultValue != null && - description.DefaultValue is not DBNull && - description.ModelMetadata is ModelMetadata modelMetadata) - { - // REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330 - var json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType); - parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json); - } - - parameter.Required |= description.IsRequired; - } - } -} \ No newline at end of file diff --git a/sample/WebApiVersioned/ConfigureSwaggerDefaultOptions.cs b/sample/WebApiVersioned/ConfigureSwaggerDefaultOptions.cs deleted file mode 100644 index 7a3279f..0000000 --- a/sample/WebApiVersioned/ConfigureSwaggerDefaultOptions.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace SampleVersionedWebApplicationSLI; - -using System.Text; -using Asp.Versioning; -using Asp.Versioning.ApiExplorer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -/// -/// Configures the Swagger generation options. -/// -/// This allows API versioning to define a Swagger document per API version after the -/// service has been resolved from the service container. -public class ConfigureSwaggerDefaultOptions : IConfigureOptions -{ - private readonly IApiVersionDescriptionProvider provider; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerDefaultOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var text = new StringBuilder("An example application with OpenAPI, Swashbuckle, and API versioning."); - var info = new OpenApiInfo() - { - Title = "Best Weather forecast API", - Version = description.ApiVersion.ToString(), - Contact = new OpenApiContact() { Name = "Xavier John", Email = "xavier@somewhere.com" }, - License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } - }; - - if (description.IsDeprecated) - { - text.Append(" This API version has been deprecated."); - } - - if (description.SunsetPolicy is SunsetPolicy policy) - { - if (policy.Date is DateTimeOffset when) - { - text.Append(" The API will be sunset on ") - .Append(when.Date.ToShortDateString()) - .Append('.'); - } - - if (policy.HasLinks) - { - text.AppendLine(); - - for (var i = 0; i < policy.Links.Count; i++) - { - var link = policy.Links[i]; - - if (link.Type == "text/html") - { - text.AppendLine(); - - if (link.Title.HasValue) - { - text.Append(link.Title.Value).Append(": "); - } - - text.Append(link.LinkTarget.OriginalString); - } - } - } - } - - info.Description = text.ToString(); - - return info; - } -} \ No newline at end of file diff --git a/sample/WebApiVersioned/Program.cs b/sample/WebApiVersioned/Program.cs index 742b310..35a6dfa 100644 --- a/sample/WebApiVersioned/Program.cs +++ b/sample/WebApiVersioned/Program.cs @@ -1,31 +1,15 @@ using Azure.Core; -using Microsoft.Extensions.Options; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; -using SampleVersionedWebApplicationSLI; +using Scalar.AspNetCore; using ServiceLevelIndicators; -using Swashbuckle.AspNetCore.SwaggerGen; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddTransient, ConfigureSwaggerDefaultOptions>(); -builder.Services.AddSwaggerGen( - options => - { - // add a custom operation filter which sets default values - options.OperationFilter(); - - var fileName = typeof(Program).Assembly.GetName().Name + ".xml"; - var filePath = Path.Combine(AppContext.BaseDirectory, fileName); - - // integrate XML comments - options.IncludeXmlComments(filePath); - }); +builder.Services.AddOpenApi(); builder.Services.AddApiVersioning() .AddMvc() .AddApiExplorer(); @@ -49,21 +33,10 @@ var app = builder.Build(); -app.UseSwagger(); -app.UseSwaggerUI( - options => - { - options.RoutePrefix = string.Empty; // make home page the swagger UI - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach (var description in descriptions) - { - var url = $"/swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint(url, name); - } - }); +// TODO: Use .AddOpenApi() from Asp.Versioning.OpenApi with WithDocumentPerVersion() +// and AddScalarTransformers() once a stable release is available. +app.MapOpenApi(); +app.MapScalarApiReference(); // Random delay. Random rnd = new Random(); diff --git a/sample/WebApiVersioned/Properties/launchSettings.json b/sample/WebApiVersioned/Properties/launchSettings.json index 5a64df5..fb1873e 100644 --- a/sample/WebApiVersioned/Properties/launchSettings.json +++ b/sample/WebApiVersioned/Properties/launchSettings.json @@ -3,6 +3,7 @@ "SampleVersionedWebApplicationSLI": { "commandName": "Project", "launchBrowser": true, + "launchUrl": "scalar/v1", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/sample/WebApiVersioned/SampleVersionedWebApplicationSLI.csproj b/sample/WebApiVersioned/SampleVersionedWebApplicationSLI.csproj index 77707f7..b276694 100644 --- a/sample/WebApiVersioned/SampleVersionedWebApplicationSLI.csproj +++ b/sample/WebApiVersioned/SampleVersionedWebApplicationSLI.csproj @@ -9,7 +9,8 @@ - + +