Seperate database entity from ui object (Reactive)
This commit is contained in:
parent
739d6bd65a
commit
747358297b
10 changed files with 176 additions and 154 deletions
|
|
@ -8,8 +8,6 @@ namespace pgLabII.PgUtils.ConnectionStrings;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ConnectionDescriptor
|
public sealed class ConnectionDescriptor
|
||||||
{
|
{
|
||||||
public string? Name { get; init; }
|
|
||||||
|
|
||||||
// Primary hosts (support multi-host). If empty, implies localhost default.
|
// Primary hosts (support multi-host). If empty, implies localhost default.
|
||||||
public IReadOnlyList<HostEndpoint> Hosts { get; init; } = new List<HostEndpoint>();
|
public IReadOnlyList<HostEndpoint> Hosts { get; init; } = new List<HostEndpoint>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,19 @@ public class ServerConfigurationMappingTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ToDescriptor_Basic_MapsExpectedFields()
|
public void ToDescriptor_Basic_MapsExpectedFields()
|
||||||
{
|
{
|
||||||
var cfg = new ServerConfiguration
|
ServerConfigurationEntity cfg = new()
|
||||||
{
|
{
|
||||||
Name = "Prod",
|
Name = "Prod",
|
||||||
Host = "db.example.com",
|
Host = "db.example.com",
|
||||||
Port = 5433,
|
Port = 5433,
|
||||||
InitialDatabase = "appdb",
|
InitialDatabase = "appdb",
|
||||||
DefaultSslMode = SslMode.Require,
|
SslMode = SslMode.Require,
|
||||||
User = new ServerUser { Name = "alice", Password = "secret" }
|
User = new ServerUser { Name = "alice", Password = "secret" }
|
||||||
};
|
};
|
||||||
|
|
||||||
var extra = new Dictionary<string,string>{{"search_path","public"}};
|
var extra = new Dictionary<string,string>{{"search_path","public"}};
|
||||||
var d = ServerConfigurationMapping.ToDescriptor(cfg, applicationName: "pgLabII", timeoutSeconds: 15, extraProperties: extra);
|
var d = ServerConfigurationMapping.ToDescriptor(cfg, applicationName: "pgLabII", timeoutSeconds: 15, extraProperties: extra);
|
||||||
|
|
||||||
Assert.Equal("Prod", d.Name);
|
|
||||||
Assert.Single(d.Hosts);
|
Assert.Single(d.Hosts);
|
||||||
Assert.Equal("db.example.com", d.Hosts[0].Host);
|
Assert.Equal("db.example.com", d.Hosts[0].Host);
|
||||||
Assert.Equal((ushort)5433, d.Hosts[0].Port);
|
Assert.Equal((ushort)5433, d.Hosts[0].Port);
|
||||||
|
|
@ -42,7 +41,7 @@ public class ServerConfigurationMappingTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ToDescriptor_OmitsEmptyFields()
|
public void ToDescriptor_OmitsEmptyFields()
|
||||||
{
|
{
|
||||||
var cfg = new ServerConfiguration
|
ServerConfigurationEntity cfg = new ()
|
||||||
{
|
{
|
||||||
Name = "Empty",
|
Name = "Empty",
|
||||||
Host = "",
|
Host = "",
|
||||||
|
|
@ -63,7 +62,6 @@ public class ServerConfigurationMappingTests
|
||||||
{
|
{
|
||||||
var desc = new ConnectionDescriptor
|
var desc = new ConnectionDescriptor
|
||||||
{
|
{
|
||||||
Name = "Staging",
|
|
||||||
Hosts = new []
|
Hosts = new []
|
||||||
{
|
{
|
||||||
new HostEndpoint{ Host = "host1", Port = 5432 },
|
new HostEndpoint{ Host = "host1", Port = 5432 },
|
||||||
|
|
@ -75,13 +73,12 @@ public class ServerConfigurationMappingTests
|
||||||
SslMode = SslMode.VerifyFull
|
SslMode = SslMode.VerifyFull
|
||||||
};
|
};
|
||||||
|
|
||||||
var cfg = ServerConfigurationMapping.FromDescriptor(desc);
|
ServerConfigurationEntity cfg = ServerConfigurationMapping.FromDescriptor(desc);
|
||||||
|
|
||||||
Assert.Equal("Staging", cfg.Name);
|
|
||||||
Assert.Equal("host1", cfg.Host);
|
Assert.Equal("host1", cfg.Host);
|
||||||
Assert.Equal((ushort)5432, cfg.Port);
|
Assert.Equal((ushort)5432, cfg.Port);
|
||||||
Assert.Equal("stagedb", cfg.InitialDatabase);
|
Assert.Equal("stagedb", cfg.InitialDatabase);
|
||||||
Assert.Equal(SslMode.VerifyFull, cfg.DefaultSslMode);
|
Assert.Equal(SslMode.VerifyFull, cfg.SslMode);
|
||||||
Assert.Equal("bob", cfg.User.Name);
|
Assert.Equal("bob", cfg.User.Name);
|
||||||
Assert.Equal("pwd", cfg.User.Password);
|
Assert.Equal("pwd", cfg.User.Password);
|
||||||
}
|
}
|
||||||
|
|
@ -89,13 +86,13 @@ public class ServerConfigurationMappingTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FromDescriptor_UpdatesExisting_PreservesMissing()
|
public void FromDescriptor_UpdatesExisting_PreservesMissing()
|
||||||
{
|
{
|
||||||
var existing = new ServerConfiguration
|
ServerConfigurationEntity existing = new()
|
||||||
{
|
{
|
||||||
Name = "Existing",
|
Name = "Existing",
|
||||||
Host = "keep-host",
|
Host = "keep-host",
|
||||||
Port = 5432,
|
Port = 5432,
|
||||||
InitialDatabase = "keepdb",
|
InitialDatabase = "keepdb",
|
||||||
DefaultSslMode = SslMode.Prefer,
|
SslMode = SslMode.Prefer,
|
||||||
User = new ServerUser { Name = "keepuser", Password = "keeppwd" }
|
User = new ServerUser { Name = "keepuser", Password = "keeppwd" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -110,7 +107,7 @@ public class ServerConfigurationMappingTests
|
||||||
Assert.Equal("new-host", cfg.Host);
|
Assert.Equal("new-host", cfg.Host);
|
||||||
Assert.Equal((ushort)5432, cfg.Port); // unchanged
|
Assert.Equal((ushort)5432, cfg.Port); // unchanged
|
||||||
Assert.Equal("keepdb", cfg.InitialDatabase); // preserved
|
Assert.Equal("keepdb", cfg.InitialDatabase); // preserved
|
||||||
Assert.Equal(SslMode.Prefer, cfg.DefaultSslMode); // preserved
|
Assert.Equal(SslMode.Prefer, cfg.SslMode); // preserved
|
||||||
Assert.Equal("keepuser", cfg.User.Name); // preserved
|
Assert.Equal("keepuser", cfg.User.Name); // preserved
|
||||||
Assert.Equal("keeppwd", cfg.User.Password); // preserved
|
Assert.Equal("keeppwd", cfg.User.Password); // preserved
|
||||||
}
|
}
|
||||||
|
|
@ -118,24 +115,23 @@ public class ServerConfigurationMappingTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Roundtrip_Basic()
|
public void Roundtrip_Basic()
|
||||||
{
|
{
|
||||||
var cfg = new ServerConfiguration
|
ServerConfigurationEntity cfg = new()
|
||||||
{
|
{
|
||||||
Name = "Round",
|
Name = "Round",
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 5432,
|
Port = 5432,
|
||||||
InitialDatabase = "postgres",
|
InitialDatabase = "postgres",
|
||||||
DefaultSslMode = SslMode.Allow,
|
SslMode = SslMode.Allow,
|
||||||
User = new ServerUser { Name = "me", Password = "pw" }
|
User = new ServerUser { Name = "me", Password = "pw" }
|
||||||
};
|
};
|
||||||
|
|
||||||
var d = ServerConfigurationMapping.ToDescriptor(cfg);
|
var d = ServerConfigurationMapping.ToDescriptor(cfg);
|
||||||
var cfg2 = ServerConfigurationMapping.FromDescriptor(d);
|
var cfg2 = ServerConfigurationMapping.FromDescriptor(d);
|
||||||
|
|
||||||
Assert.Equal(cfg.Name, cfg2.Name);
|
|
||||||
Assert.Equal(cfg.Host, cfg2.Host);
|
Assert.Equal(cfg.Host, cfg2.Host);
|
||||||
Assert.Equal(cfg.Port, cfg2.Port);
|
Assert.Equal(cfg.Port, cfg2.Port);
|
||||||
Assert.Equal(cfg.InitialDatabase, cfg2.InitialDatabase);
|
Assert.Equal(cfg.InitialDatabase, cfg2.InitialDatabase);
|
||||||
Assert.Equal(cfg.DefaultSslMode, cfg2.DefaultSslMode);
|
Assert.Equal(cfg.SslMode, cfg2.SslMode);
|
||||||
Assert.Equal(cfg.User.Name, cfg2.User.Name);
|
Assert.Equal(cfg.User.Name, cfg2.User.Name);
|
||||||
Assert.Equal(cfg.User.Password, cfg2.User.Password);
|
Assert.Equal(cfg.User.Password, cfg2.User.Password);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ public class EditServerConfigurationWindowTests
|
||||||
public void Parse_and_Generate_roundtrip_via_UI_bindings()
|
public void Parse_and_Generate_roundtrip_via_UI_bindings()
|
||||||
{
|
{
|
||||||
// Arrange: initialize Avalonia headless app once for the test
|
// Arrange: initialize Avalonia headless app once for the test
|
||||||
var vm = new EditServerConfigurationViewModel(new ServerConfiguration());
|
var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
|
||||||
var window = new EditServerConfigurationWindow(vm);
|
var window = new EditServerConfigurationWindow(vm);
|
||||||
|
|
||||||
// Act: set an URL input, auto mode, then parse
|
// Act: set an URL input, auto mode, then parse
|
||||||
|
|
@ -46,7 +46,7 @@ public class EditServerConfigurationWindowTests
|
||||||
[AvaloniaFact]
|
[AvaloniaFact]
|
||||||
public void Forced_format_overrides_auto_detection()
|
public void Forced_format_overrides_auto_detection()
|
||||||
{
|
{
|
||||||
var vm = new EditServerConfigurationViewModel(new ServerConfiguration());
|
var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
|
||||||
|
|
||||||
// Use a string with quoted values that libpq would struggle with due to incorrect quoting
|
// Use a string with quoted values that libpq would struggle with due to incorrect quoting
|
||||||
vm.InputConnectionString = "Host=\"server with spaces\";Username=\"bob\";Password=\"secret\";Database=\"db1\"";
|
vm.InputConnectionString = "Host=\"server with spaces\";Username=\"bob\";Password=\"secret\";Database=\"db1\"";
|
||||||
|
|
@ -69,7 +69,7 @@ public class EditServerConfigurationWindowTests
|
||||||
[AvaloniaFact]
|
[AvaloniaFact]
|
||||||
public void Parse_Npgsql_with_inline_host_port_updates_all_fields()
|
public void Parse_Npgsql_with_inline_host_port_updates_all_fields()
|
||||||
{
|
{
|
||||||
var vm = new EditServerConfigurationViewModel(new ServerConfiguration());
|
var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
|
||||||
vm.InputConnectionString = "Host=host.docker.internal:5432;Database=kms_quartz;Username=postgres;Password=admin;Trust Server Certificate=true";
|
vm.InputConnectionString = "Host=host.docker.internal:5432;Database=kms_quartz;Username=postgres;Password=admin;Trust Server Certificate=true";
|
||||||
vm.ForcedFormat = EditServerConfigurationViewModel.ForcedFormatOption.Auto;
|
vm.ForcedFormat = EditServerConfigurationViewModel.ForcedFormatOption.Auto;
|
||||||
vm.ParseConnectionStringCommand.Execute().Subscribe();
|
vm.ParseConnectionStringCommand.Execute().Subscribe();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace pgLabII.Infra;
|
||||||
|
|
||||||
public class LocalDb : DbContext
|
public class LocalDb : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<ServerConfiguration> ServerConfigurations => Set<ServerConfiguration>();
|
public DbSet<ServerConfigurationEntity> ServerConfigurations => Set<ServerConfigurationEntity>();
|
||||||
public DbSet<Document> Documents => Set<Document>();
|
public DbSet<Document> Documents => Set<Document>();
|
||||||
public DbSet<EditHistoryEntry> EditHistory => Set<EditHistoryEntry>();
|
public DbSet<EditHistoryEntry> EditHistory => Set<EditHistoryEntry>();
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class LocalDb : DbContext
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
new ServerConfigurationEntityConfiguration().Configure(modelBuilder.Entity<ServerConfiguration>());
|
new ServerConfigurationEntityConfiguration().Configure(modelBuilder.Entity<ServerConfigurationEntity>());
|
||||||
new ServerUserEntityConfiguration().Configure(modelBuilder.Entity<ServerUser>());
|
new ServerUserEntityConfiguration().Configure(modelBuilder.Entity<ServerUser>());
|
||||||
new DocumentEntityConfiguration().Configure(modelBuilder.Entity<Document>());
|
new DocumentEntityConfiguration().Configure(modelBuilder.Entity<Document>());
|
||||||
new EditHistoryEntityConfiguration().Configure(modelBuilder.Entity<EditHistoryEntry>());
|
new EditHistoryEntityConfiguration().Configure(modelBuilder.Entity<EditHistoryEntry>());
|
||||||
|
|
@ -40,15 +40,16 @@ public class LocalDb : DbContext
|
||||||
{
|
{
|
||||||
base.ConfigureConventions(configurationBuilder);
|
base.ConfigureConventions(configurationBuilder);
|
||||||
|
|
||||||
|
// Keep Color converter for any other entities still using Avalonia Color
|
||||||
configurationBuilder
|
configurationBuilder
|
||||||
.Properties<Color>()
|
.Properties<Color>()
|
||||||
.HaveConversion<ColorConverter>();
|
.HaveConversion<ColorConverter>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ServerConfigurationEntityConfiguration : IEntityTypeConfiguration<ServerConfiguration>
|
public class ServerConfigurationEntityConfiguration : IEntityTypeConfiguration<ServerConfigurationEntity>
|
||||||
{
|
{
|
||||||
public void Configure(EntityTypeBuilder<ServerConfiguration> b)
|
public void Configure(EntityTypeBuilder<ServerConfigurationEntity> b)
|
||||||
{
|
{
|
||||||
b.HasKey(e => e.Id);
|
b.HasKey(e => e.Id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Reactive;
|
|
||||||
using Avalonia.Media;
|
|
||||||
using Npgsql;
|
|
||||||
using pgLabII.ViewModels;
|
|
||||||
using pgLabII.Views;
|
|
||||||
using ReactiveUI;
|
|
||||||
using ReactiveUI.SourceGenerators;
|
|
||||||
|
|
||||||
namespace pgLabII.Model;
|
|
||||||
|
|
||||||
public partial class ServerConfiguration : ReactiveObject
|
|
||||||
{
|
|
||||||
private Color _color;
|
|
||||||
private bool _colorEnabled = true;
|
|
||||||
|
|
||||||
[Reactive] private Guid _id = Guid.NewGuid();
|
|
||||||
/// <summary>
|
|
||||||
/// For the user to help him identify the item
|
|
||||||
/// </summary>
|
|
||||||
[Reactive] private string _name = "";
|
|
||||||
|
|
||||||
public Color Color
|
|
||||||
{
|
|
||||||
get => _color;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_color != value)
|
|
||||||
{
|
|
||||||
_color = value;
|
|
||||||
this.RaisePropertyChanged();
|
|
||||||
this.RaisePropertyChanged(propertyName: nameof(BackgroundBrush));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ColorEnabled
|
|
||||||
{
|
|
||||||
get => _colorEnabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_colorEnabled != value)
|
|
||||||
{
|
|
||||||
_colorEnabled = value;
|
|
||||||
this.RaisePropertyChanged();
|
|
||||||
this.RaisePropertyChanged(propertyName: nameof(BackgroundBrush));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Reactive] private string _host = "";
|
|
||||||
[Reactive] private ushort _port = 5432;
|
|
||||||
[Reactive] private string _initialDatabase = "";
|
|
||||||
|
|
||||||
[Reactive] private SslMode _sslMode = SslMode.Prefer;
|
|
||||||
|
|
||||||
// Explicit property wrapper to make compiled XAML binding findable
|
|
||||||
public SslMode DefaultSslMode
|
|
||||||
{
|
|
||||||
get => _sslMode;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _sslMode, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
|
|
||||||
|
|
||||||
public ServerUser User { get; set; } = new();
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> EditCommand { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> ExploreCommand { get; }
|
|
||||||
|
|
||||||
public ServerConfiguration()
|
|
||||||
{
|
|
||||||
EditCommand = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
EditServerConfigurationWindow window = new(
|
|
||||||
new ViewModels.EditServerConfigurationViewModel(this))
|
|
||||||
{ New = false };
|
|
||||||
window.Show();
|
|
||||||
});
|
|
||||||
ExploreCommand = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SingleDatabaseWindow window = new() { DataContext = new ViewListViewModel() };
|
|
||||||
window.Show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerConfiguration(ServerConfiguration src)
|
|
||||||
: this()
|
|
||||||
{
|
|
||||||
Color = src.Color;
|
|
||||||
ColorEnabled = src.ColorEnabled;
|
|
||||||
Id = src.Id;
|
|
||||||
Name = src.Name;
|
|
||||||
Port = src.Port;
|
|
||||||
InitialDatabase = src.InitialDatabase;
|
|
||||||
DefaultSslMode = src.DefaultSslMode;
|
|
||||||
User = src.User;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
20
pgLabII/Model/ServerConfigurationEntity.cs
Normal file
20
pgLabII/Model/ServerConfigurationEntity.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace pgLabII.Model;
|
||||||
|
|
||||||
|
// Pure persistence entity for EF Core: no UI dependencies, no ReactiveObject
|
||||||
|
public class ServerConfigurationEntity
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Host { get; set; } = string.Empty;
|
||||||
|
public ushort Port { get; set; } = 5432;
|
||||||
|
public string InitialDatabase { get; set; } = string.Empty;
|
||||||
|
public SslMode SslMode { get; set; } = SslMode.Prefer;
|
||||||
|
public bool ColorEnabled { get; set; } = true;
|
||||||
|
public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray
|
||||||
|
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public ServerUser User { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ public static class ServerConfigurationMapping
|
||||||
/// - ApplicationName and TimeoutSeconds don't exist on ServerConfiguration; we preserve any passed-in
|
/// - ApplicationName and TimeoutSeconds don't exist on ServerConfiguration; we preserve any passed-in
|
||||||
/// values via optional parameters or Properties if provided by caller.
|
/// values via optional parameters or Properties if provided by caller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ConnectionDescriptor ToDescriptor(ServerConfiguration cfg,
|
public static ConnectionDescriptor ToDescriptor(ServerConfigurationEntity cfg,
|
||||||
string? applicationName = null,
|
string? applicationName = null,
|
||||||
int? timeoutSeconds = null,
|
int? timeoutSeconds = null,
|
||||||
IReadOnlyDictionary<string, string>? extraProperties = null)
|
IReadOnlyDictionary<string, string>? extraProperties = null)
|
||||||
|
|
@ -43,12 +43,11 @@ public static class ServerConfigurationMapping
|
||||||
|
|
||||||
return new ConnectionDescriptor
|
return new ConnectionDescriptor
|
||||||
{
|
{
|
||||||
Name = cfg.Name,
|
|
||||||
Hosts = hosts,
|
Hosts = hosts,
|
||||||
Database = string.IsNullOrWhiteSpace(cfg.InitialDatabase) ? null : cfg.InitialDatabase,
|
Database = string.IsNullOrWhiteSpace(cfg.InitialDatabase) ? null : cfg.InitialDatabase,
|
||||||
Username = string.IsNullOrWhiteSpace(cfg.User?.Name) ? null : cfg.User!.Name,
|
Username = string.IsNullOrWhiteSpace(cfg.User?.Name) ? null : cfg.User!.Name,
|
||||||
Password = string.IsNullOrEmpty(cfg.User?.Password) ? null : cfg.User!.Password,
|
Password = string.IsNullOrEmpty(cfg.User?.Password) ? null : cfg.User!.Password,
|
||||||
SslMode = cfg.DefaultSslMode,
|
SslMode = cfg.SslMode,
|
||||||
ApplicationName = applicationName,
|
ApplicationName = applicationName,
|
||||||
TimeoutSeconds = timeoutSeconds,
|
TimeoutSeconds = timeoutSeconds,
|
||||||
Properties = props
|
Properties = props
|
||||||
|
|
@ -61,17 +60,13 @@ public static class ServerConfigurationMapping
|
||||||
/// - If descriptor has multiple hosts, the first is mapped to Host/Port.
|
/// - If descriptor has multiple hosts, the first is mapped to Host/Port.
|
||||||
/// - If descriptor omits sslmode/database/username/password, existing values are preserved (if any).
|
/// - If descriptor omits sslmode/database/username/password, existing values are preserved (if any).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ServerConfiguration FromDescriptor(ConnectionDescriptor descriptor, ServerConfiguration? existing = null)
|
public static ServerConfigurationEntity FromDescriptor(ConnectionDescriptor descriptor, ServerConfigurationEntity? existing = null)
|
||||||
{
|
{
|
||||||
if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
|
if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
|
||||||
var cfg = existing ?? new ServerConfiguration();
|
var cfg = existing ?? new ServerConfigurationEntity();
|
||||||
|
|
||||||
// Name
|
|
||||||
if (!string.IsNullOrWhiteSpace(descriptor.Name))
|
|
||||||
cfg.Name = descriptor.Name!;
|
|
||||||
|
|
||||||
// Host/Port: take first
|
// Host/Port: take first
|
||||||
if (descriptor.Hosts != null && descriptor.Hosts.Count > 0)
|
if (descriptor.Hosts.Count > 0)
|
||||||
{
|
{
|
||||||
var h = descriptor.Hosts[0];
|
var h = descriptor.Hosts[0];
|
||||||
if (!string.IsNullOrWhiteSpace(h.Host))
|
if (!string.IsNullOrWhiteSpace(h.Host))
|
||||||
|
|
@ -86,9 +81,10 @@ public static class ServerConfigurationMapping
|
||||||
|
|
||||||
// SSL Mode
|
// SSL Mode
|
||||||
if (descriptor.SslMode.HasValue)
|
if (descriptor.SslMode.HasValue)
|
||||||
cfg.DefaultSslMode = descriptor.SslMode.Value;
|
cfg.SslMode = descriptor.SslMode.Value;
|
||||||
|
|
||||||
// User
|
// User
|
||||||
|
// Suspect, should the user object be replaced instead of updated?
|
||||||
if (cfg.User == null)
|
if (cfg.User == null)
|
||||||
cfg.User = new ServerUser();
|
cfg.User = new ServerUser();
|
||||||
if (!string.IsNullOrWhiteSpace(descriptor.Username))
|
if (!string.IsNullOrWhiteSpace(descriptor.Username))
|
||||||
|
|
@ -99,4 +95,26 @@ public static class ServerConfigurationMapping
|
||||||
// Nothing to do for ApplicationName/TimeoutSeconds here; not represented in ServerConfiguration.
|
// Nothing to do for ApplicationName/TimeoutSeconds here; not represented in ServerConfiguration.
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overloads for new UI ViewModel wrapper
|
||||||
|
public static ConnectionDescriptor ToDescriptor(pgLabII.ViewModels.ServerConfigurationViewModel cfgVm,
|
||||||
|
string? applicationName = null,
|
||||||
|
int? timeoutSeconds = null,
|
||||||
|
IReadOnlyDictionary<string, string>? extraProperties = null)
|
||||||
|
=> ToDescriptor(cfgVm.Entity, applicationName, timeoutSeconds, extraProperties);
|
||||||
|
|
||||||
|
public static void FromDescriptorInto(pgLabII.ViewModels.ServerConfigurationViewModel targetVm, ConnectionDescriptor descriptor)
|
||||||
|
{
|
||||||
|
//var updated = targetVm.Entity;
|
||||||
|
var n = FromDescriptor(descriptor, null);
|
||||||
|
// push back updated values into VM's entity to trigger the notifies
|
||||||
|
targetVm.Host = n.Host;
|
||||||
|
targetVm.Port = n.Port;
|
||||||
|
targetVm.InitialDatabase = n.InitialDatabase;
|
||||||
|
targetVm.DefaultSslMode = n.SslMode;
|
||||||
|
// Suspect, if we share Users between configurations then we should not be overriding this struct
|
||||||
|
if (targetVm.User == null) targetVm.User = new ServerUser();
|
||||||
|
targetVm.User.Name = n.User?.Name ?? string.Empty;
|
||||||
|
targetVm.User.Password = n.User?.Password ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ namespace pgLabII.ViewModels;
|
||||||
|
|
||||||
public class EditServerConfigurationViewModel : ViewModelBase
|
public class EditServerConfigurationViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public ServerConfiguration Configuration { get; set; }
|
// Prefer new UI VM; keep old model for compatibility by wrapping when needed
|
||||||
|
public ServerConfigurationViewModel Configuration { get; set; }
|
||||||
|
|
||||||
// Connection string IO
|
// Connection string IO
|
||||||
private string _inputConnectionString = string.Empty;
|
private string _inputConnectionString = string.Empty;
|
||||||
|
|
@ -86,7 +87,7 @@ public class EditServerConfigurationViewModel : ViewModelBase
|
||||||
|
|
||||||
public EditServerConfigurationViewModel()
|
public EditServerConfigurationViewModel()
|
||||||
{
|
{
|
||||||
Configuration = new();
|
Configuration = new(new ServerConfigurationEntity());
|
||||||
_service = ConnectionStringService.CreateDefault();
|
_service = ConnectionStringService.CreateDefault();
|
||||||
|
|
||||||
ParseConnectionStringCommand = ReactiveCommand.Create(ParseConnectionString);
|
ParseConnectionStringCommand = ReactiveCommand.Create(ParseConnectionString);
|
||||||
|
|
@ -97,10 +98,10 @@ public class EditServerConfigurationViewModel : ViewModelBase
|
||||||
CloseCommand = ReactiveCommand.Create(() => { });
|
CloseCommand = ReactiveCommand.Create(() => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public EditServerConfigurationViewModel(ServerConfiguration configuration)
|
public EditServerConfigurationViewModel(ServerConfigurationEntity configuration)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
Configuration = configuration;
|
Configuration = new ServerConfigurationViewModel(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DetectFormat()
|
private void DetectFormat()
|
||||||
|
|
@ -131,19 +132,21 @@ public class EditServerConfigurationViewModel : ViewModelBase
|
||||||
_ => new UrlCodec()
|
_ => new UrlCodec()
|
||||||
};
|
};
|
||||||
var r = codec.TryParse(InputConnectionString);
|
var r = codec.TryParse(InputConnectionString);
|
||||||
if (r.IsSuccess) descriptor = r.Value;
|
if (r.IsSuccess)
|
||||||
|
descriptor = r.Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var r = _service.ParseToDescriptor(InputConnectionString);
|
var r = _service.ParseToDescriptor(InputConnectionString);
|
||||||
if (r.IsSuccess) descriptor = r.Value;
|
if (r.IsSuccess)
|
||||||
|
descriptor = r.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor != null)
|
if (descriptor != null)
|
||||||
{
|
{
|
||||||
// Map into our configuration (update existing)
|
// Map into our configuration (update existing)
|
||||||
ServerConfigurationMapping.FromDescriptor(descriptor, Configuration);
|
ServerConfigurationMapping.FromDescriptorInto(Configuration, descriptor);
|
||||||
// Also set sensible default OutputFormat to the detected/forced one
|
// Also set a sensible default OutputFormat to the detected/forced one
|
||||||
if (forced == ForcedFormatOption.Auto)
|
if (forced == ForcedFormatOption.Auto)
|
||||||
{
|
{
|
||||||
if (DetectedFormat.HasValue) OutputFormat = DetectedFormat.Value;
|
if (DetectedFormat.HasValue) OutputFormat = DetectedFormat.Value;
|
||||||
|
|
|
||||||
87
pgLabII/ViewModels/ServerConfigurationViewModel.cs
Normal file
87
pgLabII/ViewModels/ServerConfigurationViewModel.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
using System;
|
||||||
|
using System.Reactive;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Npgsql;
|
||||||
|
using pgLabII.Model;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace pgLabII.ViewModels;
|
||||||
|
|
||||||
|
// UI ViewModel that wraps the persistence entity
|
||||||
|
public class ServerConfigurationViewModel : ReactiveObject
|
||||||
|
{
|
||||||
|
private readonly ServerConfigurationEntity _entity;
|
||||||
|
|
||||||
|
public ServerConfigurationViewModel(ServerConfigurationEntity entity)
|
||||||
|
{
|
||||||
|
_entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||||
|
EditCommand = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
var vm = new EditServerConfigurationViewModel(_entity);
|
||||||
|
var window = new pgLabII.Views.EditServerConfigurationWindow(vm) { New = false };
|
||||||
|
window.Show();
|
||||||
|
});
|
||||||
|
ExploreCommand = ReactiveCommand.Create(() => { /* window coordination can be injected later */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerConfigurationEntity Entity => _entity;
|
||||||
|
|
||||||
|
public Guid Id
|
||||||
|
{
|
||||||
|
get => _entity.Id;
|
||||||
|
set { if (_entity.Id != value) { _entity.Id = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _entity.Name;
|
||||||
|
set { if (_entity.Name != value) { _entity.Name = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Host
|
||||||
|
{
|
||||||
|
get => _entity.Host;
|
||||||
|
set { if (_entity.Host != value) { _entity.Host = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort Port
|
||||||
|
{
|
||||||
|
get => _entity.Port;
|
||||||
|
set { if (_entity.Port != value) { _entity.Port = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string InitialDatabase
|
||||||
|
{
|
||||||
|
get => _entity.InitialDatabase;
|
||||||
|
set { if (_entity.InitialDatabase != value) { _entity.InitialDatabase = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public SslMode DefaultSslMode
|
||||||
|
{
|
||||||
|
get => _entity.SslMode;
|
||||||
|
set { if (_entity.SslMode != value) { _entity.SslMode = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ColorEnabled
|
||||||
|
{
|
||||||
|
get => _entity.ColorEnabled;
|
||||||
|
set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color Color
|
||||||
|
{
|
||||||
|
get => Color.FromUInt32((uint)_entity.ColorArgb);
|
||||||
|
set { var argb = unchecked((int)value.ToUInt32()); if (_entity.ColorArgb != argb) { _entity.ColorArgb = argb; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
|
||||||
|
|
||||||
|
public ServerUser User
|
||||||
|
{
|
||||||
|
get => _entity.User;
|
||||||
|
set { if (!ReferenceEquals(_entity.User, value)) { _entity.User = value; this.RaisePropertyChanged(); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> EditCommand { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ExploreCommand { get; }
|
||||||
|
}
|
||||||
|
|
@ -9,12 +9,11 @@ namespace pgLabII.ViewModels;
|
||||||
|
|
||||||
public class ServerListViewModel : ViewModelBase
|
public class ServerListViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public ObservableCollection<ServerConfiguration> ServerConfigurations { get; } =
|
public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } =
|
||||||
[
|
[
|
||||||
new ServerConfiguration()
|
new (new()
|
||||||
{
|
{
|
||||||
Name = "Local pg15",
|
Name = "Local pg15",
|
||||||
Color = Colors.Aquamarine,
|
|
||||||
ColorEnabled = true,
|
ColorEnabled = true,
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 5434,
|
Port = 5434,
|
||||||
|
|
@ -24,22 +23,25 @@ public class ServerListViewModel : ViewModelBase
|
||||||
Name = "postgres",
|
Name = "postgres",
|
||||||
Password = "admin",
|
Password = "admin",
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Color = Colors.Aquamarine,
|
||||||
},
|
},
|
||||||
new ServerConfiguration()
|
new (new ()
|
||||||
{
|
{
|
||||||
Name = "Bar",
|
Name = "Bar",
|
||||||
ColorEnabled = false,
|
ColorEnabled = false,
|
||||||
Host = "db.host.nl"
|
Host = "db.host.nl"
|
||||||
}
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
public ReactiveCommand<ServerConfiguration, Unit> RemoveServerCommand { get; }
|
public ReactiveCommand<ServerConfigurationViewModel, Unit> RemoveServerCommand { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> AddServerCommand { get; }
|
public ReactiveCommand<Unit, Unit> AddServerCommand { get; }
|
||||||
|
|
||||||
public ServerListViewModel()
|
public ServerListViewModel()
|
||||||
{
|
{
|
||||||
RemoveServerCommand = ReactiveCommand.Create<ServerConfiguration, Unit>((sc) =>
|
RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel, Unit>((sc) =>
|
||||||
{
|
{
|
||||||
ServerConfigurations.Remove(sc);
|
ServerConfigurations.Remove(sc);
|
||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue