12 KiB
12 KiB
Code Generation Plan — Unit 1: master-backend
Unit Context
Unit: master-backend
Workspace root: K:\Development\Projects\SlpModularCms
Project type: Brownfield — new project added to existing solution
New projects:
src/SlpModularCms.Modules.Master/(application code)src/SlpModularCms.Modules.Master.Tests/(test code)
Modified files:
src/SlpModularCms.Api/SlpModularCms.Api.csproj— add project reference to Master moduleSlpModularCms.sln— add both new projects
Test stack (matching existing projects): xUnit, NSubstitute, FluentAssertions, EF Core InMemory
New NuGet dependency: Microsoft.Extensions.Http.Resilience (Polly integration)
Requirements covered: FR-MASTER-01/02/03/04/05/10/12/13/14; NFR-MASTER-03/04/05/06
Generation Steps
STEP 1 — Project files & solution wiring
- 1a. Create
src/SlpModularCms.Modules.Master/SlpModularCms.Modules.Master.csproj- Target:
net10.0; Nullable + ImplicitUsings enabled - ProjectReference:
SlpModularCms.Core - PackageReference:
Microsoft.Extensions.Http.Resilience(latest stable)
- Target:
- 1b. Create
src/SlpModularCms.Modules.Master.Tests/SlpModularCms.Modules.Master.Tests.csproj- Target:
net10.0;IsPackable = false - PackageReference:
xunit,xunit.runner.visualstudio,Microsoft.NET.Test.Sdk,coverlet.collector,NSubstitute,FluentAssertions,Microsoft.EntityFrameworkCore.InMemory,Microsoft.Extensions.Logging.Abstractions - ProjectReference:
SlpModularCms.Modules.Master - Global using:
Xunit
- Target:
- 1c. Modify
src/SlpModularCms.Api/SlpModularCms.Api.csproj— add<ProjectReference Include="..\SlpModularCms.Modules.Master\SlpModularCms.Modules.Master.csproj" /> - 1d. Modify
SlpModularCms.sln— add both new projects with correct GUIDs and folder placement
STEP 2 — Configuration options
- 2a. Create
src/SlpModularCms.Modules.Master/Options/MasterModuleOptions.cs- Properties:
IntegrityCheckIntervalMinutes(int, 60),HttpTimeoutSeconds(int, 10),MasterUrl(string?),CacheMinutes(int, 60),ApiKey(string?)
- Properties:
STEP 3 — Domain entities
- 3a. Create
src/SlpModularCms.Modules.Master/Data/Entities/CmsInstanceStatus.cs- Enum:
Available = 0,NotAvailable = 1,Inactive = 2
- Enum:
- 3b. Create
src/SlpModularCms.Modules.Master/Data/Entities/CmsInstance.cs- All fields per domain-entities.md (including
LastIntegrityCheckFailedAt) [ExcludeFromCodeCoverage]NOT applied — entity has no logic; covered by service tests
- All fields per domain-entities.md (including
STEP 4 — DTOs and request/response models
- 4a. Create
src/SlpModularCms.Modules.Master/Models/CmsInstanceDto.cs—[ExcludeFromCodeCoverage] - 4b. Create
src/SlpModularCms.Modules.Master/Models/CreateCmsInstanceRequest.cs—[ExcludeFromCodeCoverage] - 4c. Create
src/SlpModularCms.Modules.Master/Models/UpdateStatusRequest.cs—[ExcludeFromCodeCoverage] - 4d. Create
src/SlpModularCms.Modules.Master/Models/UpdateStatusResult.cs—[ExcludeFromCodeCoverage]
STEP 5 — EF Core DbContext
- 5a. Create
src/SlpModularCms.Modules.Master/Data/MasterDbContext.csDbSet<CmsInstance> CmsInstancesOnModelCreating: configure entity (table name, required fields, max lengths)- Constructor: accepts
DbContextOptions<MasterDbContext>
STEP 6 — Repository
- 6a. Create
src/SlpModularCms.Modules.Master/Repositories/ICmsInstanceRepository.csGetAllAsync,GetActiveAsync,GetByIdAsync,AddAsync,UpdateAsync,SaveChangesAsync
- 6b. Create
src/SlpModularCms.Modules.Master/Repositories/CmsInstanceRepository.cs- Inject
MasterDbContext; implement all methods GetActiveAsyncfiltersStatus != CmsInstanceStatus.Inactive
- Inject
STEP 7 — Security: ApiKeyProtector
- 7a. Create
src/SlpModularCms.Modules.Master/Services/IApiKeyProtector.csstring Protect(string plainApiKey)string Unprotect(string encryptedApiKey)
- 7b. Create
src/SlpModularCms.Modules.Master/Services/ApiKeyProtector.cs- Inject
IDataProtectionProvider; purpose string"SlpModularCms.Master.ApiKey" [ExcludeFromCodeCoverage]NOT applied — testable via unit test with realEphemeralDataProtectionProvider
- Inject
STEP 8 — HTTP client: SlaveApiClient
- 8a. Create
src/SlpModularCms.Modules.Master/Services/ISlaveApiClient.csRegisterMasterAsync,PushStatusAsync,GetRegisteredMasterUrlAsync
- 8b. Create
src/SlpModularCms.Modules.Master/Services/SlaveApiClient.cs- Inject
HttpClient(typed client) - Each method: build request with
X-Master-Api-Keyheader; handle non-success responses; returnfalse/nullon failure - JSON serialization:
System.Text.Json(consistent with project)
- Inject
STEP 9 — Service dependencies record
- 9a. Create
src/SlpModularCms.Modules.Master/Services/MasterServiceDependencies.cs- Record with 6 properties:
ICmsInstanceRepository,ISlaveApiClient,IApiKeyProtector,IOptions<MasterModuleOptions>,IHttpContextAccessor,ILogger<CmsInstanceService> [ExcludeFromCodeCoverage]
- Record with 6 properties:
STEP 10 — CmsInstanceService
- 10a. Create
src/SlpModularCms.Modules.Master/Services/ICmsInstanceService.csGetAllAsync,AddAsync,UpdateStatusAsync,VerifyIntegrityAsync
- 10b. Create
src/SlpModularCms.Modules.Master/Services/CmsInstanceService.cs- Inject
MasterServiceDependencies GetAllAsync: return all asCmsInstanceDto(no ApiKey)AddAsync: validate → encrypt → persist → determine masterUrl (HttpContext → config fallback) → register → updateLastContactedAtif successUpdateStatusAsync: validate → load → validate DisableMessage → persist → for non-Inactive: decrypt → push → updateLastStatusPushedAtif success → returnUpdateStatusResultVerifyIntegrityAsync: get active → per slave: decrypt → get registered URL → compare → re-register if mismatch → updateLastIntegrityCheckFailedAt/LastContactedAt- Logging per business-rules.md (Error/Warning/Information)
- Inject
STEP 11 — Background service
- 11a. Create
src/SlpModularCms.Modules.Master/BackgroundServices/IntegrityCheckBackgroundService.cs- Inject
IServiceScopeFactory,IOptions<MasterModuleOptions>,ILogger<IntegrityCheckBackgroundService> PeriodicTimerwithIntegrityCheckIntervalMinutesCreateAsyncScope()per tick; resolveICmsInstanceService; callVerifyIntegrityAsync()- Outer
try/catchlogsErrorand continues [ExcludeFromCodeCoverage]NOT applied — test with mockedIServiceScopeFactory
- Inject
STEP 12 — Controller
- 12a. Create
src/SlpModularCms.Modules.Master/Controllers/CmsInstanceController.cs[ApiController],[Route("[controller]")],[Authorize(Policy = "OwnerOnly")]GetAll(): GET →ICmsInstanceService.GetAllAsync()→ 200 OKAdd([FromBody] CreateCmsInstanceRequest): POST →ICmsInstanceService.AddAsync()→ 201 CreatedUpdateStatus(Guid id, [FromBody] UpdateStatusRequest): PUT/{id}/status→ICmsInstanceService.UpdateStatusAsync()→ 200 OK withUpdateStatusResult- Error handling: catch
KeyNotFoundException→ 404; catchArgumentException(validation) → 400
STEP 13 — Module registration
- 13a. Create
src/SlpModularCms.Modules.Master/MasterModule.cs—[ExcludeFromCodeCoverage]Name = "Master",Version = "1.0.0"RegisterServices: AddDataProtection, AddSingleton IApiKeyProtector, Configure MasterModuleOptions, AddDbContext MasterDbContext, AddScoped repo + deps + service, AddHttpClient ISlaveApiClient + AddResilienceHandler, AddHostedService IntegrityCheckBackgroundService, AddHttpContextAccessorUseModule: migrateMasterDbContextat startup
STEP 14 — EF Core migration
- 14a. Create
src/SlpModularCms.Modules.Master/Migrations/directory placeholder- Document CLI command to generate initial migration:
dotnet ef migrations add InitialCreate --project src/SlpModularCms.Modules.Master --startup-project src/SlpModularCms.Api - Note: Migration is created via CLI after Step 13 compile-succeeds; not hand-generated
- Document CLI command to generate initial migration:
STEP 15 — Unit tests: Repository
- 15a. Create
src/SlpModularCms.Modules.Master.Tests/Repositories/CmsInstanceRepositoryTests.cs- Use EF Core InMemory for
MasterDbContext - Test:
GetAllAsync,GetActiveAsync(excludes Inactive),GetByIdAsync(found/not found),AddAsync+SaveChangesAsync,UpdateAsync
- Use EF Core InMemory for
STEP 16 — Unit tests: ApiKeyProtector
- 16a. Create
src/SlpModularCms.Modules.Master.Tests/Services/ApiKeyProtectorTests.cs- Use
EphemeralDataProtectionProvider(real provider, no mocks) - Test: Protect returns non-plaintext; Unprotect(Protect(x)) == x; Unprotect with wrong key throws
- Use
STEP 17 — Unit tests: SlaveApiClient
- 17a. Create
src/SlpModularCms.Modules.Master.Tests/Services/SlaveApiClientTests.cs- Use
NSubstituteHttpMessageHandlersubstitute orMockHttpMessageHandler - Test:
RegisterMasterAsyncreturns true on 200, false on 4xx/5xx/exception - Test:
PushStatusAsyncreturns true on 200, false on failure - Test:
GetRegisteredMasterUrlAsyncreturns URL from response body, null on failure - Test:
X-Master-Api-Keyheader is set on each request
- Use
STEP 18 — Unit tests: CmsInstanceService
- 18a. Create
src/SlpModularCms.Modules.Master.Tests/Services/CmsInstanceServiceTests.cs- Substitute all 6 dependencies via NSubstitute
AddAsynctests: success path, registration failure (record still persisted), URL from HttpContext, URL from config fallbackUpdateStatusAsynctests: not found → throws, missing DisableMessage → throws, Inactive → no push, Available → persists + pushes, push fails → SlaveContactSuccess=falseVerifyIntegrityAsynctests: skip Inactive, URL match → clear flag, mismatch → re-register, unreachable → set LastIntegrityCheckFailedAtGetAllAsync: ApiKey not in DTO
STEP 19 — Unit tests: IntegrityCheckBackgroundService
- 19a. Create
src/SlpModularCms.Modules.Master.Tests/BackgroundServices/IntegrityCheckBackgroundServiceTests.cs- Test: service calls
VerifyIntegrityAsyncon tick - Test: exceptions in
VerifyIntegrityAsyncare caught and logged (service does not crash) - Use NSubstitute
IServiceScopeFactory+IServiceScope
- Test: service calls
STEP 20 — Unit tests: Controller
- 20a. Create
src/SlpModularCms.Modules.Master.Tests/Controllers/CmsInstanceControllerTests.cs- Substitute
ICmsInstanceServicevia NSubstitute GetAll: 200 with listAdd: 201 Created with dto; 400 on validation exception; 400 on argument exceptionUpdateStatus: 200 with result; 404 on KeyNotFoundException; 400 on ArgumentException
- Substitute
STEP 21 — Code documentation summary
- 21a. Create
aidlc-docs/features/master-cms-module/construction/master-backend/code/code-summary.md- List all created/modified files with paths
- Note the EF Core migration CLI command
- Note NuGet package added
File Summary
| File | Action | ExcludeFromCoverage |
|---|---|---|
SlpModularCms.Modules.Master.csproj |
Create | N/A |
SlpModularCms.Modules.Master.Tests.csproj |
Create | N/A |
SlpModularCms.Api.csproj |
Modify | N/A |
SlpModularCms.sln |
Modify | N/A |
Options/MasterModuleOptions.cs |
Create | No |
Data/Entities/CmsInstanceStatus.cs |
Create | No |
Data/Entities/CmsInstance.cs |
Create | No |
Models/CmsInstanceDto.cs |
Create | Yes |
Models/CreateCmsInstanceRequest.cs |
Create | Yes |
Models/UpdateStatusRequest.cs |
Create | Yes |
Models/UpdateStatusResult.cs |
Create | Yes |
Data/MasterDbContext.cs |
Create | No |
Repositories/ICmsInstanceRepository.cs |
Create | No |
Repositories/CmsInstanceRepository.cs |
Create | No |
Services/IApiKeyProtector.cs |
Create | No |
Services/ApiKeyProtector.cs |
Create | No |
Services/ISlaveApiClient.cs |
Create | No |
Services/SlaveApiClient.cs |
Create | No |
Services/MasterServiceDependencies.cs |
Create | Yes |
Services/ICmsInstanceService.cs |
Create | No |
Services/CmsInstanceService.cs |
Create | No |
BackgroundServices/IntegrityCheckBackgroundService.cs |
Create | No |
Controllers/CmsInstanceController.cs |
Create | No |
MasterModule.cs |
Create | Yes |
| Tests (6 files) | Create | N/A |
Total: 25 application files (24 new, 2 modified) + 6 test files + 1 code summary