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 { }
|
||||
|
||||
448
backend/WorkClub.Infrastructure/Seed/SeedDataService.cs
Normal file
448
backend/WorkClub.Infrastructure/Seed/SeedDataService.cs
Normal file
@@ -0,0 +1,448 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WorkClub.Domain.Entities;
|
||||
using WorkClub.Domain.Enums;
|
||||
using WorkClub.Infrastructure.Data;
|
||||
|
||||
namespace WorkClub.Infrastructure.Seed;
|
||||
|
||||
public class SeedDataService
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public SeedDataService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
public async Task SeedAsync()
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
// Seed clubs
|
||||
if (!context.Clubs.Any())
|
||||
{
|
||||
var clubs = new List<Club>
|
||||
{
|
||||
new Club
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = GenerateDeterministicGuid("Sunrise Tennis Club"),
|
||||
Name = "Sunrise Tennis Club",
|
||||
SportType = SportType.Tennis,
|
||||
Description = "Community tennis club for all skill levels",
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Club
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = GenerateDeterministicGuid("Valley Cycling Club"),
|
||||
Name = "Valley Cycling Club",
|
||||
SportType = SportType.Cycling,
|
||||
Description = "Cycling enthusiasts community",
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
context.Clubs.AddRange(clubs);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Get clubs for member seeding
|
||||
var tennisClub = context.Clubs.First(c => c.Name == "Sunrise Tennis Club");
|
||||
var cyclingClub = context.Clubs.First(c => c.Name == "Valley Cycling Club");
|
||||
|
||||
// Seed members
|
||||
if (!context.Members.Any())
|
||||
{
|
||||
var members = new List<Member>
|
||||
{
|
||||
// admin@test.com: Admin in Club 1, Member in Club 2
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "admin-user-id",
|
||||
DisplayName = "Admin User",
|
||||
Email = "admin@test.com",
|
||||
Role = ClubRole.Admin,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
ExternalUserId = "admin-user-id",
|
||||
DisplayName = "Admin User",
|
||||
Email = "admin@test.com",
|
||||
Role = ClubRole.Member,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// manager@test.com: Manager in Club 1
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "manager-user-id",
|
||||
DisplayName = "Manager User",
|
||||
Email = "manager@test.com",
|
||||
Role = ClubRole.Manager,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// member1@test.com: Member in Club 1 and Club 2
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "member1-user-id",
|
||||
DisplayName = "Member One",
|
||||
Email = "member1@test.com",
|
||||
Role = ClubRole.Member,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
ExternalUserId = "member1-user-id",
|
||||
DisplayName = "Member One",
|
||||
Email = "member1@test.com",
|
||||
Role = ClubRole.Member,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// member2@test.com: Member in Club 1
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "member2-user-id",
|
||||
DisplayName = "Member Two",
|
||||
Email = "member2@test.com",
|
||||
Role = ClubRole.Member,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// viewer@test.com: Viewer in Club 1
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "viewer-user-id",
|
||||
DisplayName = "Viewer User",
|
||||
Email = "viewer@test.com",
|
||||
Role = ClubRole.Viewer,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
context.Members.AddRange(members);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Get admin member IDs for work item creation
|
||||
var adminMembers = context.Members.Where(m => m.Email == "admin@test.com").ToList();
|
||||
var managerMember = context.Members.First(m => m.Email == "manager@test.com");
|
||||
var member1Members = context.Members.Where(m => m.Email == "member1@test.com").ToList();
|
||||
var member2Member = context.Members.First(m => m.Email == "member2@test.com");
|
||||
|
||||
// Seed work items
|
||||
if (!context.WorkItems.Any())
|
||||
{
|
||||
var workItems = new List<WorkItem>
|
||||
{
|
||||
// Club 1 - Tennis Club (5 items, all states)
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Court renovation",
|
||||
Description = "Resurface main court",
|
||||
Status = WorkItemStatus.Open,
|
||||
AssigneeId = null,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(14),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Equipment order",
|
||||
Description = "Purchase new tennis rackets and balls",
|
||||
Status = WorkItemStatus.Assigned,
|
||||
AssigneeId = managerMember.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(7),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Tournament planning",
|
||||
Description = "Organize annual summer tournament",
|
||||
Status = WorkItemStatus.InProgress,
|
||||
AssigneeId = member1Members.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(30),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Member handbook review",
|
||||
Description = "Update and review club rules handbook",
|
||||
Status = WorkItemStatus.Review,
|
||||
AssigneeId = member2Member.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(21),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Website update",
|
||||
Description = "Update club website with new photos",
|
||||
Status = WorkItemStatus.Done,
|
||||
AssigneeId = managerMember.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(-5),
|
||||
CreatedAt = DateTimeOffset.UtcNow.AddDays(-10),
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// Club 2 - Cycling Club (3 items)
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
Title = "Route mapping",
|
||||
Description = "Create new cycling routes for summer",
|
||||
Status = WorkItemStatus.Open,
|
||||
AssigneeId = null,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(21),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
Title = "Safety training",
|
||||
Description = "Organize safety and maintenance training",
|
||||
Status = WorkItemStatus.Assigned,
|
||||
AssigneeId = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(14),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new WorkItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
Title = "Group ride coordination",
|
||||
Description = "Schedule and coordinate weekly group rides",
|
||||
Status = WorkItemStatus.InProgress,
|
||||
AssigneeId = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(7),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
context.WorkItems.AddRange(workItems);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Seed shifts
|
||||
if (!context.Shifts.Any())
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var shifts = new List<Shift>
|
||||
{
|
||||
// Club 1 - Tennis Club (3 shifts)
|
||||
new Shift
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Court Maintenance - Yesterday",
|
||||
Description = "Daily court cleaning and maintenance",
|
||||
Location = "Main Court",
|
||||
StartTime = now.AddDays(-1).Date.ToLocalTime().AddHours(8),
|
||||
EndTime = now.AddDays(-1).Date.ToLocalTime().AddHours(12),
|
||||
Capacity = 2,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Shift
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Court Maintenance - Today",
|
||||
Description = "Daily court cleaning and maintenance",
|
||||
Location = "Main Court",
|
||||
StartTime = now.Date.ToLocalTime().AddHours(14),
|
||||
EndTime = now.Date.ToLocalTime().AddHours(18),
|
||||
Capacity = 3,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Shift
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
Title = "Tournament Setup - Next Week",
|
||||
Description = "Setup and preparation for summer tournament",
|
||||
Location = "All Courts",
|
||||
StartTime = now.AddDays(7).Date.ToLocalTime().AddHours(9),
|
||||
EndTime = now.AddDays(7).Date.ToLocalTime().AddHours(17),
|
||||
Capacity = 5,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
// Club 2 - Cycling Club (2 shifts)
|
||||
new Shift
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
Title = "Group Ride - Today",
|
||||
Description = "Weekly morning group ride",
|
||||
Location = "Park entrance",
|
||||
StartTime = now.Date.ToLocalTime().AddHours(7),
|
||||
EndTime = now.Date.ToLocalTime().AddHours(9),
|
||||
Capacity = 10,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Shift
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
Title = "Maintenance Workshop - Next Week",
|
||||
Description = "Bike maintenance and repair workshop",
|
||||
Location = "Club shed",
|
||||
StartTime = now.AddDays(7).Date.ToLocalTime().AddHours(10),
|
||||
EndTime = now.AddDays(7).Date.ToLocalTime().AddHours(14),
|
||||
Capacity = 4,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
context.Shifts.AddRange(shifts);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Seed shift signups
|
||||
if (!context.ShiftSignups.Any())
|
||||
{
|
||||
var shifts = context.Shifts.ToList();
|
||||
var signups = new List<ShiftSignup>();
|
||||
|
||||
// Add some signups for Tennis Club shifts
|
||||
var tennisShifts = shifts.Where(s => s.ClubId == tennisClub.Id).ToList();
|
||||
if (tennisShifts.Count > 0)
|
||||
{
|
||||
var tennyMembers = context.Members.Where(m => m.ClubId == tennisClub.Id).ToList();
|
||||
if (tennyMembers.Count > 0)
|
||||
{
|
||||
signups.Add(new ShiftSignup
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ShiftId = tennisShifts[0].Id,
|
||||
MemberId = tennyMembers[0].Id,
|
||||
SignedUpAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
|
||||
if (tennyMembers.Count > 1)
|
||||
{
|
||||
signups.Add(new ShiftSignup
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ShiftId = tennisShifts[0].Id,
|
||||
MemberId = tennyMembers[1].Id,
|
||||
SignedUpAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add some signups for Cycling Club shifts
|
||||
var cyclingShifts = shifts.Where(s => s.ClubId == cyclingClub.Id).ToList();
|
||||
if (cyclingShifts.Count > 0)
|
||||
{
|
||||
var cyclingMembers = context.Members.Where(m => m.ClubId == cyclingClub.Id).ToList();
|
||||
if (cyclingMembers.Count > 0)
|
||||
{
|
||||
signups.Add(new ShiftSignup
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
ShiftId = cyclingShifts[0].Id,
|
||||
MemberId = cyclingMembers[0].Id,
|
||||
SignedUpAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (signups.Count > 0)
|
||||
{
|
||||
context.ShiftSignups.AddRange(signups);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateDeterministicGuid(string input)
|
||||
{
|
||||
// Generate a deterministic GUID from a string using MD5
|
||||
var hash = MD5.HashData(Encoding.UTF8.GetBytes(input));
|
||||
return new Guid(hash.Take(16).ToArray()).ToString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user