Skip to content

Commit 9045970

Browse files
committed
Generate OpenAPI doc on build
1 parent 732045f commit 9045970

File tree

6 files changed

+440
-46
lines changed

6 files changed

+440
-46
lines changed

src/BenchmarksApps/TodosApi/Database.cs

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Microsoft.AspNetCore.Hosting.Server;
2+
using Microsoft.AspNetCore.Hosting.Server.Features;
3+
using Npgsql;
4+
5+
namespace TodosApi;
6+
7+
internal class DatabaseInitializer : IHostedService
8+
{
9+
private readonly NpgsqlDataSource _db;
10+
private readonly ILogger<DatabaseInitializer> _logger;
11+
private readonly bool _initDatabase;
12+
13+
public DatabaseInitializer(NpgsqlDataSource db, IServer server, ILogger<DatabaseInitializer> logger)
14+
{
15+
_db = db;
16+
_logger = logger;
17+
_initDatabase = Environment.GetEnvironmentVariable("SUPPRESS_DB_INIT") != "true"
18+
// Only run if this is an actual IServer implementation with addresses to listen on.
19+
// Will not be the case for TestServer, NoopServer injected by the OpenAPI doc generator tool, etc.
20+
&& server.Features.Get<IServerAddressesFeature>() is { Addresses.Count: >0 };
21+
}
22+
23+
public Task StartAsync(CancellationToken cancellationToken)
24+
{
25+
if (_initDatabase)
26+
{
27+
return Initialize(cancellationToken);
28+
}
29+
30+
_logger.LogInformation("Database initialization disabled for connection string '{connectionString}'", _db.ConnectionString);
31+
return Task.CompletedTask;
32+
}
33+
34+
public Task StopAsync(CancellationToken cancellationToken)
35+
{
36+
return Task.CompletedTask;
37+
}
38+
39+
private async Task Initialize(CancellationToken cancellationToken = default)
40+
{
41+
// NOTE: Npgsql removes the password from the connection string
42+
_logger.LogInformation("Ensuring database exists and is up to date at connection string '{connectionString}'", _db.ConnectionString);
43+
44+
var sql = $"""
45+
CREATE TABLE IF NOT EXISTS public.todos
46+
(
47+
{nameof(Todo.Id)} SERIAL PRIMARY KEY,
48+
{nameof(Todo.Title)} text NOT NULL,
49+
{nameof(Todo.DueBy)} date NULL,
50+
{nameof(Todo.IsComplete)} boolean NOT NULL DEFAULT false
51+
);
52+
DELETE FROM public.todos;
53+
INSERT INTO
54+
public.todos ({nameof(Todo.Title)}, {nameof(Todo.DueBy)}, {nameof(Todo.IsComplete)})
55+
VALUES
56+
('Wash the dishes.', CURRENT_DATE, true),
57+
('Dry the dishes.', CURRENT_DATE, true),
58+
('Turn the dishes over.', CURRENT_DATE, false),
59+
('Walk the kangaroo.', CURRENT_DATE + INTERVAL '1 day', false),
60+
('Call Grandma.', CURRENT_DATE + INTERVAL '1 day', false);
61+
""";
62+
await _db.ExecuteAsync(sql, cancellationToken);
63+
}
64+
}

src/BenchmarksApps/TodosApi/Program.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#if ENABLE_OPENAPI
2+
using Microsoft.OpenApi.Models;
3+
#endif
14
using Npgsql;
25
using TodosApi;
36

@@ -21,6 +24,7 @@
2124
var appSettings = sp.GetRequiredService<IOptions<AppSettings>>().Value;
2225
return new NpgsqlSlimDataSourceBuilder(appSettings.ConnectionString).Build();
2326
});
27+
builder.Services.AddHostedService<DatabaseInitializer>();
2428

2529
// Configure JSON serialization
2630
builder.Services.ConfigureHttpJsonOptions(options =>
@@ -36,16 +40,26 @@
3640
// Problem details
3741
builder.Services.AddProblemDetails();
3842

39-
var app = builder.Build();
43+
#if ENABLE_OPENAPI
44+
// Configure OpenAPI
45+
builder.Services.AddEndpointsApiExplorer();
46+
builder.Services.AddSwaggerGen(c =>
47+
{
48+
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todos API", Version = "v1" });
49+
});
50+
#endif
4051

41-
await Database.Initialize(app.Services, app.Logger);
52+
var app = builder.Build();
4253

4354
if (!app.Environment.IsDevelopment())
4455
{
4556
app.UseExceptionHandler();
4657
}
4758

59+
app.MapShortCircuit(StatusCodes.Status404NotFound, "/favicon.ico");
60+
4861
app.MapHealthChecks("/health");
62+
4963
// Enables testing request exception handling behavior
5064
app.MapGet("/throw", void () => throw new InvalidOperationException("You hit the throw endpoint"));
5165

src/BenchmarksApps/TodosApi/TodosApi.csproj

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,26 @@
1010
<UserSecretsId>b8ffb8d3-b768-460b-ac1f-ef267c954c85</UserSecretsId>
1111
<PublishAot>true</PublishAot>
1212
<EnableConfigurationBindingGenerator>false</EnableConfigurationBindingGenerator>
13-
<EnableLogging Condition="$(Configuration.StartsWith('Debug'))">true</EnableLogging>
13+
<EnableLogging Condition=" '$(EnableLogging)' == '' and $(Configuration.StartsWith('Debug')) ">true</EnableLogging>
14+
<EnableOpenApi Condition=" '$(EnableOpenApi)' == '' and $(Configuration.StartsWith('Debug')) ">true</EnableOpenApi>
1415
<DefineConstants Condition=" '$(EnableLogging)' == 'true' ">$(DefineConstants);ENABLE_LOGGING</DefineConstants>
16+
<DefineConstants Condition=" '$(EnableOpenApi)' == 'true' ">$(DefineConstants);ENABLE_OPENAPI</DefineConstants>
1517
</PropertyGroup>
16-
18+
1719
<ItemGroup>
1820
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.*" />
1921
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0-preview.*" />
2022
<PackageReference Include="Npgsql" Version="8.0.0-preview.3" />
23+
<Content Update="appSettings.Development.json" CopyToPublishDirectory="false" />
24+
</ItemGroup>
25+
26+
<PropertyGroup Condition=" '$(EnableOpenApi)' == 'true' ">
27+
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
28+
<OpenApiDocumentsDirectory>./</OpenApiDocumentsDirectory>
29+
</PropertyGroup>
30+
31+
<ItemGroup Condition=" '$(EnableOpenApi)' == 'true' ">
32+
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="8.0.0-preview.*" />
33+
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
2134
</ItemGroup>
2235
</Project>

0 commit comments

Comments
 (0)