refactor(configmanager): rename UI project and split test projects
Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project structure, and split the monolithic test project into Core.Tests, Cli.Tests, and Ui.Tests to align with the source project organization.
This commit is contained in:
+786
@@ -0,0 +1,786 @@
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Ui.ViewModels.PipelineSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for transformer step view models.
|
||||
/// </summary>
|
||||
public abstract class TransformerStepViewModelBase : PipelineStepViewModelBase
|
||||
{
|
||||
protected static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
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>
|
||||
/// Gets the transformer type name.
|
||||
/// </summary>
|
||||
public abstract string TransformerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts this view model back to a TransformElement.
|
||||
/// </summary>
|
||||
public abstract TransformElement ToModel();
|
||||
|
||||
/// <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);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
return doc.RootElement.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View model for ColumnDrop transformer.
|
||||
/// </summary>
|
||||
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;
|
||||
if (element.Config.HasValue)
|
||||
{
|
||||
if (element.Config.Value.TryGetProperty("columns", out var columnsProp) &&
|
||||
columnsProp.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var columns = columnsProp.EnumerateArray().Select(c => c.GetString() ?? "").Where(c => !string.IsNullOrEmpty(c));
|
||||
_columnsText = string.Join("\n", columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Gets or sets the columns to drop as newline-separated text.
|
||||
/// </summary>
|
||||
public string ColumnsText
|
||||
{
|
||||
get => _columnsText;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _columnsText, value ?? string.Empty))
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the columns as a list.
|
||||
/// </summary>
|
||||
public List<string> GetColumns()
|
||||
{
|
||||
return _columnsText.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
||||
}
|
||||
|
||||
private int GetColumnCount()
|
||||
{
|
||||
return _columnsText.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Length;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TransformElement ToModel() => new()
|
||||
{
|
||||
TransformType = TransformerType,
|
||||
Config = CreateConfigElement(new { columns = GetColumns() })
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View model for ColumnRename transformer.
|
||||
/// </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 = [];
|
||||
if (element.Config.HasValue)
|
||||
{
|
||||
if (element.Config.Value.TryGetProperty("mappings", out var mappingsProp) &&
|
||||
mappingsProp.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
foreach (var prop in mappingsProp.EnumerateObject())
|
||||
{
|
||||
var newName = prop.Value.GetString() ?? "";
|
||||
Mappings.Add(new ColumnMappingViewModel(prop.Name, newName, () =>
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
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>
|
||||
/// Gets the collection of column mappings (old name -> new name).
|
||||
/// </summary>
|
||||
public ObservableCollection<ColumnMappingViewModel> Mappings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command to add a new mapping.
|
||||
/// </summary>
|
||||
public ICommand AddMappingCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new mapping.
|
||||
/// </summary>
|
||||
public void AddMapping()
|
||||
{
|
||||
Mappings.Add(new ColumnMappingViewModel("", "", () =>
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}));
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The column mapping to remove.</param>
|
||||
public void RemoveMapping(ColumnMappingViewModel mapping)
|
||||
{
|
||||
if (Mappings.Remove(mapping))
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TransformElement ToModel() => new()
|
||||
{
|
||||
TransformType = TransformerType,
|
||||
Config = CreateConfigElement(new { mappings = Mappings.ToDictionary(m => m.OldName, m => m.NewName) })
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View model for a column rename mapping.
|
||||
/// </summary>
|
||||
public class ColumnMappingViewModel : ViewModelBase
|
||||
{
|
||||
private string _oldName;
|
||||
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;
|
||||
_newName = newName;
|
||||
_onChanged = onChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original column name.
|
||||
/// </summary>
|
||||
public string OldName
|
||||
{
|
||||
get => _oldName;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _oldName, value ?? string.Empty))
|
||||
_onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the new column name.
|
||||
/// </summary>
|
||||
public string NewName
|
||||
{
|
||||
get => _newName;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _newName, value ?? string.Empty))
|
||||
_onChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View model for JdeDate transformer (converts JDE Julian date/time to DateTime).
|
||||
/// </summary>
|
||||
public class JdeDateTransformerViewModel : TransformerStepViewModelBase
|
||||
{
|
||||
private string? _dateColumn;
|
||||
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;
|
||||
_timeColumn = null;
|
||||
_outputColumn = null;
|
||||
|
||||
if (element.Config.HasValue)
|
||||
{
|
||||
if (element.Config.Value.TryGetProperty("dateColumn", out var dateProp))
|
||||
_dateColumn = dateProp.GetString();
|
||||
if (element.Config.Value.TryGetProperty("timeColumn", out var timeProp))
|
||||
_timeColumn = timeProp.GetString();
|
||||
if (element.Config.Value.TryGetProperty("outputColumn", out var outputProp))
|
||||
_outputColumn = outputProp.GetString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
_timeColumn = null;
|
||||
_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>
|
||||
/// Gets or sets the date column name.
|
||||
/// </summary>
|
||||
public string? DateColumn
|
||||
{
|
||||
get => _dateColumn;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _dateColumn, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time column name.
|
||||
/// </summary>
|
||||
public string? TimeColumn
|
||||
{
|
||||
get => _timeColumn;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _timeColumn, value))
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output column name.
|
||||
/// </summary>
|
||||
public string? OutputColumn
|
||||
{
|
||||
get => _outputColumn;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _outputColumn, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TransformElement ToModel() => new()
|
||||
{
|
||||
TransformType = TransformerType,
|
||||
Config = CreateConfigElement(new { dateColumn = _dateColumn, timeColumn = _timeColumn, outputColumn = _outputColumn })
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies behavior when a regex pattern does not match the input value.
|
||||
/// </summary>
|
||||
public enum NonMatchBehavior
|
||||
{
|
||||
/// <summary>Keep the original value unchanged.</summary>
|
||||
KeepOriginal,
|
||||
/// <summary>Return null/DBNull.</summary>
|
||||
ReturnNull,
|
||||
/// <summary>Return an empty string.</summary>
|
||||
ReturnEmpty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View model for Regex transformer.
|
||||
/// </summary>
|
||||
public class RegexTransformerViewModel : TransformerStepViewModelBase
|
||||
{
|
||||
private string _columnName = string.Empty;
|
||||
private string _pattern = string.Empty;
|
||||
private string? _replacement = string.Empty;
|
||||
private bool _isFindReplaceMode = true;
|
||||
private bool _ignoreCase;
|
||||
private NonMatchBehavior _nonMatchBehavior = NonMatchBehavior.KeepOriginal;
|
||||
|
||||
// Test feature fields
|
||||
private string _testInput = string.Empty;
|
||||
private string _testResultValue = string.Empty;
|
||||
private string _testResultLabel = string.Empty;
|
||||
private string _testResultIcon = string.Empty;
|
||||
private string _testResultBackground = string.Empty;
|
||||
private bool _hasTestResult;
|
||||
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;
|
||||
_pattern = string.Empty;
|
||||
_replacement = null;
|
||||
_isFindReplaceMode = true;
|
||||
_ignoreCase = false;
|
||||
_nonMatchBehavior = NonMatchBehavior.KeepOriginal;
|
||||
|
||||
if (element.Config.HasValue)
|
||||
{
|
||||
if (element.Config.Value.TryGetProperty("columnName", out var colProp))
|
||||
_columnName = colProp.GetString() ?? string.Empty;
|
||||
if (element.Config.Value.TryGetProperty("pattern", out var patternProp))
|
||||
_pattern = patternProp.GetString() ?? string.Empty;
|
||||
if (element.Config.Value.TryGetProperty("replacement", out var replaceProp))
|
||||
{
|
||||
_replacement = replaceProp.ValueKind == JsonValueKind.Null ? null : replaceProp.GetString();
|
||||
_isFindReplaceMode = _replacement != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No replacement property means Match & Extract mode
|
||||
_replacement = null;
|
||||
_isFindReplaceMode = false;
|
||||
}
|
||||
if (element.Config.Value.TryGetProperty("ignoreCase", out var ignoreProp))
|
||||
_ignoreCase = ignoreProp.GetBoolean();
|
||||
if (element.Config.Value.TryGetProperty("nonMatchBehavior", out var behaviorProp))
|
||||
{
|
||||
var behaviorStr = behaviorProp.GetString();
|
||||
if (Enum.TryParse<NonMatchBehavior>(behaviorStr, true, out var behavior))
|
||||
_nonMatchBehavior = behavior;
|
||||
}
|
||||
}
|
||||
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...";
|
||||
|
||||
/// <summary>Gets or sets the column name to transform.</summary>
|
||||
public string ColumnName
|
||||
{
|
||||
get => _columnName;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _columnName, value ?? string.Empty))
|
||||
{
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the regex pattern.</summary>
|
||||
public string Pattern
|
||||
{
|
||||
get => _pattern;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _pattern, value ?? string.Empty))
|
||||
{
|
||||
ClearTestResult();
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the replacement string (Find & Replace mode).</summary>
|
||||
public string? Replacement
|
||||
{
|
||||
get => _replacement;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _replacement, value))
|
||||
{
|
||||
ClearTestResult();
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets whether Find & Replace mode is active.</summary>
|
||||
public bool IsFindReplaceMode
|
||||
{
|
||||
get => _isFindReplaceMode;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isFindReplaceMode, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsMatchExtractMode));
|
||||
OnPropertyChanged(nameof(PatternHelpText));
|
||||
OnPropertyChanged(nameof(Summary));
|
||||
ClearTestResult();
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets whether Match & Extract mode is active.</summary>
|
||||
public bool IsMatchExtractMode
|
||||
{
|
||||
get => !_isFindReplaceMode;
|
||||
set => IsFindReplaceMode = !value;
|
||||
}
|
||||
|
||||
/// <summary>Gets the help text for the pattern field based on current mode.</summary>
|
||||
public string PatternHelpText => _isFindReplaceMode
|
||||
? "Pattern to search for in the column value"
|
||||
: "Pattern with capture group - first group (parentheses) will be extracted";
|
||||
|
||||
/// <summary>Gets or sets whether matching is case-insensitive.</summary>
|
||||
public bool IgnoreCase
|
||||
{
|
||||
get => _ignoreCase;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _ignoreCase, value))
|
||||
{
|
||||
ClearTestResult();
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the behavior when pattern doesn't match.</summary>
|
||||
public NonMatchBehavior NonMatchBehavior
|
||||
{
|
||||
get => _nonMatchBehavior;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _nonMatchBehavior, value))
|
||||
{
|
||||
ClearTestResult();
|
||||
NotifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the available non-match behavior options for binding.</summary>
|
||||
public static IReadOnlyList<NonMatchBehavior> NonMatchBehaviorOptions { get; } =
|
||||
[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()
|
||||
{
|
||||
ClearTestResult();
|
||||
|
||||
if (string.IsNullOrEmpty(_pattern))
|
||||
{
|
||||
HasTestError = true;
|
||||
TestErrorMessage = "Pattern is required";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var options = _ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||
var regex = new Regex(_pattern, options);
|
||||
|
||||
string result;
|
||||
bool matched;
|
||||
|
||||
if (_isFindReplaceMode)
|
||||
{
|
||||
result = regex.Replace(_testInput, _replacement ?? string.Empty);
|
||||
matched = regex.IsMatch(_testInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = regex.Match(_testInput);
|
||||
if (match.Success && match.Groups.Count > 1)
|
||||
{
|
||||
result = match.Groups[1].Value;
|
||||
matched = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = false;
|
||||
result = _nonMatchBehavior switch
|
||||
{
|
||||
NonMatchBehavior.ReturnNull => "(null)",
|
||||
NonMatchBehavior.ReturnEmpty => "(empty)",
|
||||
_ => _testInput
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
HasTestResult = true;
|
||||
TestResultValue = result;
|
||||
TestResultLabel = matched ? "Output" : "No Match";
|
||||
TestResultIcon = matched ? "✓" : "—";
|
||||
TestResultBackground = matched ? "#22C55E" : "#F59E0B";
|
||||
}
|
||||
catch (RegexParseException ex)
|
||||
{
|
||||
HasTestError = true;
|
||||
TestErrorMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTestResult()
|
||||
{
|
||||
HasTestResult = false;
|
||||
HasTestError = false;
|
||||
TestResultValue = string.Empty;
|
||||
TestResultLabel = string.Empty;
|
||||
TestErrorMessage = string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TransformElement ToModel() => new()
|
||||
{
|
||||
TransformType = TransformerType,
|
||||
Config = CreateConfigElement(new
|
||||
{
|
||||
columnName = _columnName,
|
||||
pattern = _pattern,
|
||||
replacement = _isFindReplaceMode ? _replacement : null,
|
||||
ignoreCase = _ignoreCase,
|
||||
nonMatchBehavior = _nonMatchBehavior.ToString()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory methods for creating transformer view models.
|
||||
/// </summary>
|
||||
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
|
||||
{
|
||||
"columndrop" => new ColumnDropTransformerViewModel(element, onChanged),
|
||||
"columnrename" => new ColumnRenameTransformerViewModel(element, onChanged),
|
||||
"jdedate" => new JdeDateTransformerViewModel(element, onChanged),
|
||||
"regex" => new RegexTransformerViewModel(element, onChanged),
|
||||
_ => null // Unknown transformer type
|
||||
};
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
"columndrop" => new ColumnDropTransformerViewModel(onChanged),
|
||||
"columnrename" => new ColumnRenameTransformerViewModel(onChanged),
|
||||
"jdedate" => new JdeDateTransformerViewModel(onChanged),
|
||||
"regex" => new RegexTransformerViewModel(onChanged),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of available transformer type names.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<string> AvailableTypes => ["ColumnDrop", "ColumnRename", "JdeDate", "Regex"];
|
||||
}
|
||||
Reference in New Issue
Block a user