< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.Transports.RabbitMQ.RabbitMqReplyTargetOptions
Assembly: AsyncResponse.Transports.RabbitMQ
File(s): /_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqAsyncResponseOptions.cs
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 209
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Exchange()100%11100%
get_RoutingKey()100%11100%
get_Queue()100%11100%
get_Properties()100%11100%

File(s)

/_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqAsyncResponseOptions.cs

#LineLine coverage
 1namespace AsyncResponse.Transports.RabbitMQ;
 2
 3/// <summary>
 4/// Options for the RabbitMQ AsyncResponse transport.
 5/// </summary>
 6public sealed class RabbitMqAsyncResponseOptions
 7{
 8    public const string TransportName = "rabbitmq";
 9
 10    /// <summary>
 11    /// AMQP connection string. When set, it wins over the individual host/user/password settings.
 12    /// Default local RabbitMQ: <c>amqp://guest:guest@localhost:5672/</c>.
 13    /// </summary>
 14    public string? ConnectionString { get; set; }
 15
 16    /// <summary>RabbitMQ host name used when <see cref="ConnectionString"/> is not set.</summary>
 17    public string HostName { get; set; } = "localhost";
 18
 19    /// <summary>RabbitMQ AMQP port used when <see cref="ConnectionString"/> is not set.</summary>
 20    public int Port { get; set; } = 5672;
 21
 22    /// <summary>RabbitMQ virtual host used when <see cref="ConnectionString"/> is not set.</summary>
 23    public string VirtualHost { get; set; } = "/";
 24
 25    /// <summary>RabbitMQ user name used when <see cref="ConnectionString"/> is not set.</summary>
 26    public string UserName { get; set; } = "guest";
 27
 28    /// <summary>RabbitMQ password used when <see cref="ConnectionString"/> is not set.</summary>
 29    public string Password { get; set; } = "guest";
 30
 31    /// <summary>Client-provided connection name shown in RabbitMQ management UI and logs.</summary>
 32    public string ClientProvidedName { get; set; } = "AsyncResponse";
 33
 34    /// <summary>Enables the RabbitMQ client's automatic connection recovery. Default: <c>true</c>.</summary>
 35    public bool AutomaticRecoveryEnabled { get; set; } = true;
 36
 37    /// <summary>Enables automatic topology recovery. Default: <c>true</c>.</summary>
 38    public bool TopologyRecoveryEnabled { get; set; } = true;
 39
 40    /// <summary>
 41    /// How long the client waits between automatic recovery attempts. Must be strictly positive:
 42    /// the RabbitMQ client uses the value directly in its recovery loop. Default: 5 seconds. The
 43    /// hosted subscribers' own restart retries back off separately, governed by
 44    /// <see cref="SubscriberRetryBaseDelay"/>/<see cref="SubscriberRetryMaxDelay"/>.
 45    /// </summary>
 46    public TimeSpan NetworkRecoveryInterval { get; set; } = TimeSpan.FromSeconds(5);
 47
 48    /// <summary>Requested AMQP heartbeat interval.</summary>
 49    public TimeSpan RequestedHeartbeat { get; set; } = TimeSpan.FromSeconds(30);
 50
 51    /// <summary>Declares exchanges, queues, and bindings from this package before publish/consume. Default: <c>true</c>
 52    public bool DeclareTopology { get; set; } = true;
 53
 54    /// <summary>RabbitMQ direct exchange used to publish worker jobs.</summary>
 55    public string WorkerExchange { get; set; } = "asyncresponse.worker";
 56
 57    /// <summary>Durable queue consumed by the worker subscriber hosted service.</summary>
 58    public string WorkerQueue { get; set; } = "asyncresponse.worker";
 59
 60    /// <summary>Routing key used to bind and publish worker jobs.</summary>
 61    public string WorkerRoutingKey { get; set; } = "asyncresponse.worker";
 62
 63    /// <summary>Worker queue handling options.</summary>
 64    public RabbitMqSubscriberOptions WorkerSubscriber { get; } = new();
 65
 66    /// <summary>RabbitMQ direct exchange remote systems publish response messages to.</summary>
 67    public string ResponseExchange { get; set; } = "asyncresponse.response";
 68
 69    /// <summary>Durable queue consumed by the response-ingress hosted service.</summary>
 70    public string ResponseQueue { get; set; } = "asyncresponse.response";
 71
 72    /// <summary>Routing key used to bind and publish response messages.</summary>
 73    public string ResponseRoutingKey { get; set; } = "asyncresponse.response";
 74
 75    /// <summary>Response queue handling options.</summary>
 76    public RabbitMqSubscriberOptions ResponseSubscriber { get; } = new();
 77
 78    /// <summary>
 79    /// Optional dead-letter exchange. When set (and <see cref="DeclareTopology"/> is enabled), the worker and
 80    /// response queues are declared with <c>x-dead-letter-exchange</c> so messages rejected without requeue
 81    /// (see <see cref="RabbitMqSubscriberOptions.MaxDeliveryAttempts"/>) are routed here instead of dropped.
 82    /// Changing this on a queue that already exists requires recreating the queue (RabbitMQ rejects a redeclare
 83    /// with different arguments).
 84    /// </summary>
 85    public string? DeadLetterExchange { get; set; }
 86
 87    /// <summary>
 88    /// Optional dead-letter queue declared and bound to <see cref="DeadLetterExchange"/>. Leave null to manage
 89    /// the dead-letter queue externally. It is declared only together with <see cref="DeadLetterExchange"/>:
 90    /// set on its own (a broker policy supplies the dead-letter exchange) it must already exist — it is still
 91    /// where a capped message is parked unless <see cref="ParkQueue"/> names another queue, and a park that
 92    /// finds no queue fails loudly and the delivery is requeued. Because it is bound to the dead-letter
 93    /// exchange it receives <em>every</em> dead-lettered message: when that exchange also feeds a TTL-retry
 94    /// queue, each retry hop leaves a copy here, including hops of messages that later succeed — use
 95    /// <see cref="ParkQueue"/> for that topology.
 96    /// </summary>
 97    public string? DeadLetterQueue { get; set; }
 98
 99    /// <summary>
 100    /// Optional queue that receives only the messages the delivery cap parks (see
 101    /// <see cref="RabbitMqSubscriberOptions.MaxDeliveryAttempts"/>): a capped message that has already ridden
 102    /// the dead-letter cycle is published here through the default exchange and ACKed. Declared durable and
 103    /// deliberately <em>unbound</em> when <see cref="DeclareTopology"/> is enabled — with or without a
 104    /// <see cref="DeadLetterExchange"/> — so, unlike <see cref="DeadLetterQueue"/>, it never collects the
 105    /// retry hops of a TTL-retry cycle. When null, capped messages are parked in <see cref="DeadLetterQueue"/>.
 106    /// </summary>
 107    public string? ParkQueue { get; set; }
 108
 109    /// <summary>
 110    /// Routing key used both for the <c>x-dead-letter-routing-key</c> argument and for binding
 111    /// <see cref="DeadLetterQueue"/>. When null, dead-lettered messages keep their original routing key and the
 112    /// dead-letter queue is bound with the source queue's routing key.
 113    /// </summary>
 114    public string? DeadLetterRoutingKey { get; set; }
 115
 116    /// <summary>The logical reply target name used by <c>WithReplyTarget()</c>. Default: <c>default</c>.</summary>
 117    public string DefaultReplyTargetName { get; set; } = "default";
 118
 119    /// <summary>
 120    /// Named reply targets exposed to Core through <see cref="IAsyncResponseReplyTargetProvider"/>.
 121    /// When empty, <see cref="ResponseExchange"/> + <see cref="ResponseRoutingKey"/> become the default target.
 122    /// </summary>
 123    public Dictionary<string, RabbitMqReplyTargetOptions> ReplyTargets { get; } = new(StringComparer.Ordinal);
 124
 125    /// <summary>Message header that carries the AsyncResponse correlation id. Default: <c>correlationId</c>.</summary>
 126    public string CorrelationIdHeader { get; set; } = "correlationId";
 127
 128    /// <summary>
 129    /// JSON paths inspected when a response message does not carry the correlation id as a
 130    /// message property/header. Paths are case-insensitive and support nested JSON strings.
 131    /// </summary>
 132    public string[] CorrelationIdJsonPaths { get; set; } =
 133    [
 134        "CorrelationId",
 135        "CustomParameters",
 136        "CustomParameters.CorrelationId",
 137        "PubSubParams.CustomParameters",
 138        "PubSubParams.CustomParameters.CorrelationId",
 139        "DagJsonParameters.CorrelationId"
 140    ];
 141
 142    /// <summary>Initial delay after a subscriber loop failure.</summary>
 143    public TimeSpan SubscriberRetryBaseDelay { get; set; } = TimeSpan.FromMilliseconds(250);
 144
 145    /// <summary>Maximum delay after repeated subscriber loop failures.</summary>
 146    public TimeSpan SubscriberRetryMaxDelay { get; set; } = TimeSpan.FromSeconds(5);
 147
 148    /// <summary>
 149    /// Bounds the connection close while hosted consumers/publishers stop. The close completes in
 150    /// milliseconds when healthy; when it does not, the connection is abandoned anyway, so keep
 151    /// this short — it counts against the host's shutdown budget. Default: <c>5s</c>.
 152    /// </summary>
 153    public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
 154
 155    /// <summary>
 156    /// The hosting shutdown budget that must contain RabbitMQ channel shutdown plus
 157    /// <see cref="RabbitMqSubscriberOptions.BackgroundDrainTimeout"/> when a subscriber uses
 158    /// <see cref="RabbitMqAckMode.AckAfterEnqueue"/>.
 159    /// </summary>
 160    public TimeSpan? HostShutdownTimeout { get; set; } = TimeSpan.FromSeconds(30);
 161
 162    /// <summary>
 163    /// The broker's <c>consumer_timeout</c>, mirrored here because the client cannot read it: RabbitMQ
 164    /// closes a channel whose delivery stays unacknowledged longer than that (<c>PRECONDITION_FAILED</c>)
 165    /// and requeues the delivery, so a worker handler still running past it — in
 166    /// <see cref="RabbitMqAckMode.AckAfterHandlerCompletes"/> the delivery stays unacknowledged for the
 167    /// handler's whole run — has its job redelivered to another consumer mid-run. The value is advertised
 168    /// through <see cref="IWorkerTransportInFlightLimit.MaxInFlightDuration"/> so durable-flow timers that
 169    /// wait in process plan their waits inside it; nothing in this package enforces it. Default:
 170    /// <c>30 minutes</c>, the broker's default. Keep it equal to (or below) the broker's setting; set
 171    /// <c>null</c> only when the broker's <c>consumer_timeout</c> is disabled. Must be positive.
 172    /// </summary>
 173    public TimeSpan? BrokerConsumerTimeout { get; set; } = TimeSpan.FromMinutes(30);
 174
 175    /// <summary>Adds or replaces a named RabbitMQ reply target.</summary>
 176    public RabbitMqAsyncResponseOptions AddReplyTarget(
 177        string name,
 178        string exchange,
 179        string routingKey)
 180    {
 181        ArgumentException.ThrowIfNullOrWhiteSpace(name);
 182        ArgumentException.ThrowIfNullOrWhiteSpace(exchange);
 183        ArgumentException.ThrowIfNullOrWhiteSpace(routingKey);
 184
 185        ReplyTargets[name] = new RabbitMqReplyTargetOptions
 186        {
 187            Exchange = exchange,
 188            RoutingKey = routingKey
 189        };
 190
 191        return this;
 192    }
 193}
 194
 195/// <summary>Options for one named RabbitMQ async-response reply target.</summary>
 196public sealed class RabbitMqReplyTargetOptions
 197{
 198    /// <summary>Exchange remote systems should publish responses to.</summary>
 24199    public string? Exchange { get; set; }
 200
 201    /// <summary>Routing key remote systems should publish responses with.</summary>
 24202    public string? RoutingKey { get; set; }
 203
 204    /// <summary>Queue that receives responses for this target. Optional; included as metadata for consumers.</summary>
 16205    public string? Queue { get; set; }
 206
 207    /// <summary>Additional values copied to the transport-neutral reply target.</summary>
 22208    public Dictionary<string, string> Properties { get; } = new(StringComparer.Ordinal);
 209}