diff --git a/PettingZoo.Core/Connection/IConnection.cs b/PettingZoo.Core/Connection/IConnection.cs index f29014c..4f4d0da 100644 --- a/PettingZoo.Core/Connection/IConnection.cs +++ b/PettingZoo.Core/Connection/IConnection.cs @@ -8,7 +8,7 @@ namespace PettingZoo.Core.Connection event EventHandler StatusChanged; ISubscriber Subscribe(string exchange, string routingKey); - Task Publish(MessageInfo messageInfo); + Task Publish(PublishMessageInfo messageInfo); } diff --git a/PettingZoo.Core/Connection/ISubscriber.cs b/PettingZoo.Core/Connection/ISubscriber.cs index 3ec09c8..0148cc4 100644 --- a/PettingZoo.Core/Connection/ISubscriber.cs +++ b/PettingZoo.Core/Connection/ISubscriber.cs @@ -15,10 +15,10 @@ namespace PettingZoo.Core.Connection public class MessageReceivedEventArgs : EventArgs { - public MessageInfo MessageInfo { get; } + public ReceivedMessageInfo MessageInfo { get; } - public MessageReceivedEventArgs(MessageInfo messageInfo) + public MessageReceivedEventArgs(ReceivedMessageInfo messageInfo) { MessageInfo = messageInfo; } diff --git a/PettingZoo.Core/Connection/MessageInfo.cs b/PettingZoo.Core/Connection/MessageInfo.cs index f70abd6..fdc6e86 100644 --- a/PettingZoo.Core/Connection/MessageInfo.cs +++ b/PettingZoo.Core/Connection/MessageInfo.cs @@ -3,21 +3,72 @@ using System.Collections.Generic; namespace PettingZoo.Core.Connection { - public class MessageInfo + public class BaseMessageInfo { - public DateTime Timestamp { get; } public string Exchange { get; } public string RoutingKey { get; } public byte[] Body { get; } - public IDictionary Properties { get; } + public MessageProperties Properties { get; } - public MessageInfo(string exchange, string routingKey, byte[] body, IDictionary properties, DateTime timestamp) + public BaseMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties) { Exchange = exchange; RoutingKey = routingKey; Body = body; Properties = properties; - Timestamp = timestamp; } } + + + public class ReceivedMessageInfo : BaseMessageInfo + { + public DateTime ReceivedTimestamp { get; } + + public ReceivedMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties, DateTime receivedTimestamp) + : base(exchange, routingKey, body, properties) + { + ReceivedTimestamp = receivedTimestamp; + } + } + + + public class PublishMessageInfo : BaseMessageInfo + { + public PublishMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties) + : base(exchange, routingKey, body, properties) + { + } + } + + + public enum MessageDeliveryMode + { + NonPersistent = 1, + Persistent = 2 + } + + + public class MessageProperties + { + private static readonly IReadOnlyDictionary EmptyHeaders = new Dictionary(); + + public MessageProperties(IReadOnlyDictionary? headers) + { + Headers = headers ?? EmptyHeaders; + } + + public string? AppId { get; init; } + public string? ContentEncoding { get; init; } + public string? ContentType { get; init; } + public string? CorrelationId { get; init; } + public MessageDeliveryMode? DeliveryMode { get; init; } + public string? Expiration { get; init; } + public IReadOnlyDictionary Headers { get; } + public string? MessageId { get; init; } + public byte? Priority { get; init; } + public string? ReplyTo { get; init; } + public DateTime? Timestamp { get; init; } + public string? Type { get; init; } + public string? UserId { get; init; } + } } diff --git a/PettingZoo.Core/PettingZoo.Core.csproj b/PettingZoo.Core/PettingZoo.Core.csproj index 951e5aa..b380728 100644 --- a/PettingZoo.Core/PettingZoo.Core.csproj +++ b/PettingZoo.Core/PettingZoo.Core.csproj @@ -9,4 +9,19 @@ + + + True + True + MessagePropertiesRendererStrings.resx + + + + + + ResXFileCodeGenerator + MessagePropertiesRendererStrings.Designer.cs + + + diff --git a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs index acdc033..f3d4128 100644 --- a/PettingZoo.Core/Rendering/MessageBodyRenderer.cs +++ b/PettingZoo.Core/Rendering/MessageBodyRenderer.cs @@ -13,9 +13,9 @@ namespace PettingZoo.Core.Rendering }; - public static string Render(byte[] body, string contentType = "") + public static string Render(byte[] body, string? contentType) { - return ContentTypeHandlers.TryGetValue(contentType, out var handler) + return (contentType != null) && ContentTypeHandlers.TryGetValue(contentType, out var handler) ? handler(body) : Encoding.UTF8.GetString(body); diff --git a/PettingZoo.Core/Rendering/MessagePropertiesRenderer.cs b/PettingZoo.Core/Rendering/MessagePropertiesRenderer.cs new file mode 100644 index 0000000..2066430 --- /dev/null +++ b/PettingZoo.Core/Rendering/MessagePropertiesRenderer.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using PettingZoo.Core.Connection; + +namespace PettingZoo.Core.Rendering +{ + public class MessagePropertiesRenderer + { + public static IDictionary Render(MessageProperties properties) + { + var result = new Dictionary(); + + if (properties.AppId != null) + result.Add(MessagePropertiesRendererStrings.AppId, properties.AppId); + + if (properties.ContentEncoding != null) + result.Add(MessagePropertiesRendererStrings.ContentEncoding, properties.ContentEncoding); + + if (properties.ContentType != null) + result.Add(MessagePropertiesRendererStrings.ContentType, properties.ContentType); + + if (properties.CorrelationId != null) + result.Add(MessagePropertiesRendererStrings.CorrelationId, properties.CorrelationId); + + if (properties.DeliveryMode != null) + result.Add(MessagePropertiesRendererStrings.DeliveryMode, + properties.DeliveryMode == MessageDeliveryMode.Persistent + ? MessagePropertiesRendererStrings.DeliveryModePersistent + : MessagePropertiesRendererStrings.DeliveryModeNonPersistent); + + if (properties.Expiration != null) + result.Add(MessagePropertiesRendererStrings.Expiration, properties.Expiration); + + if (properties.MessageId != null) + result.Add(MessagePropertiesRendererStrings.MessageId, properties.MessageId); + + if (properties.Priority != null) + result.Add(MessagePropertiesRendererStrings.Priority, properties.Priority.Value.ToString()); + + if (properties.ReplyTo != null) + result.Add(MessagePropertiesRendererStrings.ReplyTo, properties.ReplyTo); + + if (properties.Timestamp != null) + result.Add(MessagePropertiesRendererStrings.Timestamp, properties.Timestamp.Value.ToString("G")); + + if (properties.Type != null) + result.Add(MessagePropertiesRendererStrings.Type, properties.Type); + + if (properties.UserId != null) + result.Add(MessagePropertiesRendererStrings.UserId, properties.UserId); + + foreach (var (key, value) in properties.Headers) + { + if (!result.TryAdd(key, value)) + result.TryAdd(MessagePropertiesRendererStrings.HeaderPrefix + key, value); + } + + return result; + } + } +} diff --git a/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.Designer.cs b/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.Designer.cs new file mode 100644 index 0000000..7125e30 --- /dev/null +++ b/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.Designer.cs @@ -0,0 +1,198 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PettingZoo.Core.Rendering { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MessagePropertiesRendererStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MessagePropertiesRendererStrings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Core.Rendering.MessagePropertiesRendererStrings", typeof(MessagePropertiesRendererStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to App ID. + /// + internal static string AppId { + get { + return ResourceManager.GetString("AppId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content encoding. + /// + internal static string ContentEncoding { + get { + return ResourceManager.GetString("ContentEncoding", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content type. + /// + internal static string ContentType { + get { + return ResourceManager.GetString("ContentType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Correlation ID. + /// + internal static string CorrelationId { + get { + return ResourceManager.GetString("CorrelationId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery mode. + /// + internal static string DeliveryMode { + get { + return ResourceManager.GetString("DeliveryMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transient (1). + /// + internal static string DeliveryModeNonPersistent { + get { + return ResourceManager.GetString("DeliveryModeNonPersistent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Persistent (2). + /// + internal static string DeliveryModePersistent { + get { + return ResourceManager.GetString("DeliveryModePersistent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expiration. + /// + internal static string Expiration { + get { + return ResourceManager.GetString("Expiration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Header: . + /// + internal static string HeaderPrefix { + get { + return ResourceManager.GetString("HeaderPrefix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message ID. + /// + internal static string MessageId { + get { + return ResourceManager.GetString("MessageId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Priority. + /// + internal static string Priority { + get { + return ResourceManager.GetString("Priority", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reply To. + /// + internal static string ReplyTo { + get { + return ResourceManager.GetString("ReplyTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timestamp. + /// + internal static string Timestamp { + get { + return ResourceManager.GetString("Timestamp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + internal static string Type { + get { + return ResourceManager.GetString("Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User ID. + /// + internal static string UserId { + get { + return ResourceManager.GetString("UserId", resourceCulture); + } + } + } +} diff --git a/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.resx b/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.resx new file mode 100644 index 0000000..ceb26c1 --- /dev/null +++ b/PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.resx @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + App ID + + + Content encoding + + + Content type + + + Correlation ID + + + Delivery mode + + + Transient (1) + + + Persistent (2) + + + Expiration + + + Header: + + + Message ID + + + Priority + + + Reply To + + + Timestamp + + + Type + + + User ID + + \ No newline at end of file diff --git a/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs b/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs index 9682070..a6f54e5 100644 --- a/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs +++ b/PettingZoo.RabbitMQ/RabbitMQClientConnection.cs @@ -81,7 +81,7 @@ namespace PettingZoo.RabbitMQ } - public Task Publish(MessageInfo messageInfo) + public Task Publish(PublishMessageInfo messageInfo) { if (model == null) throw new InvalidOperationException("Not connected"); diff --git a/PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs b/PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs index 967413f..8ad2be5 100644 --- a/PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs +++ b/PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs @@ -1,136 +1,135 @@ -using System.Collections.Generic; -using System.Globalization; +using System; +using System.Linq; using System.Text; +using PettingZoo.Core.Connection; using RabbitMQ.Client; namespace PettingZoo.RabbitMQ { public static class RabbitMQClientPropertiesConverter { - public static IDictionary Convert(IBasicProperties basicProperties) + public static MessageProperties Convert(IBasicProperties basicProperties) { - var properties = new Dictionary(); - - if (basicProperties.IsDeliveryModePresent()) - properties.Add(RabbitMQProperties.DeliveryMode, basicProperties.DeliveryMode.ToString(CultureInfo.InvariantCulture)); - - if (basicProperties.IsContentTypePresent()) - properties.Add(RabbitMQProperties.ContentType, basicProperties.ContentType); - - if (basicProperties.IsContentEncodingPresent()) - properties.Add(RabbitMQProperties.ContentEncoding, basicProperties.ContentEncoding); - - if (basicProperties.IsPriorityPresent()) - properties.Add(RabbitMQProperties.Priority, basicProperties.Priority.ToString(CultureInfo.InvariantCulture)); - - if (basicProperties.IsCorrelationIdPresent()) - properties.Add(RabbitMQProperties.Priority, basicProperties.CorrelationId); - - if (basicProperties.IsReplyToPresent()) - properties.Add(RabbitMQProperties.ReplyTo, basicProperties.ReplyTo); - - if (basicProperties.IsExpirationPresent()) - properties.Add(RabbitMQProperties.Expiration, basicProperties.Expiration); - - if (basicProperties.IsMessageIdPresent()) - properties.Add(RabbitMQProperties.MessageId, basicProperties.MessageId); - - if (basicProperties.IsTimestampPresent()) - properties.Add(RabbitMQProperties.Timestamp, basicProperties.Timestamp.UnixTime.ToString(CultureInfo.InvariantCulture)); - - if (basicProperties.IsTypePresent()) - properties.Add(RabbitMQProperties.Type, basicProperties.Type); - - if (basicProperties.IsUserIdPresent()) - properties.Add(RabbitMQProperties.UserId, basicProperties.UserId); - - if (basicProperties.IsAppIdPresent()) - properties.Add(RabbitMQProperties.UserId, basicProperties.AppId); - - if (basicProperties.IsClusterIdPresent()) - properties.Add(RabbitMQProperties.ClusterId, basicProperties.ClusterId); - - // ReSharper disable once InvertIf - if (basicProperties.Headers != null) + return new MessageProperties(basicProperties.Headers?.ToDictionary(p => p.Key, p => Encoding.UTF8.GetString((byte[])p.Value))) { - foreach (var (key, value) in basicProperties.Headers) - properties.Add(key, Encoding.UTF8.GetString((byte[]) value)); - } + DeliveryMode = basicProperties.IsDeliveryModePresent() + ? basicProperties.DeliveryMode == 2 ? MessageDeliveryMode.Persistent : + MessageDeliveryMode.NonPersistent + : null, - return properties; + ContentType = basicProperties.IsContentTypePresent() + ? basicProperties.ContentType + : null, + + ContentEncoding = basicProperties.IsContentEncodingPresent() + ? basicProperties.ContentEncoding + : null, + + Priority = basicProperties.IsPriorityPresent() + ? basicProperties.Priority + : null, + + CorrelationId = basicProperties.IsCorrelationIdPresent() + ? basicProperties.CorrelationId + : null, + + ReplyTo = basicProperties.IsReplyToPresent() + ? basicProperties.ReplyTo + : null, + + Expiration = basicProperties.IsExpirationPresent() + ? basicProperties.Expiration + : null, + + MessageId = basicProperties.IsMessageIdPresent() + ? basicProperties.MessageId + : null, + + Timestamp = basicProperties.IsTimestampPresent() + ? DateTimeOffset.FromUnixTimeMilliseconds(basicProperties.Timestamp.UnixTime).LocalDateTime + : null, + + Type = basicProperties.IsTypePresent() + ? basicProperties.Type + : null, + + UserId = basicProperties.IsUserIdPresent() + ? basicProperties.UserId + : null, + + AppId = basicProperties.IsAppIdPresent() + ? basicProperties.AppId + : null + }; } - public static IBasicProperties Convert(IDictionary properties, IBasicProperties targetProperties) + public static IBasicProperties Convert(MessageProperties properties, IBasicProperties targetProperties) { - foreach (var (key, value) in properties) - { - switch (key) - { - case RabbitMQProperties.DeliveryMode: - if (byte.TryParse(value, out var deliveryMode)) - targetProperties.DeliveryMode = deliveryMode; + if (properties.DeliveryMode != null) + targetProperties.DeliveryMode = properties.DeliveryMode == MessageDeliveryMode.Persistent ? (byte)2 : (byte)1; + else + targetProperties.ClearDeliveryMode(); - break; + if (properties.ContentType != null) + targetProperties.ContentType = properties.ContentType; + else + targetProperties.ClearContentType(); - case RabbitMQProperties.ContentType: - targetProperties.ContentType = value; - break; + if (properties.ContentEncoding != null) + targetProperties.ContentEncoding = properties.ContentEncoding; + else + targetProperties.ClearContentEncoding(); - case RabbitMQProperties.ContentEncoding: - targetProperties.ContentEncoding = value; - break; + if (properties.Priority != null) + targetProperties.Priority = properties.Priority.Value; + else + targetProperties.ClearPriority(); - case RabbitMQProperties.Priority: - if (byte.TryParse(value, out var priority)) - targetProperties.Priority = priority; - - break; + if (properties.CorrelationId != null) + targetProperties.CorrelationId = properties.CorrelationId; + else + targetProperties.ClearCorrelationId(); - case RabbitMQProperties.CorrelationId: - targetProperties.CorrelationId = value; - break; - - case RabbitMQProperties.ReplyTo: - targetProperties.ReplyTo = value; - break; + if (properties.ReplyTo != null) + targetProperties.ReplyTo = properties.ReplyTo; + else + targetProperties.ClearReplyTo(); - case RabbitMQProperties.Expiration: - targetProperties.Expiration = value; - break; + if (properties.Expiration != null) + targetProperties.Expiration = properties.Expiration; + else + targetProperties.ClearExpiration(); - case RabbitMQProperties.MessageId: - targetProperties.MessageId = value; - break; + if (properties.MessageId != null) + targetProperties.MessageId = properties.MessageId; + else + targetProperties.ClearMessageId(); - case RabbitMQProperties.Timestamp: - if (long.TryParse(value, out var timestamp)) - targetProperties.Timestamp = new AmqpTimestamp(timestamp); - - break; + if (properties.Timestamp != null) + targetProperties.Timestamp = new AmqpTimestamp(new DateTimeOffset(properties.Timestamp.Value).ToUnixTimeMilliseconds()); + else + targetProperties.ClearTimestamp(); - case RabbitMQProperties.Type: - targetProperties.Type = value; - break; + if (properties.Type != null) + targetProperties.Type = properties.Type; + else + targetProperties.ClearType(); - case RabbitMQProperties.UserId: - targetProperties.UserId = value; - break; + if (properties.UserId != null) + targetProperties.UserId = properties.UserId; + else + targetProperties.ClearUserId(); - case RabbitMQProperties.AppId: - targetProperties.AppId = value; - break; + if (properties.AppId != null) + targetProperties.AppId = properties.AppId; + else + targetProperties.ClearAppId(); - case RabbitMQProperties.ClusterId: - targetProperties.ClusterId = value; - break; - - default: - targetProperties.Headers ??= new Dictionary(); - targetProperties.Headers.Add(key, Encoding.UTF8.GetBytes(value)); - break; - } - } + if (properties.Headers.Count > 0) + targetProperties.Headers = properties.Headers.ToDictionary(p => p.Key, p => (object)Encoding.UTF8.GetBytes(p.Value)); + else + targetProperties.ClearHeaders(); return targetProperties; } diff --git a/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs b/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs index 7f7dd26..bc07d37 100644 --- a/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs +++ b/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs @@ -63,7 +63,7 @@ namespace PettingZoo.RabbitMQ private void ClientReceived(object? sender, BasicDeliverEventArgs args) { MessageReceived?.Invoke(this, new MessageReceivedEventArgs( - new MessageInfo( + new ReceivedMessageInfo( args.Exchange, args.RoutingKey, args.Body.ToArray(), diff --git a/PettingZoo.RabbitMQ/RabbitMQProperties.cs b/PettingZoo.RabbitMQ/RabbitMQProperties.cs deleted file mode 100644 index 5cab9d6..0000000 --- a/PettingZoo.RabbitMQ/RabbitMQProperties.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace PettingZoo.RabbitMQ -{ - public static class RabbitMQProperties - { - public const string ContentType = "content-type"; - public const string ContentEncoding = "content-encoding"; - public const string DeliveryMode = "delivery-mode"; - public const string Priority = "priority"; - public const string CorrelationId = "correlation-id"; - public const string ReplyTo = "reply-to"; - public const string Expiration = "expiration"; - public const string MessageId = "message-id"; - public const string Timestamp = "timestamp"; - public const string Type = "type"; - public const string UserId = "user-id"; - public const string AppId = "app-id"; - public const string ClusterId = "cluster-id"; - } -} diff --git a/PettingZoo.RabbitMQ/RabbitMQPropertiesExtensions.cs b/PettingZoo.RabbitMQ/RabbitMQPropertiesExtensions.cs deleted file mode 100644 index 41cd78f..0000000 --- a/PettingZoo.RabbitMQ/RabbitMQPropertiesExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace PettingZoo.RabbitMQ -{ - public static class RabbitMQPropertiesExtensions - { - public static string ContentType(this IDictionary properties) - { - return properties.TryGetValue(RabbitMQProperties.ContentType, out var value) - ? value - : ""; - } - } -} diff --git a/PettingZoo/PettingZoo.csproj b/PettingZoo/PettingZoo.csproj index 20c88b9..cd97af0 100644 --- a/PettingZoo/PettingZoo.csproj +++ b/PettingZoo/PettingZoo.csproj @@ -56,6 +56,11 @@ True True + + RawPublisherViewStrings.resx + True + True + PublisherViewStrings.resx True @@ -81,6 +86,10 @@ SubscribeWindowStrings.Designer.cs PublicResXFileCodeGenerator + + RawPublisherViewStrings.Designer.cs + PublicResXFileCodeGenerator + PublisherViewStrings.Designer.cs PublicResXFileCodeGenerator diff --git a/PettingZoo/Program.cs b/PettingZoo/Program.cs index 60752d2..2940ce7 100644 --- a/PettingZoo/Program.cs +++ b/PettingZoo/Program.cs @@ -12,7 +12,6 @@ using PettingZoo.UI.Connection; using PettingZoo.UI.Main; using PettingZoo.UI.Subscribe; using PettingZoo.UI.Tab; -using PettingZoo.UI.Tab.Subscriber; using SimpleInjector; namespace PettingZoo diff --git a/PettingZoo/Style.xaml b/PettingZoo/Style.xaml index bc8459b..5f5e226 100644 --- a/PettingZoo/Style.xaml +++ b/PettingZoo/Style.xaml @@ -10,6 +10,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/PettingZoo/UI/Main/MainWindow.xaml b/PettingZoo/UI/Main/MainWindow.xaml index 566aa8d..6194a98 100644 --- a/PettingZoo/UI/Main/MainWindow.xaml +++ b/PettingZoo/UI/Main/MainWindow.xaml @@ -9,7 +9,7 @@ mc:Ignorable="d" d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}" Width="800" - Height="600" + Height="800" ResizeMode="CanResizeWithGrip" Style="{StaticResource WindowStyle}" Title="{x:Static main:MainWindowStrings.WindowTitle}" diff --git a/PettingZoo/UI/Main/MainWindow.xaml.cs b/PettingZoo/UI/Main/MainWindow.xaml.cs index c41d85e..2f033ff 100644 --- a/PettingZoo/UI/Main/MainWindow.xaml.cs +++ b/PettingZoo/UI/Main/MainWindow.xaml.cs @@ -37,7 +37,8 @@ namespace PettingZoo.UI.Main private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { - viewModel.ConnectCommand.Execute(null); + // TODO support command-line parameters for easier testing + //viewModel.ConnectCommand.Execute(null); } diff --git a/PettingZoo/UI/Main/MainWindowViewModel.cs b/PettingZoo/UI/Main/MainWindowViewModel.cs index a1b594e..ad71060 100644 --- a/PettingZoo/UI/Main/MainWindowViewModel.cs +++ b/PettingZoo/UI/Main/MainWindowViewModel.cs @@ -93,8 +93,11 @@ namespace PettingZoo.UI.Main private async void ConnectExecute() { - //var newParams = connectionDialog.Show(connectionDialogParams); - var newParams = new ConnectionDialogParams("localhost", "/", 5672, "guest", "guest", true, "lef", "#"); + var newParams = connectionDialog.Show(connectionDialogParams); + + // TODO support command-line parameters for easier testing + // var newParams = new ConnectionDialogParams("localhost", "/", 5672, "guest", "guest", true, "test", "#"); + if (newParams == null) return; diff --git a/PettingZoo/UI/Tab/Publisher/PublisherView.xaml b/PettingZoo/UI/Tab/Publisher/PublisherView.xaml index 7167b3e..accf7fb 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherView.xaml +++ b/PettingZoo/UI/Tab/Publisher/PublisherView.xaml @@ -9,13 +9,20 @@ d:DesignWidth="800" d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}" Background="White"> - - - + + + + + + + + - TODO: implement publish forms - + + + + diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs index a3542e2..3bb6e75 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Windows.Controls; using System.Windows.Input; using PettingZoo.Core.Connection; @@ -16,6 +18,11 @@ namespace PettingZoo.UI.Tab.Publisher private readonly IConnection connection; private MessageType messageType; + private UserControl? messageTypeControl; + private ICommand? messageTypePublishCommand; + + private UserControl? rawPublisherView; + private UserControl? tapetiPublisherView; private readonly DelegateCommand publishCommand; private readonly TabToolbarCommand[] toolbarCommands; @@ -24,15 +31,20 @@ namespace PettingZoo.UI.Tab.Publisher public MessageType MessageType { get => messageType; - set => SetField(ref messageType, value, - otherPropertiesChanged: new[] + set + { + if (SetField(ref messageType, value, + otherPropertiesChanged: new[] + { + nameof(MessageTypeRaw), + nameof(MessageTypeTapeti) + })) { - nameof(MessageTypeRaw), - nameof(MessageTypeTapeti) - }); - + SetMessageTypeControl(value); + } + } } - + public bool MessageTypeRaw { get => MessageType == MessageType.Raw; @@ -46,6 +58,13 @@ namespace PettingZoo.UI.Tab.Publisher } + public UserControl? MessageTypeControl + { + get => messageTypeControl; + set => SetField(ref messageTypeControl, value); + } + + public ICommand PublishCommand => publishCommand; @@ -64,19 +83,49 @@ namespace PettingZoo.UI.Tab.Publisher { new TabToolbarCommand(PublishCommand, PublisherViewStrings.CommandPublish, SvgIconHelper.LoadFromResource("/Images/PublishSend.svg")) }; + + SetMessageTypeControl(MessageType.Raw); } private void PublishExecute() { - // TODO + messageTypePublishCommand?.Execute(null); } private bool PublishCanExecute() { - // TODO validate input - return true; + return messageTypePublishCommand?.CanExecute(null) ?? false; + } + + + private void SetMessageTypeControl(MessageType value) + { + switch (value) + { + case MessageType.Raw: + var rawPublisherViewModel = new RawPublisherViewModel(connection); + rawPublisherView ??= new RawPublisherView(rawPublisherViewModel); + MessageTypeControl = rawPublisherView; + + messageTypePublishCommand = rawPublisherViewModel.PublishCommand; + publishCommand.RaiseCanExecuteChanged(); + break; + + case MessageType.Tapeti: + // TODO + var tapetiPublisherViewModel = new RawPublisherViewModel(connection); + tapetiPublisherView ??= new RawPublisherView(tapetiPublisherViewModel); + MessageTypeControl = tapetiPublisherView; + + messageTypePublishCommand = tapetiPublisherViewModel.PublishCommand; + publishCommand.RaiseCanExecuteChanged(); + break; + + default: + throw new ArgumentException(); + } } } diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml b/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml new file mode 100644 index 0000000..fa939b0 --- /dev/null +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml.cs b/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml.cs new file mode 100644 index 0000000..2b8fab1 --- /dev/null +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherView.xaml.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Threading; + +namespace PettingZoo.UI.Tab.Publisher +{ + /// + /// Interaction logic for RawPublisherView.xaml + /// + public partial class RawPublisherView + { + private RawPublisherViewModel viewModel; + private DispatcherTimer checkEmptyHeaderTimer; + + + public RawPublisherView(RawPublisherViewModel viewModel) + { + this.viewModel = viewModel; + + InitializeComponent(); + DataContext = viewModel; + + checkEmptyHeaderTimer = new DispatcherTimer(); + checkEmptyHeaderTimer.Tick += CheckEmptyHeaderTimerOnTick; + checkEmptyHeaderTimer.Interval = TimeSpan.FromMilliseconds(50); + } + + private void Header_OnLostFocus(object sender, RoutedEventArgs e) + { + var dataContext = (sender as FrameworkElement)?.DataContext; + if (dataContext is not RawPublisherViewModel.Header header) + return; + + if (!header.IsEmpty()) + return; + + // At this point the focused element is null, so we need to check again in a bit. This will prevent + // the header line from being removed when jumping between empty key and value textboxes + checkEmptyHeaderTimer.Stop(); + checkEmptyHeaderTimer.Start(); + } + + + private void CheckEmptyHeaderTimerOnTick(object? sender, EventArgs e) + { + checkEmptyHeaderTimer.Stop(); + + RawPublisherViewModel.Header? focusedHeader = null; + + var focusedControl = Keyboard.FocusedElement; + if (focusedControl is FrameworkElement { DataContext: RawPublisherViewModel.Header header }) + focusedHeader = header; + + var emptyheaders = viewModel.Headers + .Take(viewModel.Headers.Count - 1) + .Where(h => h != focusedHeader && h.IsEmpty()) + .ToArray(); + + foreach (var emptyHeader in emptyheaders) + viewModel.Headers.Remove(emptyHeader); + } + } +} diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs new file mode 100644 index 0000000..d5d8cd3 --- /dev/null +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Input; +using PettingZoo.Core.Connection; + +namespace PettingZoo.UI.Tab.Publisher +{ + public class RawPublisherViewModel : BaseViewModel + { + private readonly IConnection connection; + private readonly DelegateCommand publishCommand; + private readonly DelegateCommand propertiesExpandCollapseCommand; + private bool propertiesExpanded; + + private bool sendToExchange = true; + private string exchange = ""; + private string routingKey = ""; + private string queue = ""; + + private MessageDeliveryMode deliveryMode; + + private string contentType = "application/json"; + private string correlationId = ""; + private string replyTo = ""; + private string appId = ""; + private string contentEncoding = ""; + private string expiration = ""; + private string messageId = ""; + private string priority = ""; + private string timestamp = ""; + private string typeProperty = ""; + private string userId = ""; + private string payload = ""; + + + public bool SendToExchange + { + get => sendToExchange; + set => SetField(ref sendToExchange, value, otherPropertiesChanged: new[] { nameof(SendToQueue), nameof(ExchangeVisibility), nameof(QueueVisibility) }); + } + + + public bool SendToQueue + { + get => !SendToExchange; + set => SendToExchange = !value; + } + + + public string Exchange + { + get => exchange; + set => SetField(ref exchange, value); + } + + + public string RoutingKey + { + get => routingKey; + set => SetField(ref routingKey, value); + } + + + public string Queue + { + get => queue; + set => SetField(ref queue, value); + } + + + public virtual Visibility ExchangeVisibility => SendToExchange ? Visibility.Visible : Visibility.Collapsed; + public virtual Visibility QueueVisibility => SendToQueue ? Visibility.Visible : Visibility.Collapsed; + + + public int DeliveryModeIndex + { + get => deliveryMode == MessageDeliveryMode.Persistent ? 1 : 0; + set => SetField(ref deliveryMode, value == 1 ? MessageDeliveryMode.Persistent : MessageDeliveryMode.NonPersistent); + } + + + public string ContentType + { + get => contentType; + set => SetField(ref contentType, value); + } + + + public string CorrelationId + { + get => correlationId; + set => SetField(ref correlationId, value); + } + + + public string ReplyTo + { + get => replyTo; + set => SetField(ref replyTo, value); + } + + + public string AppId + { + get => appId; + set => SetField(ref appId, value); + } + + + public string ContentEncoding + { + get => contentEncoding; + set => SetField(ref contentEncoding, value); + } + + + public string Expiration + { + get => expiration; + set => SetField(ref expiration, value); + } + + + public string MessageId + { + get => messageId; + set => SetField(ref messageId, value); + } + + + public string Priority + { + get => priority; + set => SetField(ref priority, value); + } + + + public string Timestamp + { + get => timestamp; + set => SetField(ref timestamp, value); + } + + + public string TypeProperty + { + get => typeProperty; + set => SetField(ref typeProperty, value); + } + + + public string UserId + { + get => userId; + set => SetField(ref userId, value); + } + + + public string Payload + { + get => payload; + set => SetField(ref payload, value); + } + + + public ObservableCollection
Headers { get; } = new(); + + + public ICommand PublishCommand => publishCommand; + public ICommand PropertiesExpandCollapseCommand => propertiesExpandCollapseCommand; + + + public bool PropertiesExpanded + { + get => propertiesExpanded; + set => SetField(ref propertiesExpanded, value, otherPropertiesChanged: new[] + { + nameof(PropertiesExpandedVisibility), + nameof(PropertiesExpandedCollapsedText) + }); + } + + public Visibility PropertiesExpandedVisibility => propertiesExpanded ? Visibility.Visible : Visibility.Collapsed; + public string PropertiesExpandedCollapsedText => propertiesExpanded + ? RawPublisherViewStrings.PropertiesCollapse + : RawPublisherViewStrings.PropertiesExpand; + + + protected Header lastHeader; + + + public RawPublisherViewModel(IConnection connection) + { + this.connection = connection; + + publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute); + propertiesExpandCollapseCommand = new DelegateCommand(PropertiesExpandCollapseExecute); + + AddHeader(); + } + + + private void LastHeaderChanged(object? sender, PropertyChangedEventArgs e) + { + lastHeader.PropertyChanged -= LastHeaderChanged; + AddHeader(); + } + + + [MemberNotNull(nameof(lastHeader))] + private void AddHeader() + { + lastHeader = new Header(); + lastHeader.PropertyChanged += LastHeaderChanged; + Headers.Add(lastHeader); + } + + + private void PropertiesExpandCollapseExecute() + { + PropertiesExpanded = !PropertiesExpanded; + } + + + private void PublishExecute() + { + static string? NullIfEmpty(string? value) + { + return string.IsNullOrEmpty(value) ? null : value; + } + + // TODO check parsing of priority and timestamp + // TODO support for Reply To to dynamic queue which waits for a message (or opens a new subscriber tab?) + + var headers = Headers.Where(h => h.IsValid()).ToDictionary(h => h.Key, h => h.Value); + + // TODO background worker / async + + connection.Publish(new PublishMessageInfo( + SendToExchange ? Exchange : "", + SendToExchange ? RoutingKey : Queue, + Encoding.UTF8.GetBytes(Payload), + new MessageProperties(headers) + { + AppId = NullIfEmpty(AppId), + ContentEncoding = NullIfEmpty(ContentEncoding), + ContentType = NullIfEmpty(ContentType), + CorrelationId = NullIfEmpty(CorrelationId), + DeliveryMode = deliveryMode, + Expiration = NullIfEmpty(Expiration), + MessageId = NullIfEmpty(MessageId), + Priority = !string.IsNullOrEmpty(Priority) && byte.TryParse(Priority, out var priorityValue) ? priorityValue : null, + ReplyTo = NullIfEmpty(ReplyTo), + Timestamp = !string.IsNullOrEmpty(Timestamp) && DateTime.TryParse(Timestamp, out var timestampValue) ? timestampValue : null, + Type = NullIfEmpty(TypeProperty), + UserId = NullIfEmpty(UserId) + })); + } + + + private bool PublishCanExecute() + { + // TODO validate input + return true; + } + + + public class Header : BaseViewModel + { + private string key = ""; + private string value = ""; + + + public string Key + { + get => key; + set => SetField(ref key, value); + } + + + public string Value + { + get => value; + set => SetField(ref this.value, value); + } + + + public bool IsEmpty() + { + return string.IsNullOrEmpty(Key) && string.IsNullOrEmpty(Value); + } + + + public bool IsValid() + { + return !string.IsNullOrEmpty(Key) && !string.IsNullOrEmpty(Value); + } + } + } + + + public class DesignTimeRawPublisherViewModel : RawPublisherViewModel + { + public DesignTimeRawPublisherViewModel() : base(null!) + { + PropertiesExpanded = true; + + var capturedLastHeader = lastHeader; + capturedLastHeader.Key = "Example"; + capturedLastHeader.Value = "header"; + } + + + public override Visibility ExchangeVisibility => Visibility.Visible; + public override Visibility QueueVisibility => Visibility.Visible; + } +} diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.Designer.cs b/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.Designer.cs new file mode 100644 index 0000000..3d74355 --- /dev/null +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.Designer.cs @@ -0,0 +1,297 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PettingZoo.UI.Tab.Publisher { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class RawPublisherViewStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal RawPublisherViewStrings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Tab.Publisher.RawPublisherViewStrings", typeof(RawPublisherViewStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Transient (non-persistent). + /// + public static string DeliveryModeNonPersistent { + get { + return ResourceManager.GetString("DeliveryModeNonPersistent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Persistent. + /// + public static string DeliveryModePersistent { + get { + return ResourceManager.GetString("DeliveryModePersistent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string HeaderName { + get { + return ResourceManager.GetString("HeaderName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value. + /// + public static string HeaderValue { + get { + return ResourceManager.GetString("HeaderValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to App ID. + /// + public static string LabelAppId { + get { + return ResourceManager.GetString("LabelAppId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content encoding. + /// + public static string LabelContentEncoding { + get { + return ResourceManager.GetString("LabelContentEncoding", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content type. + /// + public static string LabelContentType { + get { + return ResourceManager.GetString("LabelContentType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Correlation ID. + /// + public static string LabelCorrelationId { + get { + return ResourceManager.GetString("LabelCorrelationId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery mode. + /// + public static string LabelDeliveryMode { + get { + return ResourceManager.GetString("LabelDeliveryMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exchange. + /// + public static string LabelExchange { + get { + return ResourceManager.GetString("LabelExchange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expiration. + /// + public static string LabelExpiration { + get { + return ResourceManager.GetString("LabelExpiration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Headers. + /// + public static string LabelHeaders { + get { + return ResourceManager.GetString("LabelHeaders", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message ID. + /// + public static string LabelMessageId { + get { + return ResourceManager.GetString("LabelMessageId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Payload. + /// + public static string LabelPayload { + get { + return ResourceManager.GetString("LabelPayload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Priority. + /// + public static string LabelPriority { + get { + return ResourceManager.GetString("LabelPriority", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Properties. + /// + public static string LabelProperties { + get { + return ResourceManager.GetString("LabelProperties", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Queue. + /// + public static string LabelQueue { + get { + return ResourceManager.GetString("LabelQueue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reply To. + /// + public static string LabelReplyTo { + get { + return ResourceManager.GetString("LabelReplyTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Routing key. + /// + public static string LabelRoutingKey { + get { + return ResourceManager.GetString("LabelRoutingKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Publish to exchange (topic). + /// + public static string LabelSendToExchange { + get { + return ResourceManager.GetString("LabelSendToExchange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Publish to queue (direct). + /// + public static string LabelSendToQueue { + get { + return ResourceManager.GetString("LabelSendToQueue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timestamp. + /// + public static string LabelTimestamp { + get { + return ResourceManager.GetString("LabelTimestamp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string LabelType { + get { + return ResourceManager.GetString("LabelType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User ID. + /// + public static string LabelUserId { + get { + return ResourceManager.GetString("LabelUserId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ⏶ Collapse. + /// + public static string PropertiesCollapse { + get { + return ResourceManager.GetString("PropertiesCollapse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ⏷ Expand. + /// + public static string PropertiesExpand { + get { + return ResourceManager.GetString("PropertiesExpand", resourceCulture); + } + } + } +} diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.resx b/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.resx new file mode 100644 index 0000000..4f8be42 --- /dev/null +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherViewStrings.resx @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Transient (non-persistent) + + + Persistent + + + Name + + + Value + + + App ID + + + Content encoding + + + Content type + + + Correlation ID + + + Delivery mode + + + Exchange + + + Expiration + + + Headers + + + Message ID + + + Payload + + + Priority + + + Properties + + + Queue + + + Reply To + + + Routing key + + + Publish to exchange (topic) + + + Publish to queue (direct) + + + Timestamp + + + Type + + + User ID + + + ⏶ Collapse + + + ⏷ Expand + + \ No newline at end of file diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml index c619885..7da2b71 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml @@ -28,8 +28,8 @@ - - + + diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs b/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs index 1f695ba..64cc767 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using System.Windows.Input; using PettingZoo.Core.Connection; using PettingZoo.Core.Rendering; -using PettingZoo.RabbitMQ; // TODO update title with unread message count if tab is not active @@ -16,36 +15,36 @@ namespace PettingZoo.UI.Tab.Subscriber { private readonly ISubscriber subscriber; private readonly TaskScheduler uiScheduler; - private MessageInfo? selectedMessage; + private ReceivedMessageInfo? selectedMessage; private readonly DelegateCommand clearCommand; private readonly TabToolbarCommand[] toolbarCommands; + private IDictionary? selectedMessageProperties; public ICommand ClearCommand => clearCommand; - public ObservableCollection Messages { get; } + public ObservableCollection Messages { get; } - public MessageInfo? SelectedMessage + public ReceivedMessageInfo? SelectedMessage { get => selectedMessage; set { - if (value == selectedMessage) - return; - - selectedMessage = value; - RaisePropertyChanged(); - RaiseOtherPropertyChanged(nameof(SelectedMessageBody)); - RaiseOtherPropertyChanged(nameof(SelectedMessageProperties)); + if (SetField(ref selectedMessage, value, otherPropertiesChanged: new[] { nameof(SelectedMessageBody) })) + UpdateSelectedMessageProperties(); } } public string SelectedMessageBody => SelectedMessage != null - ? MessageBodyRenderer.Render(SelectedMessage.Body, SelectedMessage.Properties.ContentType()) + ? MessageBodyRenderer.Render(SelectedMessage.Body, SelectedMessage.Properties.ContentType) : ""; - public IDictionary? SelectedMessageProperties => SelectedMessage?.Properties; + public IDictionary? SelectedMessageProperties + { + get => selectedMessageProperties; + set => SetField(ref selectedMessageProperties, value); + } public string Title => $"{subscriber.Exchange} - {subscriber.RoutingKey}"; public IEnumerable ToolbarCommands => toolbarCommands; @@ -57,7 +56,7 @@ namespace PettingZoo.UI.Tab.Subscriber uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); - Messages = new ObservableCollection(); + Messages = new ObservableCollection(); clearCommand = new DelegateCommand(ClearExecute, ClearCanExecute); toolbarCommands = new[] @@ -93,11 +92,18 @@ namespace PettingZoo.UI.Tab.Subscriber } + private void UpdateSelectedMessageProperties() + { + SelectedMessageProperties = SelectedMessage != null + ? MessagePropertiesRenderer.Render(SelectedMessage.Properties) + : null; + } + + private void RunFromUiScheduler(Action action) { _ = Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, uiScheduler); } - } @@ -116,9 +122,9 @@ namespace PettingZoo.UI.Tab.Subscriber } - public string Exchange { get; } = "dummy"; - public string RoutingKey { get; } = "dummy"; - + public string Exchange => "dummy"; + public string RoutingKey => "dummy"; + #pragma warning disable CS0067 public event EventHandler? MessageReceived; #pragma warning restore CS0067