refactor(configmanager): split into Core, CLI, and UI projects
Extract shared models, services, and application logic into JdeScoping.ConfigManager.Core library. Add JdeScoping.ConfigManager.Cli console app with validate, test-connection, and secret commands using System.CommandLine. UI project now references Core for platform-agnostic functionality while retaining Avalonia-specific dialog and clipboard services.
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
<Project Path="src/JdeScoping.Infrastructure/JdeScoping.Infrastructure.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/utils/">
|
||||
<Project Path="src/Utils/JdeScoping.ConfigManager.Core/JdeScoping.ConfigManager.Core.csproj" />
|
||||
<Project Path="src/Utils/JdeScoping.ConfigManager.Cli/JdeScoping.ConfigManager.Cli.csproj" />
|
||||
<Project Path="src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
using System.CommandLine;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Cli.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Secret management command implementations.
|
||||
/// </summary>
|
||||
public static class SecretCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the secret list command.
|
||||
/// </summary>
|
||||
public static Command CreateListCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("list", "List all secret keys");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode = await ListSecretsAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the secret get command.
|
||||
/// </summary>
|
||||
public static Command CreateGetCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("get", "Get a secret value");
|
||||
|
||||
var keyArgument = new Argument<string>("key", "The secret key to retrieve");
|
||||
command.AddArgument(keyArgument);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string key) =>
|
||||
{
|
||||
var exitCode = await GetSecretAsync(serviceProvider, configPath, verbose, quiet, key);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, keyArgument);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the secret set command.
|
||||
/// </summary>
|
||||
public static Command CreateSetCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("set", "Set or update a secret");
|
||||
|
||||
var keyArgument = new Argument<string>("key", "The secret key");
|
||||
var valueArgument = new Argument<string>("value", "The secret value");
|
||||
command.AddArgument(keyArgument);
|
||||
command.AddArgument(valueArgument);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string key, string value) =>
|
||||
{
|
||||
var exitCode = await SetSecretAsync(serviceProvider, configPath, verbose, quiet, key, value);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, keyArgument, valueArgument);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the secret remove command.
|
||||
/// </summary>
|
||||
public static Command CreateRemoveCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("remove", "Remove a secret");
|
||||
|
||||
var keyArgument = new Argument<string>("key", "The secret key to remove");
|
||||
command.AddArgument(keyArgument);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string key) =>
|
||||
{
|
||||
var exitCode = await RemoveSecretAsync(serviceProvider, configPath, verbose, quiet, key);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, keyArgument);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the secret init command.
|
||||
/// </summary>
|
||||
public static Command CreateInitCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("init", "Initialize a new SecureStore");
|
||||
|
||||
var storePathOption = new Option<string?>(
|
||||
aliases: ["--store", "-s"],
|
||||
description: "Path for the store file");
|
||||
|
||||
var keyPathOption = new Option<string?>(
|
||||
aliases: ["--key", "-k"],
|
||||
description: "Path for the key file");
|
||||
|
||||
command.AddOption(storePathOption);
|
||||
command.AddOption(keyPathOption);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string? storePath, string? keyPath) =>
|
||||
{
|
||||
var exitCode = await InitStoreAsync(serviceProvider, configPath, verbose, quiet, storePath, keyPath);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, storePathOption, keyPathOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static async Task<int> ListSecretsAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet)
|
||||
{
|
||||
var (manager, folderPath) = await OpenStoreAsync(serviceProvider, configPath);
|
||||
if (manager == null)
|
||||
return 1;
|
||||
|
||||
try
|
||||
{
|
||||
var keys = manager.GetKeys();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"=== SecureStore Keys ({keys.Count}) ===");
|
||||
}
|
||||
|
||||
foreach (var key in keys.OrderBy(k => k))
|
||||
{
|
||||
Console.WriteLine(key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
manager.CloseStore();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> GetSecretAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet,
|
||||
string key)
|
||||
{
|
||||
var (manager, folderPath) = await OpenStoreAsync(serviceProvider, configPath);
|
||||
if (manager == null)
|
||||
return 1;
|
||||
|
||||
try
|
||||
{
|
||||
var value = manager.GetSecret(key);
|
||||
Console.WriteLine(value);
|
||||
return 0;
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: Secret '{key}' not found");
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
manager.CloseStore();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> SetSecretAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet,
|
||||
string key,
|
||||
string value)
|
||||
{
|
||||
var (manager, folderPath) = await OpenStoreAsync(serviceProvider, configPath);
|
||||
if (manager == null)
|
||||
return 1;
|
||||
|
||||
try
|
||||
{
|
||||
manager.SetSecret(key, value);
|
||||
manager.Save();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"Secret '{key}' set successfully");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
manager.CloseStore();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> RemoveSecretAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet,
|
||||
string key)
|
||||
{
|
||||
var (manager, folderPath) = await OpenStoreAsync(serviceProvider, configPath);
|
||||
if (manager == null)
|
||||
return 1;
|
||||
|
||||
try
|
||||
{
|
||||
manager.RemoveSecret(key);
|
||||
manager.Save();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"Secret '{key}' removed successfully");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: Secret '{key}' not found");
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
manager.CloseStore();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> InitStoreAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet,
|
||||
string? storePath,
|
||||
string? keyPath)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var effectiveStorePath = storePath ?? Path.Combine(folderPath, "data", "secrets.json");
|
||||
var effectiveKeyPath = keyPath ?? Path.Combine(folderPath, "data", "secrets.key");
|
||||
|
||||
if (File.Exists(effectiveStorePath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: Store already exists at {effectiveStorePath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var manager = serviceProvider.GetRequiredService<ISecureStoreManager>();
|
||||
|
||||
try
|
||||
{
|
||||
manager.CreateStore(effectiveStorePath, effectiveKeyPath);
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine("SecureStore initialized successfully");
|
||||
Console.WriteLine($"Store: {effectiveStorePath}");
|
||||
Console.WriteLine($"Key: {effectiveKeyPath}");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error creating store: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
manager.CloseStore();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<(ISecureStoreManager?, string?)> OpenStoreAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
// Load config to get SecureStore paths
|
||||
var configFileService = serviceProvider.GetRequiredService<IConfigFileService>();
|
||||
var appSettingsPath = Path.Combine(folderPath, "appsettings.json");
|
||||
|
||||
if (!File.Exists(appSettingsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: appsettings.json not found at {appSettingsPath}");
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var config = await configFileService.LoadAppSettingsAsync(appSettingsPath);
|
||||
|
||||
var storePath = Path.IsPathRooted(config.SecureStore.StorePath)
|
||||
? config.SecureStore.StorePath
|
||||
: Path.Combine(folderPath, config.SecureStore.StorePath);
|
||||
|
||||
var keyPath = Path.IsPathRooted(config.SecureStore.KeyFilePath)
|
||||
? config.SecureStore.KeyFilePath
|
||||
: Path.Combine(folderPath, config.SecureStore.KeyFilePath);
|
||||
|
||||
if (!File.Exists(storePath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: SecureStore not found at {storePath}");
|
||||
Console.Error.WriteLine("Use 'secret init' to create a new store.");
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
if (!File.Exists(keyPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: Key file not found at {keyPath}");
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var manager = serviceProvider.GetRequiredService<ISecureStoreManager>();
|
||||
manager.OpenStore(storePath, keyPath);
|
||||
|
||||
return (manager, folderPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error opening store: {ex.Message}");
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string?> GetConfigFolderAsync(IServiceProvider serviceProvider, string? configPath)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(configPath))
|
||||
{
|
||||
if (Directory.Exists(configPath))
|
||||
return configPath;
|
||||
return null;
|
||||
}
|
||||
|
||||
var autoDiscoveryService = serviceProvider.GetRequiredService<IAutoDiscoveryService>();
|
||||
return await autoDiscoveryService.FindConfigFolderAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
using System.CommandLine;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Cli.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Test connection command implementations.
|
||||
/// </summary>
|
||||
public static class TestConnectionCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the test-connection sql command.
|
||||
/// </summary>
|
||||
public static Command CreateSqlCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("sql", "Test SQL Server connection");
|
||||
|
||||
var connectionNameOption = new Option<string?>(
|
||||
aliases: ["--name", "-n"],
|
||||
description: "Name of the connection string to test");
|
||||
|
||||
command.AddOption(connectionNameOption);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string? connectionName) =>
|
||||
{
|
||||
var exitCode = await TestConnectionAsync(serviceProvider, configPath, verbose, quiet, connectionName, ConnectionProvider.SqlServer);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, connectionNameOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the test-connection oracle command.
|
||||
/// </summary>
|
||||
public static Command CreateOracleCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("oracle", "Test Oracle connection");
|
||||
|
||||
var connectionNameOption = new Option<string?>(
|
||||
aliases: ["--name", "-n"],
|
||||
description: "Name of the connection string to test");
|
||||
|
||||
command.AddOption(connectionNameOption);
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet, string? connectionName) =>
|
||||
{
|
||||
var exitCode = await TestConnectionAsync(serviceProvider, configPath, verbose, quiet, connectionName, ConnectionProvider.Oracle);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption, connectionNameOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the test-connection all command.
|
||||
/// </summary>
|
||||
public static Command CreateAllCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("all", "Test all configured connections");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode = await TestAllConnectionsAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static async Task<int> TestConnectionAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet,
|
||||
string? connectionName,
|
||||
ConnectionProvider provider)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var configFileService = serviceProvider.GetRequiredService<IConfigFileService>();
|
||||
var connectionTestService = serviceProvider.GetRequiredService<IConnectionTestService>();
|
||||
|
||||
var appSettingsPath = Path.Combine(folderPath, "appsettings.json");
|
||||
if (!File.Exists(appSettingsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: appsettings.json not found at {appSettingsPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var config = await configFileService.LoadAppSettingsAsync(appSettingsPath);
|
||||
|
||||
var entry = connectionName != null
|
||||
? config.ConnectionStrings.Entries.FirstOrDefault(e =>
|
||||
e.Name.Equals(connectionName, StringComparison.OrdinalIgnoreCase))
|
||||
: config.ConnectionStrings.Entries.FirstOrDefault(e => e.Provider == provider);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
var message = connectionName != null
|
||||
? $"Connection '{connectionName}' not found"
|
||||
: $"No {provider} connection found";
|
||||
Console.Error.WriteLine($"Error: {message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"Testing connection: {entry.Name}");
|
||||
}
|
||||
|
||||
var connectionString = entry.GenerateConnectionString();
|
||||
var result = await connectionTestService.TestConnectionAsync(connectionString, entry.Provider);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"Status: Success");
|
||||
if (result.Duration.HasValue)
|
||||
Console.WriteLine($"Duration: {result.Duration.Value.TotalMilliseconds:F0}ms");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Console.Error.WriteLine($"Status: Failed");
|
||||
Console.Error.WriteLine($"Message: {result.Message}");
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> TestAllConnectionsAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var configFileService = serviceProvider.GetRequiredService<IConfigFileService>();
|
||||
var connectionTestService = serviceProvider.GetRequiredService<IConnectionTestService>();
|
||||
|
||||
var appSettingsPath = Path.Combine(folderPath, "appsettings.json");
|
||||
if (!File.Exists(appSettingsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: appsettings.json not found at {appSettingsPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var config = await configFileService.LoadAppSettingsAsync(appSettingsPath);
|
||||
var hasFailures = false;
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine("=== Testing All Connections ===");
|
||||
Console.WriteLine($"Found {config.ConnectionStrings.Entries.Count} connection(s)");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
foreach (var entry in config.ConnectionStrings.Entries)
|
||||
{
|
||||
if (entry.Provider == ConnectionProvider.Generic)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine($"{entry.Name}: Skipped (generic provider)");
|
||||
continue;
|
||||
}
|
||||
|
||||
var connectionString = entry.GenerateConnectionString();
|
||||
var result = await connectionTestService.TestConnectionAsync(connectionString, entry.Provider);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
if (!quiet)
|
||||
{
|
||||
var duration = result.Duration.HasValue
|
||||
? $" ({result.Duration.Value.TotalMilliseconds:F0}ms)"
|
||||
: "";
|
||||
Console.WriteLine($"{entry.Name}: OK{duration}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hasFailures = true;
|
||||
Console.Error.WriteLine($"{entry.Name}: FAILED - {result.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return hasFailures ? 1 : 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string?> GetConfigFolderAsync(IServiceProvider serviceProvider, string? configPath)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(configPath))
|
||||
{
|
||||
if (Directory.Exists(configPath))
|
||||
return configPath;
|
||||
return null;
|
||||
}
|
||||
|
||||
var autoDiscoveryService = serviceProvider.GetRequiredService<IAutoDiscoveryService>();
|
||||
return await autoDiscoveryService.FindConfigFolderAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
using System.CommandLine;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Cli.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Validation command implementations.
|
||||
/// </summary>
|
||||
public static class ValidateCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the validate appsettings command.
|
||||
/// </summary>
|
||||
public static Command CreateAppSettingsCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("appsettings", "Validate appsettings.json");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode = await ValidateAppSettingsAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the validate pipelines command.
|
||||
/// </summary>
|
||||
public static Command CreatePipelinesCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("pipelines", "Validate pipeline configuration files");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode = await ValidatePipelinesAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the validate all command.
|
||||
/// </summary>
|
||||
public static Command CreateAllCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("all", "Validate all configuration files");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode1 = await ValidateAppSettingsAsync(serviceProvider, configPath, verbose, quiet);
|
||||
var exitCode2 = await ValidatePipelinesAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = Math.Max(exitCode1, exitCode2);
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the validate runtime command.
|
||||
/// </summary>
|
||||
public static Command CreateRuntimeCommand(
|
||||
IServiceProvider serviceProvider,
|
||||
Option<string?> configPathOption,
|
||||
Option<bool> verboseOption,
|
||||
Option<bool> quietOption)
|
||||
{
|
||||
var command = new Command("runtime", "Run Infrastructure validators");
|
||||
|
||||
command.SetHandler(async (string? configPath, bool verbose, bool quiet) =>
|
||||
{
|
||||
var exitCode = await ValidateRuntimeAsync(serviceProvider, configPath, verbose, quiet);
|
||||
Environment.ExitCode = exitCode;
|
||||
}, configPathOption, verboseOption, quietOption);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static async Task<int> ValidateAppSettingsAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var configFileService = serviceProvider.GetRequiredService<IConfigFileService>();
|
||||
var validationService = serviceProvider.GetRequiredService<IValidationService>();
|
||||
|
||||
var appSettingsPath = Path.Combine(folderPath, "appsettings.json");
|
||||
if (!File.Exists(appSettingsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Error: appsettings.json not found at {appSettingsPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var config = await configFileService.LoadAppSettingsAsync(appSettingsPath);
|
||||
var result = validationService.ValidateAppSettings(config);
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine("=== AppSettings Validation ===");
|
||||
Console.WriteLine($"File: {appSettingsPath}");
|
||||
}
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine("Status: Valid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {error}");
|
||||
}
|
||||
|
||||
foreach (var warning in result.Warnings)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine($"Warning: {warning}");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (ConfigLoadException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error loading configuration: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> ValidatePipelinesAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var configFileService = serviceProvider.GetRequiredService<IConfigFileService>();
|
||||
var validationService = serviceProvider.GetRequiredService<IValidationService>();
|
||||
|
||||
var pipelinesDir = Path.Combine(folderPath, "Pipelines");
|
||||
|
||||
try
|
||||
{
|
||||
var pipelines = await configFileService.LoadAllPipelinesAsync(pipelinesDir);
|
||||
var result = validationService.ValidatePipelines(pipelines);
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine("=== Pipelines Validation ===");
|
||||
Console.WriteLine($"Directory: {pipelinesDir}");
|
||||
Console.WriteLine($"Pipelines found: {pipelines.Count}");
|
||||
}
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine("Status: Valid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {error}");
|
||||
}
|
||||
|
||||
foreach (var warning in result.Warnings)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine($"Warning: {warning}");
|
||||
}
|
||||
|
||||
return result.Errors.Count > 0 ? 1 : 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error validating pipelines: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<int> ValidateRuntimeAsync(
|
||||
IServiceProvider serviceProvider,
|
||||
string? configPath,
|
||||
bool verbose,
|
||||
bool quiet)
|
||||
{
|
||||
var folderPath = await GetConfigFolderAsync(serviceProvider, configPath);
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.Error.WriteLine("Error: Could not find configuration folder. Use --config-path to specify.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var runtimeValidationService = serviceProvider.GetRequiredService<IRuntimeConfigValidationService>();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
Console.WriteLine("=== Runtime Configuration Validation ===");
|
||||
Console.WriteLine($"Folder: {folderPath}");
|
||||
}
|
||||
|
||||
var results = runtimeValidationService.ValidateRuntimeConfig(folderPath);
|
||||
var hasErrors = false;
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.Errors.Count > 0)
|
||||
{
|
||||
hasErrors = true;
|
||||
Console.Error.WriteLine($"\n[{result.ValidatorName}]");
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
Console.Error.WriteLine($" Error: {error}");
|
||||
}
|
||||
}
|
||||
else if (!quiet)
|
||||
{
|
||||
Console.WriteLine($"[{result.ValidatorName}]: OK");
|
||||
}
|
||||
|
||||
foreach (var warning in result.Warnings)
|
||||
{
|
||||
if (!quiet)
|
||||
Console.WriteLine($" Warning: {warning}");
|
||||
}
|
||||
}
|
||||
|
||||
return hasErrors ? 1 : 0;
|
||||
}
|
||||
|
||||
private static async Task<string?> GetConfigFolderAsync(IServiceProvider serviceProvider, string? configPath)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(configPath))
|
||||
{
|
||||
if (Directory.Exists(configPath))
|
||||
return configPath;
|
||||
return null;
|
||||
}
|
||||
|
||||
var autoDiscoveryService = serviceProvider.GetRequiredService<IAutoDiscoveryService>();
|
||||
return await autoDiscoveryService.FindConfigFolderAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AssemblyName>jdescoping-config</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.*" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\JdeScoping.ConfigManager.Core\JdeScoping.ConfigManager.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.CommandLine;
|
||||
using JdeScoping.ConfigManager.Cli.Commands;
|
||||
using JdeScoping.ConfigManager.Core.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Cli;
|
||||
|
||||
/// <summary>
|
||||
/// Main entry point for the jdescoping-config CLI tool.
|
||||
/// </summary>
|
||||
public static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Main entry point.
|
||||
/// </summary>
|
||||
/// <param name="args">Command-line arguments.</param>
|
||||
/// <returns>Exit code: 0 for success, non-zero for errors.</returns>
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
ConfigureServices(services);
|
||||
using var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var rootCommand = new RootCommand("JDE Scoping Tool configuration management CLI")
|
||||
{
|
||||
Name = "jdescoping-config"
|
||||
};
|
||||
|
||||
// Global options
|
||||
var configPathOption = new Option<string?>(
|
||||
aliases: ["--config-path", "-c"],
|
||||
description: "Path to configuration folder");
|
||||
|
||||
var verboseOption = new Option<bool>(
|
||||
aliases: ["--verbose", "-v"],
|
||||
description: "Enable verbose output");
|
||||
|
||||
var quietOption = new Option<bool>(
|
||||
aliases: ["--quiet", "-q"],
|
||||
description: "Suppress non-error output");
|
||||
|
||||
rootCommand.AddGlobalOption(configPathOption);
|
||||
rootCommand.AddGlobalOption(verboseOption);
|
||||
rootCommand.AddGlobalOption(quietOption);
|
||||
|
||||
// Validate command group
|
||||
var validateCommand = new Command("validate", "Validate configuration files");
|
||||
validateCommand.AddCommand(ValidateCommand.CreateAppSettingsCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
validateCommand.AddCommand(ValidateCommand.CreatePipelinesCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
validateCommand.AddCommand(ValidateCommand.CreateAllCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
validateCommand.AddCommand(ValidateCommand.CreateRuntimeCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
rootCommand.AddCommand(validateCommand);
|
||||
|
||||
// Test-connection command group
|
||||
var testConnectionCommand = new Command("test-connection", "Test database connections");
|
||||
testConnectionCommand.AddCommand(TestConnectionCommand.CreateSqlCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
testConnectionCommand.AddCommand(TestConnectionCommand.CreateOracleCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
testConnectionCommand.AddCommand(TestConnectionCommand.CreateAllCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
rootCommand.AddCommand(testConnectionCommand);
|
||||
|
||||
// Secret command group
|
||||
var secretCommand = new Command("secret", "Manage SecureStore secrets");
|
||||
secretCommand.AddCommand(SecretCommands.CreateListCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
secretCommand.AddCommand(SecretCommands.CreateGetCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
secretCommand.AddCommand(SecretCommands.CreateSetCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
secretCommand.AddCommand(SecretCommands.CreateRemoveCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
secretCommand.AddCommand(SecretCommands.CreateInitCommand(serviceProvider, configPathOption, verboseOption, quietOption));
|
||||
rootCommand.AddCommand(secretCommand);
|
||||
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
}
|
||||
|
||||
private static void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddLogging(builder => builder
|
||||
.AddConsole()
|
||||
.SetMinimumLevel(LogLevel.Warning));
|
||||
|
||||
services.AddConfigManagerCore();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Application;
|
||||
namespace JdeScoping.ConfigManager.Core.Application;
|
||||
|
||||
/// <summary>
|
||||
/// Secret CRUD use-case operations with logging.
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Application;
|
||||
namespace JdeScoping.ConfigManager.Core.Application;
|
||||
|
||||
/// <summary>
|
||||
/// Store lifecycle use-case operations with logging.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Constants;
|
||||
namespace JdeScoping.ConfigManager.Core.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Centralized constants for secure store file extensions and patterns used in file dialogs.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Constants;
|
||||
namespace JdeScoping.ConfigManager.Core.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Centralized string constants for secure store dialog titles, messages, and validation errors.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using JdeScoping.ConfigManager.Core.Application;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Core.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering ConfigManager.Core services with dependency injection.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds ConfigManager.Core services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to add services to.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddConfigManagerCore(this IServiceCollection services)
|
||||
{
|
||||
// File system abstraction
|
||||
services.AddSingleton<IFileSystem, FileSystem>();
|
||||
|
||||
// Configuration management services
|
||||
services.AddSingleton<IAutoDiscoveryService, AutoDiscoveryService>();
|
||||
services.AddSingleton<IBackupService, BackupService>();
|
||||
services.AddSingleton<IDiffService, DiffService>();
|
||||
services.AddSingleton<IValidationService, ValidationService>();
|
||||
services.AddScoped<IConfigFileService, ConfigFileService>();
|
||||
|
||||
// SecureStore services
|
||||
services.AddSingleton<ISecureStoreManager, SecureStoreManager>();
|
||||
services.AddSingleton<StoreUseCases>();
|
||||
services.AddSingleton<SecretUseCases>();
|
||||
|
||||
// Runtime validation
|
||||
services.AddSingleton<IRuntimeConfigValidationService, RuntimeConfigValidationService>();
|
||||
|
||||
// Connection testing
|
||||
services.AddSingleton<IConnectionTestService, ConnectionTestService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiffPlex" Version="1.7.*" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.*" />
|
||||
<PackageReference Include="SecureStore" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\JdeScoping.Core\JdeScoping.Core.csproj" />
|
||||
<ProjectReference Include="..\..\JdeScoping.DataSync\JdeScoping.DataSync.csproj" />
|
||||
<ProjectReference Include="..\..\JdeScoping.Infrastructure\JdeScoping.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Models;
|
||||
namespace JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Root model for appsettings.json configuration.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Models;
|
||||
namespace JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Database provider types supported by the ConnectionStrings editor.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Models;
|
||||
namespace JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single connection string entry with provider-specific fields.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Models;
|
||||
namespace JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration section for connection strings.
|
||||
+7
-2
@@ -1,7 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Models;
|
||||
namespace JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Custom JSON converter that handles the standard .NET ConnectionStrings dictionary format
|
||||
@@ -215,7 +215,12 @@ public class ConnectionStringsSectionConverter : JsonConverter<ConnectionStrings
|
||||
return entry;
|
||||
}
|
||||
|
||||
internal static void ApplyConnectionString(ConnectionStringEntry entry, string connectionString)
|
||||
/// <summary>
|
||||
/// Applies a connection string to an existing entry, parsing it according to the detected provider.
|
||||
/// </summary>
|
||||
/// <param name="entry">The entry to update.</param>
|
||||
/// <param name="connectionString">The connection string to parse and apply.</param>
|
||||
public static void ApplyConnectionString(ConnectionStringEntry entry, string connectionString)
|
||||
{
|
||||
var parsed = ParseConnectionString(entry.Name, connectionString);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for auto-discovering configuration file locations.
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents backup file information.
|
||||
/// </summary>
|
||||
public class BackupInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the full path to the backup file.
|
||||
/// </summary>
|
||||
public required string Path { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp when the backup was created.
|
||||
/// </summary>
|
||||
public required DateTime Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file size in bytes.
|
||||
/// </summary>
|
||||
public required long Size { get; init; }
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing configuration file backups.
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for loading and saving configuration files.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when configuration file loading fails.
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Result of testing a database connection.
|
||||
/// </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; }
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for testing database connections.
|
||||
+1
-15
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line in a diff output.
|
||||
@@ -58,17 +58,3 @@ public class DiffResult
|
||||
/// </summary>
|
||||
public int Deletions { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for generating diffs between text content.
|
||||
/// </summary>
|
||||
public interface IDiffService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a diff between original and modified text content.
|
||||
/// </summary>
|
||||
/// <param name="original">The original text content.</param>
|
||||
/// <param name="modified">The modified text content.</param>
|
||||
/// <returns>A diff result containing added, removed, and unchanged lines with counts.</returns>
|
||||
DiffResult GenerateDiff(string original, string modified);
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@ using DiffPlex;
|
||||
using DiffPlex.DiffBuilder;
|
||||
using DiffPlex.DiffBuilder.Model;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for generating diffs between text content.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Real file system implementation.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for auto-discovering configuration file locations.
|
||||
+1
-22
@@ -1,25 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents backup file information.
|
||||
/// </summary>
|
||||
public class BackupInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the full path to the backup file.
|
||||
/// </summary>
|
||||
public required string Path { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp when the backup was created.
|
||||
/// </summary>
|
||||
public required DateTime Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file size in bytes.
|
||||
/// </summary>
|
||||
public required long Size { get; init; }
|
||||
}
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing configuration file backups.
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for loading and saving configuration files.
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for testing database connections.
|
||||
/// </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);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for generating diffs between text content.
|
||||
/// </summary>
|
||||
public interface IDiffService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a diff between original and modified text content.
|
||||
/// </summary>
|
||||
/// <param name="original">The original text content.</param>
|
||||
/// <param name="modified">The modified text content.</param>
|
||||
/// <returns>A diff result containing added, removed, and unchanged lines with counts.</returns>
|
||||
DiffResult GenerateDiff(string original, string modified);
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Abstraction for file system operations to enable testing.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for validating runtime configuration using Infrastructure validators.
|
||||
/// </summary>
|
||||
public interface IRuntimeConfigValidationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the configuration in the specified folder using Infrastructure validators.
|
||||
/// </summary>
|
||||
/// <param name="configFolderPath">Path to the configuration folder.</param>
|
||||
/// <returns>List of validation results from each validator.</returns>
|
||||
List<RuntimeValidationResult> ValidateRuntimeConfig(string configFolderPath);
|
||||
}
|
||||
+2
-35
@@ -1,40 +1,7 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a validation operation.
|
||||
/// </summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the validation succeeded (no errors).
|
||||
/// </summary>
|
||||
public bool IsValid => Errors.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of validation errors encountered.
|
||||
/// </summary>
|
||||
public List<string> Errors { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of validation warnings encountered.
|
||||
/// </summary>
|
||||
public List<string> Warnings { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Adds an error message to the validation result.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message to add.</param>
|
||||
public void AddError(string message) => Errors.Add(message);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning message to the validation result.
|
||||
/// </summary>
|
||||
/// <param name="message">The warning message to add.</param>
|
||||
public void AddWarning(string message) => Warnings.Add(message);
|
||||
}
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for validating configuration files.
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.Core.Interfaces;
|
||||
using JdeScoping.Core.Validation;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -6,7 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service that validates configuration using Infrastructure validators.
|
||||
+1
-14
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Result of runtime configuration validation.
|
||||
@@ -25,16 +25,3 @@ public class RuntimeValidationResult
|
||||
/// </summary>
|
||||
public List<string> Warnings { get; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for validating runtime configuration using Infrastructure validators.
|
||||
/// </summary>
|
||||
public interface IRuntimeConfigValidationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the configuration in the specified folder using Infrastructure validators.
|
||||
/// </summary>
|
||||
/// <param name="configFolderPath">Path to the configuration folder.</param>
|
||||
/// <returns>List of validation results from each validator.</returns>
|
||||
List<RuntimeValidationResult> ValidateRuntimeConfig(string configFolderPath);
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace JdeScoping.ConfigManager.Services.SecureStore;
|
||||
namespace JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for managing SecureStore encrypted secret stores.
|
||||
+2
-3
@@ -1,13 +1,12 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NeoSmart.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services.SecureStore;
|
||||
namespace JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
/// <summary>
|
||||
/// Manages SecureStore encrypted secret stores for the Avalonia application.
|
||||
/// Manages SecureStore encrypted secret stores.
|
||||
/// </summary>
|
||||
public class SecureStoreManager : ISecureStoreManager, IDisposable
|
||||
{
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.Core.Interfaces;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Adapts ISecureStoreManager to ISecureStoreService for use by Infrastructure validators.
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a validation operation.
|
||||
/// </summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the validation succeeded (no errors).
|
||||
/// </summary>
|
||||
public bool IsValid => Errors.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of validation errors encountered.
|
||||
/// </summary>
|
||||
public List<string> Errors { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of validation warnings encountered.
|
||||
/// </summary>
|
||||
public List<string> Warnings { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Adds an error message to the validation result.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message to add.</param>
|
||||
public void AddError(string message) => Errors.Add(message);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning message to the validation result.
|
||||
/// </summary>
|
||||
/// <param name="message">The warning message to add.</param>
|
||||
public void AddWarning(string message) => Warnings.Add(message);
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
namespace JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for validating configuration files.
|
||||
@@ -3,9 +3,8 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using JdeScoping.ConfigManager.Application;
|
||||
using JdeScoping.ConfigManager.Core.DependencyInjection;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -50,33 +49,15 @@ public partial class App : Avalonia.Application
|
||||
.AddConsole()
|
||||
.SetMinimumLevel(LogLevel.Debug));
|
||||
|
||||
// Services - File system abstraction
|
||||
services.AddSingleton<IFileSystem, FileSystem>();
|
||||
// Add all ConfigManager.Core services
|
||||
services.AddConfigManagerCore();
|
||||
|
||||
// Services - Configuration management
|
||||
services.AddSingleton<IAutoDiscoveryService, AutoDiscoveryService>();
|
||||
services.AddSingleton<IBackupService, BackupService>();
|
||||
services.AddSingleton<IDiffService, DiffService>();
|
||||
services.AddSingleton<IValidationService, ValidationService>();
|
||||
services.AddScoped<IConfigFileService, ConfigFileService>();
|
||||
|
||||
// Platform Services
|
||||
// Platform Services (Avalonia-specific)
|
||||
services.AddSingleton<IDialogService>(sp =>
|
||||
new AvaloniaDialogService(GetMainWindow));
|
||||
services.AddSingleton<IClipboardService>(sp =>
|
||||
new AvaloniaClipboardService(GetClipboard));
|
||||
|
||||
// SecureStore Services
|
||||
services.AddSingleton<ISecureStoreManager, SecureStoreManager>();
|
||||
services.AddSingleton<StoreUseCases>();
|
||||
services.AddSingleton<SecretUseCases>();
|
||||
|
||||
// Runtime Validation Services
|
||||
services.AddSingleton<IRuntimeConfigValidationService, RuntimeConfigValidationService>();
|
||||
|
||||
// Connection Testing
|
||||
services.AddSingleton<IConnectionTestService, ConnectionTestService>();
|
||||
|
||||
// ViewModels
|
||||
services.AddTransient<MainWindowViewModel>();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Converters;
|
||||
|
||||
|
||||
@@ -16,22 +16,15 @@
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.*" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.*" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.*" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PackageReference Include="DiffPlex" Version="1.7.*" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.*" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.1.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.*" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.*" />
|
||||
<PackageReference Include="SecureStore" Version="1.2.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\JdeScoping.Core\JdeScoping.Core.csproj" />
|
||||
<ProjectReference Include="..\..\JdeScoping.DataSync\JdeScoping.DataSync.csproj" />
|
||||
<ProjectReference Include="..\..\JdeScoping.Infrastructure\JdeScoping.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\JdeScoping.ConfigManager.Core\JdeScoping.ConfigManager.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Views.Dialogs;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Result of testing a database connection.
|
||||
/// </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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for testing database connections.
|
||||
/// </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);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
+7
-6
@@ -1,8 +1,9 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
@@ -52,10 +53,10 @@ public class ConnectionStringsFormViewModel : ViewModelBase
|
||||
: null;
|
||||
|
||||
// Update entry's RawConnectionString with SecureStore value if available
|
||||
if (!string.IsNullOrEmpty(secureStoreValue))
|
||||
{
|
||||
ConnectionStringsSectionConverter.ApplyConnectionString(entry, secureStoreValue);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(secureStoreValue))
|
||||
{
|
||||
ConnectionStringsSectionConverter.ApplyConnectionString(entry, secureStoreValue);
|
||||
}
|
||||
|
||||
Connections.Add(new ConnectionStringEntryViewModel(entry, OnEntryChanged));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Media;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Utils\JdeScoping.ConfigManager.Core\JdeScoping.ConfigManager.Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\Utils\JdeScoping.ConfigManager\JdeScoping.ConfigManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Models;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using System.IO;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services.SecureStore;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.ViewModels.Dialogs;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Dialogs;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using JdeScoping.ConfigManager.Constants;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Core.Constants;
|
||||
using JdeScoping.ConfigManager.Core.Models;
|
||||
using JdeScoping.ConfigManager.Core.Services;
|
||||
using JdeScoping.ConfigManager.Core.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.ConfigManager.Services.SecureStore;
|
||||
using JdeScoping.ConfigManager.ViewModels;
|
||||
using JdeScoping.ConfigManager.ViewModels.Forms;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
Reference in New Issue
Block a user