diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..9db0418
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,9 @@
+version 2
+
+build
+ os ubuntu-22.04
+ tools
+ python 3.12
+
+sphinx
+ configuration docs/conf.py
\ No newline at end of file
diff --git a/Tapeti/Config/ITapetiConfig.cs b/Tapeti/Config/ITapetiConfig.cs
index 4bdad56..27c393e 100644
--- a/Tapeti/Config/ITapetiConfig.cs
+++ b/Tapeti/Config/ITapetiConfig.cs
@@ -17,7 +17,10 @@ namespace Tapeti.Config
///
/// Various Tapeti features which can be turned on or off.
///
- ITapetiConfigFeatues Features { get; }
+ ///
+ /// Calling this method will freeze the feature set if is used.
+ ///
+ ITapetiConfigFeatures GetFeatures();
///
/// Provides access to the different kinds of registered middleware.
@@ -34,7 +37,7 @@ namespace Tapeti.Config
///
/// Various Tapeti features which can be turned on or off.
///
- public interface ITapetiConfigFeatues
+ public interface ITapetiConfigFeatures
{
///
/// Determines whether 'publisher confirms' are used. This RabbitMQ features allows Tapeti to
diff --git a/Tapeti/Config/ITapetiConfigBuilder.cs b/Tapeti/Config/ITapetiConfigBuilder.cs
index 5d517c9..6351293 100644
--- a/Tapeti/Config/ITapetiConfigBuilder.cs
+++ b/Tapeti/Config/ITapetiConfigBuilder.cs
@@ -53,6 +53,40 @@ namespace Tapeti.Config
void RegisterBinding(IBinding binding);
+ ///
+ ITapetiConfigBuilder DisablePublisherConfirms();
+
+ ///
+ ITapetiConfigBuilder SetPublisherConfirms(bool enabled);
+
+ ///
+ ITapetiConfigBuilder EnableDeclareDurableQueues();
+
+ ///
+ ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled);
+
+ ///
+ ITapetiConfigBuilder DisableVerifyDurableQueues();
+
+ ///
+ ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled);
+
+
+ ///
+ /// Allows the core features to be determine on-demand when first required by the connection instead
+ /// of before is constructed.
+ ///
+ /// Called when the feature set is required. From that moment on the feature set is frozen.
+ ITapetiConfigBuilder DelayFeatures(Action onBuild);
+ }
+
+
+
+ ///
+ /// Configures Tapeti core features. Every method returns the builder instance for method chaining.
+ ///
+ public interface ITapetiConfigFeaturesBuilder
+ {
///
/// Disables 'publisher confirms'. This RabbitMQ features allows Tapeti to be notified if a message
/// has no route, and guarantees delivery for request-response style messages and those marked with
@@ -62,7 +96,7 @@ namespace Tapeti.Config
/// and disables Tapeti.Flow from verifying if a request/response can be routed. This may
/// result in never-ending flows. Only disable if you can accept those consequences.
///
- ITapetiConfigBuilder DisablePublisherConfirms();
+ ITapetiConfigFeaturesBuilder DisablePublisherConfirms();
///
@@ -74,7 +108,7 @@ namespace Tapeti.Config
/// and disables Tapeti.Flow from verifying if a request/response can be routed. This may
/// result in never-ending flows. Only disable if you can accept those consequences.
///
- ITapetiConfigBuilder SetPublisherConfirms(bool enabled);
+ ITapetiConfigFeaturesBuilder SetPublisherConfirms(bool enabled);
///
@@ -84,7 +118,7 @@ namespace Tapeti.Config
/// Note that access to the RabbitMQ Management plugin's REST API is required for this
/// feature to work, since AMQP does not provide a way to query existing bindings.
///
- ITapetiConfigBuilder EnableDeclareDurableQueues();
+ ITapetiConfigFeaturesBuilder EnableDeclareDurableQueues();
///
/// Configures the automatic creation of durable queues and updating of their bindings.
@@ -93,7 +127,7 @@ namespace Tapeti.Config
/// Note that access to the RabbitMQ Management plugin's REST API is required for this
/// feature to work, since AMQP does not provide a way to query existing bindings.
///
- ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled);
+ ITapetiConfigFeaturesBuilder SetDeclareDurableQueues(bool enabled);
///
@@ -102,7 +136,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying.
///
- ITapetiConfigBuilder DisableVerifyDurableQueues();
+ ITapetiConfigFeaturesBuilder DisableVerifyDurableQueues();
///
@@ -111,7 +145,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying.
///
- ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled);
+ ITapetiConfigFeaturesBuilder SetVerifyDurableQueues(bool enabled);
}
diff --git a/Tapeti/Connection/TapetiClient.cs b/Tapeti/Connection/TapetiClient.cs
index 3818238..23d5cbf 100644
--- a/Tapeti/Connection/TapetiClient.cs
+++ b/Tapeti/Connection/TapetiClient.cs
@@ -135,7 +135,7 @@ namespace Tapeti.Connection
DeclareExchange(channel, exchange);
// The delivery tag is lost after a reconnect, register under the new tag
- if (config.Features.PublisherConfirms)
+ if (config.GetFeatures().PublisherConfirms)
{
lastDeliveryTag++;
@@ -848,7 +848,7 @@ namespace Tapeti.Connection
}
- if (config.Features.PublisherConfirms)
+ if (config.GetFeatures().PublisherConfirms)
{
lastDeliveryTag = 0;
diff --git a/Tapeti/Connection/TapetiSubscriber.cs b/Tapeti/Connection/TapetiSubscriber.cs
index df0db5a..b7023eb 100644
--- a/Tapeti/Connection/TapetiSubscriber.cs
+++ b/Tapeti/Connection/TapetiSubscriber.cs
@@ -125,9 +125,9 @@ namespace Tapeti.Connection
CustomBindingTarget bindingTarget;
- if (config.Features.DeclareDurableQueues)
+ if (config.GetFeatures().DeclareDurableQueues)
bindingTarget = new DeclareDurableQueuesBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);
- else if (config.Features.VerifyDurableQueues)
+ else if (config.GetFeatures().VerifyDurableQueues)
bindingTarget = new PassiveDurableQueuesBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);
else
bindingTarget = new NoVerifyBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);
diff --git a/Tapeti/TapetiConfig.cs b/Tapeti/TapetiConfig.cs
index ee48d3a..56188a7 100644
--- a/Tapeti/TapetiConfig.cs
+++ b/Tapeti/TapetiConfig.cs
@@ -135,7 +135,7 @@ namespace Tapeti
///
public ITapetiConfigBuilder DisablePublisherConfirms()
{
- GetConfig().SetPublisherConfirms(false);
+ GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this;
}
@@ -143,7 +143,7 @@ namespace Tapeti
///
public ITapetiConfigBuilder SetPublisherConfirms(bool enabled)
{
- GetConfig().SetPublisherConfirms(enabled);
+ GetConfig().GetFeaturesBuilder().SetPublisherConfirms(enabled);
return this;
}
@@ -151,7 +151,7 @@ namespace Tapeti
///
public ITapetiConfigBuilder EnableDeclareDurableQueues()
{
- GetConfig().SetDeclareDurableQueues(true);
+ GetConfig().GetFeaturesBuilder().EnableDeclareDurableQueues();
return this;
}
@@ -159,7 +159,7 @@ namespace Tapeti
///
public ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled)
{
- GetConfig().SetDeclareDurableQueues(enabled);
+ GetConfig().GetFeaturesBuilder().SetDeclareDurableQueues(enabled);
return this;
}
@@ -167,7 +167,7 @@ namespace Tapeti
///
public ITapetiConfigBuilder DisableVerifyDurableQueues()
{
- GetConfig().SetVerifyDurableQueues(false);
+ GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this;
}
@@ -175,7 +175,15 @@ namespace Tapeti
///
public ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled)
{
- GetConfig().SetVerifyDurableQueues(enabled);
+ GetConfig().GetFeaturesBuilder().SetVerifyDurableQueues(enabled);
+ return this;
+ }
+
+
+ ///
+ public ITapetiConfigBuilder DelayFeatures(Action onBuild)
+ {
+ GetConfig().GetFeaturesBuilder().DelayFeatures(onBuild);
return this;
}
@@ -221,12 +229,13 @@ namespace Tapeti
///
internal class Config : ITapetiConfig
{
- private readonly ConfigFeatures features = new();
+ private ConfigFeaturesBuilder? featuresBuilder = new();
+ private ITapetiConfigFeatures? features;
+
private readonly ConfigMiddleware middleware = new();
private readonly ConfigBindings bindings = new();
public IDependencyResolver DependencyResolver { get; }
- public ITapetiConfigFeatues Features => features;
public ITapetiConfigMiddleware Middleware => middleware;
public ITapetiConfigBindings Bindings => bindings;
@@ -237,6 +246,17 @@ namespace Tapeti
}
+ public ITapetiConfigFeatures GetFeatures()
+ {
+ if (features != null)
+ return features;
+
+ features = featuresBuilder!.Build();
+ featuresBuilder = null;
+ return features;
+ }
+
+
public void Lock()
{
bindings.Lock();
@@ -260,24 +280,17 @@ namespace Tapeti
}
- public void SetPublisherConfirms(bool enabled)
+ internal ConfigFeaturesBuilder GetFeaturesBuilder()
{
- features.PublisherConfirms = enabled;
- }
+ if (featuresBuilder == null)
+ throw new InvalidOperationException("Tapeti features are already frozen");
- public void SetDeclareDurableQueues(bool enabled)
- {
- features.DeclareDurableQueues = enabled;
- }
-
- public void SetVerifyDurableQueues(bool enabled)
- {
- features.VerifyDurableQueues = enabled;
+ return featuresBuilder;
}
}
- internal class ConfigFeatures : ITapetiConfigFeatues
+ internal class ConfigFeatures : ITapetiConfigFeatures
{
public bool PublisherConfirms { get; internal set; } = true;
public bool DeclareDurableQueues { get; internal set; }
@@ -285,6 +298,66 @@ namespace Tapeti
}
+ internal class ConfigFeaturesBuilder : ITapetiConfigFeaturesBuilder
+ {
+ private bool publisherConfirms = true;
+ private bool declareDurableQueues;
+ private bool verifyDurableQueues = true;
+ private Action? onBuild;
+
+
+ public ITapetiConfigFeaturesBuilder DisablePublisherConfirms()
+ {
+ return SetPublisherConfirms(false);
+ }
+
+ public ITapetiConfigFeaturesBuilder SetPublisherConfirms(bool enabled)
+ {
+ publisherConfirms = enabled;
+ return this;
+ }
+
+ public ITapetiConfigFeaturesBuilder EnableDeclareDurableQueues()
+ {
+ return SetDeclareDurableQueues(true);
+ }
+
+ public ITapetiConfigFeaturesBuilder SetDeclareDurableQueues(bool enabled)
+ {
+ declareDurableQueues = enabled;
+ return this;
+ }
+
+ public ITapetiConfigFeaturesBuilder DisableVerifyDurableQueues()
+ {
+ return SetVerifyDurableQueues(false);
+ }
+
+ public ITapetiConfigFeaturesBuilder SetVerifyDurableQueues(bool enabled)
+ {
+ verifyDurableQueues = enabled;
+ return this;
+ }
+
+ // ReSharper disable once ParameterHidesMember
+ public void DelayFeatures(Action onBuild)
+ {
+ this.onBuild = onBuild;
+ }
+
+ public ITapetiConfigFeatures Build()
+ {
+ onBuild?.Invoke(this);
+ return new ConfigFeatures
+ {
+ DeclareDurableQueues = declareDurableQueues,
+ PublisherConfirms = publisherConfirms,
+ VerifyDurableQueues = verifyDurableQueues
+ };
+ }
+ }
+
+
internal class ConfigMiddleware : ITapetiConfigMiddleware
{
private readonly List messageMiddleware = new();