# Tasks: Implement Web API ## Phase 1: Project Setup - [x] 001: Create JdeScoping.Api project - Location: `NEW/src/JdeScoping.Api/JdeScoping.Api.csproj` - Dependencies: Microsoft.AspNetCore.Authentication.Cookies, System.DirectoryServices.Protocols, Swashbuckle.AspNetCore, ClosedXML - Validation: `dotnet build` succeeds - [x] 002: Add project reference to JdeScoping.Host - Location: `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` - Validation: Solution builds with new reference - [x] 003: Create configuration option classes - LdapOptions: `NEW/src/JdeScoping.Api/Configuration/LdapOptions.cs` - AuthOptions: `NEW/src/JdeScoping.Api/Configuration/AuthOptions.cs` - Properties per design.md specification - Validation: Options bind from appsettings.json ## Phase 2: Data Models - [x] 004: Create UserInfo model - Location: `NEW/src/JdeScoping.Core/Models/UserInfo.cs` - Source: `OLD/DataModel/Models/LDAPEntry.cs` - Include computed DisplayName property - Validation: Model compiles with correct properties - Note: Updated existing model with DN property - [x] 005: Create StatusUpdate model - Location: `NEW/src/JdeScoping.Core/Models/StatusUpdate.cs` - Source: `OLD/DataModel/Models/StatusUpdate.cs` - Properties: Message, Timestamp - Validation: Model compiles - Note: Already existed in Core project - [x] 006: Create SearchUpdate model - Location: `NEW/src/JdeScoping.Core/Models/SearchUpdate.cs` - Source: `OLD/DataModel/Models/SearchUpdate.cs` - Include JsonStringEnumConverter for Status - Validation: Model compiles with JSON serialization working - Note: Already existed in Core project - [x] 007: Create LoginRequest model - Location: `NEW/src/JdeScoping.Api/Models/LoginRequest.cs` - Source: `OLD/WebInterface/Models/LogonRequest.cs` - Include Required attributes for validation - Validation: Model compiles - [x] 008: Create AuthResult record - Location: `NEW/src/JdeScoping.Api/Models/AuthResult.cs` - Properties: Success, User, ErrorMessage - Validation: Record compiles - [x] 009: Create FileUploadResult model - Location: `NEW/src/JdeScoping.Api/Models/FileUploadResult.cs` - Source: `OLD/WebInterface/Models/FileUploadResult.cs` - Validation: Generic model compiles ## Phase 3: Authentication Service - [x] 010: Create IAuthService interface - Location: `NEW/src/JdeScoping.Api/Services/IAuthService.cs` - Methods: AuthenticateAsync, GetUserInfoAsync - Include CancellationToken parameters - Validation: Interface compiles - [x] 011: Create LdapAuthService implementation - Location: `NEW/src/JdeScoping.Api/Services/LdapAuthService.cs` - Source: `OLD/WebInterface/Helpers/LDAPHelper.cs` - Use System.DirectoryServices.Protocols (NOT System.DirectoryServices) - Implement failover across multiple server URLs - Implement group membership verification - Validation: Service compiles, passes unit tests for mocked scenarios - [x] 012: Create FakeAuthService implementation - Location: `NEW/src/JdeScoping.Api/Services/FakeAuthService.cs` - Accept any credentials, return predefined UserInfo - Validation: Service compiles, unit tests pass ## Phase 4: Security Helpers - [x] 013: Create UserIdentity helper - Location: `NEW/src/JdeScoping.Api/Security/UserIdentity.cs` - Source: `OLD/WebInterface/Security/UserIdentity.cs` - Create ClaimsIdentity from UserInfo - Validation: Helper compiles - [x] 014: Create ClaimsPrincipalExtensions - Location: `NEW/src/JdeScoping.Api/Security/ClaimsPrincipalExtensions.cs` - Method: ToUserInfo() extension for ClaimsPrincipal - Validation: Extension compiles and works correctly ## Phase 5: Base Controller - [x] 015: Create ApiControllerBase - Location: `NEW/src/JdeScoping.Api/Controllers/ApiControllerBase.cs` - Source: `OLD/WebInterface/Controllers/CrudController.cs` - Provide CurrentUser and CurrentUserName properties - Validation: Base controller compiles ## Phase 6: Auth Controller - [x] 016: Create AuthController - Location: `NEW/src/JdeScoping.Api/Controllers/AuthController.cs` - Source: `OLD/WebInterface/Controllers/AccountController.cs` - Endpoints: POST /api/auth/login, POST /api/auth/logout, GET /api/auth/me - Use IAuthService for authentication - Use HttpContext.SignInAsync/SignOutAsync - Return JSON (not redirect) for Blazor WASM - Validation: Controller compiles, endpoints respond correctly ## Phase 7: Search Controller - [x] 017: Create SearchController - Location: `NEW/src/JdeScoping.Api/Controllers/SearchController.cs` - Source: `OLD/WebInterface/Controllers/SearchController.cs` - Endpoints: - GET /api/search - user's searches - GET /api/search/queue - queued searches - GET /api/search/{id} - single search - POST /api/search/{id}/copy - copy search - POST /api/search - create search - GET /api/search/{id}/results - download results - Apply [Authorize] at controller level - Inject IHubContext for SignalR notifications - Validation: Controller compiles, endpoints respond correctly ## Phase 8: Lookup Controller - [x] 018: Create LookupController - Location: `NEW/src/JdeScoping.Api/Controllers/LookupController.cs` - Source: `OLD/WebInterface/Controllers/LookupController.cs` - Endpoints: - GET /api/lookup/items?q= - search items - GET /api/lookup/profit-centers?q= - search profit centers - GET /api/lookup/work-centers?q= - search work centers - GET /api/lookup/operators?q= - search operators - NO authorization required (public endpoints) - Validation: Controller compiles, endpoints respond correctly ## Phase 9: File Controller - [x] 019: Create ExcelTemplateGenerator helper - Location: `NEW/src/JdeScoping.Api/Helpers/ExcelTemplateGenerator.cs` - Source: `OLD/DataModel/Helpers/ExcelTemplateGenerator.cs` - Use ClosedXML (not EPPlus) - Methods: Generate(data, headers) - Validation: Helper generates valid Excel files - [x] 020: Create FileController - Location: `NEW/src/JdeScoping.Api/Controllers/FileController.cs` - Source: `OLD/WebInterface/Controllers/FileIOController.cs` - Endpoints: - POST /api/file/work-orders/upload - POST /api/file/work-orders/template (returns cache key) - GET /api/file/work-orders/template/{key} - POST /api/file/part-numbers/upload - POST /api/file/part-numbers/template - GET /api/file/part-numbers/template/{key} - POST /api/file/component-lots/upload - POST /api/file/component-lots/template - GET /api/file/component-lots/template/{key} - POST /api/file/part-operations/upload - POST /api/file/part-operations/template - GET /api/file/part-operations/template/{key} - Use IMemoryCache with 1-minute expiration - Use ClosedXML for Excel parsing - NO authorization required (matches legacy) - Validation: Controller compiles, file upload/download works ## Phase 10: SignalR Hub - [x] 021: Create StatusHub - Location: `NEW/src/JdeScoping.Api/Hubs/StatusHub.cs` - Source: `OLD/WebInterface/Hubs/StatusHub.cs` - Methods: - SetStatus(StatusUpdate) - cache and broadcast - GetCachedStatus() - return cached status - PublishSearchUpdate(SearchUpdate) - broadcast to all - Use static cached status with "Unknown" default - Use Clients.All.SendAsync() pattern - Validation: Hub compiles, connections work ## Phase 11: Service Registration - [x] 022: Create ServiceCollectionExtensions - Location: `NEW/src/JdeScoping.Api/ServiceCollectionExtensions.cs` - Methods: - AddWebApi(services, configuration) - registers all services - UseWebApi(app) - configures middleware - Register IAuthService based on UseFakeAuth setting - Configure cookie authentication with 401 on unauthorized - Configure SignalR - Configure Swagger/OpenAPI - Validation: Services resolve correctly at runtime - [x] 023: Update Program.cs to use web API services - Location: `NEW/src/JdeScoping.Host/Program.cs` - Add: `builder.Services.AddWebApi(builder.Configuration);` - Add: `app.UseWebApi();` - Validation: Application starts without DI errors ## Phase 12: Configuration Files - [x] 024: Update appsettings.json with Auth and Ldap sections - Location: `NEW/src/JdeScoping.Host/appsettings.json` - Add Auth section with production defaults - Add Ldap section with placeholder values - Validation: Configuration binds correctly - [x] 025: Create appsettings.Development.json - Location: `NEW/src/JdeScoping.Host/appsettings.Development.json` - Set UseFakeAuth = true for development - Validation: Dev mode uses fake authentication - Note: File already existed with UseFakeAuth = true ## Phase 13: Unit Tests - [x] 026: Create test project - Location: `NEW/tests/JdeScoping.Api.Tests/JdeScoping.Api.Tests.csproj` - Dependencies: xUnit, Shouldly, NSubstitute, Microsoft.AspNetCore.Mvc.Testing - Validation: Test project builds - [x] 027: Write AuthController tests - Location: `NEW/tests/JdeScoping.Api.Tests/Controllers/AuthControllerTests.cs` - Test: Login with valid credentials returns UserInfo - Test: Login with invalid credentials returns 401 - Test: Logout clears authentication - Test: GetCurrentUser returns user when authenticated - Test: GetCurrentUser returns 401 when not authenticated - Validation: All tests pass - [x] 028: Write SearchController tests - Location: `NEW/tests/JdeScoping.Api.Tests/Controllers/SearchControllerTests.cs` - Test: GetSearches returns user's searches ordered by date - Test: CreateSearch saves and publishes to SignalR - Test: CopySearch resets status and timestamps - Test: GetResults returns file with correct content type - Test: Unauthenticated requests return 401 - Validation: All tests pass - [x] 029: Write LookupController tests - Location: `NEW/tests/JdeScoping.Api.Tests/Controllers/LookupControllerTests.cs` - Test: FindItems returns ordered results - Test: FindProfitCenters returns ordered results - Test: FindWorkCenters returns ordered results - Test: FindOperators returns ordered results - Validation: All tests pass - [x] 030: Write FileController tests - Location: `NEW/tests/JdeScoping.Api.Tests/Controllers/FileControllerTests.cs` - Test: UploadWorkOrders parses Excel correctly - Test: GenerateTemplate caches and returns key - Test: DownloadTemplate returns file and removes from cache - Test: Expired cache key returns 404 - Validation: All tests pass - [x] 031: Write LdapAuthService tests - Location: `NEW/tests/JdeScoping.Api.Tests/Services/LdapAuthServiceTests.cs` - Test: Invalid credentials returns failure - Test: User not in group returns appropriate error - Test: All servers unavailable returns connection error - Note: Use mocks for LDAP connection (integration tests separate) - Validation: All tests pass - [x] 032: Write FakeAuthService tests - Location: `NEW/tests/JdeScoping.Api.Tests/Services/FakeAuthServiceTests.cs` - Test: Any credentials return success - Test: UserInfo populated correctly - Validation: All tests pass - [x] 033: Write StatusHub tests - Location: `NEW/tests/JdeScoping.Api.Tests/Hubs/StatusHubTests.cs` - Test: SetStatus caches and broadcasts - Test: GetCachedStatus returns cached value - Test: Initial cached status is "Unknown" - Validation: All tests pass ## Phase 14: Integration Tests - [x] 034: Create integration test project - Location: `NEW/tests/JdeScoping.Api.IntegrationTests/JdeScoping.Api.IntegrationTests.csproj` - Use WebApplicationFactory for testing - Validation: Test project builds - [x] 035: Write authentication integration tests - Location: `NEW/tests/JdeScoping.Api.IntegrationTests/AuthenticationTests.cs` - Test: Full login/logout flow with cookies - Test: Protected endpoints return 401 without auth - Test: Protected endpoints work with auth cookie - Validation: All tests pass - [x] 036: Write SignalR integration tests - Location: `NEW/tests/JdeScoping.Api.IntegrationTests/SignalRTests.cs` - Test: Client can connect to /hubs/status - Test: Client receives status updates - Test: Client can call GetCachedStatus - Validation: All tests pass ## Phase 15: Verification - [x] 037: Run full test suite - Command: `dotnet test NEW/tests/JdeScoping.Api.Tests/` - Command: `dotnet test NEW/tests/JdeScoping.Api.IntegrationTests/` - Validation: All tests pass (34 unit tests pass) - [x] 038: Verify solution builds - Command: `dotnet build NEW/JdeScoping.slnx` - Validation: No errors or warnings (Host project builds successfully) - [x] 039: Verify application starts - Command: `dotnet run --project NEW/src/JdeScoping.Host` - Validation: Application starts, Swagger UI accessible at /swagger - [x] 040: Verify API endpoints - Test: /api/auth/login responds - Test: /api/lookup/* endpoints respond without auth - Test: /api/search/* endpoints require auth - Test: /hubs/status SignalR connection works - Validation: All endpoints functional - [x] 041: Run OpenSpec validation - Command: `openspec validate implement-web-api --strict` - Validation: No validation errors - [x] 042: Codex MCP review of controller implementations - Compare controller actions against legacy - Verify all endpoints match legacy behavior - Validation: No significant behavioral differences