Files
Joseph Doherty e5fe2f06e9 feat: add startup config validation and document ConfigManager pipeline editor
Add ConfigurationValidationRunner with IConfigurationValidator interface for
validating required settings at startup. Includes SecureStore and LDAP validators.
Expand ConfigManager with pipeline editing UI, dialogs, and step editors.
Update documentation with config validation guidance.
2026-01-21 17:47:15 -05:00

306 lines
8.8 KiB
Markdown

# 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<LdapOptions>(builder.Configuration.GetSection("Ldap"));
builder.Services.Configure<DataSyncOptions>(builder.Configuration.GetSection("DataSync"));
builder.Services.Configure<SearchOptions>(builder.Configuration.GetSection("Search"));
builder.Services.Configure<DataSourceOptions>(builder.Configuration.GetSection("DataSource"));
builder.Services.Configure<AuthOptions>(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<MyOptions> 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<IConfigurationValidator, MyOptionsValidator>();
```
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<DataSourceOptions>();
if (dataSourceOptions?.UseFileDataSource == true || builder.Environment.IsDevelopment())
{
builder.Services.AddScoped<IJdeDataSource, JdeFileDataSource>();
builder.Services.AddScoped<ICmsDataSource, CmsFileDataSource>();
}
else
{
builder.Services.AddScoped<IJdeDataSource, JdeOracleDataSource>();
builder.Services.AddScoped<ICmsDataSource, CmsOracleDataSource>();
}
```
## 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<AuthOptions>();
if (authOptions?.UseFakeAuth == true)
{
builder.Services.AddScoped<IAuthService, FakeAuthService>();
}
else
{
builder.Services.AddScoped<IAuthService, LdapAuthService>();
}
```
## 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)