Adds SlpModularCms.Api.SlpSoftware and extracts shared CmsHost composition
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
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
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
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# Component Methods — SlpSoftware Production API
|
||||
|
||||
**Note**: Signatures and high-level purpose only. Detailed business rules (exact exclusivity algorithm, validation rules, reorder edge cases) are defined in Functional Design for the relevant unit (CONSTRUCTION phase).
|
||||
|
||||
## `CmsHost` (static class, `SlpModularCms.Core.Hosting`)
|
||||
|
||||
| Method | Input | Output | Purpose |
|
||||
|---|---|---|---|
|
||||
| `ConfigureServices` | `WebApplicationBuilder builder` | `ModuleOrchestrator` | Runs logging/Sentry setup, discovers and registers module services, registers core infrastructure (CORS, rate limiting, health checks, security headers, observability, Data Protection), and configures MVC controllers. Returns the `ModuleOrchestrator` instance so the caller can pass it into `ConfigurePipeline` after `builder.Build()`. |
|
||||
| `ConfigurePipeline` | `WebApplication app`, `ModuleOrchestrator orchestrator` | `void` | Runs the Core DB migration, wires the exception handler, security headers, rate limiter, Development-only OpenAPI/Scalar, HTTPS redirection, static content + SPA fallbacks, CORS, module middleware (`orchestrator.UseModules`), authentication/authorization, controller mapping, health checks, and the Sentry tunnel — in the exact order `Api/Program.cs` uses today, since that order encodes real constraints (documented as code comments in the current `Program.cs`). |
|
||||
|
||||
Each project's `Program.cs` becomes:
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Configuration.AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true);
|
||||
var orchestrator = CmsHost.ConfigureServices(builder);
|
||||
var app = builder.Build();
|
||||
CmsHost.ConfigurePipeline(app, orchestrator);
|
||||
app.Run();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `IOfferingRepository` (`SlpModularCms.Modules.Offerings.Repositories`)
|
||||
|
||||
| Method | Input | Output | Purpose |
|
||||
|---|---|---|---|
|
||||
| `GetAllAsync` | — | `IReadOnlyList<Offering>` | All non-deleted offerings, ordered by `DisplayOrder`. |
|
||||
| `GetByIdAsync` | `Guid id` | `Offering?` | Single non-deleted offering, or `null`. |
|
||||
| `AddAsync` | `Offering offering` | `Offering` | Persist a new offering. |
|
||||
| `UpdateAsync` | `Offering offering` | `Offering` | Persist changes to an existing offering. |
|
||||
| `GetMaxDisplayOrderAsync` | — | `int` | Used by the service to append new offerings at the end of the display order. |
|
||||
| `GetFeaturedAsync` | — | `Offering?` | The currently-featured offering (if any), used to enforce exclusivity. |
|
||||
|
||||
---
|
||||
|
||||
## `IOfferingsService` (`SlpModularCms.Modules.Offerings.Services`)
|
||||
|
||||
| Method | Input | Output | Purpose |
|
||||
|---|---|---|---|
|
||||
| `GetPublicOfferingsAsync` | — | `IReadOnlyList<OfferingDto>` | Backs FR-6 / US-01, US-02, US-03. |
|
||||
| `GetAllForAdminAsync` | — | `IReadOnlyList<OfferingAdminDto>` | Backs the admin list view. |
|
||||
| `CreateAsync` | `CreateOfferingRequest request` | `OfferingAdminDto` | Backs US-04. Assigns a new `Id` (Q4 = A) and appends to the end of `DisplayOrder`. If `request.Featured` is `true`, un-features the previously-featured offering (US-10). |
|
||||
| `UpdateAsync` | `Guid id`, `UpdateOfferingRequest request` | `OfferingAdminDto` | Backs US-05. Same featured-exclusivity handling as `CreateAsync` when `request.Featured` is `true`. |
|
||||
| `DeleteAsync` | `Guid id` | `void` | Backs US-06/US-07. Soft-delete (Q5 = B) — always allowed, including for the last remaining offering. |
|
||||
| `ReorderAsync` | `IReadOnlyList<Guid> orderedIds` | `void` | Backs US-08 (drag-and-drop). Full-list reorder — reassigns `DisplayOrder` to match the given sequence. |
|
||||
| `MoveUpAsync` | `Guid id` | `void` | Backs US-09. Swaps `DisplayOrder` with the immediately preceding offering. |
|
||||
| `MoveDownAsync` | `Guid id` | `void` | Backs US-09. Swaps `DisplayOrder` with the immediately following offering. |
|
||||
|
||||
---
|
||||
|
||||
## `OfferingsController` (`SlpModularCms.Modules.Offerings.Controllers`)
|
||||
|
||||
| Action | Route | Auth | Input | Output | Purpose |
|
||||
|---|---|---|---|---|---|
|
||||
| `GetOfferings` | `GET /api/v1/offerings` | `[AllowAnonymous]` | — | `200 OK`, `OfferingDto[]` | FR-6 |
|
||||
| `GetAllForAdmin` | `GET /api/v1/offerings/admin` | `AdminOnly` | — | `200 OK`, `OfferingAdminDto[]` | Admin list view |
|
||||
| `Create` | `POST /api/v1/offerings/admin` | `AdminOnly` | `CreateOfferingRequest` | `201 Created`, `OfferingAdminDto` | US-04 |
|
||||
| `Update` | `PUT /api/v1/offerings/admin/{id}` | `AdminOnly` | `UpdateOfferingRequest` | `200 OK`, `OfferingAdminDto` | US-05 |
|
||||
| `Delete` | `DELETE /api/v1/offerings/admin/{id}` | `AdminOnly` | — | `204 No Content` | US-06/US-07 |
|
||||
| `Reorder` | `PUT /api/v1/offerings/admin/reorder` | `AdminOnly` | `ReorderOfferingsRequest` (ordered `Guid[]`) | `204 No Content` | US-08 |
|
||||
| `MoveUp` | `POST /api/v1/offerings/admin/{id}/move-up` | `AdminOnly` | — | `204 No Content` | US-09 |
|
||||
| `MoveDown` | `POST /api/v1/offerings/admin/{id}/move-down` | `AdminOnly` | — | `204 No Content` | US-09 |
|
||||
|
||||
**Note**: Exact route naming (e.g. `/admin` suffix vs. a route-group prefix) may be refined in Functional Design or Code Generation Planning; the split shown here keeps the public route exactly as FR-6 specifies while keeping admin routes obviously distinct, consistent with decision Q3 (single controller, per-action authorization).
|
||||
|
||||
---
|
||||
|
||||
## `OfferingsModule` (`SlpModularCms.Modules.Offerings`)
|
||||
|
||||
| Method | Input | Output | Purpose |
|
||||
|---|---|---|---|
|
||||
| `RegisterServices` | `IServiceCollection services` | `void` | Registers `OfferingsDbContext` (MySQL), `IOfferingRepository`/`OfferingRepository`, `IOfferingsService`/`OfferingsService`. |
|
||||
| `UseModule` | `IApplicationBuilder app` | `void` | Applies pending `OfferingsDbContext` migrations. |
|
||||
Reference in New Issue
Block a user