63 lines
4.6 KiB
Markdown
63 lines
4.6 KiB
Markdown
# Code Generation Plan — Unit 2: slave-availability-extension
|
|
|
|
## Scope
|
|
|
|
**Modify**: `src/SlpModularCms.Modules.Availability/` (existing project)
|
|
**Create**: `src/SlpModularCms.Modules.Availability.Master.Tests/` (new test project)
|
|
**Update**: `SlpModularCms.sln` (add new test project)
|
|
**Update**: existing `AvailabilityMiddlewareTests.cs` in `Availability.Tests` (new 3-param signature)
|
|
|
|
---
|
|
|
|
## Steps
|
|
|
|
### Part A — Production Code (Availability module)
|
|
|
|
- [x] **Step 1** — `SlpModularCms.Modules.Availability.csproj` — add `InternalsVisibleTo` for new test project
|
|
- [x] **Step 2** — `Data/Entities/MasterRegistration.cs` — entity with singleton Id constant
|
|
- [x] **Step 3** — `Data/AvailabilityDbContext.cs` — DbContext with MasterRegistrations DbSet + OnModelCreating
|
|
- [x] **Step 4** — `Repositories/IMasterRegistrationRepository.cs` — `GetAsync`, `AddAsync`, `Update`, `SaveChangesAsync`
|
|
- [x] **Step 5** — `Repositories/MasterRegistrationRepository.cs` — EF Core implementation
|
|
- [x] **Step 6** — `Services/IMasterApiKeyProtector.cs` — `Protect` + `Unprotect` (null on crypto failure)
|
|
- [x] **Step 7** — `Services/MasterApiKeyProtector.cs` — wraps `IDataProtectionProvider`; purpose `"SlpModularCms.Availability.MasterApiKey"`
|
|
- [x] **Step 8** — `Services/MasterGateStatus.cs` — `record MasterGateStatus(bool IsAvailable, string? DisableMessage)`
|
|
- [x] **Step 9** — `Services/MasterAvailabilityServiceDependencies.cs` — record with repo, protector, logger
|
|
- [x] **Step 10** — `Services/IMasterAvailabilityService.cs` — `RegisterAsync(bool)`, `PushStatusAsync(bool)`, `GetRegisteredUrlAsync(string?)`, `GetMasterStatus()`
|
|
- [x] **Step 11** — `Services/MasterAvailabilityService.cs` — volatile static fields + implementation of all 4 methods
|
|
- [x] **Step 12** — `Models/RegisterMasterRequest.cs` + `Models/PushStatusRequest.cs` — `[ExcludeFromCodeCoverage]` records
|
|
- [x] **Step 13** — `Controllers/MasterController.cs` — 3 endpoints; header extraction; delegate to service; Unauthorized() on false/null
|
|
- [x] **Step 14** — `Middleware/AvailabilityMiddleware.cs` — extend: add `/api/v1/master/` bypass; move admin check before local gate; add master gate between admin check and local gate
|
|
- [x] **Step 15** — `AvailabilityModule.cs` — add DbContext, protector, repo, deps, service registrations; add `MigrateAsync` in UseModule
|
|
|
|
### Part B — Test Project (new)
|
|
|
|
- [x] **Step 16** — `SlpModularCms.Modules.Availability.Master.Tests.csproj` — new project; copy packages from Master.Tests; add `Microsoft.IdentityModel.Tokens` for JWT tests
|
|
- [x] **Step 17** — `Repositories/MasterRegistrationRepositoryTests.cs` — EF InMemory; GetAsync (null + found); AddAsync; Update; SaveChanges
|
|
- [x] **Step 18** — `Services/MasterApiKeyProtectorTests.cs` — `EphemeralDataProtectionProvider`; round-trip; invalid ciphertext returns null
|
|
- [x] **Step 19** — `Services/MasterAvailabilityServiceTests.cs` — NSubstitute; all 4 public methods; key mismatch; first registration; re-registration; static cache state
|
|
- [x] **Step 20** — `Controllers/MasterControllerTests.cs` — NSubstitute; missing header → 401; success → 200; service returns false/null → 401
|
|
- [x] **Step 21** — `Middleware/AvailabilityMiddlewareMasterGateTests.cs` — NSubstitute; master gate blocks → 503; master gate passes → local gate evaluated; `/api/v1/master/` bypasses; master gate + admin JWT → pass through
|
|
|
|
### Part C — Cross-cutting
|
|
|
|
- [x] **Step 22** — `SlpModularCms.sln` — add `Availability.Master.Tests` project with new GUID
|
|
- [x] **Step 23** — `src/SlpModularCms.Modules.Availability.Tests/AvailabilityMiddlewareTests.cs` — update all `InvokeAsync(context, _service)` calls to `InvokeAsync(context, _service, masterSvc)` (masterSvc stub returning `IsAvailable=true`)
|
|
|
|
---
|
|
|
|
## Key Design Notes
|
|
|
|
| Concern | Decision |
|
|
|---------|----------|
|
|
| Singleton Id | `public static readonly Guid SingletonId = new("00000000-0000-0000-0000-000000000001")` |
|
|
| IMasterAvailabilityService.RegisterAsync | Returns `bool` (true=ok, false=key mismatch) |
|
|
| IMasterAvailabilityService.PushStatusAsync | Returns `bool` (true=ok, false=key mismatch) |
|
|
| IMasterAvailabilityService.GetRegisteredUrlAsync | Returns `string?` (null=unauthorized) |
|
|
| Unprotect null guard | `storedKey is null \|\| storedKey != incoming` → treat as mismatch |
|
|
| Middleware InvokeAsync order | bypass paths → admin JWT → master gate → local gate |
|
|
| Existing AvailabilityMiddlewareTests | Add stub `IMasterAvailabilityService` returning `IsAvailable=true` as 3rd param |
|
|
|
|
---
|
|
|
|
*Artifact path*: `aidlc-docs/features/master-cms-module/construction/plans/slave-availability-extension-code-generation-plan.md`
|