diff --git a/Source/EasyNetQ.Management.Client.ApprovalTests/EasyNetQ.Management.Client.approved.txt b/Source/EasyNetQ.Management.Client.ApprovalTests/EasyNetQ.Management.Client.approved.txt index 9878a6cd..2b9de93a 100644 --- a/Source/EasyNetQ.Management.Client.ApprovalTests/EasyNetQ.Management.Client.approved.txt +++ b/Source/EasyNetQ.Management.Client.ApprovalTests/EasyNetQ.Management.Client.approved.txt @@ -545,6 +545,13 @@ namespace EasyNetQ.Management.Client.Model public int Credit { get; init; } public bool Drain { get; init; } } + public enum DeadLetterStrategy + { + [System.Runtime.Serialization.EnumMember(Value="at-most-once")] + AtMostOnce = 0, + [System.Runtime.Serialization.EnumMember(Value="at-least-once")] + AtLeastOnce = 1, + } public class Definitions : System.IEquatable { public Definitions(string RabbitVersion, System.Collections.Generic.IReadOnlyList Users, System.Collections.Generic.IReadOnlyList Vhosts, System.Collections.Generic.IReadOnlyList Permissions, System.Collections.Generic.IReadOnlyList Queues, System.Collections.Generic.IReadOnlyList Exchanges, System.Collections.Generic.IReadOnlyList Bindings) { } @@ -625,6 +632,13 @@ namespace EasyNetQ.Management.Client.Model public long? ExactlyCount { get; init; } public System.Collections.Generic.IReadOnlyList? Nodes { get; init; } } + public enum HaPromote + { + [System.Runtime.Serialization.EnumMember(Value="when-synced")] + WhenSynced = 0, + [System.Runtime.Serialization.EnumMember(Value="always")] + Always = 1, + } public enum HaSyncMode { Manual = 0, @@ -813,6 +827,15 @@ namespace EasyNetQ.Management.Client.Model public int Exchanges { get; init; } public int Queues { get; init; } } + public enum Overflow + { + [System.Runtime.Serialization.EnumMember(Value="drop-head")] + DropHead = 0, + [System.Runtime.Serialization.EnumMember(Value="reject-publish")] + RejectPublish = 1, + [System.Runtime.Serialization.EnumMember(Value="reject-publish-dlx")] + RejectPublishDlx = 2, + } public class Overview : System.IEquatable { public Overview(string ManagementVersion, System.Collections.Generic.IReadOnlyList ExchangeTypes, string RabbitmqVersion, string ErlangVersion, EasyNetQ.Management.Client.Model.MessageStats MessageStats, EasyNetQ.Management.Client.Model.QueueTotals QueueTotals, EasyNetQ.Management.Client.Model.ObjectTotals ObjectTotals, string Node, System.Collections.Generic.IReadOnlyList Listeners, System.Collections.Generic.IReadOnlyList Contexts) { } @@ -931,13 +954,45 @@ namespace EasyNetQ.Management.Client.Model } public class PolicyDefinition : System.IEquatable { - public PolicyDefinition(EasyNetQ.Management.Client.Model.HaMode? HaMode = default, EasyNetQ.Management.Client.Model.HaParams? HaParams = null, EasyNetQ.Management.Client.Model.HaSyncMode? HaSyncMode = default, int? HaSyncBatchSize = default, string? FederationUpstream = null, string? FederationUpstreamSet = null, string? AlternateExchange = null, string? DeadLetterExchange = null, string? DeadLetterRoutingKey = null, string? QueueMode = null, uint? MessageTtl = default, uint? Expires = default, uint? MaxLength = default) { } + public PolicyDefinition( + uint? MaxLength = default, + long? MaxLengthBytes = default, + EasyNetQ.Management.Client.Model.Overflow? Overflow = default, + uint? Expires = default, + string? DeadLetterExchange = null, + string? DeadLetterRoutingKey = null, + uint? MessageTtl = default, + uint? ConsumerTimeout = default, + EasyNetQ.Management.Client.Model.HaMode? HaMode = default, + EasyNetQ.Management.Client.Model.HaParams? HaParams = null, + EasyNetQ.Management.Client.Model.HaSyncMode? HaSyncMode = default, + int? HaSyncBatchSize = default, + EasyNetQ.Management.Client.Model.HaPromote? HaPromoteOnShutdown = default, + EasyNetQ.Management.Client.Model.HaPromote? HaPromoteOnFailure = default, + EasyNetQ.Management.Client.Model.QueueVersion? QueueVersion = default, + EasyNetQ.Management.Client.Model.QueueLocator? QueueMasterLocator = default, + uint? DeliveryLimit = default, + EasyNetQ.Management.Client.Model.DeadLetterStrategy? DeadLetterStrategy = default, + EasyNetQ.Management.Client.Model.QueueLocator? QueueLeaderLocator = default, + string? MaxAge = null, + uint? StreamMaxSegmentSizeBytes = default, + string? AlternateExchange = null, + string? FederationUpstream = null, + string? FederationUpstreamSet = null, + string? QueueMode = null) { } [System.Text.Json.Serialization.JsonPropertyName("alternate-exchange")] public string? AlternateExchange { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("consumer-timeout")] + public uint? ConsumerTimeout { get; init; } [System.Text.Json.Serialization.JsonPropertyName("dead-letter-exchange")] public string? DeadLetterExchange { get; init; } [System.Text.Json.Serialization.JsonPropertyName("dead-letter-routing-key")] public string? DeadLetterRoutingKey { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("dead-letter-strategy")] + public EasyNetQ.Management.Client.Model.DeadLetterStrategy? DeadLetterStrategy { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("delivery-limit")] + public uint? DeliveryLimit { get; init; } [System.Text.Json.Serialization.JsonPropertyName("expires")] public uint? Expires { get; init; } [System.Text.Json.Serialization.JsonPropertyName("federation-upstream-set")] @@ -948,16 +1003,40 @@ namespace EasyNetQ.Management.Client.Model public EasyNetQ.Management.Client.Model.HaMode? HaMode { get; init; } [System.Text.Json.Serialization.JsonPropertyName("ha-params")] public EasyNetQ.Management.Client.Model.HaParams? HaParams { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("ha-promote-on-failure")] + public EasyNetQ.Management.Client.Model.HaPromote? HaPromoteOnFailure { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("ha-promote-on-shutdown")] + public EasyNetQ.Management.Client.Model.HaPromote? HaPromoteOnShutdown { get; init; } [System.Text.Json.Serialization.JsonPropertyName("ha-sync-batch-size")] public int? HaSyncBatchSize { get; init; } [System.Text.Json.Serialization.JsonPropertyName("ha-sync-mode")] public EasyNetQ.Management.Client.Model.HaSyncMode? HaSyncMode { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("max-age")] + public string? MaxAge { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("max-length-bytes")] + public long? MaxLengthBytes { get; init; } [System.Text.Json.Serialization.JsonPropertyName("max-length")] public uint? MaxLength { get; init; } [System.Text.Json.Serialization.JsonPropertyName("message-ttl")] public uint? MessageTtl { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("overflow")] + public EasyNetQ.Management.Client.Model.Overflow? Overflow { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("queue-leader-locator")] + public EasyNetQ.Management.Client.Model.QueueLocator? QueueLeaderLocator { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(EasyNetQ.Management.Client.Serialization.JsonStringEnumConverterEx?))] + [System.Text.Json.Serialization.JsonPropertyName("queue-master-locator")] + public EasyNetQ.Management.Client.Model.QueueLocator? QueueMasterLocator { get; init; } [System.Text.Json.Serialization.JsonPropertyName("queue-mode")] public string? QueueMode { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonNumberEnumConverter?))] + [System.Text.Json.Serialization.JsonPropertyName("queue-version")] + public EasyNetQ.Management.Client.Model.QueueVersion? QueueVersion { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("stream-max-segment-size-bytes")] + public uint? StreamMaxSegmentSizeBytes { get; init; } } public class PolicyInfo : System.IEquatable { @@ -1047,6 +1126,13 @@ namespace EasyNetQ.Management.Client.Model public bool Durable { get; init; } public string Name { get; init; } } + public enum QueueLocator + { + [System.Runtime.Serialization.EnumMember(Value="client-local")] + ClientLocal = 0, + [System.Runtime.Serialization.EnumMember(Value="balanced")] + Balanced = 1, + } public class QueueName : System.IEquatable { public QueueName(string Name, string Vhost) { } @@ -1063,6 +1149,11 @@ namespace EasyNetQ.Management.Client.Model public long MessagesUnacknowledged { get; init; } public long Messages { get; init; } } + public enum QueueVersion + { + V1 = 1, + V2 = 2, + } public class RatesCriteria : System.IEquatable { public RatesCriteria(int MsgRatesAge, int MsgRatesIncr) { } diff --git a/Source/EasyNetQ.Management.Client.IntegrationTests/ManagementClientTests.cs b/Source/EasyNetQ.Management.Client.IntegrationTests/ManagementClientTests.cs index 98333fbb..92fee6d4 100644 --- a/Source/EasyNetQ.Management.Client.IntegrationTests/ManagementClientTests.cs +++ b/Source/EasyNetQ.Management.Client.IntegrationTests/ManagementClientTests.cs @@ -1,4 +1,4 @@ -using EasyNetQ.Management.Client.Model; +using EasyNetQ.Management.Client.Model; namespace EasyNetQ.Management.Client.IntegrationTests; @@ -216,12 +216,24 @@ public async Task Should_be_able_to_create_all_the_definitions_in_a_policy() const int priority = 999; const HaMode haMode = HaMode.All; const HaSyncMode haSyncMode = HaSyncMode.Automatic; + const HaPromote haPromoteOnFailure = HaPromote.Always; + const HaPromote haPromoteOnShutdown = HaPromote.WhenSynced; + QueueVersion queueVersion = fixture.RabbitmqVersion >= new Version("3.12") ? QueueVersion.V2 : QueueVersion.V1; + const QueueLocator queueMasterLocator = QueueLocator.ClientLocal; + const uint deliveryLimit = 3; + const Model.DeadLetterStrategy deadLetterStrategy = Model.DeadLetterStrategy.AtLeastOnce; + const QueueLocator queueLeaderLocator = QueueLocator.Balanced; + const string maxAge = "1h"; + const uint streamMaxSegmentSizeBytes = 50000; const string alternateExchange = "a-sample-alternate-exchange"; const string deadLetterExchange = "a-sample-dead-letter-exchange"; const string deadLetterRoutingKey = "a-sample-dead-letter-exchange-key"; const uint messageTtl = 5000; const uint expires = 10000; const uint maxLength = 500; + const long maxLengthBytes = 5000; + const Overflow overflow = Overflow.RejectPublish; + uint? consumerTimeout = fixture.RabbitmqVersion >= new Version("3.12") ? 3600000 : null; await fixture.ManagementClient.CreatePolicyAsync( new Policy( @@ -231,12 +243,28 @@ await fixture.ManagementClient.CreatePolicyAsync( Definition: new PolicyDefinition( HaMode: haMode, HaSyncMode: haSyncMode, + HaPromoteOnFailure: haPromoteOnFailure, + HaPromoteOnShutdown: haPromoteOnShutdown, + QueueVersion: queueVersion, + QueueMasterLocator: queueMasterLocator, + + DeliveryLimit: deliveryLimit, + DeadLetterStrategy: deadLetterStrategy, + QueueLeaderLocator: queueLeaderLocator, + + MaxAge: maxAge, + StreamMaxSegmentSizeBytes: streamMaxSegmentSizeBytes, + AlternateExchange: alternateExchange, + DeadLetterExchange: deadLetterExchange, DeadLetterRoutingKey: deadLetterRoutingKey, MessageTtl: messageTtl, Expires: expires, - MaxLength: maxLength + MaxLength: maxLength, + MaxLengthBytes: maxLengthBytes, + Overflow: overflow, + ConsumerTimeout: consumerTimeout ), Priority: priority ) @@ -247,12 +275,24 @@ await fixture.ManagementClient.CreatePolicyAsync( && p.Priority == priority && p.Definition.HaMode == haMode && p.Definition.HaSyncMode == haSyncMode + && p.Definition.HaPromoteOnFailure == haPromoteOnFailure + && p.Definition.HaPromoteOnShutdown == haPromoteOnShutdown + && p.Definition.QueueVersion == queueVersion + && p.Definition.QueueMasterLocator == queueMasterLocator + && p.Definition.DeliveryLimit == deliveryLimit + && p.Definition.DeadLetterStrategy == deadLetterStrategy + && p.Definition.QueueLeaderLocator == queueLeaderLocator + && p.Definition.MaxAge == maxAge + && p.Definition.StreamMaxSegmentSizeBytes == streamMaxSegmentSizeBytes && p.Definition.AlternateExchange == alternateExchange && p.Definition.DeadLetterExchange == deadLetterExchange && p.Definition.DeadLetterRoutingKey == deadLetterRoutingKey && p.Definition.MessageTtl == messageTtl && p.Definition.Expires == expires - && p.Definition.MaxLength == maxLength) + && p.Definition.MaxLength == maxLength + && p.Definition.MaxLengthBytes == maxLengthBytes + && p.Definition.Overflow == overflow + && p.Definition.ConsumerTimeout == consumerTimeout) ); } @@ -437,6 +477,55 @@ await fixture.ManagementClient.CreatePolicyAsync( ); } + [Fact] + public async Task Should_be_able_to_create_max_length_bytes_policy() + { + const string policyName = "a-sample-max-length-bytes"; + const uint maxLengthBytes = 500; + await fixture.ManagementClient.CreatePolicyAsync( + new Policy( + Name: policyName, + Pattern: "averyuncommonpattern", + Vhost: Vhost.Name, + Definition: new PolicyDefinition( + MaxLengthBytes: maxLengthBytes + ) + ) + ); + Assert.Equal(1, (await fixture.ManagementClient.GetPoliciesAsync()).Count( + p => p.Name == policyName + && p.Vhost == Vhost.Name + && p.Definition.MaxLengthBytes == maxLengthBytes) + ); + } + + [Fact] + public async Task Should_be_able_to_create_overflow_policy() + { + foreach (Overflow overflow in Enum.GetValues(typeof(Overflow))) + { + const string policyName = "a-sample-overflow"; + const uint maxLengthBytes = 500; + await fixture.ManagementClient.CreatePolicyAsync( + new Policy( + Name: policyName, + Pattern: "averyuncommonpattern", + Vhost: Vhost.Name, + Definition: new PolicyDefinition( + MaxLengthBytes: maxLengthBytes, + Overflow: overflow + ) + ) + ); + Assert.Equal(1, (await fixture.ManagementClient.GetPoliciesAsync()).Count( + p => p.Name == policyName + && p.Vhost == Vhost.Name + && p.Definition.MaxLengthBytes == maxLengthBytes + && p.Definition.Overflow == overflow) + ); + } + } + [Fact] public async Task Should_be_able_to_create_message_ttl_policy() { @@ -1187,7 +1276,7 @@ public async Task Should_throw_when_trying_to_close_unknown_connection() [Fact] public async Task Should_be_able_to_create_shovel_parameter_on_queue() - { + { var srcUri = new AmqpUri(fixture.Endpoint.Host, fixture.Endpoint.Port, fixture.User, fixture.Password); var destUri = new AmqpUri($"{fixture.Endpoint.Host}-1", fixture.Endpoint.Port, fixture.User, fixture.Password); @@ -1220,10 +1309,10 @@ await fixture.ManagementClient.CreateShovelAsync( [Fact] public async Task Should_be_able_to_create_shovel_parameter_on_exchange() - { + { var srcUri = new AmqpUri(fixture.Endpoint.Host, fixture.Endpoint.Port, fixture.User, fixture.Password); - var destUri = new AmqpUri($"{fixture.Endpoint.Host}-1", fixture.Endpoint.Port, fixture.User, fixture.Password); - + var destUri = new AmqpUri($"{fixture.Endpoint.Host}-1", fixture.Endpoint.Port, fixture.User, fixture.Password); + var shovelName = "exchange-shovel"; await fixture.ManagementClient.CreateShovelAsync( @@ -1256,8 +1345,8 @@ public async Task Should_be_able_to_throw_on_non_existant_shovel() { await Assert.ThrowsAsync(async () => await fixture.ManagementClient.GetShovelAsync(Vhost.Name, "non-existant-shovel")); - } - + } + [Fact] public async Task Should_be_able_to_get_existant_shovel() { diff --git a/Source/EasyNetQ.Management.Client.IntegrationTests/RabbitMQFixture.cs b/Source/EasyNetQ.Management.Client.IntegrationTests/RabbitMQFixture.cs index fcbdee47..70a97436 100644 --- a/Source/EasyNetQ.Management.Client.IntegrationTests/RabbitMQFixture.cs +++ b/Source/EasyNetQ.Management.Client.IntegrationTests/RabbitMQFixture.cs @@ -28,6 +28,8 @@ public RabbitMqFixture() public IManagementClient ManagementClient { get; private set; } + public Version RabbitmqVersion { get; private set; } + public async Task InitializeAsync() { using var cts = new CancellationTokenSource(InitializationTimeout); @@ -45,6 +47,9 @@ public async Task InitializeAsync() ManagementClient = new ManagementClient(Endpoint, User, Password); await WaitForRabbitMqReadyAsync(cts.Token); + + var overview = await ManagementClient.GetOverviewAsync(); + RabbitmqVersion = new Version(overview.RabbitmqVersion); } public async Task DisposeAsync() diff --git a/Source/EasyNetQ.Management.Client/Model/DeadLetterStrategy.cs b/Source/EasyNetQ.Management.Client/Model/DeadLetterStrategy.cs new file mode 100644 index 00000000..1eefcf6b --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Model/DeadLetterStrategy.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace EasyNetQ.Management.Client.Model; + +public enum DeadLetterStrategy +{ + [EnumMember(Value = "at-most-once")] + AtMostOnce, + [EnumMember(Value = "at-least-once")] + AtLeastOnce +} diff --git a/Source/EasyNetQ.Management.Client/Model/HaPromote.cs b/Source/EasyNetQ.Management.Client/Model/HaPromote.cs new file mode 100644 index 00000000..59bf0475 --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Model/HaPromote.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace EasyNetQ.Management.Client.Model; + +public enum HaPromote +{ + [EnumMember(Value = "when-synced")] + WhenSynced, + [EnumMember(Value = "always")] + Always +} diff --git a/Source/EasyNetQ.Management.Client/Model/Overflow.cs b/Source/EasyNetQ.Management.Client/Model/Overflow.cs new file mode 100644 index 00000000..023a783a --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Model/Overflow.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; + +namespace EasyNetQ.Management.Client.Model; + +public enum Overflow +{ + [EnumMember(Value = "drop-head")] + DropHead, + [EnumMember(Value = "reject-publish")] + RejectPublish, + [EnumMember(Value = "reject-publish-dlx")] + RejectPublishDlx +} diff --git a/Source/EasyNetQ.Management.Client/Model/PolicyDefinition.cs b/Source/EasyNetQ.Management.Client/Model/PolicyDefinition.cs index dfabe221..d04c3687 100644 --- a/Source/EasyNetQ.Management.Client/Model/PolicyDefinition.cs +++ b/Source/EasyNetQ.Management.Client/Model/PolicyDefinition.cs @@ -1,9 +1,29 @@ -using System.Text.Json.Serialization; +using EasyNetQ.Management.Client.Serialization; +using System.Text.Json.Serialization; namespace EasyNetQ.Management.Client.Model; public record PolicyDefinition ( + // Queues [All types] + [property: JsonPropertyName("max-length")] + uint? MaxLength = null, + [property: JsonPropertyName("max-length-bytes")] + long? MaxLengthBytes = null, + [property: JsonPropertyName("overflow"), JsonConverter(typeof(JsonStringEnumConverterEx))] + Overflow? Overflow = null, + [property: JsonPropertyName("expires")] + uint? Expires = null, + [property: JsonPropertyName("dead-letter-exchange")] + string? DeadLetterExchange = null, + [property: JsonPropertyName("dead-letter-routing-key")] + string? DeadLetterRoutingKey = null, + [property: JsonPropertyName("message-ttl")] + uint? MessageTtl = null, + [property: JsonPropertyName("consumer-timeout")] + uint? ConsumerTimeout = null, + + // Queues [Classic] [property: JsonPropertyName("ha-mode")] HaMode? HaMode = null, [property: JsonPropertyName("ha-params")] @@ -12,22 +32,39 @@ public record PolicyDefinition HaSyncMode? HaSyncMode = null, [property: JsonPropertyName("ha-sync-batch-size")] int? HaSyncBatchSize = null, + [property: JsonPropertyName("ha-promote-on-shutdown"), JsonConverter(typeof(JsonStringEnumConverterEx))] + HaPromote? HaPromoteOnShutdown = null, + [property: JsonPropertyName("ha-promote-on-failure"), JsonConverter(typeof(JsonStringEnumConverterEx))] + HaPromote? HaPromoteOnFailure = null, + [property: JsonPropertyName("queue-version"), JsonConverter(typeof(JsonNumberEnumConverter))] + QueueVersion? QueueVersion = null, + [property: JsonPropertyName("queue-master-locator"), JsonConverter(typeof(JsonStringEnumConverterEx))] + QueueLocator? QueueMasterLocator = null, + + // Queues [Quorum] + [property: JsonPropertyName("delivery-limit")] + uint? DeliveryLimit = null, + [property: JsonPropertyName("dead-letter-strategy"), JsonConverter(typeof(JsonStringEnumConverterEx))] + DeadLetterStrategy? DeadLetterStrategy = null, + [property: JsonPropertyName("queue-leader-locator"), JsonConverter(typeof(JsonStringEnumConverterEx))] + QueueLocator? QueueLeaderLocator = null, + + // Streams + [property: JsonPropertyName("max-age")] + string? MaxAge = null, + [property: JsonPropertyName("stream-max-segment-size-bytes")] + uint? StreamMaxSegmentSizeBytes = null, + + // Exchanges + [property: JsonPropertyName("alternate-exchange")] + string? AlternateExchange = null, + + // Federation [property: JsonPropertyName("federation-upstream")] string? FederationUpstream = null, [property: JsonPropertyName("federation-upstream-set")] string? FederationUpstreamSet = null, - [property: JsonPropertyName("alternate-exchange")] - string? AlternateExchange = null, - [property: JsonPropertyName("dead-letter-exchange")] - string? DeadLetterExchange = null, - [property: JsonPropertyName("dead-letter-routing-key")] - string? DeadLetterRoutingKey = null, + [property: JsonPropertyName("queue-mode")] - string? QueueMode = null, - [property: JsonPropertyName("message-ttl")] - uint? MessageTtl = null, - [property: JsonPropertyName("expires")] - uint? Expires = null, - [property: JsonPropertyName("max-length")] - uint? MaxLength = null + string? QueueMode = null ); diff --git a/Source/EasyNetQ.Management.Client/Model/QueueLocator.cs b/Source/EasyNetQ.Management.Client/Model/QueueLocator.cs new file mode 100644 index 00000000..838a0729 --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Model/QueueLocator.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace EasyNetQ.Management.Client.Model; + +public enum QueueLocator +{ + [EnumMember(Value = "client-local")] + ClientLocal, + [EnumMember(Value = "balanced")] + Balanced +} diff --git a/Source/EasyNetQ.Management.Client/Model/QueueVersion.cs b/Source/EasyNetQ.Management.Client/Model/QueueVersion.cs new file mode 100644 index 00000000..e3da357e --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Model/QueueVersion.cs @@ -0,0 +1,7 @@ +namespace EasyNetQ.Management.Client.Model; + +public enum QueueVersion +{ + V1 = 1, + V2 = 2 +} diff --git a/Source/EasyNetQ.Management.Client/Serialization/JsonNumberEnumConverter.cs b/Source/EasyNetQ.Management.Client/Serialization/JsonNumberEnumConverter.cs new file mode 100644 index 00000000..7468c30c --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Serialization/JsonNumberEnumConverter.cs @@ -0,0 +1,38 @@ +#if !NET8_0_OR_GREATER + +namespace System.Text.Json.Serialization; + +internal sealed class JsonNumberEnumConverter : JsonConverter where TEnum : struct, Enum +{ + private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(typeof(TEnum)); + static JsonNumberEnumConverter() + { + switch (s_enumTypeCode) + { + case TypeCode.Int32: break; + default: throw new NotImplementedException($"{typeof(JsonNumberEnumConverter)}: underlying type {s_enumTypeCode} is not supported"); + } + } + + public JsonNumberEnumConverter() { } + + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (s_enumTypeCode) + { + case TypeCode.Int32: return (TEnum)(object)reader.GetInt32(); + default: throw new NotImplementedException(); + } + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + switch (s_enumTypeCode) + { + case TypeCode.Int32: writer.WriteNumberValue((int)(object)value); break; + default: throw new NotImplementedException(); + } + } +} + +#endif diff --git a/Source/EasyNetQ.Management.Client/Serialization/JsonStringEnumConverterEx.cs b/Source/EasyNetQ.Management.Client/Serialization/JsonStringEnumConverterEx.cs new file mode 100644 index 00000000..8face71c --- /dev/null +++ b/Source/EasyNetQ.Management.Client/Serialization/JsonStringEnumConverterEx.cs @@ -0,0 +1,54 @@ +using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using System.Text.Json; + +namespace EasyNetQ.Management.Client.Serialization; + +// https://github.com/dotnet/runtime/issues/31081#issuecomment-848697673 +internal class JsonStringEnumConverterEx : JsonConverter where TEnum : struct, Enum +{ + private readonly Dictionary _enumToString = new Dictionary(); + private readonly Dictionary _stringToEnum = new Dictionary(); + + public JsonStringEnumConverterEx() + { + var type = typeof(TEnum); + var values = Enum.GetValues(type); + + foreach (TEnum value in values) + { + var enumMember = type.GetMember(value.ToString())[0]; + var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false) + .Cast() + .FirstOrDefault(); + + if (attr?.Value != null) + { + _enumToString.Add(value, attr.Value); + _stringToEnum.Add(attr.Value, value); + } + else + { + _enumToString.Add(value, value.ToString()); + _stringToEnum.Add(value.ToString(), value); + } + } + } + + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var stringValue = reader.GetString(); + + if (stringValue != null && _stringToEnum.TryGetValue(stringValue, out var enumValue)) + { + return enumValue; + } + + return default; + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(_enumToString[value]); + } +}