Files
jdescopingtool/DOCUMENTATION/Architecture/Configuration.md
T
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

8.8 KiB

Configuration

The application uses standard ASP.NET Core configuration with appsettings.json and environment variables for sensitive values.

appsettings.json Structure

{
  "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:

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:

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/:

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:

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:

{
  "DataSource": {
    "UseFileDataSource": true,
    "FileDirectory": "DevData"
  }
}

Place data export files in the DevData directory:

DevData/
├── workorders.json
├── lots.json
├── items.json
└── lotusage.json

Registration Logic

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:

{
  "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

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:

# 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