Files
slp-modular-cms/aidlc-docs/features/slpsoftware-api/construction/plans/slpsoftware-client-setup-code-generation-plan.md
SluijsensandClaude Sonnet 5 fa389e42ee
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Successful in 6m10s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m59s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m27s
Continuous Integration / backend-test (pull_request) Failing after 7m48s
Continuous Integration / frontend-build (pull_request) Successful in 2m5s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Adds SlpModularCms.Api.SlpSoftware and extracts shared CmsHost composition
Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project
in the Clients solution folder, intended to eventually become the deployed
API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as
SlpModularCms.Api plus a future Offerings module.

- Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into
  SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline),
  shared by both Client projects so they cannot drift apart
- Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into
  Core, since CmsHost cannot live in Api but Core cannot depend on Api
- Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database
  and dev ports (5286/7223, distinct from Api's and Api.Slave's)
- Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline
  regression tests (security headers, health check, SPA fallback, rate
  limiting), scoped to Api per NFR Design
- Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave
- Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing
  refresh token) as unhandled errors -- pre-existing, unrelated to this
  feature's own scope, found while testing the new instance

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
2026-08-02 01:28:39 +02:00

87 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Code Generation Plan — Unit: SlpSoftware Client Setup
## Unit Context
**Stories implemented**: None directly (unit-of-work-story-map.md — this unit is a purely technical enabling unit).
**Functional requirements implemented**: FR-1, FR-2, FR-3.
**Dependencies**: None on other units (this unit is the dependency Unit 2 "Offerings" needs).
**Expected interfaces produced**: `SlpModularCms.Core.Hosting.CmsHost` (`ConfigureServices`/`ConfigurePipeline`), consumed by both Client projects' `Program.cs`.
**Database entities owned**: None.
**Workspace root**: `K:\Development\Projects\SlpModularCms` (brownfield — modify/move existing files where noted, never duplicate).
## Investigation Finding That Shapes This Plan
Reading `SlpModularCms.Api/Program.cs` and `SlpModularCms.Api/Extensions/StaticContentExtensions.cs` directly (not assumed) revealed that `UseCmsStaticContent()`/`MapCmsSpaFallbacks()` — needed by `CmsHost.ConfigurePipeline` — currently live in the **`Api` project itself**, not in `Core`. Since `Core` cannot depend on `Api` (wrong dependency direction — `Api` depends on `Core`, never the reverse), this file (and its embedded `WebsitePlaceholder.html` resource) must move into `Core` **before** `CmsHost` can call it. This is Step 1 below, not an afterthought.
---
## Steps
### Step 1 — Move Static Content Hosting into Core (prerequisite for CmsHost)
- [x] Move `src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs``src/SlpModularCms.Core/Hosting/StaticContentExtensions.cs`; change namespace `SlpModularCms.Api.Extensions``SlpModularCms.Core.Hosting`; update `PlaceholderResourceName` from `"SlpModularCms.Api.Extensions.WebsitePlaceholder.html"` to `"SlpModularCms.Core.Hosting.WebsitePlaceholder.html"`
- [x] Move `src/SlpModularCms.Api/Extensions/WebsitePlaceholder.html``src/SlpModularCms.Core/Hosting/WebsitePlaceholder.html`
- [x] `SlpModularCms.Core.csproj`: add `<EmbeddedResource Include="Hosting\WebsitePlaceholder.html" />`
- [x] `SlpModularCms.Api.csproj`: remove the now-obsolete `<EmbeddedResource Include="Extensions\WebsitePlaceholder.html" />` item group and its explanatory comment (the file no longer lives there)
- [x] Delete the now-empty `src/SlpModularCms.Api/Extensions/` directory if nothing else remains in it
### Step 2 — `CmsHostOptions` (Business Logic Generation — Core)
- [x] Create `src/SlpModularCms.Core/Hosting/CmsHostOptions.cs`: an intentionally empty class (NFR Design Pattern 2 / Q2 = B) — a pure extension point, no properties yet
### Step 3 — `CmsHost` (Business Logic Generation — Core)
- [x] Create `src/SlpModularCms.Core/Hosting/CmsHost.cs` with:
- `public static ModuleOrchestrator ConfigureServices(WebApplicationBuilder builder, CmsHostOptions options)` — reproduces `Api/Program.cs` lines for: `appsettings.local.json` loading stays in each project's own `Program.cs` (NOT moved here — application-design.md component-methods.md is explicit that bootstrap lines stay per-project); logging (`AddCmsLogging`), Sentry (`UseCmsSentry`), `ModuleOrchestrator` construction + `DiscoverModules()`, `AddCoreInfrastructure`, `AddCmsCors`, `AddCmsRateLimiting`, `AddCmsHealthChecks`, `AddCmsSecurityHeaders`, `AddCmsObservability`, `AddCmsDataProtection` (before module services — order preserved exactly), `RegisterModuleServices`, `AddSingleton(orchestrator)`, `AddControllers` with `ApiPrefixConvention("api/v1")` + `JsonStringEnumConverter`. Returns the orchestrator.
- `public static void ConfigurePipeline(WebApplication app, ModuleOrchestrator orchestrator, CmsHostOptions options)` — reproduces: `MigrateCoreDatabase`, `UseExceptionHandler`, `UseCmsSecurityHeaders`, `UseRateLimiter`, Development-only `MapOpenApi`/`MapScalarApiReference`, `UseHttpsRedirection`, `UseCmsStaticContent`, `UseCors`, `orchestrator.UseModules(app)`, `UseAuthentication`/`UseAuthorization`, `MapControllers`, `MapCmsHealthChecks`, `MapSentryTunnel`, `MapCmsSpaFallbacks` — same order as today's `Program.cs`, since that order encodes real constraints documented in its comments
- [x] Both methods accept `CmsHostOptions` per NFR-CS-02, even though it currently has no properties to read
### Step 4 — Repoint `SlpModularCms.Api/Program.cs` (Modify In-Place)
- [x] Rewrite `src/SlpModularCms.Api/Program.cs` to the thin form: create builder, load `appsettings.local.json`, `var orchestrator = CmsHost.ConfigureServices(builder, new CmsHostOptions());`, `var app = builder.Build();`, `CmsHost.ConfigurePipeline(app, orchestrator, new CmsHostOptions());`, `app.Run();`
- [x] No behavior change — verified by Step 7's regression tests
### Step 5 — New Client Project: `SlpModularCms.Api.SlpSoftware` (Project Structure Setup)
- [x] Create `src/SlpModularCms.Api.SlpSoftware/SlpModularCms.Api.SlpSoftware.csproj` — mirrors `SlpModularCms.Api.csproj` (SDK, `TargetFramework`, `Nullable`, `ImplicitUsings`, same package references: `Asp.Versioning.Mvc`, `Microsoft.AspNetCore.Authentication.JwtBearer`, `Microsoft.AspNetCore.OpenApi`, `Microsoft.EntityFrameworkCore.Design`, `Scalar.AspNetCore`), **without** the `WebsitePlaceholder.html` embedded resource (that now lives in `Core`, shared) and **without** a `Modules.Offerings` reference (added later by Unit 2, per unit-of-work.md)
- `ProjectReference`: `SlpModularCms.Core`, `SlpModularCms.Modules.Availability`, `SlpModularCms.Modules.Identity`, `SlpModularCms.Modules.Master` (FR-2)
- [x] Create `src/SlpModularCms.Api.SlpSoftware/Program.cs` — same thin shape as Step 4's rewritten `Api/Program.cs`
- [x] Create `src/SlpModularCms.Api.SlpSoftware/appsettings.json` — mirrors `Api`'s structure/keys (connection string placeholder, JWT settings, Availability, MasterModule, MasterPolling, Cors, RateLimiting, SecurityHeaders, Observability sections)
- [x] Create `src/SlpModularCms.Api.SlpSoftware/appsettings.Development.json` — mirrors `Api`'s Development file, but with its **own isolated local dev database name** (Infrastructure Design Q1 = B), e.g. `Database=SlpModularCmsSlpSoftwareDev`
- [x] **Not creating** `appsettings.local.json` — it's git-ignored (verified: listed in `.gitignore`, not tracked in git) and personal per-developer; the developer creates their own copy locally if needed, same as for `Api`
- [x] **Not creating** `Program.Coverage.cs` for this project in this unit — Infrastructure/NFR Design (Q1 = C) scoped the new pipeline regression tests to `Api` only, so there is no test target requiring `Program` to be a public partial class here yet; add it in a future unit/feature if `Api.SlpSoftware`-specific pipeline tests are ever introduced
### Step 6 — Solution File Updates (`SlpModularCms.sln`)
- [x] Add `SlpModularCms.Api.SlpSoftware` project entry, nested under the existing (currently empty) `Clients` solution folder (`{D72703E6-B021-4360-B1EE-0E99999B5899}`)
- [x] Add `SlpModularCms.Api.Tests` project entry (Step 7), nested directly under `Tests` (`{2F43D186-C7D5-4AB1-B821-4D595CA2ECB3}`) — mirroring how `SlpModularCms.Core.Tests` is nested directly under `Tests`, not under `Tests/Modules`
- [x] Add both new projects' GUIDs to `ProjectConfigurationPlatforms` (Debug/Release × Any CPU/x64/x86, matching the existing pattern for every other project)
- [x] Add both new projects' GUIDs to `NestedProjects`
### Step 7 — Pipeline Regression Tests (Business Logic Unit Testing, NFR-CS-01)
- [x] Create `src/SlpModularCms.Api.Tests/SlpModularCms.Api.Tests.csproj` — same SDK-style shape as `SlpModularCms.Core.Tests.csproj` (xunit, FluentAssertions, `Microsoft.NET.Test.Sdk`, `coverlet.collector`), plus `Microsoft.AspNetCore.Mvc.Testing` (provides `WebApplicationFactory<TEntryPoint>`); `ProjectReference` to `SlpModularCms.Api.csproj` (its `Program.Coverage.cs` already makes `Program` a public partial class, so no further change needed there)
- [x] Create `src/SlpModularCms.Api.Tests/PipelineTests.cs` using `WebApplicationFactory<Program>`, asserting (NFR-CS-01 / NFR Design Pattern 1):
- Required security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) present on a representative response
- `/health` returns a successful response
- A non-file `/admin/{path}` route resolves to the admin SPA's `index.html` fallback (or, absent a built SPA in the test environment, at minimum does not 404 as a missing-file/static-asset request would)
- A burst of requests against a rate-limited route eventually receives `429 Too Many Requests`
### Step 8 — Business Logic Summary
- [x] Create `aidlc-docs/features/slpsoftware-api/construction/slpsoftware-client-setup/code/summary.md` (markdown only) summarizing: files moved (Step 1), `CmsHost`/`CmsHostOptions` added, `Api/Program.cs` rewritten, `Api.SlpSoftware` created, solution file changes, tests added
### Step 9 — API Layer / Repository Layer Generation
- [x] **N/A** — this unit introduces no new HTTP endpoints or persisted entities (FR-1/FR-2/FR-3 are pure composition/project-scaffolding); these categories apply to Unit 2 "Offerings" instead
### Step 10 — Database Migration Scripts
- [x] **N/A** — no new data model in this unit
### Step 11 — Documentation Generation
- [x] Update root `README.md`: add `SlpModularCms.Api.SlpSoftware` to the project-structure description alongside `SlpModularCms.Api`/`SlpModularCms.Api.Slave`, and add a short note under the existing architecture/hosting section explaining `CmsHost` as the shared composition point both Client projects call, plus a one-line note that `Api.SlpSoftware` uses its own local dev database (Infrastructure Design Q1 = B)
### Step 12 — Deployment Artifacts Generation
- [x] **N/A for this Construction stage** — per Infrastructure Design, no `.gitea/workflows/*.yaml` or Gitea Actions variable changes happen here; the CI/CD retarget is explicitly Operations-phase work (D-7/D-15)
### Step 13 — Build and Test Verification (automatic, Step 13.5 of the workflow)
- [x] Build the full solution (or at minimum `Api`, `Api.SlpSoftware`, `Core`, `Api.Tests`) and confirm it compiles
- [x] Run `SlpModularCms.Api.Tests` (new) and confirm all pipeline regression tests pass against `Api`
- [x] Run `SlpModularCms.Core.Tests` (existing) and confirm nothing regressed from the Step 1 file move
- [x] Fix and retry on any failure; only surface to the user if a fix requires a decision only they can make
---
**Scope reminder**: this plan implements Unit 1 only. Unit 2 "Offerings" (all 12 user stories) is a separate Code Generation pass, after this unit is approved and its own Build and Test step is green.