# Configuration The application uses standard ASP.NET Core configuration with `appsettings.json` and environment variables for sensitive values. ## appsettings.json Structure ```json { "ConnectionStrings": { "SqlServer": "Server=localhost;Database=LotFinder;Integrated Security=true;TrustServerCertificate=true", "JdeOracle": "Data Source=jde-server:1521/JDEPROD;User Id=${JDE_USER};Password=${JDE_PASSWORD}", "CmsOracle": "Data Source=cms-server:1521/CMSPROD;User Id=${CMS_USER};Password=${CMS_PASSWORD}" }, "DataSource": { "UseFileDataSource": false, "FileDirectory": "DevData" }, "Auth": { "UseFakeAuth": false }, "Ldap": { "Url": "LDAP://your-domain.com", "BaseDn": "DC=your-domain,DC=com", "RequiredGroup": "CN=LotFinderUsers,OU=Groups,DC=your-domain,DC=com" }, "DataSync": { "MassSchedule": "0 2 * * 0", "DailySchedule": "0 3 * * *", "HourlySchedule": "0 * * * *", "BatchSize": 10000 }, "Search": { "MaxResultRows": 100000, "TimeoutSeconds": 300, "MaxConcurrentSearches": 5 }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } } } ``` ## Environment Variables Sensitive values are provided via environment variables at runtime: | Variable | Purpose | |----------|---------| | `JDE_USER` | JDE Oracle username | | `JDE_PASSWORD` | JDE Oracle password | | `CMS_USER` | CMS Oracle username | | `CMS_PASSWORD` | CMS Oracle password | For local development, use User Secrets or a `.env` file (not committed to source control). ## Strongly-Typed Options Configuration sections are bound to strongly-typed options classes: ```csharp public class LdapOptions { public string Url { get; set; } public string BaseDn { get; set; } public string RequiredGroup { get; set; } } public class DataSyncOptions { public string MassSchedule { get; set; } public string DailySchedule { get; set; } public string HourlySchedule { get; set; } public int BatchSize { get; set; } = 10000; } public class SearchOptions { public int MaxResultRows { get; set; } = 100000; public int TimeoutSeconds { get; set; } = 300; public int MaxConcurrentSearches { get; set; } = 5; } public class DataSourceOptions { public bool UseFileDataSource { get; set; } = false; public string FileDirectory { get; set; } = "DevData"; } public class AuthOptions { public bool UseFakeAuth { get; set; } = false; } ``` Registered in `Program.cs`: ```csharp builder.Services.Configure(builder.Configuration.GetSection("Ldap")); builder.Services.Configure(builder.Configuration.GetSection("DataSync")); builder.Services.Configure(builder.Configuration.GetSection("Search")); builder.Services.Configure(builder.Configuration.GetSection("DataSource")); builder.Services.Configure(builder.Configuration.GetSection("Auth")); ``` ## Configuration Validation The application validates configuration at startup using `ConfigurationValidationRunner`. This catches configuration errors early, before the application attempts to use misconfigured services. ### How validation works 1. The runner resolves all `IConfigurationValidator` implementations from DI 2. Validators execute in order (sorted by `Order` property, lower values run first) 3. Each validator returns a `ConfigurationValidationResult` with errors and warnings 4. Warnings are logged but don't prevent startup 5. Errors cause startup to fail with detailed logging ### Built-in validators | Validator | Order | Purpose | |-----------|-------|---------| | `SecureStoreValidator` | 100 | Validates required secrets exist in the SecureStore | | `LdapOptionsValidator` | 200 | Validates LDAP configuration settings | ### Creating a new validator Implement `IConfigurationValidator` in `JdeScoping.Infrastructure/Validation/`: ```csharp using JdeScoping.Core.Validation; public class MyOptionsValidator : IConfigurationValidator { private readonly MyOptions _options; public int Order => 300; // Run after existing validators public string Name => "MyOptions"; public MyOptionsValidator(IOptions options) { _options = options.Value; } public ConfigurationValidationResult Validate() { var result = new ConfigurationValidationResult(Name); if (string.IsNullOrEmpty(_options.RequiredSetting)) result.AddError("RequiredSetting must be configured"); if (_options.Timeout < 1000) result.AddWarning("Timeout below 1000ms may cause issues"); return result; } } ``` Register the validator in `DependencyInjection.cs`: ```csharp services.AddSingleton(); ``` The runner automatically discovers and executes all registered validators. ### Validation result types - **Errors** - Fatal configuration problems that prevent startup. Use `result.AddError()` for missing required settings, invalid values, or conditions that would cause runtime failures. - **Warnings** - Non-fatal issues that should be logged but allow startup. Use `result.AddWarning()` for suboptimal settings or deprecated configurations. ### Order conventions Follow these conventions for the `Order` property: | Range | Purpose | |-------|---------| | 100 | Security/secrets (SecureStore) | | 200 | Authentication (LDAP) | | 300-399 | Data sources and connections | | 400-499 | Service-specific validation | | 500+ | Application-level validation | Lower-ordered validators run first, allowing dependent validators to assume earlier validations passed. ## Data Source Configuration The JDE and CMS data sources support two implementations: | Implementation | Use Case | |----------------|----------| | Oracle (`JdeOracleDataSource`, `CmsOracleDataSource`) | Production - connects to Oracle databases | | File (`JdeFileDataSource`, `CmsFileDataSource`) | Development - reads from exported JSON/CSV files | ### Development Setup For development without Oracle access, set `UseFileDataSource: true` in `appsettings.Development.json`: ```json { "DataSource": { "UseFileDataSource": true, "FileDirectory": "DevData" } } ``` Place data export files in the `DevData` directory: ``` DevData/ ├── workorders.json ├── lots.json ├── items.json └── lotusage.json ``` ### Registration Logic ```csharp var dataSourceOptions = builder.Configuration .GetSection("DataSource").Get(); if (dataSourceOptions?.UseFileDataSource == true || builder.Environment.IsDevelopment()) { builder.Services.AddScoped(); builder.Services.AddScoped(); } else { builder.Services.AddScoped(); builder.Services.AddScoped(); } ``` ## Authentication Configuration Authentication supports two implementations: | Implementation | Use Case | |----------------|----------| | LDAP (`LdapAuthService`) | Production - authenticates against real LDAP server | | Fake (`FakeAuthService`) | Development - accepts any non-empty credentials | ### Development Setup For development without LDAP access, set `UseFakeAuth: true` in `appsettings.Development.json`: ```json { "Auth": { "UseFakeAuth": true } } ``` The fake auth service: - Accepts any non-empty username/password combination - Returns the username as the display name - Always returns `true` for group membership checks ### Registration Logic ```csharp var authOptions = builder.Configuration .GetSection("Auth").Get(); if (authOptions?.UseFakeAuth == true) { builder.Services.AddScoped(); } else { builder.Services.AddScoped(); } ``` ## Cron Expressions Data sync schedules use cron expressions, parsed by the Cronos library: | Expression | Meaning | |------------|---------| | `0 2 * * 0` | Sunday at 2:00 AM | | `0 3 * * *` | Daily at 3:00 AM | | `0 * * * *` | Every hour on the hour | ## Windows Service Installation When installing as a Windows Service, environment variables can be set: ```powershell # Create service sc.exe create JdeScopingTool binPath= "C:\Services\JdeScoping\JdeScoping.Host.exe" # Set environment variables for the service $envVars = "JDE_USER=myuser`0JDE_PASSWORD=mypass`0CMS_USER=cmsuser`0CMS_PASSWORD=cmspass" Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\JdeScopingTool" -Name "Environment" -Value $envVars ``` ## Related Documentation - [Host Project](./HostProject.md) - [Data Flow](./DataFlow.md)