1
0
mirror of synced 2024-11-22 12:43:51 +00:00

Implemented exporting and import of publisher messages

This commit is contained in:
Mark van Renswoude 2022-01-23 20:33:27 +01:00
parent b3c432d629
commit d6b9970d51
20 changed files with 407 additions and 120 deletions

View File

@ -1,4 +1,6 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
namespace PettingZoo.Core.ExportImport.Publisher namespace PettingZoo.Core.ExportImport.Publisher
@ -11,48 +13,189 @@ namespace PettingZoo.Core.ExportImport.Publisher
public class PublisherMessage public class PublisherMessage : IEquatable<PublisherMessage>
{ {
public PublisherMessageType MessageType { get; set; } public PublisherMessageType MessageType { get; init; }
public bool SendToExchange { get; set; } public bool SendToExchange { get; init; }
public string? Exchange { get; set; } public string? Exchange { get; init; }
public string? RoutingKey { get; set; } public string? RoutingKey { get; init; }
public string? Queue { get; set; } public string? Queue { get; init; }
public bool ReplyToNewSubscriber { get; set; } public bool ReplyToNewSubscriber { get; init; }
public string? ReplyTo { get; set; } public string? ReplyTo { get; init; }
public RawPublisherMessage? RawPublisherMessage { get; set; } public RawPublisherMessage? RawPublisherMessage { get; init; }
public TapetiPublisherMessage? TapetiPublisherMessage { get; set; } public TapetiPublisherMessage? TapetiPublisherMessage { get; init; }
}
public class RawPublisherMessage public bool Equals(PublisherMessage? other)
{ {
public MessageDeliveryMode DeliveryMode { get; set; } if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
public string? ContentType { get; set; } return MessageType == other.MessageType &&
public string? CorrelationId { get; set; } SendToExchange == other.SendToExchange &&
public string? AppId { get; set; } Exchange == other.Exchange &&
public string? ContentEncoding { get; set; } RoutingKey == other.RoutingKey &&
public string? Expiration { get; set; } Queue == other.Queue &&
public string? MessageId { get; set; } ReplyToNewSubscriber == other.ReplyToNewSubscriber &&
public string? Priority { get; set; } ReplyTo == other.ReplyTo &&
public string? Timestamp { get; set; } Equals(RawPublisherMessage, other.RawPublisherMessage) &&
public string? TypeProperty { get; set; } Equals(TapetiPublisherMessage, other.TapetiPublisherMessage);
public string? UserId { get; set; }
public string? Payload { get; set; }
public bool EnableMacros { get; set; }
public Dictionary<string, string>? Headers { get; set; }
} }
public class TapetiPublisherMessage public override bool Equals(object? obj)
{ {
public string? CorrelationId { get; set; } if (obj == null) return false;
public string? Payload { get; set; } if (ReferenceEquals(this, obj)) return true;
public bool EnableMacros { get; set; }
public string? ClassName { get; set; } return obj is PublisherMessage publisherMessage && Equals(publisherMessage);
public string? AssemblyName { get; set; } }
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 : IEquatable<RawPublisherMessage>
{
public MessageDeliveryMode DeliveryMode { get; init; }
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<string, string>? 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<string, string>? 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 : IEquatable<TapetiPublisherMessage>
{
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);
}
} }
} }

View File

@ -2,7 +2,7 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PettingZoo.Core.ExportImport.Subscriber; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.Export namespace PettingZoo.Tapeti.ExportImport
{ {
public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat
{ {

View File

@ -10,8 +10,7 @@ using Newtonsoft.Json.Linq;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Subscriber; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.ExportImport
namespace PettingZoo.Tapeti.Export
{ {
public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat
{ {

View File

@ -8,7 +8,7 @@
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace PettingZoo.Tapeti.Export { namespace PettingZoo.Tapeti.ExportImport {
using System; using System;
@ -39,7 +39,7 @@ namespace PettingZoo.Tapeti.Export {
internal static global::System.Resources.ResourceManager ResourceManager { internal static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.ReferenceEquals(resourceMan, null)) { 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; resourceMan = temp;
} }
return resourceMan; return resourceMan;

View File

@ -9,7 +9,7 @@ using Newtonsoft.Json;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Subscriber; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.Export namespace PettingZoo.Tapeti.ExportImport
{ {
public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat
{ {

View File

@ -33,7 +33,7 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>AssemblyParserStrings.resx</DependentUpon> <DependentUpon>AssemblyParserStrings.resx</DependentUpon>
</Compile> </Compile>
<Compile Update="Export\TapetiCmdImportExportStrings.Designer.cs"> <Compile Update="ExportImport\TapetiCmdImportExportStrings.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>TapetiCmdImportExportStrings.resx</DependentUpon> <DependentUpon>TapetiCmdImportExportStrings.resx</DependentUpon>
@ -60,7 +60,7 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AssemblyParserStrings.Designer.cs</LastGenOutput> <LastGenOutput>AssemblyParserStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Export\TapetiCmdImportExportStrings.resx"> <EmbeddedResource Update="ExportImport\TapetiCmdImportExportStrings.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TapetiCmdImportExportStrings.Designer.cs</LastGenOutput> <LastGenOutput>TapetiCmdImportExportStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>

View File

@ -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;
};
};
}
}
}

View File

@ -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);
};
}
}
}

View File

@ -11,7 +11,7 @@ using PettingZoo.Core.Settings;
using PettingZoo.RabbitMQ; using PettingZoo.RabbitMQ;
using PettingZoo.Settings.LiteDB; using PettingZoo.Settings.LiteDB;
using PettingZoo.Tapeti; using PettingZoo.Tapeti;
using PettingZoo.Tapeti.Export; using PettingZoo.Tapeti.ExportImport;
using PettingZoo.UI.Connection; using PettingZoo.UI.Connection;
using PettingZoo.UI.Main; using PettingZoo.UI.Main;
using PettingZoo.UI.Subscribe; using PettingZoo.UI.Subscribe;

View File

@ -12,7 +12,7 @@
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Style="{StaticResource WindowStyle}" Style="{StaticResource WindowStyle}"
Title="{Binding Title}" Title="{Binding Title}"
FocusManager.FocusedElement="{Binding ElementName=DisplayNameTextBox}" FocusManager.FocusedElement="{Binding ElementName=ValueTextBox}"
d:DataContext="{d:DesignInstance local:InputDialogViewModel}"> d:DataContext="{d:DesignInstance local:InputDialogViewModel}">
<StackPanel Margin="8"> <StackPanel Margin="8">
<TextBox Name="ValueTextBox" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" /> <TextBox Name="ValueTextBox" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />

View File

@ -25,7 +25,7 @@
<ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" /> <ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" />
</Window.Resources> </Window.Resources>
<DockPanel> <DockPanel>
<controls:NoOverflowToolbar DockPanel.Dock="Top" ToolBarTray.IsLocked="True"> <controls:AutoOverflowToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
<Button Command="{Binding ConnectCommand}"> <Button Command="{Binding ConnectCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Connect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <Image Source="{svgc:SvgImage Source=/Images/Connect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
@ -83,7 +83,7 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</controls:NoOverflowToolbar> </controls:AutoOverflowToolBar>
<StatusBar DockPanel.Dock="Bottom"> <StatusBar DockPanel.Dock="Bottom">
<StatusBarItem> <StatusBarItem>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">

View File

@ -16,7 +16,7 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="300" /> <ColumnDefinition Width="350" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
@ -95,7 +95,7 @@
<Label Grid.Row="0" Style="{StaticResource HeaderLabel}" Content="{x:Static res:PublisherViewStrings.PanelTitleMessages}"/> <Label Grid.Row="0" Style="{StaticResource HeaderLabel}" Content="{x:Static res:PublisherViewStrings.PanelTitleMessages}"/>
<controls:NoOverflowToolbar Grid.Row="1" ToolBarTray.IsLocked="True" Margin="0,0,0,4"> <controls:AutoOverflowToolBar Grid.Row="1" ToolBarTray.IsLocked="True" Margin="0,0,0,4">
<!-- TODO load button in addition to double-click. I don't like hidden-only functionality --> <!-- TODO load button in addition to double-click. I don't like hidden-only functionality -->
<Button Command="{Binding SaveCommand}"> <Button Command="{Binding SaveCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@ -116,8 +116,21 @@
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarDelete}" /> <TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarDelete}" />
</StackPanel> </StackPanel>
</Button> </Button>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Button Command="{Binding ExportCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Export.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarExport}" />
</StackPanel>
</Button>
<Button Command="{Binding ImportCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Import.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarImport}" />
</StackPanel>
</Button>
<!-- TODO export / import --> <!-- TODO export / import -->
</controls:NoOverflowToolbar> </controls:AutoOverflowToolBar>
<ListBox Grid.Row="2" ItemsSource="{Binding StoredMessages}" SelectedValue="{Binding SelectedStoredMessage}"> <ListBox Grid.Row="2" ItemsSource="{Binding StoredMessages}" SelectedValue="{Binding SelectedStoredMessage}">
<ListBox.Resources> <ListBox.Resources>
@ -147,7 +160,7 @@
</Style> </Style>
</TextBlock.Style> </TextBlock.Style>
<TextBlock.InputBindings> <TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.LoadStoredMessage, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" /> <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.LoadStoredMessageCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" />
</TextBlock.InputBindings> </TextBlock.InputBindings>
</TextBlock> </TextBlock>
</DataTemplate> </DataTemplate>

View File

@ -1,16 +1,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using Accessibility;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Publisher; using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.Core.Settings; using PettingZoo.Core.Settings;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
using UserControl = System.Windows.Controls.UserControl;
namespace PettingZoo.UI.Tab.Publisher namespace PettingZoo.UI.Tab.Publisher
{ {
@ -46,10 +53,13 @@ namespace PettingZoo.UI.Tab.Publisher
private readonly DelegateCommand saveCommand; private readonly DelegateCommand saveCommand;
private readonly DelegateCommand saveAsCommand; private readonly DelegateCommand saveAsCommand;
private readonly DelegateCommand deleteCommand; 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 readonly TabToolbarCommand[] toolbarCommands;
private Window? tabHostWindow; private Window? tabHostWindow;
private bool disableCheckCanSave;
public bool SendToExchange public bool SendToExchange
@ -167,12 +177,10 @@ namespace PettingZoo.UI.Tab.Publisher
public StoredPublisherMessage? SelectedStoredMessage public StoredPublisherMessage? SelectedStoredMessage
{ {
get => 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 public StoredPublisherMessage? ActiveStoredMessage
{ {
get => activeStoredMessage; get => activeStoredMessage;
@ -184,7 +192,9 @@ namespace PettingZoo.UI.Tab.Publisher
public ICommand SaveCommand => saveCommand; public ICommand SaveCommand => saveCommand;
public ICommand SaveAsCommand => saveAsCommand; public ICommand SaveAsCommand => saveAsCommand;
public ICommand DeleteCommand => deleteCommand; public ICommand DeleteCommand => deleteCommand;
public ICommand LoadStoredMessage => loadStoredMessage; public ICommand LoadStoredMessageCommand => loadStoredMessageCommand;
public ICommand ExportCommand => exportCommand;
public ICommand ImportCommand => importCommand;
public string Title => SendToQueue public string Title => SendToQueue
@ -214,10 +224,12 @@ namespace PettingZoo.UI.Tab.Publisher
this.tabFactory = tabFactory; this.tabFactory = tabFactory;
publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute); publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute);
saveCommand = new DelegateCommand(SaveExecute); saveCommand = new DelegateCommand(SaveExecute, SaveCanExecute);
saveAsCommand = new DelegateCommand(SaveAsExecute); saveAsCommand = new DelegateCommand(SaveAsExecute);
deleteCommand = new DelegateCommand(DeleteExecute, DeleteCanExecute); deleteCommand = new DelegateCommand(DeleteExecute, SelectedMessageCanExecute);
loadStoredMessage = new DelegateCommand(LoadStoredMessageExecute, LoadStoredMessageCanExecute); loadStoredMessageCommand = new DelegateCommand(LoadStoredMessageExecute, SelectedMessageCanExecute);
exportCommand = new DelegateCommand(ExportExecute, SelectedMessageCanExecute);
importCommand = new DelegateCommand(ImportExecute);
toolbarCommands = new[] toolbarCommands = new[]
{ {
@ -229,6 +241,9 @@ namespace PettingZoo.UI.Tab.Publisher
SetMessageTypeControl(fromReceivedMessage); SetMessageTypeControl(fromReceivedMessage);
else else
SetMessageTypeControl(PublisherMessageType.Raw); SetMessageTypeControl(PublisherMessageType.Raw);
PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
} }
@ -268,6 +283,11 @@ namespace PettingZoo.UI.Tab.Publisher
publishCommand.RaiseCanExecuteChanged(); 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); rawPublisherView = new RawPublisherView(rawPublisherViewModel);
} }
else else
@ -287,6 +307,8 @@ namespace PettingZoo.UI.Tab.Publisher
publishCommand.RaiseCanExecuteChanged(); publishCommand.RaiseCanExecuteChanged();
}; };
tapetiPublisherViewModel.PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel); tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel);
if (tabHostWindow != null) 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() private void SaveExecute()
{ {
storedPublisherMessagesViewModel.Save(SelectedStoredMessage, GetPublisherMessage(), message => if (ActiveStoredMessage == null)
return;
storedPublisherMessagesViewModel.Save(ActiveStoredMessage, GetPublisherMessage(), message =>
{ {
ActiveStoredMessage = message; ActiveStoredMessage = message;
SelectedStoredMessage = message; SelectedStoredMessage = message;
saveCommand.RaiseCanExecuteChanged();
}); });
} }
private void SaveAsExecute() private void SaveAsExecute()
{ {
storedPublisherMessagesViewModel.SaveAs(GetPublisherMessage(), SelectedStoredMessage?.DisplayName, message => storedPublisherMessagesViewModel.SaveAs(GetPublisherMessage(), ActiveStoredMessage?.DisplayName, message =>
{ {
ActiveStoredMessage = message; ActiveStoredMessage = message;
SelectedStoredMessage = message; SelectedStoredMessage = message;
@ -394,7 +430,7 @@ namespace PettingZoo.UI.Tab.Publisher
} }
private bool DeleteCanExecute() private bool SelectedMessageCanExecute()
{ {
return SelectedStoredMessage != null; return SelectedStoredMessage != null;
} }
@ -407,6 +443,9 @@ namespace PettingZoo.UI.Tab.Publisher
var message = SelectedStoredMessage.Message; var message = SelectedStoredMessage.Message;
disableCheckCanSave = true;
try
{
MessageType = message.MessageType; MessageType = message.MessageType;
SendToExchange = message.SendToExchange; SendToExchange = message.SendToExchange;
Exchange = message.Exchange ?? ""; Exchange = message.Exchange ?? "";
@ -433,11 +472,66 @@ namespace PettingZoo.UI.Tab.Publisher
ActiveStoredMessage = SelectedStoredMessage; ActiveStoredMessage = SelectedStoredMessage;
} }
finally
private bool LoadStoredMessageCanExecute()
{ {
return SelectedStoredMessage != null; disableCheckCanSave = false;
saveCommand.RaiseCanExecuteChanged();
}
}
private static readonly JsonSerializerSettings ExportImportSettings = new()
{
Converters = new List<JsonConverter> { 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<PublisherMessage>(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;
});
} }

View File

@ -186,6 +186,15 @@ namespace PettingZoo.UI.Tab.Publisher {
} }
} }
/// <summary>
/// Looks up a localized string similar to PettingZoo message (*.pubmsg.json)|*.pubmsg.json.
/// </summary>
public static string StoredMessagesExportImportFilter {
get {
return ResourceManager.GetString("StoredMessagesExportImportFilter", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Publish: {0}. /// Looks up a localized string similar to Publish: {0}.
/// </summary> /// </summary>
@ -213,6 +222,24 @@ namespace PettingZoo.UI.Tab.Publisher {
} }
} }
/// <summary>
/// Looks up a localized string similar to Export....
/// </summary>
public static string ToolbarExport {
get {
return ResourceManager.GetString("ToolbarExport", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import....
/// </summary>
public static string ToolbarImport {
get {
return ResourceManager.GetString("ToolbarImport", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Save. /// Looks up a localized string similar to Save.
/// </summary> /// </summary>

View File

@ -159,6 +159,9 @@
<data name="ReplyToCorrelationIdPrefix" xml:space="preserve"> <data name="ReplyToCorrelationIdPrefix" xml:space="preserve">
<value>Re: </value> <value>Re: </value>
</data> </data>
<data name="StoredMessagesExportImportFilter" xml:space="preserve">
<value>PettingZoo message (*.pubmsg.json)|*.pubmsg.json</value>
</data>
<data name="TabTitle" xml:space="preserve"> <data name="TabTitle" xml:space="preserve">
<value>Publish: {0}</value> <value>Publish: {0}</value>
</data> </data>
@ -168,6 +171,12 @@
<data name="ToolbarDelete" xml:space="preserve"> <data name="ToolbarDelete" xml:space="preserve">
<value>Delete</value> <value>Delete</value>
</data> </data>
<data name="ToolbarExport" xml:space="preserve">
<value>Export...</value>
</data>
<data name="ToolbarImport" xml:space="preserve">
<value>Import...</value>
</data>
<data name="ToolbarSave" xml:space="preserve"> <data name="ToolbarSave" xml:space="preserve">
<value>Save</value> <value>Save</value>
</data> </data>

View File

@ -219,9 +219,7 @@ namespace PettingZoo.UI.Tab.Publisher
Payload = Payload, Payload = Payload,
EnableMacros = EnableMacros, EnableMacros = EnableMacros,
Headers = Headers.Count > 0 Headers = Headers.Where(h => !h.IsEmpty()).ToDictionary(h => h.Key, h => h.Value)
? Headers.ToDictionary(h => h.Key, h => h.Value)
: null
}; };
} }
@ -243,12 +241,18 @@ namespace PettingZoo.UI.Tab.Publisher
EnableMacros = message.EnableMacros; EnableMacros = message.EnableMacros;
if (message.Headers != null) if (message.Headers != null)
{
Headers.ReplaceAll(message.Headers.Select(p => new Header Headers.ReplaceAll(message.Headers.Select(p => new Header
{ {
Key = p.Key, Key = p.Key,
Value = p.Value Value = p.Value
})); }));
} }
else
Headers.Clear();
AddHeader();
}
private static bool AnyNotEmpty(params string?[] values) private static bool AnyNotEmpty(params string?[] values)

View File

@ -32,21 +32,15 @@ namespace PettingZoo.UI.Tab.Publisher
} }
public void Save(StoredPublisherMessage? selectedMessage, PublisherMessage message, Action<StoredPublisherMessage> onSaved) public void Save(StoredPublisherMessage overwriteMessage, PublisherMessage message, Action<StoredPublisherMessage> onSaved)
{ {
if (selectedMessage == null)
{
SaveAs(message, null, onSaved);
return;
}
Task.Run(async () => 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(() => await Application.Current.Dispatcher.BeginInvoke(() =>
{ {
var index = StoredMessages.IndexOf(selectedMessage); var index = StoredMessages.IndexOf(overwriteMessage);
if (index >= 0) if (index >= 0)
StoredMessages[index] = updatedMessage; StoredMessages[index] = updatedMessage;
else else
@ -75,7 +69,6 @@ namespace PettingZoo.UI.Tab.Publisher
onSaved(storedMessage); onSaved(storedMessage);
}); });
}); });
} }

View File

@ -83,14 +83,14 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="200"/> <RowDefinition Height="200"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<controls:NoOverflowToolbar Grid.Column="0" Grid.Row="0" ToolBarTray.IsLocked="True" Margin="0,0,0,4" Background="Transparent"> <controls:AutoOverflowToolBar Grid.Column="0" Grid.Row="0" ToolBarTray.IsLocked="True" Margin="0,0,0,4" Background="Transparent">
<Button Command="{Binding CreatePublisherCommand}"> <Button Command="{Binding CreatePublisherCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Publish.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <Image Source="{svgc:SvgImage Source=/Images/Publish.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:SubscriberViewStrings.ContextPublish}" /> <TextBlock Margin="3,0,0,0" Text="{x:Static res:SubscriberViewStrings.ContextPublish}" />
</StackPanel> </StackPanel>
</Button> </Button>
</controls:NoOverflowToolbar> </controls:AutoOverflowToolBar>
<Border Grid.Column="0" Grid.Row="1" Style="{StaticResource SidePanel}"> <Border Grid.Column="0" Grid.Row="1" Style="{StaticResource SidePanel}">
<DockPanel> <DockPanel>

View File

@ -13,7 +13,7 @@
Width="800" Width="800"
WindowStyle="ThreeDBorderWindow"> WindowStyle="ThreeDBorderWindow">
<DockPanel> <DockPanel>
<controls:NoOverflowToolbar DockPanel.Dock="Top" ToolBarTray.IsLocked="True"> <controls:AutoOverflowToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
<Button Command="{Binding DockCommand}"> <Button Command="{Binding DockCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Dock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <Image Source="{svgc:SvgImage Source=/Images/Dock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
@ -38,7 +38,7 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</controls:NoOverflowToolbar> </controls:AutoOverflowToolBar>
<ContentControl Content="{Binding Content}" /> <ContentControl Content="{Binding Content}" />
</DockPanel> </DockPanel>