docs: add XML documentation to NEW solution src files

Fix 173 of 175 documentation issues across 48 files using CommentChecker,
adding missing <summary>, <param>, and <inheritdoc /> tags to public APIs.
This commit is contained in:
Joseph Doherty
2026-01-27 06:19:20 -05:00
parent bfc1c8064a
commit 227a749cdf
53 changed files with 667 additions and 124 deletions
@@ -47,6 +47,7 @@ public partial class DataSyncRequests : ComponentBase
/// <summary>
/// Loads initial data when the component is initialized.
/// </summary>
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await LoadPipelinesAsync();
@@ -64,18 +64,20 @@ internal sealed class TransformingDataReader : IDataReader
public bool IsDBNull(int i) => _transformer.IsDBNull(i, _source);
/// <summary>
/// Gets the value of the column at the specified ordinal, applying transformations.
/// Gets the value of the column at the specified ordinal.
/// </summary>
/// <param name="i">The zero-based column ordinal.</param>
/// <returns>The transformed value of the column.</returns>
public object this[int i] => GetValue(i);
public object this[int i]
{
get => GetValue(i);
}
/// <summary>
/// Gets the value of the column with the specified name, applying transformations.
/// Gets the value of the column with the specified name.
/// </summary>
/// <param name="name">The name of the column.</param>
/// <returns>The transformed value of the column.</returns>
public object this[string name] => GetValue(GetOrdinal(name));
public object this[string name]
{
get => GetValue(GetOrdinal(name));
}
// Row navigation - delegated directly to source
/// <summary>
@@ -63,6 +63,9 @@ public class DataUpdateTask
/// <summary>
/// Creates a DataUpdateTask from an EtlPipelineConfig.
/// </summary>
/// <param name="pipeline">The ETL pipeline configuration.</param>
/// <param name="updateType">The type of update (Mass, Daily, or Hourly).</param>
/// <param name="minimumDt">Optional minimum datetime for incremental updates.</param>
public static DataUpdateTask FromPipeline(
EtlPipelineConfig pipeline,
UpdateTypes updateType,
@@ -46,6 +46,10 @@ public class PipelineRegistry : IPipelineRegistry
/// <summary>
/// Initializes a new instance of the <see cref="PipelineRegistry"/> class.
/// </summary>
/// <param name="options">Data sync configuration options.</param>
/// <param name="validator">Pipeline configuration validator.</param>
/// <param name="logger">Logger instance for diagnostics.</param>
/// <param name="environment">Host environment information.</param>
public PipelineRegistry(
IOptions<DataSyncOptions> options,
IPipelineValidator validator,
@@ -19,6 +19,10 @@ public class PipelineRegistryInitializer : IHostedService
/// <summary>
/// Initializes a new instance of the <see cref="PipelineRegistryInitializer"/> class.
/// </summary>
/// <param name="registry">The pipeline registry to initialize.</param>
/// <param name="options">Data sync configuration options.</param>
/// <param name="lifetime">Application lifetime management.</param>
/// <param name="logger">Logger instance for diagnostics.</param>
public PipelineRegistryInitializer(
IPipelineRegistry registry,
IOptions<DataSyncOptions> options,
@@ -16,10 +16,6 @@
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="ConfigValidation\" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
@@ -20,6 +20,12 @@ public class ConnectionStringValidator : IConfigurationValidator
/// <inheritdoc />
public string Name => "ConnectionStrings";
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionStringValidator"/> class.
/// </summary>
/// <param name="configuration">Application configuration.</param>
/// <param name="secureStore">Service for retrieving encrypted connection strings.</param>
/// <param name="logger">Logger instance for diagnostics.</param>
public ConnectionStringValidator(
IConfiguration configuration,
ISecureStoreService secureStore,
@@ -19,6 +19,11 @@ public class LdapOptionsValidator : IConfigurationValidator
/// <inheritdoc />
public string Name => "LdapOptions";
/// <summary>
/// Initializes a new instance of the <see cref="LdapOptionsValidator"/> class.
/// </summary>
/// <param name="options">LDAP configuration options.</param>
/// <param name="logger">Logger instance for diagnostics.</param>
public LdapOptionsValidator(
IOptions<LdapOptions> options,
ILogger<LdapOptionsValidator> logger)
@@ -21,6 +21,12 @@ public class SecureStoreValidator : IConfigurationValidator
/// <inheritdoc />
public string Name => "SecureStore";
/// <summary>
/// Initializes a new instance of the <see cref="SecureStoreValidator"/> class.
/// </summary>
/// <param name="secureStore">Service for retrieving encrypted secrets.</param>
/// <param name="options">SecureStore configuration options.</param>
/// <param name="logger">Logger instance for diagnostics.</param>
public SecureStoreValidator(
ISecureStoreService secureStore,
IOptions<SecureStoreOptions> options,
@@ -3,9 +3,10 @@
xmlns:converters="clr-namespace:JdeScoping.ConfigManager.Converters"
x:Class="JdeScoping.ConfigManager.App"
RequestedThemeVariant="Dark">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
</Application.Styles>
<Application.Resources>
<ResourceDictionary>
<!-- Converters -->
@@ -38,6 +38,11 @@ public class ProviderToVisibilityConverter : IValueConverter
/// <summary>
/// Converts a ConnectionProvider to a boolean indicating visibility.
/// </summary>
/// <param name="value">The ConnectionProvider value to convert.</param>
/// <param name="targetType">The target type (bool).</param>
/// <param name="parameter">Unused converter parameter.</param>
/// <param name="culture">The culture for conversion.</param>
/// <returns>True if the value matches the target provider; otherwise, false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is ConnectionProvider provider)
@@ -50,6 +55,11 @@ public class ProviderToVisibilityConverter : IValueConverter
/// <summary>
/// Not implemented - this is a one-way converter.
/// </summary>
/// <param name="value">The value to convert back.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Unused converter parameter.</param>
/// <param name="culture">The culture for conversion.</param>
/// <exception cref="NotImplementedException">This converter does not support reverse conversion.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
@@ -5,25 +5,74 @@ namespace JdeScoping.ConfigManager.Models;
/// </summary>
public class ConnectionStringEntry
{
/// <summary>
/// Gets or sets the name of this connection string entry.
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the database provider type for this connection.
/// </summary>
public ConnectionProvider Provider { get; set; } = ConnectionProvider.Generic;
// SqlServer fields
/// <summary>
/// Gets or sets the SQL Server server name or address.
/// </summary>
public string? Server { get; set; }
/// <summary>
/// Gets or sets the SQL Server database name.
/// </summary>
public string? Database { get; set; }
/// <summary>
/// Gets or sets the user ID for database authentication.
/// </summary>
public string? UserId { get; set; }
/// <summary>
/// Gets or sets the password for database authentication.
/// </summary>
public string? Password { get; set; }
/// <summary>
/// Gets or sets the encryption setting for SQL Server connection ("True" or "False").
/// </summary>
public string Encrypt { get; set; } = "True";
/// <summary>
/// Gets or sets a value indicating whether to trust server certificate for SQL Server.
/// </summary>
public bool TrustServerCertificate { get; set; }
/// <summary>
/// Gets or sets the connection timeout in seconds for SQL Server.
/// </summary>
public int ConnectionTimeout { get; set; } = 30;
/// <summary>
/// Gets or sets the application name for SQL Server connection identification.
/// </summary>
public string? ApplicationName { get; set; }
// Oracle fields
/// <summary>
/// Gets or sets the Oracle server host name or address.
/// </summary>
public string? Host { get; set; }
/// <summary>
/// Gets or sets the Oracle server port number.
/// </summary>
public int Port { get; set; } = 1521;
/// <summary>
/// Gets or sets the Oracle service name.
/// </summary>
public string? ServiceName { get; set; }
// Generic fields
/// <summary>
/// Gets or sets the raw connection string for Generic provider type.
/// </summary>
public string? RawConnectionString { get; set; }
/// <summary>
@@ -9,5 +9,8 @@ namespace JdeScoping.ConfigManager.Models;
[JsonConverter(typeof(ConnectionStringsSectionConverter))]
public class ConnectionStringsSection
{
/// <summary>
/// Gets or sets the list of connection string entries.
/// </summary>
public List<ConnectionStringEntry> Entries { get; set; } = new();
}
@@ -19,6 +19,7 @@ namespace JdeScoping.ConfigManager.Models;
/// </remarks>
public class ConnectionStringsSectionConverter : JsonConverter<ConnectionStringsSection>
{
/// <inheritdoc />
public override ConnectionStringsSection? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
@@ -61,18 +62,16 @@ public class ConnectionStringsSectionConverter : JsonConverter<ConnectionStrings
? reader.GetString()
: null;
if (!string.IsNullOrEmpty(connectionString))
{
var entry = ParseConnectionString(propertyName, connectionString);
section.Entries.Add(entry);
}
}
}
}
var entry = ParseConnectionString(propertyName, connectionString ?? string.Empty);
section.Entries.Add(entry);
}
}
}
return section;
}
/// <inheritdoc />
public override void Write(
Utf8JsonWriter writer,
ConnectionStringsSection value,
@@ -95,23 +94,57 @@ public class ConnectionStringsSectionConverter : JsonConverter<ConnectionStrings
/// <summary>
/// Parses a connection string and attempts to detect the provider type.
/// </summary>
private static ConnectionStringEntry ParseConnectionString(string name, string connectionString)
{
var entry = new ConnectionStringEntry
{
Name = name,
RawConnectionString = connectionString
};
// Try to detect provider and parse structured fields
var parts = ParseConnectionStringParts(connectionString);
// Detect Oracle first (Data Source with host:port/service pattern)
if (parts.TryGetValue("data source", out var dataSource) &&
IsOracleDataSource(dataSource))
{
entry.Provider = ConnectionProvider.Oracle;
ParseOracleDataSource(entry, dataSource);
internal static ConnectionStringEntry ParseConnectionString(string name, string connectionString)
{
var entry = new ConnectionStringEntry
{
Name = name,
RawConnectionString = connectionString
};
// Try to detect provider and parse structured fields
var parts = ParseConnectionStringParts(connectionString);
// Detect Oracle using HOST/Service Name/Port pattern (DDTek.Oracle style)
var hasHost = parts.TryGetValue("host", out var host);
var hasServiceName = parts.TryGetValue("service name", out var serviceName);
var hasPort = parts.TryGetValue("port", out var portText);
if (hasHost || hasServiceName || hasPort)
{
entry.Provider = ConnectionProvider.Oracle;
if (hasHost && !string.IsNullOrEmpty(host))
{
entry.Host = host;
}
if (hasServiceName && !string.IsNullOrEmpty(serviceName))
{
entry.ServiceName = serviceName;
}
if (hasPort && !string.IsNullOrEmpty(portText) && int.TryParse(portText, out var port))
{
entry.Port = port;
}
if (parts.TryGetValue("user id", out var oraUserId))
{
entry.UserId = oraUserId;
}
if (parts.TryGetValue("password", out var oraPassword))
{
entry.Password = oraPassword;
}
}
// Detect Oracle first (Data Source with host:port/service pattern)
else if (parts.TryGetValue("data source", out var dataSource) &&
IsOracleDataSource(dataSource))
{
entry.Provider = ConnectionProvider.Oracle;
ParseOracleDataSource(entry, dataSource);
if (parts.TryGetValue("user id", out var oraUserId))
{
@@ -179,8 +212,27 @@ public class ConnectionStringsSectionConverter : JsonConverter<ConnectionStrings
entry.Provider = ConnectionProvider.Generic;
}
return entry;
}
return entry;
}
internal static void ApplyConnectionString(ConnectionStringEntry entry, string connectionString)
{
var parsed = ParseConnectionString(entry.Name, connectionString);
entry.Provider = parsed.Provider;
entry.Server = parsed.Server;
entry.Database = parsed.Database;
entry.UserId = parsed.UserId;
entry.Password = parsed.Password;
entry.Encrypt = parsed.Encrypt;
entry.TrustServerCertificate = parsed.TrustServerCertificate;
entry.ConnectionTimeout = parsed.ConnectionTimeout;
entry.ApplicationName = parsed.ApplicationName;
entry.Host = parsed.Host;
entry.Port = parsed.Port;
entry.ServiceName = parsed.ServiceName;
entry.RawConnectionString = parsed.RawConnectionString;
}
/// <summary>
/// Parses a connection string into key-value pairs.
@@ -11,6 +11,13 @@ public class ConnectionTestService : IConnectionTestService
{
private const int ConnectionTimeoutSeconds = 10;
/// <summary>
/// Tests a database connection asynchronously.
/// </summary>
/// <param name="connectionString">The connection string to test.</param>
/// <param name="provider">The database provider type.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>A ConnectionTestResult indicating success or failure of the test.</returns>
public async Task<ConnectionTestResult> TestConnectionAsync(
string connectionString,
ConnectionProvider provider,
@@ -7,8 +7,19 @@ namespace JdeScoping.ConfigManager.Services;
/// </summary>
public class ConnectionTestResult
{
/// <summary>
/// Gets a value indicating whether the connection test was successful.
/// </summary>
public bool Success { get; init; }
/// <summary>
/// Gets the message describing the result of the connection test.
/// </summary>
public string Message { get; init; } = string.Empty;
/// <summary>
/// Gets the elapsed time of the connection test operation.
/// </summary>
public TimeSpan? Duration { get; init; }
}
@@ -17,5 +28,12 @@ public class ConnectionTestResult
/// </summary>
public interface IConnectionTestService
{
/// <summary>
/// Tests a database connection asynchronously.
/// </summary>
/// <param name="connectionString">The connection string to test.</param>
/// <param name="provider">The database provider type.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>A ConnectionTestResult indicating success or failure of the test.</returns>
Task<ConnectionTestResult> TestConnectionAsync(string connectionString, ConnectionProvider provider, CancellationToken cancellationToken = default);
}
@@ -11,6 +11,10 @@ public class DiffPreviewDialogViewModel : ViewModelBase
{
private bool _result;
/// <summary>
/// Initializes a new instance of the <see cref="DiffPreviewDialogViewModel"/> class.
/// </summary>
/// <param name="diff">The diff result containing the changes to preview.</param>
public DiffPreviewDialogViewModel(DiffResult diff)
{
ArgumentNullException.ThrowIfNull(diff);
@@ -25,19 +29,48 @@ public class DiffPreviewDialogViewModel : ViewModelBase
CancelCommand = new RelayCommand(() => { Result = false; RequestClose?.Invoke(); });
}
/// <summary>
/// Gets the collection of diff lines to display in the preview.
/// </summary>
public ObservableCollection<DiffLineViewModel> Lines { get; }
/// <summary>
/// Gets the number of lines inserted in the diff.
/// </summary>
public int Insertions { get; }
/// <summary>
/// Gets the number of lines deleted in the diff.
/// </summary>
public int Deletions { get; }
/// <summary>
/// Gets a value indicating whether the diff contains any changes.
/// </summary>
public bool HasChanges { get; }
/// <summary>
/// Gets or sets the dialog result (true if saved, false if canceled).
/// </summary>
public bool Result
{
get => _result;
private set => SetProperty(ref _result, value);
}
/// <summary>
/// Gets the command to save the diff changes and close the dialog.
/// </summary>
public ICommand SaveCommand { get; }
/// <summary>
/// Gets the command to cancel and close the dialog without saving.
/// </summary>
public ICommand CancelCommand { get; }
/// <summary>
/// Gets or sets the action to request dialog closure.
/// </summary>
public Action? RequestClose { get; set; }
}
@@ -46,6 +79,10 @@ public class DiffPreviewDialogViewModel : ViewModelBase
/// </summary>
public class DiffLineViewModel
{
/// <summary>
/// Initializes a new instance of the <see cref="DiffLineViewModel"/> class.
/// </summary>
/// <param name="line">The diff line data to represent.</param>
public DiffLineViewModel(DiffLine line)
{
OldLineNumber = line.OldLineNumber?.ToString() ?? "";
@@ -66,10 +103,33 @@ public class DiffLineViewModel
};
}
/// <summary>
/// Gets the original line number (empty string if not applicable).
/// </summary>
public string OldLineNumber { get; }
/// <summary>
/// Gets the new line number (empty string if not applicable).
/// </summary>
public string NewLineNumber { get; }
/// <summary>
/// Gets the text content of the diff line.
/// </summary>
public string Text { get; }
/// <summary>
/// Gets the type of diff line (added, removed, or context).
/// </summary>
public DiffLineType Type { get; }
/// <summary>
/// Gets the background color for the diff line visualization.
/// </summary>
public string Background { get; }
/// <summary>
/// Gets the border color for the diff line visualization.
/// </summary>
public string BorderColor { get; }
}
@@ -10,6 +10,11 @@ public class AuthFormViewModel : ViewModelBase
private readonly AuthSection _model;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="AuthFormViewModel"/> class.
/// </summary>
/// <param name="model">The auth section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public AuthFormViewModel(AuthSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -52,11 +52,10 @@ public class ConnectionStringsFormViewModel : ViewModelBase
: null;
// Update entry's RawConnectionString with SecureStore value if available
if (!string.IsNullOrEmpty(secureStoreValue))
{
entry.RawConnectionString = secureStoreValue;
entry.Provider = ConnectionProvider.Generic; // Use Generic since we have the full string
}
if (!string.IsNullOrEmpty(secureStoreValue))
{
ConnectionStringsSectionConverter.ApplyConnectionString(entry, secureStoreValue);
}
Connections.Add(new ConnectionStringEntryViewModel(entry, OnEntryChanged));
}
@@ -10,6 +10,11 @@ public class DataAccessFormViewModel : ViewModelBase
private readonly DataAccessSection _model;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="DataAccessFormViewModel"/> class.
/// </summary>
/// <param name="model">The data access section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public DataAccessFormViewModel(DataAccessSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -10,6 +10,11 @@ public class DataSyncFormViewModel : ViewModelBase
private readonly DataSyncSection _model;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="DataSyncFormViewModel"/> class.
/// </summary>
/// <param name="model">The data sync section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public DataSyncFormViewModel(DataSyncSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -21,6 +21,11 @@ public class ExcelExportFormViewModel : ViewModelBase
.ToList()
.AsReadOnly();
/// <summary>
/// Initializes a new instance of the <see cref="ExcelExportFormViewModel"/> class.
/// </summary>
/// <param name="model">The excel export section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public ExcelExportFormViewModel(ExcelExportSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -10,6 +10,11 @@ public class LdapFormViewModel : ViewModelBase
private readonly LdapSection _model;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="LdapFormViewModel"/> class.
/// </summary>
/// <param name="model">The LDAP section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public LdapFormViewModel(LdapSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -17,6 +17,14 @@ public class PipelineEditorViewModel : ViewModelBase
private PipelineStepViewModelBase? _selectedStep;
private object? _selectedStepEditor;
/// <summary>
/// Initializes a new instance of the <see cref="PipelineEditorViewModel"/> class.
/// </summary>
/// <param name="name">The pipeline name.</param>
/// <param name="model">The pipeline configuration model.</param>
/// <param name="availableConnections">List of available connection names from configuration.</param>
/// <param name="dialogService">Service for showing dialogs.</param>
/// <param name="onChanged">Callback invoked when the pipeline configuration changes.</param>
public PipelineEditorViewModel(string name, EtlPipelineConfig model, IReadOnlyList<string> availableConnections, IDialogService dialogService, Action onChanged)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
@@ -318,14 +326,49 @@ public class PipelineEditorViewModel : ViewModelBase
}
// Commands
/// <summary>
/// Gets the command to add a pre-script step.
/// </summary>
public ICommand AddPreScriptCommand { get; }
/// <summary>
/// Gets the command to add a transformer step.
/// </summary>
public ICommand AddTransformerCommand { get; }
/// <summary>
/// Gets the command to add a post-script step.
/// </summary>
public ICommand AddPostScriptCommand { get; }
/// <summary>
/// Gets the command to remove a pipeline step.
/// </summary>
public ICommand RemoveStepCommand { get; }
/// <summary>
/// Gets the command to delete the currently selected step.
/// </summary>
public ICommand DeleteSelectedStepCommand { get; }
/// <summary>
/// Gets the command to move a step up in its collection.
/// </summary>
public ICommand MoveStepUpCommand { get; }
/// <summary>
/// Gets the command to move a step down in its collection.
/// </summary>
public ICommand MoveStepDownCommand { get; }
/// <summary>
/// Gets the command to move the selected step up.
/// </summary>
public ICommand MoveSelectedStepUpCommand { get; }
/// <summary>
/// Gets the command to move the selected step down.
/// </summary>
public ICommand MoveSelectedStepDownCommand { get; }
/// <summary>
@@ -333,10 +376,11 @@ public class PipelineEditorViewModel : ViewModelBase
/// </summary>
public IReadOnlyList<string> AvailableTransformerTypes => TransformerFactory.AvailableTypes;
/// <summary>
/// Property to track selected transformer type for adding.
/// </summary>
private string? _selectedTransformerType;
/// <summary>
/// Gets or sets the selected transformer type for adding a new transformer.
/// </summary>
public string? SelectedTransformerType
{
get => _selectedTransformerType;
@@ -438,6 +482,7 @@ public class PipelineEditorViewModel : ViewModelBase
/// <summary>
/// Adds a specific transformer type.
/// </summary>
/// <param name="typeName">The name of the transformer type to add.</param>
public void AddTransformerOfType(string typeName)
{
var vm = TransformerFactory.CreateNew(typeName, () =>
@@ -10,6 +10,11 @@ public class SearchFormViewModel : ViewModelBase
private readonly SearchSection _model;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="SearchFormViewModel"/> class.
/// </summary>
/// <param name="model">The search section model to edit.</param>
/// <param name="onChanged">The callback to invoke when configuration changes.</param>
public SearchFormViewModel(SearchSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -194,6 +194,7 @@ public class MainWindowViewModel : ViewModelBase
/// <param name="secureStoreManager">Service for managing encrypted secret stores.</param>
/// <param name="clipboardService">Service for clipboard operations.</param>
/// <param name="runtimeValidationService">Service for runtime configuration validation.</param>
/// <param name="connectionTestService">Service for testing database connections.</param>
/// <param name="logger">Optional logger for recording view model activities.</param>
public MainWindowViewModel(
IFileSystem fileSystem,
@@ -271,6 +272,11 @@ public class MainWindowViewModel : ViewModelBase
/// </summary>
private class NullClipboardService : IClipboardService
{
/// <summary>
/// Sets text to clipboard (no-op for design-time).
/// </summary>
/// <param name="text">The text to set (ignored in this implementation).</param>
/// <returns>A completed task.</returns>
public Task SetTextAsync(string text) => Task.CompletedTask;
}
@@ -9,14 +9,26 @@ public class DestinationStepViewModel : PipelineStepViewModelBase
{
private readonly DestinationElement _model;
/// <summary>
/// Initializes a new instance of the <see cref="DestinationStepViewModel"/> class.
/// </summary>
/// <param name="model">The destination configuration model.</param>
/// <param name="onChanged">Callback invoked when the destination configuration changes.</param>
public DestinationStepViewModel(DestinationElement model, Action onChanged) : base(onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
/// <inheritdoc />
public override PipelineStepType StepType => PipelineStepType.Destination;
/// <inheritdoc />
public override string DisplayName => "Destination";
/// <inheritdoc />
public override string Icon => "󰆼"; // mdi-database
/// <inheritdoc />
public override string Summary => !string.IsNullOrEmpty(Table) ? $"→ {Table}" : "(no table)";
/// <summary>
@@ -23,6 +23,10 @@ public abstract class PipelineStepViewModelBase : ViewModelBase
private bool _isSelected;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="PipelineStepViewModelBase"/> class.
/// </summary>
/// <param name="onChanged">Callback invoked when the step changes.</param>
protected PipelineStepViewModelBase(Action onChanged)
{
_onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged));
@@ -73,6 +77,11 @@ public class PreScriptStepViewModel : PipelineStepViewModelBase
{
private readonly ScriptElement _model;
/// <summary>
/// Initializes a new instance of the <see cref="PreScriptStepViewModel"/> class with an existing script.
/// </summary>
/// <param name="model">The script element to wrap.</param>
/// <param name="onChanged">Callback invoked when the script changes.</param>
public PreScriptStepViewModel(ScriptElement model, Action onChanged) : base(onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -81,14 +90,19 @@ public class PreScriptStepViewModel : PipelineStepViewModelBase
/// <summary>
/// Creates a new pre-script with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the script changes.</param>
public PreScriptStepViewModel(Action onChanged) : base(onChanged)
{
_model = new ScriptElement { Connection = "lotfinder", Script = string.Empty };
}
/// <inheritdoc />
public override PipelineStepType StepType => PipelineStepType.PreScript;
/// <inheritdoc />
public override string DisplayName => "Pre-Script";
/// <inheritdoc />
public override string Icon => "󰯂"; // mdi-script-text
/// <inheritdoc />
public override string Summary => TruncateScript(_model.Script);
/// <summary>
@@ -146,6 +160,11 @@ public class PostScriptStepViewModel : PipelineStepViewModelBase
{
private readonly ScriptElement _model;
/// <summary>
/// Initializes a new instance of the <see cref="PostScriptStepViewModel"/> class with an existing script.
/// </summary>
/// <param name="model">The script element to wrap.</param>
/// <param name="onChanged">Callback invoked when the script changes.</param>
public PostScriptStepViewModel(ScriptElement model, Action onChanged) : base(onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -154,14 +173,19 @@ public class PostScriptStepViewModel : PipelineStepViewModelBase
/// <summary>
/// Creates a new post-script with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the script changes.</param>
public PostScriptStepViewModel(Action onChanged) : base(onChanged)
{
_model = new ScriptElement { Connection = "lotfinder", Script = string.Empty };
}
/// <inheritdoc />
public override PipelineStepType StepType => PipelineStepType.PostScript;
/// <inheritdoc />
public override string DisplayName => "Post-Script";
/// <inheritdoc />
public override string Icon => "󰯂"; // mdi-script-text
/// <inheritdoc />
public override string Summary => TruncateScript(_model.Script);
/// <summary>
@@ -11,6 +11,12 @@ public class SourceStepViewModel : PipelineStepViewModelBase
{
private readonly SourceElement _model;
/// <summary>
/// Initializes a new instance of the <see cref="SourceStepViewModel"/> class.
/// </summary>
/// <param name="model">The source configuration model.</param>
/// <param name="availableConnections">List of available connection names from configuration.</param>
/// <param name="onChanged">Callback invoked when the source configuration changes.</param>
public SourceStepViewModel(SourceElement model, IReadOnlyList<string> availableConnections, Action onChanged) : base(onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
@@ -33,9 +39,16 @@ public class SourceStepViewModel : PipelineStepViewModelBase
/// </summary>
public IReadOnlyList<string> AvailableConnections { get; }
/// <inheritdoc />
public override PipelineStepType StepType => PipelineStepType.Source;
/// <inheritdoc />
public override string DisplayName => "Source";
/// <inheritdoc />
public override string Icon => "󰆼"; // mdi-database
/// <inheritdoc />
public override string Summary => $"{Connection}: {TruncateQuery(Query)}";
/// <summary>
@@ -121,6 +134,7 @@ public class SourceStepViewModel : PipelineStepViewModelBase
/// <summary>
/// Removes a parameter.
/// </summary>
/// <param name="parameter">The parameter view model to remove.</param>
public void RemoveParameter(ParameterViewModel parameter)
{
if (Parameters.Remove(parameter))
@@ -159,6 +173,12 @@ public class ParameterViewModel : ViewModelBase
private string? _value;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="ParameterViewModel"/> class.
/// </summary>
/// <param name="key">The parameter key used in the dictionary.</param>
/// <param name="model">The parameter configuration model.</param>
/// <param name="onChanged">Callback invoked when the parameter changes.</param>
public ParameterViewModel(string key, ParameterElement model, Action onChanged)
{
_key = key;
@@ -16,11 +16,17 @@ public abstract class TransformerStepViewModelBase : PipelineStepViewModelBase
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
/// <summary>
/// Initializes a new instance of the <see cref="TransformerStepViewModelBase"/> class.
/// </summary>
/// <param name="onChanged">Callback invoked when the transformer changes.</param>
protected TransformerStepViewModelBase(Action onChanged) : base(onChanged)
{
}
/// <inheritdoc />
public override PipelineStepType StepType => PipelineStepType.Transformer;
/// <inheritdoc />
public override string Icon => "󰁖"; // mdi-cog-transfer
/// <summary>
@@ -36,6 +42,8 @@ public abstract class TransformerStepViewModelBase : PipelineStepViewModelBase
/// <summary>
/// Helper to create a JsonElement from an object.
/// </summary>
/// <param name="config">The object to serialize into a JsonElement.</param>
/// <returns>A JsonElement containing the serialized configuration.</returns>
protected static JsonElement CreateConfigElement(object config)
{
var json = JsonSerializer.Serialize(config, JsonOptions);
@@ -51,6 +59,11 @@ public class ColumnDropTransformerViewModel : TransformerStepViewModelBase
{
private string _columnsText;
/// <summary>
/// Initializes a new instance of the <see cref="ColumnDropTransformerViewModel"/> class with an existing configuration.
/// </summary>
/// <param name="element">The transform element containing the column configuration.</param>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public ColumnDropTransformerViewModel(TransformElement element, Action onChanged) : base(onChanged)
{
_columnsText = string.Empty;
@@ -65,13 +78,20 @@ public class ColumnDropTransformerViewModel : TransformerStepViewModelBase
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ColumnDropTransformerViewModel"/> class with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public ColumnDropTransformerViewModel(Action onChanged) : base(onChanged)
{
_columnsText = string.Empty;
}
/// <inheritdoc />
public override string TransformerType => "ColumnDrop";
/// <inheritdoc />
public override string DisplayName => "Column Drop";
/// <inheritdoc />
public override string Summary => GetColumnCount() > 0 ? $"Drop {GetColumnCount()} columns" : "No columns";
/// <summary>
@@ -103,6 +123,7 @@ public class ColumnDropTransformerViewModel : TransformerStepViewModelBase
return _columnsText.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Length;
}
/// <inheritdoc />
public override TransformElement ToModel() => new()
{
TransformType = TransformerType,
@@ -115,6 +136,11 @@ public class ColumnDropTransformerViewModel : TransformerStepViewModelBase
/// </summary>
public class ColumnRenameTransformerViewModel : TransformerStepViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ColumnRenameTransformerViewModel"/> class with an existing configuration.
/// </summary>
/// <param name="element">The transform element containing the mapping configuration.</param>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public ColumnRenameTransformerViewModel(TransformElement element, Action onChanged) : base(onChanged)
{
Mappings = [];
@@ -137,14 +163,21 @@ public class ColumnRenameTransformerViewModel : TransformerStepViewModelBase
AddMappingCommand = new RelayCommand(AddMapping);
}
/// <summary>
/// Initializes a new instance of the <see cref="ColumnRenameTransformerViewModel"/> class with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public ColumnRenameTransformerViewModel(Action onChanged) : base(onChanged)
{
Mappings = [];
AddMappingCommand = new RelayCommand(AddMapping);
}
/// <inheritdoc />
public override string TransformerType => "ColumnRename";
/// <inheritdoc />
public override string DisplayName => "Column Rename";
/// <inheritdoc />
public override string Summary => Mappings.Count > 0 ? $"Rename {Mappings.Count} columns" : "No mappings";
/// <summary>
@@ -174,6 +207,7 @@ public class ColumnRenameTransformerViewModel : TransformerStepViewModelBase
/// <summary>
/// Removes a mapping.
/// </summary>
/// <param name="mapping">The column mapping to remove.</param>
public void RemoveMapping(ColumnMappingViewModel mapping)
{
if (Mappings.Remove(mapping))
@@ -183,6 +217,7 @@ public class ColumnRenameTransformerViewModel : TransformerStepViewModelBase
}
}
/// <inheritdoc />
public override TransformElement ToModel() => new()
{
TransformType = TransformerType,
@@ -199,6 +234,12 @@ public class ColumnMappingViewModel : ViewModelBase
private string _newName;
private readonly Action _onChanged;
/// <summary>
/// Initializes a new instance of the <see cref="ColumnMappingViewModel"/> class.
/// </summary>
/// <param name="oldName">The original column name.</param>
/// <param name="newName">The new column name.</param>
/// <param name="onChanged">Callback invoked when the mapping changes.</param>
public ColumnMappingViewModel(string oldName, string newName, Action onChanged)
{
_oldName = oldName;
@@ -242,6 +283,11 @@ public class JdeDateTransformerViewModel : TransformerStepViewModelBase
private string? _timeColumn;
private string? _outputColumn;
/// <summary>
/// Initializes a new instance of the <see cref="JdeDateTransformerViewModel"/> class from a TransformElement.
/// </summary>
/// <param name="element">The transform element containing date/time column configuration.</param>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public JdeDateTransformerViewModel(TransformElement element, Action onChanged) : base(onChanged)
{
_dateColumn = null;
@@ -259,6 +305,10 @@ public class JdeDateTransformerViewModel : TransformerStepViewModelBase
}
}
/// <summary>
/// Initializes a new instance of the <see cref="JdeDateTransformerViewModel"/> class with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public JdeDateTransformerViewModel(Action onChanged) : base(onChanged)
{
_dateColumn = null;
@@ -266,9 +316,13 @@ public class JdeDateTransformerViewModel : TransformerStepViewModelBase
_outputColumn = null;
}
/// <inheritdoc />
public override string TransformerType => "JdeDate";
/// <inheritdoc />
public override string DisplayName => "JDE Date Convert";
/// <inheritdoc />
public override string Icon => "󰃭"; // mdi-calendar
/// <inheritdoc />
public override string Summary => !string.IsNullOrEmpty(_outputColumn) ? $"→ {_outputColumn}" : "Configure...";
/// <summary>
@@ -316,6 +370,7 @@ public class JdeDateTransformerViewModel : TransformerStepViewModelBase
}
}
/// <inheritdoc />
public override TransformElement ToModel() => new()
{
TransformType = TransformerType,
@@ -358,6 +413,11 @@ public class RegexTransformerViewModel : TransformerStepViewModelBase
private bool _hasTestError;
private string _testErrorMessage = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="RegexTransformerViewModel"/> class from a TransformElement.
/// </summary>
/// <param name="element">The transform element containing regex configuration.</param>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public RegexTransformerViewModel(TransformElement element, Action onChanged) : base(onChanged)
{
_columnName = string.Empty;
@@ -396,14 +456,22 @@ public class RegexTransformerViewModel : TransformerStepViewModelBase
TestPatternCommand = new RelayCommand(ExecuteTestPattern);
}
/// <summary>
/// Initializes a new instance of the <see cref="RegexTransformerViewModel"/> class with default values.
/// </summary>
/// <param name="onChanged">Callback invoked when the configuration changes.</param>
public RegexTransformerViewModel(Action onChanged) : base(onChanged)
{
TestPatternCommand = new RelayCommand(ExecuteTestPattern);
}
/// <inheritdoc />
public override string TransformerType => "Regex";
/// <inheritdoc />
public override string DisplayName => "Regex Transform";
/// <inheritdoc />
public override string Icon => "󰑑"; // mdi-regex
/// <inheritdoc />
public override string Summary => !string.IsNullOrEmpty(_columnName)
? $"{_columnName}: {(_isFindReplaceMode ? "Replace" : "Extract")}"
: "Configure...";
@@ -512,54 +580,81 @@ public class RegexTransformerViewModel : TransformerStepViewModelBase
[NonMatchBehavior.KeepOriginal, NonMatchBehavior.ReturnNull, NonMatchBehavior.ReturnEmpty];
// Test feature properties
/// <summary>
/// Gets or sets the test input value for pattern testing.
/// </summary>
public string TestInput
{
get => _testInput;
set => SetProperty(ref _testInput, value ?? string.Empty);
}
/// <summary>
/// Gets or sets the result value from pattern testing.
/// </summary>
public string TestResultValue
{
get => _testResultValue;
set => SetProperty(ref _testResultValue, value);
}
/// <summary>
/// Gets or sets the label describing the test result (e.g., "Output" or "No Match").
/// </summary>
public string TestResultLabel
{
get => _testResultLabel;
set => SetProperty(ref _testResultLabel, value);
}
/// <summary>
/// Gets or sets the icon for the test result.
/// </summary>
public string TestResultIcon
{
get => _testResultIcon;
set => SetProperty(ref _testResultIcon, value);
}
/// <summary>
/// Gets or sets the background color for the test result display.
/// </summary>
public string TestResultBackground
{
get => _testResultBackground;
set => SetProperty(ref _testResultBackground, value);
}
/// <summary>
/// Gets or sets a value indicating whether a test result is available.
/// </summary>
public bool HasTestResult
{
get => _hasTestResult;
set => SetProperty(ref _hasTestResult, value);
}
/// <summary>
/// Gets or sets a value indicating whether a test error occurred.
/// </summary>
public bool HasTestError
{
get => _hasTestError;
set => SetProperty(ref _hasTestError, value);
}
/// <summary>
/// Gets or sets the error message from test execution.
/// </summary>
public string TestErrorMessage
{
get => _testErrorMessage;
set => SetProperty(ref _testErrorMessage, value);
}
/// <summary>
/// Gets the command to execute pattern testing.
/// </summary>
public ICommand TestPatternCommand { get; }
private void ExecuteTestPattern()
@@ -628,6 +723,7 @@ public class RegexTransformerViewModel : TransformerStepViewModelBase
TestErrorMessage = string.Empty;
}
/// <inheritdoc />
public override TransformElement ToModel() => new()
{
TransformType = TransformerType,
@@ -650,6 +746,9 @@ public static class TransformerFactory
/// <summary>
/// Creates a transformer view model from a TransformElement.
/// </summary>
/// <param name="element">The transform element containing configuration.</param>
/// <param name="onChanged">Callback invoked when the view model changes.</param>
/// <returns>A transformer view model of the appropriate type, or null if the transformer type is unknown.</returns>
public static TransformerStepViewModelBase? Create(TransformElement element, Action onChanged)
{
return element.TransformType?.ToLowerInvariant() switch
@@ -665,6 +764,9 @@ public static class TransformerFactory
/// <summary>
/// Creates a new transformer view model by type name.
/// </summary>
/// <param name="typeName">The type name of the transformer to create.</param>
/// <param name="onChanged">Callback invoked when the view model changes.</param>
/// <returns>A new transformer view model of the specified type, or null if the type name is unknown.</returns>
public static TransformerStepViewModelBase? CreateNew(string typeName, Action onChanged)
{
return typeName?.ToLowerInvariant() switch
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Controls;
public partial class FlowArrow : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="FlowArrow"/> class.
/// </summary>
public FlowArrow()
{
InitializeComponent();
@@ -12,6 +12,9 @@ public partial class PipelineStepCard : UserControl
public static readonly StyledProperty<string> StepColorProperty =
AvaloniaProperty.Register<PipelineStepCard, string>(nameof(StepColor), "#3B82F6");
/// <summary>
/// Initializes a new instance of the <see cref="PipelineStepCard"/> class.
/// </summary>
public PipelineStepCard()
{
InitializeComponent();
@@ -5,11 +5,18 @@ namespace JdeScoping.ConfigManager.Views.Dialogs;
public partial class DiffPreviewDialog : Window
{
/// <summary>
/// Initializes a new instance of the <see cref="DiffPreviewDialog"/> class.
/// </summary>
public DiffPreviewDialog()
{
InitializeComponent();
}
/// <summary>
/// Initializes a new instance of the <see cref="DiffPreviewDialog"/> class with a view model.
/// </summary>
/// <param name="viewModel">The view model to bind to the dialog.</param>
public DiffPreviewDialog(DiffPreviewDialogViewModel viewModel) : this()
{
DataContext = viewModel;
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class ColumnDropEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ColumnDropEditorView"/> class.
/// </summary>
public ColumnDropEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class ColumnRenameEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ColumnRenameEditorView"/> class.
/// </summary>
public ColumnRenameEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class DestinationEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="DestinationEditorView"/> class.
/// </summary>
public DestinationEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class JdeDateEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="JdeDateEditorView"/> class.
/// </summary>
public JdeDateEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class PostScriptEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="PostScriptEditorView"/> class.
/// </summary>
public PostScriptEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class RegexEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="RegexEditorView"/> class.
/// </summary>
public RegexEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class ScriptEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ScriptEditorView"/> class.
/// </summary>
public ScriptEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Editors;
public partial class SourceEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="SourceEditorView"/> class.
/// </summary>
public SourceEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class AuthFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthFormView"/> class.
/// </summary>
public AuthFormView()
{
InitializeComponent();
@@ -279,71 +279,26 @@
</Border>
</StackPanel>
<!-- Connection List -->
<Border BorderBrush="#2D3540" BorderThickness="1" CornerRadius="4">
<StackPanel>
<!-- Header Row -->
<Grid Background="#151920" Height="32">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Name" Foreground="#9BA8B8"
FontSize="12" FontWeight="Medium"
VerticalAlignment="Center" Margin="12,0"/>
<TextBlock Grid.Column="1" Text="Provider" Foreground="#9BA8B8"
FontSize="12" FontWeight="Medium"
VerticalAlignment="Center" Margin="8,0"/>
<TextBlock Grid.Column="2" Text="Server" Foreground="#9BA8B8"
FontSize="12" FontWeight="Medium"
VerticalAlignment="Center" Margin="8,0"/>
</Grid>
<!-- List Items -->
<ListBox ItemsSource="{Binding Connections}"
SelectedItem="{Binding SelectedConnection}"
SelectionMode="Single"
Background="#0D0F12"
MaxHeight="180"
MinHeight="100">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="#2563EB"/>
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="#1E3A5F"/>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="36" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}"
Foreground="#E6EDF5" FontSize="13"
VerticalAlignment="Center" Margin="12,0"/>
<TextBlock Grid.Column="1" Text="{Binding ProviderDisplay}"
Foreground="#9BA8B8" FontSize="13"
VerticalAlignment="Center" Margin="8,0"/>
<TextBlock Grid.Column="2" Text="{Binding ServerDisplay}"
Foreground="#9BA8B8" FontSize="13"
VerticalAlignment="Center" Margin="8,0"/>
<Border Grid.ColumnSpan="3" BorderBrush="#2D3540"
BorderThickness="0,0,0,1" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
<!-- Connection List -->
<Border BorderBrush="#2D3540" BorderThickness="1" CornerRadius="4">
<DataGrid ItemsSource="{Binding Connections}"
SelectedItem="{Binding SelectedConnection}"
AutoGenerateColumns="False"
IsReadOnly="True"
SelectionMode="Single"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column"
Background="#0D0F12"
RowBackground="#0D0F12"
MaxHeight="220"
MinHeight="100">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="Auto"/>
<DataGridTextColumn Header="Provider" Binding="{Binding ProviderDisplay}" Width="Auto"/>
<DataGridTextColumn Header="Server" Binding="{Binding ServerDisplay}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Border>
<!-- Toolbar -->
<StackPanel Orientation="Horizontal" Spacing="8">
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class ConnectionStringsFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionStringsFormView"/> class.
/// </summary>
public ConnectionStringsFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class DataAccessFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="DataAccessFormView"/> class.
/// </summary>
public DataAccessFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class DataSyncFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="DataSyncFormView"/> class.
/// </summary>
public DataSyncFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class ExcelExportFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ExcelExportFormView"/> class.
/// </summary>
public ExcelExportFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class LdapFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="LdapFormView"/> class.
/// </summary>
public LdapFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class PipelineEditorView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="PipelineEditorView"/> class.
/// </summary>
public PipelineEditorView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class SearchFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="SearchFormView"/> class.
/// </summary>
public SearchFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class SecretFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="SecretFormView"/> class.
/// </summary>
public SecretFormView()
{
InitializeComponent();
@@ -4,6 +4,9 @@ namespace JdeScoping.ConfigManager.Views.Forms;
public partial class SecureStoreInfoFormView : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="SecureStoreInfoFormView"/> class.
/// </summary>
public SecureStoreInfoFormView()
{
InitializeComponent();
@@ -22,7 +22,7 @@ public class ConnectionStringsFormViewModelTests
}
[Fact]
public void Constructor_InitializesFromModel()
public void Constructor_InitializesFromModel()
{
// Arrange
var model = new ConnectionStringsSection
@@ -54,11 +54,67 @@ public class ConnectionStringsFormViewModelTests
sut.Connections[0].Server.ShouldBe("server1");
sut.Connections[1].Name.ShouldBe("Connection2");
sut.Connections[1].Provider.ShouldBe(ConnectionProvider.Oracle);
sut.Connections[1].Host.ShouldBe("oracle-host");
}
[Fact]
public void Constructor_ThrowsOnNullModel()
sut.Connections[1].Host.ShouldBe("oracle-host");
}
[Fact]
public void Constructor_LoadsAndParsesSqlServerConnectionStringFromSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "LotFinder" }
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
_secureStoreManager.GetSecret("LotFinder")
.Returns("Server=localhost,1434;Database=ScopingTool;User Id=scopingapp;Password=pass;TrustServerCertificate=true");
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.SqlServer);
sut.Connections[0].Server.ShouldBe("localhost,1434");
sut.Connections[0].Database.ShouldBe("ScopingTool");
sut.Connections[0].UserId.ShouldBe("scopingapp");
sut.Connections[0].Password.ShouldBe("pass");
sut.Connections[0].TrustServerCertificate.ShouldBeTrue();
}
[Fact]
public void Constructor_LoadsAndParsesOracleConnectionStringFromSecureStore()
{
// Arrange
var model = new ConnectionStringsSection
{
Entries = new List<ConnectionStringEntry>
{
new ConnectionStringEntry { Name = "CMS" }
}
};
_secureStoreManager.IsStoreOpen.Returns(true);
_secureStoreManager.GetSecret("CMS")
.Returns("HOST=ha-iman;Service Name=imanprd;Fetch Array Size=1280000;Port=1522;User ID=app_teamcenter;Password=pass;");
// Act
var sut = new ConnectionStringsFormViewModel(model, _secureStoreManager, () => { }, _dialogService, _connectionTestService);
// Assert
sut.Connections.Count.ShouldBe(1);
sut.Connections[0].Provider.ShouldBe(ConnectionProvider.Oracle);
sut.Connections[0].Host.ShouldBe("ha-iman");
sut.Connections[0].ServiceName.ShouldBe("imanprd");
sut.Connections[0].Port.ShouldBe(1522);
sut.Connections[0].UserId.ShouldBe("app_teamcenter");
sut.Connections[0].Password.ShouldBe("pass");
}
[Fact]
public void Constructor_ThrowsOnNullModel()
{
// Act & Assert
Should.Throw<ArgumentNullException>(() =>