From d6b9970d518cfa39eb993065e02543f8c0a93511 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 23 Jan 2022 20:33:27 +0100 Subject: [PATCH] Implemented exporting and import of publisher messages --- .../Publisher/PublisherMessage.cs | 207 +++++++++++++++--- .../BaseTapetiCmdExportImportFormat.cs | 2 +- .../TapetiCmdExportFormat.cs | 3 +- .../TapetiCmdImportExportStrings.Designer.cs | 4 +- .../TapetiCmdImportExportStrings.resx | 0 .../TapetiCmdImportFormat.cs | 2 +- PettingZoo.Tapeti/PettingZoo.Tapeti.csproj | 4 +- .../Controls/AutoOverflowToolBar.cs | 26 +++ PettingZoo.WPF/Controls/NoOverflowToolbar.cs | 21 -- PettingZoo/Program.cs | 2 +- PettingZoo/UI/InputDialog.xaml | 2 +- PettingZoo/UI/Main/MainWindow.xaml | 4 +- .../UI/Tab/Publisher/PublisherView.xaml | 21 +- .../UI/Tab/Publisher/PublisherViewModel.cs | 162 +++++++++++--- .../PublisherViewStrings.Designer.cs | 27 +++ .../Tab/Publisher/PublisherViewStrings.resx | 9 + .../UI/Tab/Publisher/RawPublisherViewModel.cs | 10 +- .../StoredPublisherMessagesViewModel.cs | 13 +- .../UI/Tab/Subscriber/SubscriberView.xaml | 4 +- .../Tab/Undocked/UndockedTabHostWindow.xaml | 4 +- 20 files changed, 407 insertions(+), 120 deletions(-) rename PettingZoo.Tapeti/{Export => ExportImport}/BaseTapetiCmdExportImportFormat.cs (97%) rename PettingZoo.Tapeti/{Export => ExportImport}/TapetiCmdExportFormat.cs (98%) rename PettingZoo.Tapeti/{Export => ExportImport}/TapetiCmdImportExportStrings.Designer.cs (95%) rename PettingZoo.Tapeti/{Export => ExportImport}/TapetiCmdImportExportStrings.resx (100%) rename PettingZoo.Tapeti/{Export => ExportImport}/TapetiCmdImportFormat.cs (98%) create mode 100644 PettingZoo.WPF/Controls/AutoOverflowToolBar.cs delete mode 100644 PettingZoo.WPF/Controls/NoOverflowToolbar.cs diff --git a/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs b/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs index a680d5d..9c930cd 100644 --- a/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs +++ b/PettingZoo.Core/ExportImport/Publisher/PublisherMessage.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using PettingZoo.Core.Connection; namespace PettingZoo.Core.ExportImport.Publisher @@ -11,48 +13,189 @@ namespace PettingZoo.Core.ExportImport.Publisher - public class PublisherMessage + public class PublisherMessage : IEquatable { - public PublisherMessageType MessageType { get; set; } - public bool SendToExchange { get; set; } - public string? Exchange { get; set; } - public string? RoutingKey { get; set; } - public string? Queue { get; set; } - public bool ReplyToNewSubscriber { get; set; } - public string? ReplyTo { get; set; } + public PublisherMessageType MessageType { get; init; } + public bool SendToExchange { get; init; } + public string? Exchange { get; init; } + public string? RoutingKey { get; init; } + public string? Queue { get; init; } + public bool ReplyToNewSubscriber { get; init; } + public string? ReplyTo { get; init; } - public RawPublisherMessage? RawPublisherMessage { get; set; } - public TapetiPublisherMessage? TapetiPublisherMessage { get; set; } + public RawPublisherMessage? RawPublisherMessage { get; init; } + public TapetiPublisherMessage? TapetiPublisherMessage { get; init; } + + + public bool Equals(PublisherMessage? other) + { + if (other == null) return false; + if (ReferenceEquals(this, other)) return true; + + return MessageType == other.MessageType && + SendToExchange == other.SendToExchange && + Exchange == other.Exchange && + RoutingKey == other.RoutingKey && + Queue == other.Queue && + ReplyToNewSubscriber == other.ReplyToNewSubscriber && + ReplyTo == other.ReplyTo && + Equals(RawPublisherMessage, other.RawPublisherMessage) && + Equals(TapetiPublisherMessage, other.TapetiPublisherMessage); + } + + + public override bool Equals(object? obj) + { + if (obj == null) return false; + if (ReferenceEquals(this, obj)) return true; + + return obj is PublisherMessage publisherMessage && Equals(publisherMessage); + } + + + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add((int)MessageType); + hashCode.Add(SendToExchange); + hashCode.Add(Exchange); + hashCode.Add(RoutingKey); + hashCode.Add(Queue); + hashCode.Add(ReplyToNewSubscriber); + hashCode.Add(ReplyTo); + hashCode.Add(RawPublisherMessage); + hashCode.Add(TapetiPublisherMessage); + return hashCode.ToHashCode(); + } } - public class RawPublisherMessage + public class RawPublisherMessage : IEquatable { - public MessageDeliveryMode DeliveryMode { get; set; } + public MessageDeliveryMode DeliveryMode { get; init; } - public string? ContentType { get; set; } - public string? CorrelationId { get; set; } - public string? AppId { get; set; } - public string? ContentEncoding { get; set; } - public string? Expiration { get; set; } - public string? MessageId { get; set; } - public string? Priority { get; set; } - public string? Timestamp { get; set; } - public string? TypeProperty { get; set; } - public string? UserId { get; set; } - public string? Payload { get; set; } - public bool EnableMacros { get; set; } + public string? ContentType { get; init; } + public string? CorrelationId { get; init; } + public string? AppId { get; init; } + public string? ContentEncoding { get; init; } + public string? Expiration { get; init; } + public string? MessageId { get; init; } + public string? Priority { get; init; } + public string? Timestamp { get; init; } + public string? TypeProperty { get; init; } + public string? UserId { get; init; } + public string? Payload { get; init; } + public bool EnableMacros { get; init; } - public Dictionary? Headers { get; set; } + public Dictionary? Headers { get; init; } + + + public bool Equals(RawPublisherMessage? other) + { + if (other == null) return false; + if (ReferenceEquals(this, other)) return true; + + return DeliveryMode == other.DeliveryMode && + ContentType == other.ContentType && + CorrelationId == other.CorrelationId && + AppId == other.AppId && + ContentEncoding == other.ContentEncoding && + Expiration == other.Expiration && + MessageId == other.MessageId && + Priority == other.Priority && + Timestamp == other.Timestamp && + TypeProperty == other.TypeProperty && + UserId == other.UserId && + Payload == other.Payload && + EnableMacros == other.EnableMacros && + HeadersEquals(other.Headers); + } + + + private bool HeadersEquals(Dictionary? other) + { + if (other == null) + return Headers == null || Headers.Count == 0; + + if (Headers == null) + return other.Count == 0; + + return other.OrderBy(h => h.Key).SequenceEqual(Headers.OrderBy(h => h.Key)); + } + + + public override bool Equals(object? obj) + { + if (obj == null) return false; + if (ReferenceEquals(this, obj)) return true; + + return obj is RawPublisherMessage rawPublisherMessage && Equals(rawPublisherMessage); + } + + + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add((int)DeliveryMode); + hashCode.Add(ContentType); + hashCode.Add(CorrelationId); + hashCode.Add(AppId); + hashCode.Add(ContentEncoding); + hashCode.Add(Expiration); + hashCode.Add(MessageId); + hashCode.Add(Priority); + hashCode.Add(Timestamp); + hashCode.Add(TypeProperty); + hashCode.Add(UserId); + hashCode.Add(Payload); + hashCode.Add(EnableMacros); + + if (Headers != null) + foreach (var (key, value) in Headers) + { + hashCode.Add(key); + hashCode.Add(value); + } + + return hashCode.ToHashCode(); + } } - public class TapetiPublisherMessage + public class TapetiPublisherMessage : IEquatable { - public string? CorrelationId { get; set; } - public string? Payload { get; set; } - public bool EnableMacros { get; set; } - public string? ClassName { get; set; } - public string? AssemblyName { get; set; } + public string? CorrelationId { get; init; } + public string? Payload { get; init; } + public bool EnableMacros { get; init; } + public string? ClassName { get; init; } + public string? AssemblyName { get; init; } + + + public bool Equals(TapetiPublisherMessage? other) + { + if (other == null) return false; + if (ReferenceEquals(this, other)) return true; + + return CorrelationId == other.CorrelationId && + Payload == other.Payload && + EnableMacros == other.EnableMacros && + ClassName == other.ClassName && + AssemblyName == other.AssemblyName; + } + + + public override bool Equals(object? obj) + { + if (obj == null) return false; + if (ReferenceEquals(this, obj)) return true; + + return obj is TapetiPublisherMessage tapetiPublisherMessage && Equals(tapetiPublisherMessage); + } + + + public override int GetHashCode() + { + return HashCode.Combine(CorrelationId, Payload, EnableMacros, ClassName, AssemblyName); + } } } diff --git a/PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs b/PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs similarity index 97% rename from PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs rename to PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs index 1262c8a..cd54b2c 100644 --- a/PettingZoo.Tapeti/Export/BaseTapetiCmdExportImportFormat.cs +++ b/PettingZoo.Tapeti/ExportImport/BaseTapetiCmdExportImportFormat.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json.Linq; using PettingZoo.Core.ExportImport.Subscriber; -namespace PettingZoo.Tapeti.Export +namespace PettingZoo.Tapeti.ExportImport { public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat { diff --git a/PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs similarity index 98% rename from PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs rename to PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs index ebc83d8..fdce801 100644 --- a/PettingZoo.Tapeti/Export/TapetiCmdExportFormat.cs +++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdExportFormat.cs @@ -10,8 +10,7 @@ using Newtonsoft.Json.Linq; using PettingZoo.Core.Connection; using PettingZoo.Core.ExportImport.Subscriber; - -namespace PettingZoo.Tapeti.Export +namespace PettingZoo.Tapeti.ExportImport { public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat { diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs similarity index 95% rename from PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs index a9d3bf5..38103e4 100644 --- a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.Designer.cs +++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace PettingZoo.Tapeti.Export { +namespace PettingZoo.Tapeti.ExportImport { using System; @@ -39,7 +39,7 @@ namespace PettingZoo.Tapeti.Export { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.Export.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.ExportImport.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly); resourceMan = temp; } return resourceMan; diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.resx b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.resx similarity index 100% rename from PettingZoo.Tapeti/Export/TapetiCmdImportExportStrings.resx rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportExportStrings.resx diff --git a/PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs similarity index 98% rename from PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs rename to PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs index 00cf5d2..c5af72e 100644 --- a/PettingZoo.Tapeti/Export/TapetiCmdImportFormat.cs +++ b/PettingZoo.Tapeti/ExportImport/TapetiCmdImportFormat.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json; using PettingZoo.Core.Connection; using PettingZoo.Core.ExportImport.Subscriber; -namespace PettingZoo.Tapeti.Export +namespace PettingZoo.Tapeti.ExportImport { public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat { diff --git a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj index 68c879d..171a51d 100644 --- a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj +++ b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj @@ -33,7 +33,7 @@ True AssemblyParserStrings.resx - + True True TapetiCmdImportExportStrings.resx @@ -60,7 +60,7 @@ ResXFileCodeGenerator AssemblyParserStrings.Designer.cs - + ResXFileCodeGenerator TapetiCmdImportExportStrings.Designer.cs diff --git a/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs b/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs new file mode 100644 index 0000000..2d531ec --- /dev/null +++ b/PettingZoo.WPF/Controls/AutoOverflowToolBar.cs @@ -0,0 +1,26 @@ +using System.Windows; +using System.Windows.Controls; + +namespace PettingZoo.WPF.Controls +{ + public class AutoOverflowToolBar : ToolBar + { + public AutoOverflowToolBar() + { + Loaded += (_, _) => + { + // Hide overflow arrow on the right side of the toolbar when not required + if (Template.FindName("OverflowButton", this) is not FrameworkElement overflowButton) + return; + + if (!overflowButton.IsEnabled) + overflowButton.Visibility = Visibility.Hidden; + + overflowButton.IsEnabledChanged += (_, _) => + { + overflowButton.Visibility = overflowButton.IsEnabled ? Visibility.Visible : Visibility.Hidden; + }; + }; + } + } +} diff --git a/PettingZoo.WPF/Controls/NoOverflowToolbar.cs b/PettingZoo.WPF/Controls/NoOverflowToolbar.cs deleted file mode 100644 index 09c9061..0000000 --- a/PettingZoo.WPF/Controls/NoOverflowToolbar.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace PettingZoo.WPF.Controls -{ - public class NoOverflowToolbar : ToolBar - { - public NoOverflowToolbar() - { - Loaded += (_, _) => - { - // Hide arrow on the right side of the toolbar - if (Template.FindName("OverflowGrid", this) is FrameworkElement overflowGrid) - overflowGrid.Visibility = Visibility.Collapsed; - - if (Template.FindName("MainPanelBorder", this) is FrameworkElement mainPanelBorder) - mainPanelBorder.Margin = new Thickness(0); - }; - } - } -} diff --git a/PettingZoo/Program.cs b/PettingZoo/Program.cs index bdc95f3..6bdc1fe 100644 --- a/PettingZoo/Program.cs +++ b/PettingZoo/Program.cs @@ -11,7 +11,7 @@ using PettingZoo.Core.Settings; using PettingZoo.RabbitMQ; using PettingZoo.Settings.LiteDB; using PettingZoo.Tapeti; -using PettingZoo.Tapeti.Export; +using PettingZoo.Tapeti.ExportImport; using PettingZoo.UI.Connection; using PettingZoo.UI.Main; using PettingZoo.UI.Subscribe; diff --git a/PettingZoo/UI/InputDialog.xaml b/PettingZoo/UI/InputDialog.xaml index b7feb2e..932eba8 100644 --- a/PettingZoo/UI/InputDialog.xaml +++ b/PettingZoo/UI/InputDialog.xaml @@ -12,7 +12,7 @@ WindowStartupLocation="CenterOwner" Style="{StaticResource WindowStyle}" Title="{Binding Title}" - FocusManager.FocusedElement="{Binding ElementName=DisplayNameTextBox}" + FocusManager.FocusedElement="{Binding ElementName=ValueTextBox}" d:DataContext="{d:DesignInstance local:InputDialogViewModel}"> diff --git a/PettingZoo/UI/Main/MainWindow.xaml b/PettingZoo/UI/Main/MainWindow.xaml index 07f9873..d9a638e 100644 --- a/PettingZoo/UI/Main/MainWindow.xaml +++ b/PettingZoo/UI/Main/MainWindow.xaml @@ -25,7 +25,7 @@ - + + + + - + @@ -147,7 +160,7 @@ - + diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs index d63898f..2f85f46 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs @@ -1,16 +1,23 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; +using System.Windows.Forms; using System.Windows.Input; +using Accessibility; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using PettingZoo.Core.Connection; using PettingZoo.Core.ExportImport.Publisher; using PettingZoo.Core.Generator; using PettingZoo.Core.Macros; using PettingZoo.Core.Settings; using PettingZoo.WPF.ViewModel; +using UserControl = System.Windows.Controls.UserControl; namespace PettingZoo.UI.Tab.Publisher { @@ -46,10 +53,13 @@ namespace PettingZoo.UI.Tab.Publisher private readonly DelegateCommand saveCommand; private readonly DelegateCommand saveAsCommand; private readonly DelegateCommand deleteCommand; - private readonly DelegateCommand loadStoredMessage; + private readonly DelegateCommand loadStoredMessageCommand; + private readonly DelegateCommand exportCommand; + private readonly DelegateCommand importCommand; private readonly TabToolbarCommand[] toolbarCommands; private Window? tabHostWindow; + private bool disableCheckCanSave; public bool SendToExchange @@ -167,12 +177,10 @@ namespace PettingZoo.UI.Tab.Publisher public StoredPublisherMessage? SelectedStoredMessage { get => selectedStoredMessage; - set => SetField(ref selectedStoredMessage, value, delegateCommandsChanged: new[] { deleteCommand }); + set => SetField(ref selectedStoredMessage, value, delegateCommandsChanged: new[] { loadStoredMessageCommand, deleteCommand, exportCommand }); } - // TODO detect changes from ActiveStoredMessage and show indication in the UI - public StoredPublisherMessage? ActiveStoredMessage { get => activeStoredMessage; @@ -184,7 +192,9 @@ namespace PettingZoo.UI.Tab.Publisher public ICommand SaveCommand => saveCommand; public ICommand SaveAsCommand => saveAsCommand; public ICommand DeleteCommand => deleteCommand; - public ICommand LoadStoredMessage => loadStoredMessage; + public ICommand LoadStoredMessageCommand => loadStoredMessageCommand; + public ICommand ExportCommand => exportCommand; + public ICommand ImportCommand => importCommand; public string Title => SendToQueue @@ -214,10 +224,12 @@ namespace PettingZoo.UI.Tab.Publisher this.tabFactory = tabFactory; publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute); - saveCommand = new DelegateCommand(SaveExecute); + saveCommand = new DelegateCommand(SaveExecute, SaveCanExecute); saveAsCommand = new DelegateCommand(SaveAsExecute); - deleteCommand = new DelegateCommand(DeleteExecute, DeleteCanExecute); - loadStoredMessage = new DelegateCommand(LoadStoredMessageExecute, LoadStoredMessageCanExecute); + deleteCommand = new DelegateCommand(DeleteExecute, SelectedMessageCanExecute); + loadStoredMessageCommand = new DelegateCommand(LoadStoredMessageExecute, SelectedMessageCanExecute); + exportCommand = new DelegateCommand(ExportExecute, SelectedMessageCanExecute); + importCommand = new DelegateCommand(ImportExecute); toolbarCommands = new[] { @@ -229,6 +241,9 @@ namespace PettingZoo.UI.Tab.Publisher SetMessageTypeControl(fromReceivedMessage); else SetMessageTypeControl(PublisherMessageType.Raw); + + + PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); }; } @@ -268,6 +283,11 @@ namespace PettingZoo.UI.Tab.Publisher publishCommand.RaiseCanExecuteChanged(); }; + // This is becoming a bit messy, find a cleaner way... + // TODO monitor header changes as well, instead of only the collection + rawPublisherViewModel.PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); }; + rawPublisherViewModel.Headers.CollectionChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); }; + rawPublisherView = new RawPublisherView(rawPublisherViewModel); } else @@ -287,6 +307,8 @@ namespace PettingZoo.UI.Tab.Publisher publishCommand.RaiseCanExecuteChanged(); }; + tapetiPublisherViewModel.PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); }; + tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel); if (tabHostWindow != null) @@ -356,19 +378,33 @@ namespace PettingZoo.UI.Tab.Publisher } + private bool SaveCanExecute() + { + if (disableCheckCanSave) + return false; + + return ActiveStoredMessage != null && !GetPublisherMessage().Equals(ActiveStoredMessage.Message); + } + + private void SaveExecute() { - storedPublisherMessagesViewModel.Save(SelectedStoredMessage, GetPublisherMessage(), message => + if (ActiveStoredMessage == null) + return; + + storedPublisherMessagesViewModel.Save(ActiveStoredMessage, GetPublisherMessage(), message => { ActiveStoredMessage = message; SelectedStoredMessage = message; + + saveCommand.RaiseCanExecuteChanged(); }); } private void SaveAsExecute() { - storedPublisherMessagesViewModel.SaveAs(GetPublisherMessage(), SelectedStoredMessage?.DisplayName, message => + storedPublisherMessagesViewModel.SaveAs(GetPublisherMessage(), ActiveStoredMessage?.DisplayName, message => { ActiveStoredMessage = message; SelectedStoredMessage = message; @@ -394,7 +430,7 @@ namespace PettingZoo.UI.Tab.Publisher } - private bool DeleteCanExecute() + private bool SelectedMessageCanExecute() { return SelectedStoredMessage != null; } @@ -407,37 +443,95 @@ namespace PettingZoo.UI.Tab.Publisher var message = SelectedStoredMessage.Message; - MessageType = message.MessageType; - SendToExchange = message.SendToExchange; - Exchange = message.Exchange ?? ""; - RoutingKey = message.RoutingKey ?? ""; - Queue = message.Queue ?? ""; - ReplyToNewSubscriber = message.ReplyToNewSubscriber; - ReplyTo = message.ReplyTo ?? ""; - - // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault - switch (message.MessageType) + disableCheckCanSave = true; + try { - case PublisherMessageType.Raw: - if (message.RawPublisherMessage != null) - rawPublisherViewModel?.LoadPublisherMessage(message.RawPublisherMessage); + MessageType = message.MessageType; + SendToExchange = message.SendToExchange; + Exchange = message.Exchange ?? ""; + RoutingKey = message.RoutingKey ?? ""; + Queue = message.Queue ?? ""; + ReplyToNewSubscriber = message.ReplyToNewSubscriber; + ReplyTo = message.ReplyTo ?? ""; - break; + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (message.MessageType) + { + case PublisherMessageType.Raw: + if (message.RawPublisherMessage != null) + rawPublisherViewModel?.LoadPublisherMessage(message.RawPublisherMessage); - case PublisherMessageType.Tapeti: - if (message.TapetiPublisherMessage != null) - tapetiPublisherViewModel?.LoadPublisherMessage(message.TapetiPublisherMessage); + break; - break; + case PublisherMessageType.Tapeti: + if (message.TapetiPublisherMessage != null) + tapetiPublisherViewModel?.LoadPublisherMessage(message.TapetiPublisherMessage); + + break; + } + + ActiveStoredMessage = SelectedStoredMessage; + } + finally + { + disableCheckCanSave = false; + saveCommand.RaiseCanExecuteChanged(); } - - ActiveStoredMessage = SelectedStoredMessage; } - private bool LoadStoredMessageCanExecute() + private static readonly JsonSerializerSettings ExportImportSettings = new() { - return SelectedStoredMessage != null; + Converters = new List { new StringEnumConverter() } + }; + + + + private void ExportExecute() + { + if (SelectedStoredMessage == null) + return; + + var invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); + var invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); + var suggestedFilename = Regex.Replace(SelectedStoredMessage.DisplayName, invalidRegStr, "_"); + + var dialog = new SaveFileDialog + { + Filter = PublisherViewStrings.StoredMessagesExportImportFilter, + FileName = suggestedFilename + }; + + if (dialog.ShowDialog() != DialogResult.OK) + return; + + File.WriteAllText(dialog.FileName, JsonConvert.SerializeObject(SelectedStoredMessage.Message, ExportImportSettings), Encoding.UTF8); + } + + + private void ImportExecute() + { + var dialog = new OpenFileDialog + { + Filter = PublisherViewStrings.StoredMessagesExportImportFilter + }; + + if (dialog.ShowDialog() != DialogResult.OK) + return; + + var fileContents = File.ReadAllText(dialog.FileName, Encoding.UTF8); + var message = JsonConvert.DeserializeObject(fileContents, ExportImportSettings); + if (message == null) + return; + + var displayName = dialog.FileName.EndsWith(".pubmsg.json") + ? Path.GetFileName(dialog.FileName)[..^".pubmsg.json".Length] + : Path.GetFileNameWithoutExtension(dialog.FileName); + + storedPublisherMessagesViewModel.SaveAs(message, displayName, storedMessage => + { + SelectedStoredMessage = storedMessage; + }); } diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs index c9df135..c6d9307 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs @@ -186,6 +186,15 @@ namespace PettingZoo.UI.Tab.Publisher { } } + /// + /// Looks up a localized string similar to PettingZoo message (*.pubmsg.json)|*.pubmsg.json. + /// + public static string StoredMessagesExportImportFilter { + get { + return ResourceManager.GetString("StoredMessagesExportImportFilter", resourceCulture); + } + } + /// /// Looks up a localized string similar to Publish: {0}. /// @@ -213,6 +222,24 @@ namespace PettingZoo.UI.Tab.Publisher { } } + /// + /// Looks up a localized string similar to Export.... + /// + public static string ToolbarExport { + get { + return ResourceManager.GetString("ToolbarExport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import.... + /// + public static string ToolbarImport { + get { + return ResourceManager.GetString("ToolbarImport", resourceCulture); + } + } + /// /// Looks up a localized string similar to Save. /// diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx index b6b91e7..ea96531 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx @@ -159,6 +159,9 @@ Re: + + PettingZoo message (*.pubmsg.json)|*.pubmsg.json + Publish: {0} @@ -168,6 +171,12 @@ Delete + + Export... + + + Import... + Save diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs index f66602b..f44d607 100644 --- a/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs @@ -219,9 +219,7 @@ namespace PettingZoo.UI.Tab.Publisher Payload = Payload, EnableMacros = EnableMacros, - Headers = Headers.Count > 0 - ? Headers.ToDictionary(h => h.Key, h => h.Value) - : null + Headers = Headers.Where(h => !h.IsEmpty()).ToDictionary(h => h.Key, h => h.Value) }; } @@ -243,11 +241,17 @@ namespace PettingZoo.UI.Tab.Publisher EnableMacros = message.EnableMacros; if (message.Headers != null) + { Headers.ReplaceAll(message.Headers.Select(p => new Header { Key = p.Key, Value = p.Value })); + } + else + Headers.Clear(); + + AddHeader(); } diff --git a/PettingZoo/UI/Tab/Publisher/StoredPublisherMessagesViewModel.cs b/PettingZoo/UI/Tab/Publisher/StoredPublisherMessagesViewModel.cs index 273f366..f8c6870 100644 --- a/PettingZoo/UI/Tab/Publisher/StoredPublisherMessagesViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/StoredPublisherMessagesViewModel.cs @@ -32,21 +32,15 @@ namespace PettingZoo.UI.Tab.Publisher } - public void Save(StoredPublisherMessage? selectedMessage, PublisherMessage message, Action onSaved) + public void Save(StoredPublisherMessage overwriteMessage, PublisherMessage message, Action onSaved) { - if (selectedMessage == null) - { - SaveAs(message, null, onSaved); - return; - } - Task.Run(async () => { - var updatedMessage = await publisherMessagesRepository.Update(selectedMessage.Id, selectedMessage.DisplayName, message); + var updatedMessage = await publisherMessagesRepository.Update(overwriteMessage.Id, overwriteMessage.DisplayName, message); await Application.Current.Dispatcher.BeginInvoke(() => { - var index = StoredMessages.IndexOf(selectedMessage); + var index = StoredMessages.IndexOf(overwriteMessage); if (index >= 0) StoredMessages[index] = updatedMessage; else @@ -75,7 +69,6 @@ namespace PettingZoo.UI.Tab.Publisher onSaved(storedMessage); }); }); - } diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml index c8e5185..6e113a9 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml @@ -83,14 +83,14 @@ - + - + diff --git a/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml b/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml index 76c9516..867be1d 100644 --- a/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml +++ b/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml @@ -13,7 +13,7 @@ Width="800" WindowStyle="ThreeDBorderWindow"> - +