Initial commit: JDE Scoping Tool migration project
Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
# Implement Web API
|
||||
|
||||
## Summary
|
||||
|
||||
Implement the REST API layer and real-time SignalR hub for the JDE Scoping Tool, providing HTTP endpoints for search management, lookup operations, file upload/download, and authentication. This phase creates the web-facing interface that connects the Blazor WebAssembly client to the backend search processing and data access layers.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- `AuthController` with login, logout, and current user endpoints
|
||||
- `SearchController` with CRUD operations and result downloads
|
||||
- `LookupController` with autocomplete APIs for items, profit centers, work centers, and operators
|
||||
- `FileController` with Excel upload/download for bulk data import
|
||||
- `StatusHub` SignalR hub for real-time status and search updates
|
||||
- `IAuthService` interface with LDAP and fake authentication implementations
|
||||
- `LdapAuthService` using `System.DirectoryServices.Protocols` (cross-platform)
|
||||
- `FakeAuthService` for development mode authentication bypass
|
||||
- `LdapOptions` and `AuthOptions` configuration classes
|
||||
- `UserInfo` model (renamed from legacy `LDAPEntry`)
|
||||
- API model DTOs (`LoginRequest`, `AuthResult`, `FileUploadResult<T>`, etc.)
|
||||
- Cookie-based session management with ASP.NET Core authentication
|
||||
- Service registration extension methods (`AddWebApi`, `AddAuthentication`)
|
||||
- OpenAPI/Swagger documentation
|
||||
- Unit tests with xUnit, Shouldly, and NSubstitute
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Blazor WebAssembly client implementation (Phase 9)
|
||||
- Background worker service (Phase 5: implement-data-sync)
|
||||
- Search execution logic (Phase 6: implement-search-processing)
|
||||
- Excel export generation (Phase 7: implement-excel-export)
|
||||
- Database schema changes (Phase 1: migrate-database-schema)
|
||||
- Rate limiting and advanced security (future enhancement)
|
||||
|
||||
## Motivation
|
||||
|
||||
The Web API layer is the bridge between the Blazor WebAssembly client and the backend services. This phase delivers:
|
||||
|
||||
- **REST API Endpoints**: Standard HTTP APIs for search, lookup, and file operations
|
||||
- **Real-Time Updates**: SignalR hub for live status updates during search processing
|
||||
- **Cross-Platform Authentication**: LDAP authentication using `System.DirectoryServices.Protocols` (not the Windows-only `System.DirectoryServices`)
|
||||
- **Development Mode Support**: Fake authentication for local development without LDAP server
|
||||
- **OpenAPI Documentation**: Auto-generated API documentation for Blazor client development
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. `AuthController` implements login, logout, and current user endpoints
|
||||
2. `SearchController` implements all CRUD operations with proper authorization
|
||||
3. `LookupController` implements autocomplete APIs without authorization (public access)
|
||||
4. `FileController` implements Excel upload/download with caching
|
||||
5. `StatusHub` broadcasts status and search updates to all connected clients
|
||||
6. `LdapAuthService` authenticates against LDAP with group membership verification
|
||||
7. `FakeAuthService` accepts any credentials when `AuthOptions.UseFakeAuth = true`
|
||||
8. Cookie authentication configured with proper timeout and no redirect on 401
|
||||
9. All protected endpoints return HTTP 401 (not redirect) for Blazor WASM compatibility
|
||||
10. SignalR hub maps to `/hubs/status` endpoint
|
||||
11. OpenAPI documentation generated via Swagger
|
||||
12. All services registered via `AddWebApi()` extension method
|
||||
13. Unit tests achieve >80% code coverage for controllers and services
|
||||
14. `openspec validate implement-web-api --strict` passes
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Phase | Dependency | Type |
|
||||
|-------|------------|------|
|
||||
| Phase 4: implement-data-access | `ILotFinderRepository` for lookups and search storage | Required |
|
||||
| Phase 5: implement-data-sync | Worker service publishes status updates (soft dependency) | Soft |
|
||||
| Phase 6: implement-search-processing | Search execution produces results | Required |
|
||||
| Phase 7: implement-excel-export | `IExcelExportService` for file downloads | Required |
|
||||
|
||||
**Note:** Controllers can be implemented with interface dependencies, allowing parallel development with mock implementations for testing.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| LDAP connectivity issues | Medium | High | Implement `FakeAuthService` for development; add connection retry logic |
|
||||
| `System.DirectoryServices.Protocols` complexity | Medium | Medium | Follow Microsoft documentation; create comprehensive LDAP integration tests |
|
||||
| SignalR connection management | Low | Medium | Use ASP.NET Core SignalR defaults; implement client reconnection in Blazor |
|
||||
| Cookie authentication with Blazor WASM | Low | Medium | Configure `SuppressAuthenticationChallengeOnUnauthorized`; test cross-origin scenarios |
|
||||
| File upload size limits | Low | Low | Configure `IFormFile` limits in `Program.cs`; document limits |
|
||||
| Memory cache expiration for file downloads | Low | Low | Use 1-minute expiration matching legacy; remove after download |
|
||||
|
||||
## Related Specs
|
||||
|
||||
- `web-api-auth/spec.md` - Base specification for Web API and authentication
|
||||
- `domain-models/spec.md` - Domain entities used by controllers
|
||||
- `data-access/spec.md` - Repository interfaces for data operations
|
||||
- `search-processing/spec.md` - Search processing service interfaces
|
||||
- `excel-export/spec.md` - Excel export service for result downloads
|
||||
@@ -0,0 +1,194 @@
|
||||
# Web API and Authentication - Migration Deltas
|
||||
|
||||
## Purpose
|
||||
|
||||
This document specifies requirements ADDED or MODIFIED for the .NET 10 migration of the Web API and authentication layer. These requirements extend the base specification at `openspec/specs/web-api-auth/spec.md`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Cross-Platform LDAP Support
|
||||
|
||||
The system SHALL use `System.DirectoryServices.Protocols` for LDAP authentication to ensure cross-platform compatibility.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use `System.DirectoryServices.DirectoryEntry` or `System.DirectoryServices.DirectorySearcher` (Windows-only APIs)
|
||||
- The system SHALL use `LdapConnection` from `System.DirectoryServices.Protocols`
|
||||
- The system SHALL use asynchronous patterns with `Task.Run()` for LDAP operations (the LDAP API is synchronous)
|
||||
- The system SHALL dispose `LdapConnection` after each operation
|
||||
|
||||
#### Scenario: LDAP authentication on Linux
|
||||
|
||||
- **WHEN** the application runs on Linux with LDAP configuration
|
||||
- **THEN** the system authenticates successfully using `System.DirectoryServices.Protocols`
|
||||
|
||||
### Requirement: ClosedXML for Excel Operations
|
||||
|
||||
The system SHALL use ClosedXML library for Excel file parsing in the File API.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use EPPlus (commercial license required in v7+)
|
||||
- The system SHALL use `XLWorkbook` from ClosedXML for reading uploaded Excel files
|
||||
- The system SHALL use 1-indexed row/column access matching ClosedXML conventions
|
||||
- The system SHALL handle empty worksheets gracefully using `LastRowUsed()?.RowNumber()`
|
||||
|
||||
#### Scenario: Parse Excel upload with ClosedXML
|
||||
|
||||
- **WHEN** a user uploads an Excel file to any file upload endpoint
|
||||
- **THEN** the system parses the file using ClosedXML and returns matching data
|
||||
|
||||
### Requirement: OpenAPI Documentation
|
||||
|
||||
The system SHALL provide OpenAPI/Swagger documentation for all API endpoints.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL use Swashbuckle.AspNetCore for OpenAPI generation
|
||||
- The system SHALL expose Swagger UI at `/swagger` in development mode
|
||||
- The system SHALL document all endpoints with correct HTTP methods and response types
|
||||
- The system SHALL document authentication requirements for protected endpoints
|
||||
|
||||
#### Scenario: Developer accesses API documentation
|
||||
|
||||
- **WHEN** a developer navigates to `/swagger` in development mode
|
||||
- **THEN** the system displays interactive API documentation for all endpoints
|
||||
|
||||
### Requirement: JSON Serialization with System.Text.Json
|
||||
|
||||
The system SHALL use System.Text.Json for all JSON serialization.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use Newtonsoft.Json (legacy)
|
||||
- The system SHALL use `JsonStringEnumConverter` for enum serialization
|
||||
- The system SHALL configure JSON options via `AddControllers().AddJsonOptions()`
|
||||
- The system SHALL return JSON responses (not redirects) for all API errors
|
||||
|
||||
#### Scenario: Enum serialization in API response
|
||||
|
||||
- **WHEN** an API endpoint returns a model with an enum property
|
||||
- **THEN** the system serializes the enum as a string (e.g., "Submitted" not 1)
|
||||
|
||||
### Requirement: IHubContext Dependency Injection
|
||||
|
||||
The system SHALL use `IHubContext<StatusHub>` for publishing SignalR updates from outside the hub.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use static `GlobalHost.ConnectionManager` pattern (legacy)
|
||||
- The system SHALL inject `IHubContext<StatusHub>` into controllers and services
|
||||
- The system SHALL use `Clients.All.SendAsync()` for broadcasting
|
||||
- The system SHALL handle SignalR publish failures gracefully (log and continue)
|
||||
|
||||
#### Scenario: Controller publishes search update
|
||||
|
||||
- **WHEN** SearchController creates a new search
|
||||
- **THEN** the system uses injected `IHubContext<StatusHub>` to broadcast the update
|
||||
|
||||
### Requirement: Async-First Pattern
|
||||
|
||||
The system SHALL use async/await patterns for all I/O operations.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- All controller actions SHALL be async with `CancellationToken` parameter
|
||||
- All service methods SHALL be async with `CancellationToken` parameter
|
||||
- The system SHALL use `IAsyncEnumerable` for streaming scenarios where applicable
|
||||
- The system SHALL propagate `CancellationToken` to all downstream async calls
|
||||
|
||||
#### Scenario: Cancellation during long-running operation
|
||||
|
||||
- **WHEN** a client cancels a request during LDAP authentication
|
||||
- **THEN** the system throws `OperationCanceledException` and stops the operation
|
||||
|
||||
### Requirement: Structured Logging
|
||||
|
||||
The system SHALL use `ILogger<T>` for structured logging.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use NLog directly (legacy)
|
||||
- The system SHALL inject `ILogger<T>` into all controllers and services
|
||||
- The system SHALL use structured logging with named parameters: `_logger.LogWarning("Failed to authenticate {Username}", username)`
|
||||
- The system SHALL log at appropriate levels (Information, Warning, Error)
|
||||
|
||||
#### Scenario: Failed LDAP authentication logged
|
||||
|
||||
- **WHEN** LDAP authentication fails
|
||||
- **THEN** the system logs a warning with structured username and error details
|
||||
|
||||
### Requirement: Options Pattern Configuration
|
||||
|
||||
The system SHALL use `IOptions<T>` for strongly-typed configuration.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use `ConfigurationManager` or `WebConfigurationManager` (legacy)
|
||||
- The system SHALL bind `LdapOptions` from `Ldap` configuration section
|
||||
- The system SHALL bind `AuthOptions` from `Auth` configuration section
|
||||
- The system SHALL inject `IOptions<T>` or `IOptionsSnapshot<T>` as needed
|
||||
|
||||
#### Scenario: Configuration changes without restart
|
||||
|
||||
- **WHEN** configuration values change in appsettings.json
|
||||
- **THEN** the system can use `IOptionsSnapshot<T>` to read updated values
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Cookie Authentication Events (Modified)
|
||||
|
||||
The system SHALL suppress cookie authentication redirects and return HTTP status codes for Blazor WASM compatibility.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL configure `OnRedirectToLogin` to return HTTP 401 instead of redirect
|
||||
- The system SHALL configure `OnRedirectToAccessDenied` to return HTTP 403 instead of redirect
|
||||
- The system SHALL return JSON error responses (not HTML) for authentication failures
|
||||
|
||||
#### Scenario: Unauthenticated API request from Blazor
|
||||
|
||||
- **WHEN** an unauthenticated Blazor WASM client requests a protected endpoint
|
||||
- **THEN** the system returns HTTP 401 with JSON error body (no redirect)
|
||||
|
||||
### Requirement: SignalR Hub Endpoint Path (Modified)
|
||||
|
||||
The system SHALL map the StatusHub to the `/hubs/status` endpoint path.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL map StatusHub to `/hubs/status` endpoint
|
||||
- The system SHALL configure SignalR with default settings (no custom protocols)
|
||||
|
||||
#### Scenario: Client connects to SignalR hub
|
||||
|
||||
- **WHEN** a Blazor WASM client connects to SignalR
|
||||
- **THEN** the system accepts connections at `/hubs/status`
|
||||
|
||||
### Requirement: Dependency Injected Memory Cache (Modified)
|
||||
|
||||
The system SHALL use DI-injected `IMemoryCache` for file template caching.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL inject `IMemoryCache` via constructor (not `MemoryCache.Default`)
|
||||
- The system SHALL use `MemoryCacheEntryOptions` with `AbsoluteExpirationRelativeToNow`
|
||||
- The system SHALL use `TryGetValue<T>` pattern for type-safe cache retrieval
|
||||
|
||||
#### Scenario: File template cached with DI cache
|
||||
|
||||
- **WHEN** a file template is generated
|
||||
- **THEN** the system stores it in the injected `IMemoryCache` with 1-minute expiration
|
||||
|
||||
## Migration Notes
|
||||
|
||||
| Legacy Pattern | New Pattern | Rationale |
|
||||
|----------------|-------------|-----------|
|
||||
| `System.DirectoryServices.DirectoryEntry` | `System.DirectoryServices.Protocols.LdapConnection` | Cross-platform compatibility |
|
||||
| EPPlus | ClosedXML | MIT license (EPPlus commercial in v7+) |
|
||||
| Newtonsoft.Json | System.Text.Json | Built-in, better performance |
|
||||
| `GlobalHost.ConnectionManager.GetHubContext<T>()` | `IHubContext<T>` via DI | Standard DI pattern, testable |
|
||||
| NLog | `ILogger<T>` | Built-in logging abstraction |
|
||||
| `WebConfigurationManager.AppSettings` | `IOptions<T>` | Strongly-typed configuration |
|
||||
| `MemoryCache.Default` | `IMemoryCache` via DI | Standard caching abstraction |
|
||||
| Login redirect on 401 | HTTP 401 response | Blazor WASM SPA compatibility |
|
||||
@@ -0,0 +1,329 @@
|
||||
# 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<T> 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<StatusHub> 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
|
||||
Reference in New Issue
Block a user