Add sample data toggle to user provisioning

This commit is contained in:
2026-02-01 10:29:00 +01:00
parent 990e67e88c
commit 56aacb0134
7 changed files with 59 additions and 29 deletions

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace ASTRAIN.Api.Endpoints; namespace ASTRAIN.Api.Endpoints;
@@ -22,9 +23,10 @@ internal static class ExerciseEndpoints
/// <returns>The same route group for chaining.</returns> /// <returns>The same route group for chaining.</returns>
public static RouteGroupBuilder MapExerciseEndpoints(this RouteGroupBuilder group) public static RouteGroupBuilder MapExerciseEndpoints(this RouteGroupBuilder group)
{ {
group.MapGet("/users/{userId}/exercises", async (string userId, AppDbContext db) => group.MapGet("/users/{userId}/exercises", async (string userId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var items = await db.Exercises var items = await db.Exercises
.Where(e => e.UserId == user.Id) .Where(e => e.UserId == user.Id)
.OrderBy(e => e.Name) .OrderBy(e => e.Name)
@@ -35,9 +37,10 @@ internal static class ExerciseEndpoints
.WithSummary("List exercises") .WithSummary("List exercises")
.WithDescription("Returns the exercises for the specified user."); .WithDescription("Returns the exercises for the specified user.");
group.MapPost("/users/{userId}/exercises", async (string userId, ExerciseUpsertRequest request, AppDbContext db) => group.MapPost("/users/{userId}/exercises", async (string userId, ExerciseUpsertRequest request, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
if (string.IsNullOrWhiteSpace(request.Name)) if (string.IsNullOrWhiteSpace(request.Name))
{ {
return Results.BadRequest("Name is required."); return Results.BadRequest("Name is required.");
@@ -57,9 +60,10 @@ internal static class ExerciseEndpoints
.WithSummary("Create exercise") .WithSummary("Create exercise")
.WithDescription("Creates a new exercise for the specified user."); .WithDescription("Creates a new exercise for the specified user.");
group.MapPut("/users/{userId}/exercises/{exerciseId:int}", async (string userId, int exerciseId, ExerciseUpsertRequest request, AppDbContext db) => group.MapPut("/users/{userId}/exercises/{exerciseId:int}", async (string userId, int exerciseId, ExerciseUpsertRequest request, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var exercise = await db.Exercises.FirstOrDefaultAsync(e => e.Id == exerciseId && e.UserId == user.Id); var exercise = await db.Exercises.FirstOrDefaultAsync(e => e.Id == exerciseId && e.UserId == user.Id);
if (exercise is null) if (exercise is null)
{ {
@@ -79,9 +83,10 @@ internal static class ExerciseEndpoints
.WithSummary("Update exercise") .WithSummary("Update exercise")
.WithDescription("Updates the name of an exercise for the specified user."); .WithDescription("Updates the name of an exercise for the specified user.");
group.MapDelete("/users/{userId}/exercises/{exerciseId:int}", async (string userId, int exerciseId, AppDbContext db) => group.MapDelete("/users/{userId}/exercises/{exerciseId:int}", async (string userId, int exerciseId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var exercise = await db.Exercises.FirstOrDefaultAsync(e => e.Id == exerciseId && e.UserId == user.Id); var exercise = await db.Exercises.FirstOrDefaultAsync(e => e.Id == exerciseId && e.UserId == user.Id);
if (exercise is null) if (exercise is null)
{ {

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace ASTRAIN.Api.Endpoints; namespace ASTRAIN.Api.Endpoints;
@@ -22,9 +23,10 @@ internal static class RoutineEndpoints
/// <returns>The same route group for chaining.</returns> /// <returns>The same route group for chaining.</returns>
public static RouteGroupBuilder MapRoutineEndpoints(this RouteGroupBuilder group) public static RouteGroupBuilder MapRoutineEndpoints(this RouteGroupBuilder group)
{ {
group.MapGet("/users/{userId}/routines", async (string userId, AppDbContext db) => group.MapGet("/users/{userId}/routines", async (string userId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var routines = await db.Routines var routines = await db.Routines
.Include(r => r.Exercises) .Include(r => r.Exercises)
.ThenInclude(re => re.Exercise) .ThenInclude(re => re.Exercise)
@@ -46,9 +48,10 @@ internal static class RoutineEndpoints
.WithSummary("List routines") .WithSummary("List routines")
.WithDescription("Returns all routines for the specified user."); .WithDescription("Returns all routines for the specified user.");
group.MapGet("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db) => group.MapGet("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var routine = await db.Routines var routine = await db.Routines
.Include(r => r.Exercises) .Include(r => r.Exercises)
.ThenInclude(re => re.Exercise) .ThenInclude(re => re.Exercise)
@@ -72,9 +75,10 @@ internal static class RoutineEndpoints
.WithSummary("Get routine") .WithSummary("Get routine")
.WithDescription("Returns a specific routine and its exercises."); .WithDescription("Returns a specific routine and its exercises.");
group.MapPost("/users/{userId}/routines", async (string userId, RoutineUpsertRequest request, AppDbContext db) => group.MapPost("/users/{userId}/routines", async (string userId, RoutineUpsertRequest request, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
if (string.IsNullOrWhiteSpace(request.Name)) if (string.IsNullOrWhiteSpace(request.Name))
{ {
return Results.BadRequest("Name is required."); return Results.BadRequest("Name is required.");
@@ -113,9 +117,10 @@ internal static class RoutineEndpoints
.WithSummary("Create routine") .WithSummary("Create routine")
.WithDescription("Creates a routine and associates exercises with it."); .WithDescription("Creates a routine and associates exercises with it.");
group.MapPut("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, RoutineUpsertRequest request, AppDbContext db) => group.MapPut("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, RoutineUpsertRequest request, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var routine = await db.Routines var routine = await db.Routines
.Include(r => r.Exercises) .Include(r => r.Exercises)
.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); .FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id);
@@ -160,9 +165,10 @@ internal static class RoutineEndpoints
.WithSummary("Update routine") .WithSummary("Update routine")
.WithDescription("Updates routine metadata and exercise ordering."); .WithDescription("Updates routine metadata and exercise ordering.");
group.MapDelete("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db) => group.MapDelete("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id);
if (routine is null) if (routine is null)
{ {

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace ASTRAIN.Api.Endpoints; namespace ASTRAIN.Api.Endpoints;
@@ -22,9 +23,10 @@ internal static class RoutineRunEndpoints
/// <returns>The same route group for chaining.</returns> /// <returns>The same route group for chaining.</returns>
public static RouteGroupBuilder MapRoutineRunEndpoints(this RouteGroupBuilder group) public static RouteGroupBuilder MapRoutineRunEndpoints(this RouteGroupBuilder group)
{ {
group.MapGet("/users/{userId}/routines/{routineId:int}/last-run", async (string userId, int routineId, AppDbContext db) => group.MapGet("/users/{userId}/routines/{routineId:int}/last-run", async (string userId, int routineId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var lastRun = await db.RoutineRuns var lastRun = await db.RoutineRuns
.Include(rr => rr.Entries) .Include(rr => rr.Entries)
.Where(rr => rr.UserId == user.Id && rr.RoutineId == routineId) .Where(rr => rr.UserId == user.Id && rr.RoutineId == routineId)
@@ -47,9 +49,10 @@ internal static class RoutineRunEndpoints
.WithSummary("Get last routine run") .WithSummary("Get last routine run")
.WithDescription("Returns the most recent run summary for a routine."); .WithDescription("Returns the most recent run summary for a routine.");
group.MapPost("/users/{userId}/routines/{routineId:int}/runs", async (string userId, int routineId, RoutineRunRequest request, AppDbContext db) => group.MapPost("/users/{userId}/routines/{routineId:int}/runs", async (string userId, int routineId, RoutineRunRequest request, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id); var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id);
if (routine is null) if (routine is null)
{ {

View File

@@ -4,6 +4,7 @@ using ASTRAIN.Shared.Responses;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
namespace ASTRAIN.Api.Endpoints; namespace ASTRAIN.Api.Endpoints;
@@ -19,17 +20,19 @@ internal static class UserEndpoints
/// <returns>The same route group for chaining.</returns> /// <returns>The same route group for chaining.</returns>
public static RouteGroupBuilder MapUserEndpoints(this RouteGroupBuilder group) public static RouteGroupBuilder MapUserEndpoints(this RouteGroupBuilder group)
{ {
group.MapGet("/users/ensure", async (string? userId, AppDbContext db) => group.MapGet("/users/ensure", async (string? userId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
return Results.Ok(new EnsureUserResponse(user.Id)); return Results.Ok(new EnsureUserResponse(user.Id));
}) })
.WithSummary("Ensure user") .WithSummary("Ensure user")
.WithDescription("Ensures a user exists and returns the user id."); .WithDescription("Ensures a user exists and returns the user id.");
group.MapGet("/users/{userId}", async (string userId, AppDbContext db) => group.MapGet("/users/{userId}", async (string userId, AppDbContext db, IConfiguration config) =>
{ {
var user = await UserProvisioning.EnsureUserAsync(db, userId); var populateSampleData = config.GetValue("SampleData:Enabled", false);
var user = await UserProvisioning.EnsureUserAsync(db, userId, populateSampleData);
return Results.Ok(new EnsureUserResponse(user.Id)); return Results.Ok(new EnsureUserResponse(user.Id));
}) })
.WithSummary("Get user") .WithSummary("Get user")

View File

@@ -15,8 +15,9 @@ internal static class UserProvisioning
/// </summary> /// </summary>
/// <param name="db">The application database context.</param> /// <param name="db">The application database context.</param>
/// <param name="userId">Optional user id to validate or create.</param> /// <param name="userId">Optional user id to validate or create.</param>
/// <param name="populateSampleData">Whether to populate sample data for new users.</param>
/// <returns>The existing or newly created user.</returns> /// <returns>The existing or newly created user.</returns>
public static async Task<User> EnsureUserAsync(AppDbContext db, string? userId) public static async Task<User> EnsureUserAsync(AppDbContext db, string? userId, bool populateSampleData)
{ {
if (!string.IsNullOrWhiteSpace(userId) && IsValidUserId(userId)) if (!string.IsNullOrWhiteSpace(userId) && IsValidUserId(userId))
{ {
@@ -29,7 +30,10 @@ internal static class UserProvisioning
var created = new User { Id = userId }; var created = new User { Id = userId };
db.Users.Add(created); db.Users.Add(created);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
if (populateSampleData)
{
await PopulateSampleDataAsync(db, created); await PopulateSampleDataAsync(db, created);
}
return created; return created;
} }
@@ -45,7 +49,10 @@ internal static class UserProvisioning
var user = new User { Id = newId }; var user = new User { Id = newId };
db.Users.Add(user); db.Users.Add(user);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
if (populateSampleData)
{
await PopulateSampleDataAsync(db, user); await PopulateSampleDataAsync(db, user);
}
return user; return user;
} }
} }

View File

@@ -4,5 +4,8 @@
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
},
"SampleData": {
"Enabled": false
} }
} }

View File

@@ -8,5 +8,8 @@
"ConnectionStrings": { "ConnectionStrings": {
"Default": "Data Source=Data/astrain.db" "Default": "Data Source=Data/astrain.db"
}, },
"SampleData": {
"Enabled": false
},
"AllowedHosts": "*" "AllowedHosts": "*"
} }