feat(seed): add development seed data script
- Create SeedDataService in Infrastructure/Seed with idempotent seeding - Seed 2 clubs: Sunrise Tennis Club, Valley Cycling Club - Seed 7 member records (5 unique Keycloak test users) - Seed 8 work items covering all status states - Seed 5 shifts with date variety (past, today, future) - Seed shift signups for realistic partial capacity - Register SeedDataService in Program.cs with development-only guard - Use deterministic GUID generation from club names - Ensure all tenant IDs match for RLS compliance - Track in learnings.md and evidence files for Task 22 QA
This commit is contained in:
@@ -1,19 +1,85 @@
|
||||
using Finbuckle.MultiTenant;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WorkClub.Api.Auth;
|
||||
using WorkClub.Api.Middleware;
|
||||
using WorkClub.Application.Interfaces;
|
||||
using WorkClub.Infrastructure.Data;
|
||||
using WorkClub.Infrastructure.Services;
|
||||
using WorkClub.Infrastructure.Seed;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
builder.Services.AddMultiTenant<TenantInfo>()
|
||||
.WithHeaderStrategy("X-Tenant-Id")
|
||||
.WithClaimStrategy("tenant_id")
|
||||
.WithInMemoryStore(options =>
|
||||
{
|
||||
options.IsCaseSensitive = false;
|
||||
});
|
||||
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
|
||||
builder.Services.AddScoped<SeedDataService>();
|
||||
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = builder.Configuration["Keycloak:Authority"];
|
||||
options.Audience = builder.Configuration["Keycloak:Audience"];
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddScoped<IClaimsTransformation, ClubRoleClaimsTransformation>();
|
||||
|
||||
builder.Services.AddAuthorizationBuilder()
|
||||
.AddPolicy("RequireAdmin", policy => policy.RequireRole("Admin"))
|
||||
.AddPolicy("RequireManager", policy => policy.RequireRole("Admin", "Manager"))
|
||||
.AddPolicy("RequireMember", policy => policy.RequireRole("Admin", "Manager", "Member"))
|
||||
.AddPolicy("RequireViewer", policy => policy.RequireAuthenticatedUser());
|
||||
|
||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
|
||||
using var scope = app.Services.CreateScope();
|
||||
var seedService = scope.ServiceProvider.GetRequiredService<SeedDataService>();
|
||||
await seedService.SeedAsync();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseMultiTenant();
|
||||
app.UseMiddleware<TenantValidationMiddleware>();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
|
||||
{
|
||||
Predicate = _ => false
|
||||
});
|
||||
|
||||
app.MapHealthChecks("/health/ready");
|
||||
app.MapHealthChecks("/health/startup");
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
@@ -33,9 +99,14 @@ app.MapGet("/weatherforecast", () =>
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
|
||||
app.MapGet("/api/test", () => Results.Ok(new { message = "Test endpoint" }))
|
||||
.RequireAuthorization();
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
|
||||
public partial class Program { }
|
||||
|
||||
Reference in New Issue
Block a user