Skip to content

Commit

Permalink
Merge pull request #434 from osu-tournament-rating/client-codegen
Browse files Browse the repository at this point in the history
Improve swagger doc generation
  • Loading branch information
hburn7 authored Sep 28, 2024
2 parents a636c6f + cc2d044 commit 8acc17e
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 3 deletions.
1 change: 1 addition & 0 deletions API/API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.PostgreSQL" Version="2.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
</ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
Expand Down
8 changes: 8 additions & 0 deletions API/Middlewares/RequestLoggingMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ public class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggi
{
public async Task Invoke(HttpContext context)
{
#if DEBUG
if (context.Request.Path.StartsWithSegments("/swagger"))
{
await next(context);
return;
}
#endif

await LogRequest(context.Request);

Stream originalBodyStream = context.Response.Body;
Expand Down
89 changes: 87 additions & 2 deletions API/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using System.Text.Json.Serialization;
Expand All @@ -15,6 +16,7 @@
using API.Repositories.Interfaces;
using API.Services.Implementations;
using API.Services.Interfaces;
using API.SwaggerGen;
using API.Utilities;
using API.Utilities.Extensions;
using Asp.Versioning;
Expand All @@ -33,6 +35,7 @@
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Npgsql;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Metrics;
Expand All @@ -42,6 +45,10 @@
using OsuSharp.Extensions;
using Serilog;
using Serilog.Events;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
using Unchase.Swashbuckle.AspNetCore.Extensions.Options;

#region WebApplicationBuilder Configuration

Expand Down Expand Up @@ -233,7 +240,7 @@ await context.HttpContext.Response.WriteAsync(

#endregion

#region Swagger Configuration
#region SwaggerGen Configuration

builder
.Services.AddApiVersioning(options =>
Expand All @@ -251,6 +258,7 @@ await context.HttpContext.Response.WriteAsync(
});

builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen(options =>
{
options.SchemaGeneratorOptions.SupportNonNullableReferenceTypes = true;
Expand All @@ -264,7 +272,55 @@ await context.HttpContext.Response.WriteAsync(
"The official resource for reading and writing data within the osu! Tournament Rating platform."
}
);
options.IncludeXmlComments($"{AppDomain.CurrentDomain.BaseDirectory}API.xml");
string[] xmlDocPaths =
[
$"{AppDomain.CurrentDomain.BaseDirectory}API.xml",
$"{AppDomain.CurrentDomain.BaseDirectory}Database.xml"
];
foreach (var xmlDoc in xmlDocPaths)
{
options.IncludeXmlCommentsWithRemarks(xmlDoc);
}
var unknownMethodCount = 0;
options.CustomOperationIds(description =>
{
var controller = description.ActionDescriptor.RouteValues["controller"] ?? "undocumented";
string method;
if (description.TryGetMethodInfo(out MethodInfo info))
{
var methodName = info.Name.Replace("Async", string.Empty, StringComparison.OrdinalIgnoreCase);
method = char.ToLower(methodName[0]) + methodName[1..];
}
else
{
method = $"method_{unknownMethodCount}";
unknownMethodCount++;
}
return $"{controller}_{method}";
});
options.AddEnumsWithValuesFixFilters(enumsOptions =>
{
enumsOptions.IncludeDescriptions = true;
enumsOptions.IncludeXEnumRemarks = true;
enumsOptions.DescriptionSource = DescriptionSources.XmlComments;
enumsOptions.ApplySchemaFilter = true;
enumsOptions.ApplyParameterFilter = true;
enumsOptions.ApplyDocumentFilter = true;
foreach (var xmlDoc in xmlDocPaths)
{
enumsOptions.IncludeXmlCommentsFrom(xmlDoc);
}
});
options.SchemaFilter<BitwiseFlagEnumSchemaFilter>();
});

#endregion
Expand Down Expand Up @@ -543,6 +599,35 @@ await context.HttpContext.Response.WriteAsync(

#endregion

#region Swagger Doc Generation

if (args.Contains("--swagger-to-file"))
{
app.Logger.LogInformation("Saving Swagger spec to file");

// Get the swagger document provider from the service container
OpenApiDocument? swagger = app.Services.GetRequiredService<ISwaggerProvider>().GetSwagger("v1");
if (swagger is null)
{
app.Logger.LogError("Could not resolve the swagger spec, exiting");
return;
}

// Serialize the swagger doc to JSON
var stringWriter = new StringWriter();
swagger.SerializeAsV3(new OpenApiJsonWriter(stringWriter));

// Write to base path
var path = $"{AppDomain.CurrentDomain.BaseDirectory}swagger.json";
File.WriteAllText(path, stringWriter.ToString());

app.Logger.LogInformation("Swagger spec saved to: {Path}", path);

return;
}

#endregion

app.Logger.LogInformation("Running!");

app.Run();
21 changes: 21 additions & 0 deletions API/SwaggerGen/BitwiseFlagEnumSchemaFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Reflection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace API.SwaggerGen;

/// <summary>
/// Appends a custom attribute to the schemas generated for enums that are bitwise flags
/// </summary>
public class BitwiseFlagEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
Type? type = context.Type;
if (type is not null && type.IsEnum && type.GetCustomAttribute<FlagsAttribute>() is not null)
{
schema.Extensions.Add("x-bitwiseFlag", new OpenApiBoolean(true));
}
}
}
8 changes: 8 additions & 0 deletions Database/Database.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
<NoWarn>CS1591</NoWarn>
<NoWarn>CS8981</NoWarn>
</PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.5" />
Expand Down
2 changes: 1 addition & 1 deletion Database/Entities/AuditEntityBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Database.Entities;

/// <summary>
/// Base class for a <typeparamref name="TAudit"/> entity that serves as an audit for a <see cref="TAuditable"/> entity
/// Base class for a <typeparamref name="TAudit"/> entity that serves as an audit for an auditable entity
/// </summary>
/// <typeparam name="TAuditable">Derived audit</typeparam>
/// <typeparam name="TAudit">Entity to be audited</typeparam>
Expand Down

0 comments on commit 8acc17e

Please sign in to comment.