diff --git a/NEW/tests/JdeScoping.Database.Tests/debug_test.csx b/NEW/tests/JdeScoping.Database.Tests/debug_test.csx new file mode 100644 index 0000000..0f1b9ac --- /dev/null +++ b/NEW/tests/JdeScoping.Database.Tests/debug_test.csx @@ -0,0 +1,72 @@ +#r "nuget: Dapper, 2.1.35" +#r "nuget: Microsoft.Data.SqlClient, 5.2.2" + +using System; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Data.SqlClient; + +var connectionString = "Server=localhost,1434;Database=ScopingTool;User Id=sa;Password=ScopingTool_SA_2024Dev;TrustServerCertificate=true"; +var testMisNumber = "TEST_DAPPER_001"; + +using var connection = new SqlConnection(connectionString); +await connection.OpenAsync(); + +// Cleanup +await connection.ExecuteAsync("DELETE FROM MisData_Curr WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM MisData_Hist WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM mis_temp WHERE MIS_IIS_Number = @MisNumber OR MIS_IIS_Number = @MisNumberWithPrefix", + new { MisNumber = testMisNumber, MisNumberWithPrefix = $"IIS_{testMisNumber}" }); + +Console.WriteLine("Cleanup done"); + +// Insert test data +await connection.ExecuteAsync( + @"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber, + TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date) + VALUES (@MisNumber, '12345', 'SITE1', 'A', '1', + 'Test Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)", + new { MisNumber = testMisNumber, ReleaseDate = DateTime.Now }); + +Console.WriteLine("Inserted staging data"); + +// Check staging data +var stagingCount = await connection.QuerySingleAsync( + "SELECT COUNT(*) FROM mis_temp WHERE MIS_IIS_Number = @MisNumber", + new { MisNumber = testMisNumber }); +Console.WriteLine($"Staging count before proc: {stagingCount}"); + +// Run procedure +Console.WriteLine("Running stored procedure..."); +await connection.ExecuteAsync("EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1"); +Console.WriteLine("Stored procedure completed"); + +// Check result +var record = await connection.QuerySingleOrDefaultAsync( + @"SELECT MisNumber, ItemNumber, BranchCode, RevID, CharNumber, Status, ObsoleteDate + FROM MisData_Curr + WHERE MisNumber = @MisNumber", + new { MisNumber = testMisNumber }); + +if (record == null) +{ + Console.WriteLine("FAIL: Record is null!"); + + // Additional debugging - check if it got deleted during the proc + var stagingCountAfter = await connection.QuerySingleAsync( + "SELECT COUNT(*) FROM mis_temp WHERE MIS_IIS_Number = @MisNumber", + new { MisNumber = testMisNumber }); + Console.WriteLine($"Staging count after proc: {stagingCountAfter}"); +} +else +{ + Console.WriteLine($"SUCCESS: Found record - Status={record.Status}"); +} + +// Cleanup +await connection.ExecuteAsync("DELETE FROM MisData_Curr WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM MisData_Hist WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM mis_temp WHERE MIS_IIS_Number = @MisNumber OR MIS_IIS_Number = @MisNumberWithPrefix", + new { MisNumber = testMisNumber, MisNumberWithPrefix = $"IIS_{testMisNumber}" }); + +Console.WriteLine("Test complete"); diff --git a/TestScripts/playwright/debug-screenshot.png b/TestScripts/playwright/debug-screenshot.png new file mode 100644 index 0000000..a5df24e Binary files /dev/null and b/TestScripts/playwright/debug-screenshot.png differ diff --git a/TestScripts/playwright/playwright-report/index.html b/TestScripts/playwright/playwright-report/index.html new file mode 100644 index 0000000..fb857fd --- /dev/null +++ b/TestScripts/playwright/playwright-report/index.html @@ -0,0 +1,85 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/TestScripts/playwright/test-results/.last-run.json b/TestScripts/playwright/test-results/.last-run.json new file mode 100644 index 0000000..cbcc1fb --- /dev/null +++ b/TestScripts/playwright/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/aot_prep.md b/aot_prep.md new file mode 100644 index 0000000..e4d94ce --- /dev/null +++ b/aot_prep.md @@ -0,0 +1,413 @@ +# AOT Readiness Implementation Plan + +## Overview + +Make the JdeScopingTool .NET 10 solution ready for Native AOT compilation by addressing three critical areas: +1. **JSON Source Generation** - Replace reflection-based System.Text.Json with source generators +2. **Dapper AOT** - Enable compile-time SQL mapping generation +3. **Configuration Binding** - Replace reflection-based options binding and validation + +**Current AOT Score:** ~25/100 (1 of 14 projects ready) +**Target AOT Score:** 90/100 (all projects AOT-compatible with known library exceptions) + +--- + +## Current State Summary + +### Projects by AOT Readiness + +| Status | Project | Key Blockers | +|--------|---------|--------------| +| ✅ Ready | `JdeScoping.Domain` | None - pure domain models | +| ❌ Needs Work | `JdeScoping.DataAccess` | Dapper, SqlKata, Oracle driver | +| ❌ Needs Work | `JdeScoping.DataSync` | Dapper + STJ without source gen | +| ❌ Needs Work | `JdeScoping.Core` | STJ + Configuration.Binder | +| ❌ Needs Work | `JdeScoping.Infrastructure` | STJ + SecureStore + LDAP | +| ❌ Needs Work | `JdeScoping.Api` | STJ + Swashbuckle + config binding | +| ❌ Needs Work | `JdeScoping.Host` | Depends on API/DataSync | +| ❌ Needs Work | `JdeScoping.Client` | STJ + Radzen + SignalR | +| ❌ Needs Work | `JdeScoping.ExcelIO` | ClosedXML | +| ❌ Needs Work | `JdeScoping.Database` | dbup-sqlserver | +| ❌ Needs Work | ConfigManager.* | STJ + System.CommandLine + Avalonia | + +### No Existing AOT Configuration + +- No `PublishAot` property +- No `IsAotCompatible` markers +- No `TrimMode` settings +- No `[DynamicallyAccessedMembers]` attributes +- No `[RequiresUnreferencedCode]` attributes + +--- + +## Phase 1: Solution-Wide AOT Configuration + +### 1.1 Create Directory.Build.props +**File:** `NEW/src/Directory.Build.props` (new) + +```xml + + + true + true + true + true + true + false + + +``` + +### 1.2 Update Host Project for AOT Publishing +**File:** `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` + +Add: +```xml + + false + true + partial + +``` + +--- + +## Phase 2: JSON Source Generation (~45 types) + +### 2.1 Create JsonSerializerContext Classes + +| Project | Context File | Types Count | +|---------|-------------|-------------| +| JdeScoping.Core | `Json/CoreJsonContext.cs` | ~30 (ViewModels, Auth, Search) | +| JdeScoping.DataSync | `Json/DataSyncJsonContext.cs` | ~6 (ETL pipeline configs) | +| JdeScoping.Api | `Json/ApiJsonContext.cs` | ~10 (FileUploadResult, HealthCheck) | +| JdeScoping.Client | `Json/ClientJsonContext.cs` | ~15 (Client-side types) | +| ConfigManager.Core | `Json/ConfigManagerJsonContext.cs` | ~13 (ConfigModel sections) | + +### 2.2 Types Requiring [JsonSerializable] Attributes + +#### Core Authentication & Identity Types +- `UserInfoDto` - `JdeScoping.Core/Models/Auth/UserInfoDto.cs` +- `LoginResultModel` - `JdeScoping.Core/Models/Auth/LoginResultModel.cs` +- `LoginModel` - `JdeScoping.Core/Models/Auth/LoginModel.cs` + +#### Search & Criteria Types +- `SearchCriteria` - `JdeScoping.Core/Models/Search/SearchCriteria.cs` +- `Search` - `JdeScoping.Core/Models/Search/Search.cs` +- `SearchViewModel` - `JdeScoping.Core/ViewModels/SearchViewModel.cs` +- `SearchUpdateViewModel` - `JdeScoping.Core/ViewModels/SearchUpdateViewModel.cs` +- `StatusUpdateViewModel` - `JdeScoping.Core/ViewModels/StatusUpdateViewModel.cs` + +#### File Upload & Result Types +- `FileUploadResult` - `JdeScoping.Client/Models/FileUploadResult.cs` +- `FileUploadResult` - `JdeScoping.Api/Models/FileUploadResult.cs` + +#### Data Sync & Manual Sync Types +- `ManualSyncRequestViewModel` - `JdeScoping.Api/Contracts/ManualSync/ManualSyncRequestViewModel.cs` +- `CreateManualSyncRequestDto` - `JdeScoping.Core/ViewModels/CreateManualSyncRequestDto.cs` +- `PipelineInfoViewModel` - `JdeScoping.Api/Contracts/ManualSync/PipelineInfoViewModel.cs` + +#### ETL Pipeline Configuration Types +- `EtlPipelineConfig` - `JdeScoping.DataSync/Configuration/EtlPipelineConfig.cs` +- `SourceElement` - `JdeScoping.DataSync/Configuration/SourceElement.cs` +- `DestinationElement` - `JdeScoping.DataSync/Configuration/DestinationElement.cs` +- `TransformElement` - `JdeScoping.DataSync/Configuration/TransformElement.cs` +- `ScriptElement` - `JdeScoping.DataSync/Configuration/ScriptElement.cs` +- `ParameterElement` - `JdeScoping.DataSync/Configuration/ParameterElement.cs` + +#### Configuration Model Types +- `ConfigModel` - `Utils/JdeScoping.ConfigManager.Core/Models/ConfigModel.cs` +- `ConnectionStringsSection` - `Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringsSection.cs` +- `ConnectionStringEntry` - `Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringEntry.cs` + +#### Data ViewModels +- `WorkOrderViewModel`, `ItemViewModel`, `WorkCenterViewModel`, `ProfitCenterViewModel` +- `PartOperationViewModel`, `JdeUserViewModel`, `LotViewModel`, `ComponentLotViewModel`, `OperatorViewModel` + +### 2.3 Key Files to Create + +**`NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs`** - Central context with: +- All ViewModels (SearchViewModel, WorkOrderViewModel, ItemViewModel, etc.) +- Auth models (UserInfoDto, LoginResultModel, LoginModel) +- SearchCriteria, ApiError, ValidationError + +**`NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs`**: +- EtlPipelineConfig, SourceElement, DestinationElement, TransformElement + +**`NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs`**: +- FileUploadResult instantiations for each ViewModel type +- HealthCheckResponse (replace anonymous type) + +### 2.4 Registration Updates + +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** (line ~80): +```csharp +.AddJsonOptions(options => +{ + options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, CoreJsonContext.Default); + options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, ApiJsonContext.Default); +}); +``` + +**`NEW/src/JdeScoping.Client/Services/ApiClientBase.cs`** (line ~16): +```csharp +private static readonly JsonSerializerOptions JsonOptions = new() +{ + TypeInfoResolver = JsonTypeInfoResolver.Combine( + ClientJsonContext.Default, + CoreJsonContext.Default) +}; +``` + +### 2.5 Fix Anonymous Type in Health Check +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** - Replace anonymous object with typed `HealthCheckResponse` class. + +--- + +## Phase 3: Dapper AOT (15+ model types) + +### 3.1 Dapper Usage Locations + +| File | Dapper Methods | Model Types | +|------|---------------|-------------| +| `LotFinderRepository.SearchManagement.cs` | QueryAsync, QueryFirstOrDefaultAsync | Search | +| `LotFinderRepository.Lookups.cs` | QueryAsync | Item, Lot, WorkOrder, WorkCenter, ProfitCenter, JdeUser | +| `LotFinderRepository.DataSync.cs` | QueryAsync | DataUpdate | +| `SearchProcessor.cs` | QueryAsync, QuerySingleOrDefaultAsync | SearchResult, MisSearchResult, MisNonMatchSearchResult | +| `ManualSyncRequestService.cs` | QueryAsync, QuerySingleAsync, ExecuteAsync | ManualSyncRequest | +| `WorkOrderTraversalService.cs` | QuerySingleAsync | (int, int) tuple | +| `DataUpdateRepository.cs` | QueryAsync, ExecuteScalarAsync, ExecuteAsync | DataUpdate, (string, int, DateTime?, int) tuple | +| `SearchRepository.cs` | QueryFirstOrDefaultAsync, ExecuteAsync | Search | + +### 3.2 Add Dapper.AOT Package +**Files:** `JdeScoping.DataAccess.csproj`, `JdeScoping.DataSync.csproj` + +```xml + + + $(InterceptorsPreviewNamespaces);Dapper.AOT + +``` + +### 3.3 Enable AOT for Assemblies +**`NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs`** (new): +```csharp +[assembly: DapperAot] +``` + +### 3.4 Fix Private Field Mapping Pattern +Change 6 models from private to internal for Dapper.AOT compatibility: + +| Model | File | +|-------|------| +| Item | `Core/Models/Inventory/Item.cs` | +| Lot | `Core/Models/Inventory/Lot.cs` | +| WorkOrder | `Core/Models/WorkOrders/WorkOrder.cs` | +| WorkCenter | `Core/Models/Organization/WorkCenter.cs` | +| ProfitCenter | `Core/Models/Organization/ProfitCenter.cs` | +| JdeUser | `Core/Models/Organization/JdeUser.cs` | + +Change: +```csharp +private int LastUpdateDate { get; set; } // Before +[Column("LastUpdateDate")] +internal int LastUpdateDate { get; set; } // After +``` + +### 3.5 Replace Tuple Queries with Named Types +Create explicit result types: + +**`NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs`** (new): +```csharp +public sealed class TraversalResult +{ + public int IterationsCompleted { get; init; } + public int TotalWorkOrders { get; init; } +} +``` + +**`NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs`** (new): +```csharp +public sealed class TableSyncStatusRow +{ + public string TableName { get; init; } = string.Empty; + public int UpdateType { get; init; } + public DateTime? LastSuccessfulSync { get; init; } + public int RecentFailures { get; init; } +} +``` + +--- + +## Phase 4: Configuration Binding + +### 4.1 Options Classes Inventory + +| Project | Options Class | Has DataAnnotations | Uses ValidateDataAnnotations | +|---------|--------------|---------------------|------------------------------| +| JdeScoping.Core | SecureStoreOptions | No | No | +| JdeScoping.Core | RsaKeyOptions | No | No | +| JdeScoping.Infrastructure | LdapOptions | Yes (1 Range) | No | +| JdeScoping.Api | AuthOptions | No | No | +| JdeScoping.DataAccess | DataAccessOptions | No | No | +| JdeScoping.DataAccess | SearchProcessingOptions | No | No | +| JdeScoping.DataAccess | SearchProcessingConfiguration | No | No | +| JdeScoping.DataSync | DataSyncOptions | Yes (6 Range) | **Yes** | +| JdeScoping.DataSync | WorkProcessorOptions | Yes (3 Range) | **Yes** | +| JdeScoping.ExcelIO | ExcelExportOptions | No | No | +| JdeScoping.DataSync.Dev | DevPipelineOptions | No | No | + +### 4.2 Create AOT-Compatible Validators +Replace `ValidateDataAnnotations()` with `IValidateOptions`: + +**`NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs`** (new) +**`NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs`** (new) + +### 4.3 Update DependencyInjection Files + +**`NEW/src/JdeScoping.DataSync/DependencyInjection.cs`** (lines 27-36): +```csharp +// Before +.ValidateDataAnnotations() + +// After +services.AddSingleton, DataSyncOptionsValidator>(); +``` + +**`NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs`** (lines 33-35): +Replace `GetSection().Get()` with `GetSection().GetValue()`. + +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** (lines 33-35): +Replace `GetSection().Get()` with explicit property binding. + +### 4.4 Remove Unused Package +**`NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj`**: +Remove `Microsoft.Extensions.Options.DataAnnotations` reference. + +--- + +## Phase 5: Third-Party Library Handling + +### 5.1 Package Risk Assessment + +| Risk Level | Packages | +|------------|----------| +| 🔴 High | Dapper (mitigated by Dapper.AOT), SqlKata, Swashbuckle, ClosedXML, dbup-sqlserver | +| 🟠 Medium | protobuf-net-data, System.CommandLine, SecureStore | +| 🟡 Low | Microsoft.Data.SqlClient, Oracle driver, LDAP, Serilog | +| 🟢 Safe | Cronos, OneOf (has source generator), ZstdSharp | + +### 5.2 Create Trimmer Roots +**`NEW/src/JdeScoping.Host/TrimmerRoots.xml`** (new): + +```xml + + + + + + + + + + + + + + +``` + +### 5.3 Known Limitations +These packages have partial/no AOT support - preserved via trimmer roots: +- ClosedXML (Excel generation) +- Oracle.ManagedDataAccess.Core +- dbup-sqlserver (migrations - dev only) +- Swashbuckle (Swagger - dev only) + +--- + +## Implementation Sequence + +| Step | Task | Files | +|------|------|-------| +| 1 | Create Directory.Build.props | 1 new | +| 2 | Create CoreJsonContext | 1 new | +| 3 | Create DataSyncJsonContext | 1 new | +| 4 | Create ApiJsonContext + HealthCheckResponse | 1 new | +| 5 | Create ClientJsonContext | 1 new | +| 6 | Create ConfigManagerJsonContext | 1 new | +| 7 | Update Api DependencyInjection (JSON registration) | 1 edit | +| 8 | Update ApiClientBase (JSON options) | 1 edit | +| 9 | Update PipelineRegistry (JSON options) | 1 edit | +| 10 | Add Dapper.AOT packages | 2 edits | +| 11 | Create DapperAotConfiguration | 1 new | +| 12 | Update 6 models (private→internal) | 6 edits | +| 13 | Create TraversalResult, TableSyncStatusRow | 2 new | +| 14 | Update tuple queries to use named types | 2 edits | +| 15 | Create DataSyncOptionsValidator | 1 new | +| 16 | Create WorkProcessorOptionsValidator | 1 new | +| 17 | Update DataSync DependencyInjection | 1 edit | +| 18 | Update Infrastructure DependencyInjection | 1 edit | +| 19 | Update Api DependencyInjection (config) | 1 edit | +| 20 | Update Host csproj (AOT settings) | 1 edit | +| 21 | Create TrimmerRoots.xml | 1 new | +| 22 | Remove DataAnnotations package | 1 edit | + +**Total: 14 new files, 18 edits** + +--- + +## Verification + +### Build Verification +```bash +dotnet build NEW/JdeScoping.slnx +``` +Should complete with no new errors. + +### AOT Analysis +```bash +dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishAot=true --no-build +``` +Check for AOT compatibility warnings. + +### Trim Analysis +```bash +dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishTrimmed=true +``` +Review trim warnings and verify TrimmerRoots coverage. + +### Test Suite +```bash +dotnet test NEW/JdeScoping.slnx +``` +All existing tests should pass. + +### E2E Verification +Run Playwright tests to verify JSON serialization works end-to-end. + +--- + +## Critical Files Summary + +**New Files (14):** +- `NEW/src/Directory.Build.props` +- `NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs` +- `NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs` +- `NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs` +- `NEW/src/JdeScoping.Client/Json/ClientJsonContext.cs` +- `NEW/src/Utils/JdeScoping.ConfigManager.Core/Json/ConfigManagerJsonContext.cs` +- `NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs` +- `NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs` +- `NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs` +- `NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs` +- `NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs` +- `NEW/src/JdeScoping.Host/TrimmerRoots.xml` + +**Key Edits:** +- `NEW/src/JdeScoping.Api/DependencyInjection.cs` - JSON + config +- `NEW/src/JdeScoping.Client/Services/ApiClientBase.cs` - JSON options +- `NEW/src/JdeScoping.DataSync/DependencyInjection.cs` - Validators +- `NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs` - Config binding +- `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` - AOT settings +- 6 model files for private→internal field change diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..5fca3f8 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file