chore(backend): add explicit test data seed command
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
namespace Socialize.Api.Infrastructure.Development;
|
|
||||||
|
|
||||||
public record DevelopmentSeedOptions
|
|
||||||
{
|
|
||||||
public const string SectionName = "DevelopmentSeed";
|
|
||||||
|
|
||||||
public bool Enabled { get; init; } = true;
|
|
||||||
}
|
|
||||||
@@ -16,11 +16,10 @@ using Socialize.Api.Modules.Organizations.Data;
|
|||||||
using Socialize.Api.Modules.Organizations.Services;
|
using Socialize.Api.Modules.Organizations.Services;
|
||||||
using Socialize.Api.Modules.Workspaces.Data;
|
using Socialize.Api.Modules.Workspaces.Data;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Socialize.Api.Infrastructure.Development;
|
namespace Socialize.Api.Infrastructure.TestData;
|
||||||
|
|
||||||
public static class DevelopmentSeedExtensions
|
public static class TestDataSeedExtensions
|
||||||
{
|
{
|
||||||
private static readonly Guid OrganizationId = Guid.Parse("99999999-9999-9999-9999-999999999999");
|
private static readonly Guid OrganizationId = Guid.Parse("99999999-9999-9999-9999-999999999999");
|
||||||
private static readonly Guid WorkspaceId = Guid.Parse("11111111-1111-1111-1111-111111111111");
|
private static readonly Guid WorkspaceId = Guid.Parse("11111111-1111-1111-1111-111111111111");
|
||||||
@@ -39,23 +38,11 @@ public static class DevelopmentSeedExtensions
|
|||||||
private static readonly Guid ClientCommentId = Guid.Parse("77777777-7777-7777-7777-777777777777");
|
private static readonly Guid ClientCommentId = Guid.Parse("77777777-7777-7777-7777-777777777777");
|
||||||
private static readonly Guid NotificationId = Guid.Parse("88888888-8888-8888-8888-888888888888");
|
private static readonly Guid NotificationId = Guid.Parse("88888888-8888-8888-8888-888888888888");
|
||||||
|
|
||||||
public static async Task<IApplicationBuilder> UseDevelopmentSeedAsync(
|
public static async Task<IServiceProvider> SeedTestDataAsync(
|
||||||
this IApplicationBuilder app,
|
this IServiceProvider services,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
IHostEnvironment environment = app.ApplicationServices.GetRequiredService<IHostEnvironment>();
|
using IServiceScope scope = services.CreateScope();
|
||||||
if (!environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
using IServiceScope scope = app.ApplicationServices.CreateScope();
|
|
||||||
IOptions<DevelopmentSeedOptions> options = scope.ServiceProvider.GetRequiredService<IOptions<DevelopmentSeedOptions>>();
|
|
||||||
if (!options.Value.Enabled)
|
|
||||||
{
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
UserManager userManager = scope.ServiceProvider.GetRequiredService<UserManager>();
|
UserManager userManager = scope.ServiceProvider.GetRequiredService<UserManager>();
|
||||||
AppDbContext dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
AppDbContext dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
@@ -64,7 +51,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
id: Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
|
id: Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
|
||||||
username: "manager",
|
username: "manager",
|
||||||
email: "manager@socialize.local",
|
email: "manager@socialize.local",
|
||||||
password: "manager",
|
password: "Manager1!",
|
||||||
alias: "Northstar Manager",
|
alias: "Northstar Manager",
|
||||||
firstname: "Morgan",
|
firstname: "Morgan",
|
||||||
lastname: "Reid",
|
lastname: "Reid",
|
||||||
@@ -80,7 +67,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
id: Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
|
id: Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
|
||||||
username: "client",
|
username: "client",
|
||||||
email: "client@socialize.local",
|
email: "client@socialize.local",
|
||||||
password: "client",
|
password: "Client1!",
|
||||||
alias: "Sofia Martin",
|
alias: "Sofia Martin",
|
||||||
firstname: "Sofia",
|
firstname: "Sofia",
|
||||||
lastname: "Martin",
|
lastname: "Martin",
|
||||||
@@ -97,7 +84,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
id: Guid.Parse("cccccccc-cccc-cccc-cccc-cccccccccccc"),
|
id: Guid.Parse("cccccccc-cccc-cccc-cccc-cccccccccccc"),
|
||||||
username: "provider",
|
username: "provider",
|
||||||
email: "provider@socialize.local",
|
email: "provider@socialize.local",
|
||||||
password: "provider",
|
password: "Provider1!",
|
||||||
alias: "Alex Studio",
|
alias: "Alex Studio",
|
||||||
firstname: "Alex",
|
firstname: "Alex",
|
||||||
lastname: "Studio",
|
lastname: "Studio",
|
||||||
@@ -115,7 +102,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
id: Guid.Parse("dddddddd-dddd-dddd-dddd-dddddddddddd"),
|
id: Guid.Parse("dddddddd-dddd-dddd-dddd-dddddddddddd"),
|
||||||
username: "dev",
|
username: "dev",
|
||||||
email: "dev@socialize.local",
|
email: "dev@socialize.local",
|
||||||
password: "dev",
|
password: "Developer1!",
|
||||||
alias: "Socialize Dev",
|
alias: "Socialize Dev",
|
||||||
firstname: "Jo",
|
firstname: "Jo",
|
||||||
lastname: "Bumble",
|
lastname: "Bumble",
|
||||||
@@ -138,7 +125,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
dbContext,
|
dbContext,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
return app;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<User> EnsureUserAsync(
|
private static async Task<User> EnsureUserAsync(
|
||||||
@@ -175,7 +162,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
if (!createResult.Succeeded)
|
if (!createResult.Succeeded)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
$"Failed to seed development user '{username}': {string.Join(", ", createResult.Errors.Select(error => error.Description))}");
|
$"Failed to seed test user '{username}': {string.Join(", ", createResult.Errors.Select(error => error.Description))}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +182,7 @@ public static class DevelopmentSeedExtensions
|
|||||||
if (!passwordResetResult.Succeeded)
|
if (!passwordResetResult.Succeeded)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
$"Failed to set development password for '{username}': {string.Join(", ", passwordResetResult.Errors.Select(error => error.Description))}");
|
$"Failed to set test password for '{username}': {string.Join(", ", passwordResetResult.Errors.Select(error => error.Description))}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Socialize.Api.Modules.Workspaces.Data;
|
using Socialize.Api.Modules.Workspaces.Data;
|
||||||
using Socialize.Api.Infrastructure.Development;
|
|
||||||
|
|
||||||
namespace Socialize.Api.Modules.Workspaces;
|
namespace Socialize.Api.Modules.Workspaces;
|
||||||
|
|
||||||
@@ -8,9 +7,6 @@ public static class DependencyInjection
|
|||||||
public static WebApplicationBuilder AddWorkspaceModule(
|
public static WebApplicationBuilder AddWorkspaceModule(
|
||||||
this WebApplicationBuilder builder)
|
this WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<DevelopmentSeedOptions>(
|
|
||||||
builder.Configuration.GetSection(DevelopmentSeedOptions.SectionName));
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using Socialize;
|
|||||||
using Socialize.Api.Infrastructure.BlobStorage.Configuration;
|
using Socialize.Api.Infrastructure.BlobStorage.Configuration;
|
||||||
using Socialize.Api.Infrastructure.BlobStorage.Services;
|
using Socialize.Api.Infrastructure.BlobStorage.Services;
|
||||||
using Socialize.Api.Infrastructure;
|
using Socialize.Api.Infrastructure;
|
||||||
using Socialize.Api.Infrastructure.Development;
|
using Socialize.Api.Infrastructure.TestData;
|
||||||
using Socialize.Api.Modules.Approvals;
|
using Socialize.Api.Modules.Approvals;
|
||||||
using Socialize.Api.Modules.Assets;
|
using Socialize.Api.Modules.Assets;
|
||||||
using Socialize.Api.Modules.Channels;
|
using Socialize.Api.Modules.Channels;
|
||||||
@@ -23,6 +23,7 @@ using Socialize.Api.Modules.Workspaces;
|
|||||||
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
bool seedTestData = args.Any(arg => string.Equals(arg, "seed-testdata", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
string? vaultUri = Environment.GetEnvironmentVariable("VaultUri");
|
string? vaultUri = Environment.GetEnvironmentVariable("VaultUri");
|
||||||
if (!string.IsNullOrWhiteSpace(vaultUri))
|
if (!string.IsNullOrWhiteSpace(vaultUri))
|
||||||
@@ -78,6 +79,25 @@ builder.AddCalendarIntegrationsModule();
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
if (seedTestData)
|
||||||
|
{
|
||||||
|
if (app.Environment.IsProduction()
|
||||||
|
&& !string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("CONFIRM_PRODUCTION_SEED"),
|
||||||
|
"true",
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Refusing to seed test data in Production without CONFIRM_PRODUCTION_SEED=true.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.UseAppDataAsync();
|
||||||
|
await app.UseIdentityModuleAsync();
|
||||||
|
await app.Services.SeedTestDataAsync();
|
||||||
|
Console.WriteLine("Seeded test data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
app.UseForwardedHeaders(
|
app.UseForwardedHeaders(
|
||||||
new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto }
|
new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto }
|
||||||
);
|
);
|
||||||
@@ -90,7 +110,6 @@ app.UseAuthorization();
|
|||||||
// Initialize and seed the db.
|
// Initialize and seed the db.
|
||||||
await app.UseAppDataAsync();
|
await app.UseAppDataAsync();
|
||||||
await app.UseIdentityModuleAsync();
|
await app.UseIdentityModuleAsync();
|
||||||
await app.UseDevelopmentSeedAsync();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
|
|||||||
@@ -22,8 +22,5 @@
|
|||||||
"Lifetime": "00:05:00",
|
"Lifetime": "00:05:00",
|
||||||
"RefreshTokenLifetime": "0.00:30:00"
|
"RefreshTokenLifetime": "0.00:30:00"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"DevelopmentSeed": {
|
|
||||||
"Enabled": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,5 @@
|
|||||||
"LocalBlobStorage": {
|
"LocalBlobStorage": {
|
||||||
"RootPath": "App_Data/blob-storage",
|
"RootPath": "App_Data/blob-storage",
|
||||||
"RequestPath": "/api/storage"
|
"RequestPath": "/api/storage"
|
||||||
},
|
|
||||||
"DevelopmentSeed": {
|
|
||||||
"Enabled": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Replace frontend-derived fake channels with real workspace-owned channel records
|
|||||||
|
|
||||||
- Add a backend `Channels` module with a `Channel` table.
|
- Add a backend `Channels` module with a `Channel` table.
|
||||||
- Add list and create endpoints for workspace channels.
|
- Add list and create endpoints for workspace channels.
|
||||||
- Seed development channels and align seeded content publication targets to those configured channel names.
|
- Seed test channels and align seeded content publication targets to those configured channel names.
|
||||||
- Update the frontend channels store to load and create channels through the API.
|
- Update the frontend channels store to load and create channels through the API.
|
||||||
- Keep the Channels page UI shape intact.
|
- Keep the Channels page UI shape intact.
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Replace frontend-derived fake channels with real workspace-owned channel records
|
|||||||
|
|
||||||
- `backend/src/Socialize.Api/Data/AppDbContext.cs`
|
- `backend/src/Socialize.Api/Data/AppDbContext.cs`
|
||||||
- `backend/src/Socialize.Api/Modules/Channels/`
|
- `backend/src/Socialize.Api/Modules/Channels/`
|
||||||
- `backend/src/Socialize.Api/Infrastructure/Development/DevelopmentSeedExtensions.cs`
|
- `backend/src/Socialize.Api/Infrastructure/TestData/TestDataSeedExtensions.cs`
|
||||||
- `frontend/src/features/channels/stores/channelsStore.js`
|
- `frontend/src/features/channels/stores/channelsStore.js`
|
||||||
- `docs/FEATURES/channels.md`
|
- `docs/FEATURES/channels.md`
|
||||||
|
|
||||||
|
|||||||
20
docs/TASKS/development/001-script-testdata-seed.md
Normal file
20
docs/TASKS/development/001-script-testdata-seed.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Task: Add explicit test data seed script
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Move demo/test data seeding out of API startup and into an explicit script command that can be run intentionally against any configured environment.
|
||||||
|
|
||||||
|
## Relevant Files
|
||||||
|
|
||||||
|
- `backend/src/Socialize.Api/Program.cs`
|
||||||
|
- `backend/src/Socialize.Api/Infrastructure/TestData/TestDataSeedExtensions.cs`
|
||||||
|
- `backend/src/Socialize.Api/appsettings.Development.json`
|
||||||
|
- `backend/src/Socialize.Api/appsettings.Production.json`
|
||||||
|
- `scripts/seed-testdata.sh`
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet build backend/Socialize.slnx
|
||||||
|
./scripts/seed-testdata.sh
|
||||||
|
```
|
||||||
19
scripts/seed-testdata.sh
Executable file
19
scripts/seed-testdata.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
export ASPNETCORE_ENVIRONMENT="${ASPNETCORE_ENVIRONMENT:-Development}"
|
||||||
|
|
||||||
|
if [[ "$ASPNETCORE_ENVIRONMENT" == "Production" && "${CONFIRM_PRODUCTION_SEED:-}" != "true" ]]; then
|
||||||
|
echo "Refusing to seed test data in Production without CONFIRM_PRODUCTION_SEED=true." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
dotnet run \
|
||||||
|
--no-launch-profile \
|
||||||
|
--project backend/src/Socialize.Api/Socialize.Api.csproj \
|
||||||
|
-- seed-testdata "$@"
|
||||||
Reference in New Issue
Block a user