feat: implement ETL pipeline redesign and ConfigManager improvements
- Add pipeline registry with JSON-based configuration and hot-reload support - Implement manual sync request feature with API, client UI, and database - Improve ConfigManager: connection string dropdown in pipeline editor, step delete/reorder functionality, and fix JSON parsing for ConnectionStrings
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the pipeline destination.
|
||||
/// </summary>
|
||||
public class DestinationElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Target table name in the cache database.
|
||||
/// </summary>
|
||||
public string Table { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Columns used to match existing records for upsert.
|
||||
/// </summary>
|
||||
public List<string> MatchColumns { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Columns to exclude from UPDATE operations.
|
||||
/// </summary>
|
||||
public List<string> ExcludeFromUpdate { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ETL pipeline definition loaded from a JSON file.
|
||||
/// </summary>
|
||||
public class EtlPipelineConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique name of the pipeline (must match filename, case-insensitive).
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the pipeline is enabled for execution.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, pipeline can only be triggered via ManualSyncRequest.
|
||||
/// No interval validation is required for manual-only pipelines.
|
||||
/// </summary>
|
||||
public bool IsManualOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval for mass sync in minutes. Null if mass sync not supported.
|
||||
/// </summary>
|
||||
public int? MassSyncIntervalMinutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval for daily sync in minutes. Null if daily sync not supported.
|
||||
/// </summary>
|
||||
public int? DailySyncIntervalMinutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval for hourly sync in minutes. Null if hourly sync not supported.
|
||||
/// </summary>
|
||||
public int? HourlySyncIntervalMinutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scripts to run before the main sync. Optional.
|
||||
/// </summary>
|
||||
public List<ScriptElement> PreScripts { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The data source configuration. Required.
|
||||
/// </summary>
|
||||
public SourceElement Source { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Data transformations to apply. Optional.
|
||||
/// </summary>
|
||||
public List<TransformElement> Transforms { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The destination configuration. Required.
|
||||
/// </summary>
|
||||
public DestinationElement Destination { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Scripts to run after the main sync. Optional.
|
||||
/// </summary>
|
||||
public List<ScriptElement> PostScripts { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the pipeline supports mass sync.
|
||||
/// </summary>
|
||||
public bool SupportsMassSync => MassSyncIntervalMinutes.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the pipeline supports daily sync.
|
||||
/// </summary>
|
||||
public bool SupportsDailySync => DailySyncIntervalMinutes.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the pipeline supports hourly sync.
|
||||
/// </summary>
|
||||
public bool SupportsHourlySync => HourlySyncIntervalMinutes.HasValue;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a query parameter.
|
||||
/// </summary>
|
||||
public class ParameterElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameter name as used in query (e.g., ":dateUpdated").
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Format conversion (jdeJulian, jdeTime, etc.).
|
||||
/// </summary>
|
||||
public string? Format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Source of the value (offset = from last sync time).
|
||||
/// </summary>
|
||||
public string Source { get; set; } = "offset";
|
||||
|
||||
/// <summary>
|
||||
/// Static value if source is not offset.
|
||||
/// </summary>
|
||||
public string? Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a pre/post script.
|
||||
/// </summary>
|
||||
public class ScriptElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection identifier for script execution.
|
||||
/// </summary>
|
||||
public string Connection { get; set; } = "lotfinder";
|
||||
|
||||
/// <summary>
|
||||
/// SQL script to execute.
|
||||
/// </summary>
|
||||
public string Script { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the pipeline data source.
|
||||
/// </summary>
|
||||
public class SourceElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection identifier (jde, cms, giw, lotfinder).
|
||||
/// </summary>
|
||||
public string Connection { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Query for incremental syncs (daily/hourly).
|
||||
/// </summary>
|
||||
public string Query { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Query for mass sync. Falls back to Query if not specified.
|
||||
/// </summary>
|
||||
public string? MassQuery { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Query parameters with format and source configuration.
|
||||
/// </summary>
|
||||
public Dictionary<string, ParameterElement> Parameters { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace JdeScoping.DataSync.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a data transformation.
|
||||
/// </summary>
|
||||
public class TransformElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of transformation (ColumnDrop, ColumnRename, JdeDate, Regex, etc.).
|
||||
/// </summary>
|
||||
public string TransformType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Transform-specific configuration as raw JSON.
|
||||
/// Using JsonElement avoids Dictionary<string, object> deserialization issues
|
||||
/// where values would become JsonElement anyway without custom converters.
|
||||
/// </summary>
|
||||
public JsonElement? Config { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user