db663cc82d
Adds a new ConnectionStrings section to ConfigManager allowing users to manage database connection strings with provider selection, connection testing, and visual feedback for connection state.
422 lines
13 KiB
Markdown
422 lines
13 KiB
Markdown
# ConnectionStrings Editor - Implementation Plan
|
|
|
|
## Overview
|
|
|
|
Step-by-step implementation guide for adding ConnectionStrings editor to ConfigManager.
|
|
|
|
**Estimated tasks:** 12 tasks in 4 batches
|
|
|
|
---
|
|
|
|
## Batch 1: Data Models
|
|
|
|
### Task 1: Create ConnectionProvider enum
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Models/ConnectionProvider.cs`
|
|
|
|
```csharp
|
|
namespace JdeScoping.ConfigManager.Models;
|
|
|
|
/// <summary>
|
|
/// Database provider types supported by the ConnectionStrings editor.
|
|
/// </summary>
|
|
public enum ConnectionProvider
|
|
{
|
|
Generic,
|
|
SqlServer,
|
|
Oracle
|
|
}
|
|
```
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
### Task 2: Create ConnectionStringEntry model
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Models/ConnectionStringEntry.cs`
|
|
|
|
```csharp
|
|
namespace JdeScoping.ConfigManager.Models;
|
|
|
|
/// <summary>
|
|
/// Represents a single connection string entry with provider-specific fields.
|
|
/// </summary>
|
|
public class ConnectionStringEntry
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public ConnectionProvider Provider { get; set; } = ConnectionProvider.Generic;
|
|
|
|
// SqlServer fields
|
|
public string? Server { get; set; }
|
|
public string? Database { get; set; }
|
|
public string? UserId { get; set; }
|
|
public string? Password { get; set; }
|
|
public string Encrypt { get; set; } = "True";
|
|
public bool TrustServerCertificate { get; set; }
|
|
public int ConnectionTimeout { get; set; } = 30;
|
|
public string? ApplicationName { get; set; }
|
|
|
|
// Oracle fields
|
|
public string? Host { get; set; }
|
|
public int Port { get; set; } = 1521;
|
|
public string? ServiceName { get; set; }
|
|
|
|
// Generic fields
|
|
public string? RawConnectionString { get; set; }
|
|
|
|
/// <summary>
|
|
/// Generates the connection string based on the Provider type.
|
|
/// </summary>
|
|
public string GenerateConnectionString()
|
|
{
|
|
return Provider switch
|
|
{
|
|
ConnectionProvider.SqlServer => GenerateSqlServerConnectionString(),
|
|
ConnectionProvider.Oracle => GenerateOracleConnectionString(),
|
|
ConnectionProvider.Generic => RawConnectionString ?? string.Empty,
|
|
_ => string.Empty
|
|
};
|
|
}
|
|
|
|
private string GenerateSqlServerConnectionString()
|
|
{
|
|
var parts = new List<string>();
|
|
|
|
if (!string.IsNullOrWhiteSpace(Server))
|
|
parts.Add($"Server={Server}");
|
|
if (!string.IsNullOrWhiteSpace(Database))
|
|
parts.Add($"Database={Database}");
|
|
if (!string.IsNullOrWhiteSpace(UserId))
|
|
parts.Add($"User Id={UserId}");
|
|
if (!string.IsNullOrWhiteSpace(Password))
|
|
parts.Add($"Password={Password}");
|
|
if (!string.IsNullOrWhiteSpace(Encrypt))
|
|
parts.Add($"Encrypt={Encrypt}");
|
|
if (TrustServerCertificate)
|
|
parts.Add("TrustServerCertificate=True");
|
|
if (ConnectionTimeout != 30)
|
|
parts.Add($"Connection Timeout={ConnectionTimeout}");
|
|
if (!string.IsNullOrWhiteSpace(ApplicationName))
|
|
parts.Add($"Application Name={ApplicationName}");
|
|
|
|
return string.Join(";", parts);
|
|
}
|
|
|
|
private string GenerateOracleConnectionString()
|
|
{
|
|
var parts = new List<string>();
|
|
|
|
var host = Host ?? "localhost";
|
|
var port = Port > 0 ? Port : 1521;
|
|
var service = ServiceName ?? "";
|
|
|
|
parts.Add($"Data Source=//{host}:{port}/{service}");
|
|
|
|
if (!string.IsNullOrWhiteSpace(UserId))
|
|
parts.Add($"User Id={UserId}");
|
|
if (!string.IsNullOrWhiteSpace(Password))
|
|
parts.Add($"Password={Password}");
|
|
if (ConnectionTimeout > 0 && ConnectionTimeout != 30)
|
|
parts.Add($"Connection Timeout={ConnectionTimeout}");
|
|
|
|
return string.Join(";", parts);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
### Task 3: Create ConnectionStringsSection model
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Models/ConnectionStringsSection.cs`
|
|
|
|
```csharp
|
|
namespace JdeScoping.ConfigManager.Models;
|
|
|
|
/// <summary>
|
|
/// Configuration section for connection strings.
|
|
/// </summary>
|
|
public class ConnectionStringsSection
|
|
{
|
|
public List<ConnectionStringEntry> Entries { get; set; } = new();
|
|
}
|
|
```
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
### Task 4: Add ConnectionStrings to ConfigModel
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Models/ConfigModel.cs`
|
|
|
|
Add property to existing ConfigModel class:
|
|
|
|
```csharp
|
|
public ConnectionStringsSection ConnectionStrings { get; set; } = new();
|
|
```
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
## Batch 2: ViewModels
|
|
|
|
### Task 5: Create ConnectionStringEntryViewModel
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/Forms/ConnectionStringEntryViewModel.cs`
|
|
|
|
Create ViewModel that wraps ConnectionStringEntry with:
|
|
- All properties exposed with change notification
|
|
- `GeneratedConnectionString` computed property
|
|
- `IsPasswordVisible` toggle property
|
|
- `TogglePasswordVisibilityCommand`
|
|
- `ProviderDisplay` and `ServerDisplay` for table columns
|
|
|
|
Key implementation details:
|
|
- Constructor takes `ConnectionStringEntry model` and `Action onChanged`
|
|
- All setters call `OnPropertyChanged()` and `_onChanged()`
|
|
- When Provider changes, also notify `GeneratedConnectionString`
|
|
- ServerDisplay returns Server (SqlServer), Host (Oracle), or "-" (Generic)
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
### Task 6: Create ConnectionStringsFormViewModel
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/Forms/ConnectionStringsFormViewModel.cs`
|
|
|
|
Create main ViewModel with:
|
|
- `Connections : ObservableCollection<ConnectionStringEntryViewModel>`
|
|
- `SelectedConnection : ConnectionStringEntryViewModel?`
|
|
- `HasSelection : bool` (computed from SelectedConnection != null)
|
|
- `ConnectionCount : int` (computed from Connections.Count)
|
|
- `AvailableProviders : IReadOnlyList<ConnectionProvider>`
|
|
- `EncryptOptions : IReadOnlyList<string>` = ["True", "False", "Strict"]
|
|
|
|
Commands:
|
|
- `AddConnectionCommand` - creates new entry with default name "NewConnection", selects it
|
|
- `DeleteConnectionCommand` - requires confirmation via IDialogService, removes selected
|
|
- `ValidateConnectionCommand` - validates syntax, shows result via IDialogService
|
|
- `TestConnectionCommand` - tests actual connection, shows modal result
|
|
|
|
Constructor takes:
|
|
- `ConnectionStringsSection model`
|
|
- `Action onChanged`
|
|
- `IDialogService dialogService`
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
### Task 7: Create IConnectionTestService interface and implementation
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Services/IConnectionTestService.cs`
|
|
|
|
```csharp
|
|
namespace JdeScoping.ConfigManager.Services;
|
|
|
|
public interface IConnectionTestService
|
|
{
|
|
Task<ConnectionTestResult> TestConnectionAsync(string connectionString, ConnectionProvider provider);
|
|
}
|
|
|
|
public class ConnectionTestResult
|
|
{
|
|
public bool Success { get; init; }
|
|
public string Message { get; init; } = string.Empty;
|
|
public TimeSpan? Duration { get; init; }
|
|
}
|
|
```
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Services/ConnectionTestService.cs`
|
|
|
|
Implementation that:
|
|
- Uses `Microsoft.Data.SqlClient` for SqlServer
|
|
- Uses `Oracle.ManagedDataAccess.Client` for Oracle (or stub if not available)
|
|
- Returns success/failure with timing and error message
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
## Batch 3: Views (AXAML)
|
|
|
|
### Task 8: Create ConnectionStringsFormView.axaml
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Views/Forms/ConnectionStringsFormView.axaml`
|
|
|
|
Create view with:
|
|
1. Header section ("Connection Strings" with separator)
|
|
2. Connections list section:
|
|
- DataGrid with Name, Provider, Server columns
|
|
- Height="200", selection mode single
|
|
- Toolbar with Add, Delete buttons
|
|
3. Placeholder section (when no selection):
|
|
- "Select a connection string to edit" text
|
|
4. Edit form section (when selection exists):
|
|
- Name + Provider fields (always visible)
|
|
- ContentControl with DataTemplates for provider-specific fields
|
|
5. Action buttons section:
|
|
- Validate, Test Connection buttons
|
|
|
|
Use existing form styling:
|
|
- Background="#0D0F12", BorderBrush="#2D3540"
|
|
- Input Background="#232A35"
|
|
- FontFamily="JetBrains Mono" for values
|
|
- MaxWidth="800" (wider than other forms for table)
|
|
|
|
**Verification:** Build succeeds and view renders.
|
|
|
|
---
|
|
|
|
### Task 9: Create provider-specific DataTemplates
|
|
|
|
Within ConnectionStringsFormView.axaml, create DataTemplates in UserControl.Resources:
|
|
|
|
**SqlServerTemplate:**
|
|
- Server, Database fields (row)
|
|
- UserId, Password fields (row, password with reveal button)
|
|
- Encrypt dropdown, TrustServerCertificate checkbox (row)
|
|
- ConnectionTimeout, ApplicationName fields (row)
|
|
- Connection string preview box
|
|
|
|
**OracleTemplate:**
|
|
- Host, Port fields (row)
|
|
- ServiceName field
|
|
- UserId, Password fields (row, password with reveal button)
|
|
- ConnectionTimeout field
|
|
- Connection string preview box
|
|
|
|
**GenericTemplate:**
|
|
- Info banner explaining generic usage
|
|
- Connection String multiline TextBox
|
|
|
|
Use ContentControl with binding to switch templates based on Provider.
|
|
|
|
**Verification:** Build succeeds, templates render correctly.
|
|
|
|
---
|
|
|
|
### Task 10: Create ConnectionStringsFormView.axaml.cs code-behind
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/Views/Forms/ConnectionStringsFormView.axaml.cs`
|
|
|
|
Standard Avalonia code-behind:
|
|
|
|
```csharp
|
|
using Avalonia.Controls;
|
|
|
|
namespace JdeScoping.ConfigManager.Views.Forms;
|
|
|
|
public partial class ConnectionStringsFormView : UserControl
|
|
{
|
|
public ConnectionStringsFormView()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification:** Build succeeds.
|
|
|
|
---
|
|
|
|
## Batch 4: Integration & Tests
|
|
|
|
### Task 11: Integrate into MainWindowViewModel
|
|
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/ViewModels/MainWindowViewModel.cs`
|
|
|
|
Updates needed:
|
|
|
|
1. In `BuildSettingsNodes()` method, add ConnectionStrings node:
|
|
```csharp
|
|
new TreeNodeViewModel("ConnectionStrings", "🔗", TreeNodeType.SettingsSection)
|
|
{
|
|
SectionKey = "ConnectionStrings"
|
|
}
|
|
```
|
|
|
|
2. In `LoadFormViewModelForNode()` method, add case for ConnectionStrings:
|
|
```csharp
|
|
"ConnectionStrings" => new ConnectionStringsFormViewModel(
|
|
_appSettingsConfig!.ConnectionStrings,
|
|
MarkCurrentNodeModified,
|
|
_dialogService,
|
|
_connectionTestService),
|
|
```
|
|
|
|
3. Register `IConnectionTestService` in DI (App.axaml.cs or Program.cs)
|
|
|
|
**Verification:**
|
|
- Build succeeds
|
|
- ConnectionStrings appears in Settings tree
|
|
- Clicking node shows form
|
|
|
|
---
|
|
|
|
### Task 12: Add unit tests
|
|
|
|
**File:** `NEW/tests/JdeScoping.ConfigManager.Tests/Models/ConnectionStringEntryTests.cs`
|
|
|
|
Test cases:
|
|
- `GenerateConnectionString_SqlServer_ProducesCorrectFormat`
|
|
- `GenerateConnectionString_SqlServer_OmitsDefaultTimeout`
|
|
- `GenerateConnectionString_Oracle_ProducesEZConnectFormat`
|
|
- `GenerateConnectionString_Generic_ReturnsRawString`
|
|
- `DefaultValues_AreCorrect`
|
|
|
|
**File:** `NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/ConnectionStringEntryViewModelTests.cs`
|
|
|
|
Test cases:
|
|
- `Constructor_InitializesFromModel`
|
|
- `PropertyChange_UpdatesModel`
|
|
- `PropertyChange_InvokesOnChanged`
|
|
- `TogglePasswordVisibility_TogglesIsPasswordVisible`
|
|
- `ProviderDisplay_ReturnsCorrectString`
|
|
- `ServerDisplay_ReturnsServerForSqlServer`
|
|
- `ServerDisplay_ReturnsHostForOracle`
|
|
- `ServerDisplay_ReturnsDashForGeneric`
|
|
|
|
**File:** `NEW/tests/JdeScoping.ConfigManager.Tests/ViewModels/Forms/ConnectionStringsFormViewModelTests.cs`
|
|
|
|
Test cases:
|
|
- `Constructor_InitializesFromModel`
|
|
- `AddConnection_CreatesNewEntryAndSelectsIt`
|
|
- `DeleteConnection_RemovesSelectedEntry`
|
|
- `SelectedConnection_UpdatesHasSelection`
|
|
- `HasSelection_IsFalseWhenNothingSelected`
|
|
- `ConnectionCount_ReflectsCollectionSize`
|
|
|
|
**Verification:** All tests pass.
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Batch | Tasks | Description |
|
|
|-------|-------|-------------|
|
|
| 1 | 1-4 | Data models (ConnectionProvider, ConnectionStringEntry, ConnectionStringsSection, ConfigModel update) |
|
|
| 2 | 5-7 | ViewModels (ConnectionStringEntryViewModel, ConnectionStringsFormViewModel, IConnectionTestService) |
|
|
| 3 | 8-10 | Views (ConnectionStringsFormView, DataTemplates, code-behind) |
|
|
| 4 | 11-12 | Integration (MainWindowViewModel, DI registration) and unit tests |
|
|
|
|
## Post-Implementation Verification
|
|
|
|
After all tasks complete:
|
|
|
|
1. `dotnet build NEW/JdeScoping.slnx` - should succeed
|
|
2. `dotnet test NEW/JdeScoping.slnx` - all tests should pass
|
|
3. Run ConfigManager app:
|
|
- Open a config folder
|
|
- Navigate to Settings → ConnectionStrings
|
|
- Add a new SqlServer connection
|
|
- Fill in fields, verify preview updates
|
|
- Test connection works
|
|
- Save config, verify appsettings.json updated
|