diff --git a/Akka.Persistence.MongoDb.sln b/Akka.Persistence.MongoDb.sln
index b3f311a..7ff99fb 100644
--- a/Akka.Persistence.MongoDb.sln
+++ b/Akka.Persistence.MongoDb.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29306.81
+# Visual Studio Version 17
+VisualStudioVersion = 17.6.33723.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.MongoDb", "src\Akka.Persistence.MongoDb\Akka.Persistence.MongoDb.csproj", "{E945AABA-2779-41E8-9B43-8898FFD64F22}"
EndProject
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BE1178E1
build.sh = build.sh
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.MongoDb.Hosting", "src\Akka.Persistence.MongoDb.Hosting\Akka.Persistence.MongoDb.Hosting.csproj", "{72B8C165-FE00-465F-A2E9-60B4B79F81AF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -29,6 +31,10 @@ Global
{0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Akka.Persistence.MongoDb.Hosting/Akka.Persistence.MongoDb.Hosting.csproj b/src/Akka.Persistence.MongoDb.Hosting/Akka.Persistence.MongoDb.Hosting.csproj
new file mode 100644
index 0000000..9a02ec8
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Hosting/Akka.Persistence.MongoDb.Hosting.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+ $(NetStandardLibVersion)
+ latest
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Hosting/AkkaPersistenceMongoDbHostingExtensions.cs b/src/Akka.Persistence.MongoDb.Hosting/AkkaPersistenceMongoDbHostingExtensions.cs
new file mode 100644
index 0000000..b4b6ae5
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Hosting/AkkaPersistenceMongoDbHostingExtensions.cs
@@ -0,0 +1,215 @@
+using System;
+using Akka.Actor;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.MongoDb.Hosting;
+
+public static class AkkaPersistenceMongoDbHostingExtensions
+{
+ ///
+ /// Adds Akka.Persistence.SqlServer support to this .
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ /// Connection string used for database access.
+ ///
+ ///
+ ///
+ /// Determines which settings should be added by this method call.
+ ///
+ /// Default:
+ ///
+ ///
+ ///
+ /// Should the SQL store table be initialized automatically.
+ ///
+ /// Default: false
+ ///
+ ///
+ ///
+ /// An used to configure an instance.
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// The configuration identifier for the plugins
+ ///
+ /// Default: "sql-server"
+ ///
+ ///
+ ///
+ /// A bool flag to set the plugin as the default persistence plugin for the
+ ///
+ /// Default: true
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when is set and is set to
+ ///
+ ///
+ public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ string connectionString,
+ PersistenceMode mode = PersistenceMode.Both,
+ bool autoInitialize = true,
+ Action? journalBuilder = null,
+ string pluginIdentifier = "mongodb",
+ bool isDefaultPlugin = true)
+ {
+ if (mode == PersistenceMode.SnapshotStore && journalBuilder is { })
+ throw new Exception(
+ $"{nameof(journalBuilder)} can only be set when {nameof(mode)} is set to either {PersistenceMode.Both} or {PersistenceMode.Journal}");
+
+ var journalOpt = new MongoDbJournalOptions(isDefaultPlugin, pluginIdentifier)
+ {
+ ConnectionString = connectionString,
+ AutoInitialize = autoInitialize,
+ };
+
+ var adapters = new AkkaPersistenceJournalBuilder(journalOpt.Identifier, builder);
+ journalBuilder?.Invoke(adapters);
+ journalOpt.Adapters = adapters;
+
+ var snapshotOpt = new MongoDbSnapshotOptions(isDefaultPlugin, pluginIdentifier)
+ {
+ ConnectionString = connectionString,
+ AutoInitialize = autoInitialize,
+ };
+
+ return mode switch
+ {
+ PersistenceMode.Journal => builder.WithMongoDbPersistence(journalOpt, null),
+ PersistenceMode.SnapshotStore => builder.WithMongoDbPersistence(null, snapshotOpt),
+ PersistenceMode.Both => builder.WithMongoDbPersistence(journalOpt, snapshotOpt),
+ _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid PersistenceMode defined.")
+ };
+ }
+
+ ///
+ /// Adds Akka.Persistence.MongoDb support to this . At least one of the
+ /// configurator delegate needs to be populated else this method will throw an exception.
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ ///
+ /// An that modifies an instance of ,
+ /// used to configure the journal plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// An that modifies an instance of ,
+ /// used to configure the snapshot store plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// A bool flag to set the plugin as the default persistence plugin for the
+ ///
+ /// Default: true
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when both and are null.
+ ///
+ public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ Action? journalOptionConfigurator = null,
+ Action? snapshotOptionConfigurator = null,
+ bool isDefaultPlugin = true)
+ {
+ if (journalOptionConfigurator is null && snapshotOptionConfigurator is null)
+ throw new ArgumentException(
+ $"{nameof(journalOptionConfigurator)} and {nameof(snapshotOptionConfigurator)} could not both be null");
+
+ MongoDbJournalOptions? journalOptions = null;
+ if (journalOptionConfigurator is { })
+ {
+ journalOptions = new MongoDbJournalOptions(isDefaultPlugin);
+ journalOptionConfigurator(journalOptions);
+ }
+
+ MongoDbSnapshotOptions? snapshotOptions = null;
+ if (snapshotOptionConfigurator is { })
+ {
+ snapshotOptions = new MongoDbSnapshotOptions(isDefaultPlugin);
+ snapshotOptionConfigurator(snapshotOptions);
+ }
+
+ return builder.WithMongoDbPersistence(journalOptions, snapshotOptions);
+ }
+
+ ///
+ /// Adds Akka.Persistence.MongoDb support to this . At least one of the options
+ /// have to be populated else this method will throw an exception.
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ ///
+ /// An instance of , used to configure the journal plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// An instance of , used to configure the snapshot store plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when both and are null.
+ ///
+ public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ MongoDbJournalOptions? journalOptions = null,
+ MongoDbSnapshotOptions? snapshotOptions = null)
+ {
+ if (journalOptions is null && snapshotOptions is null)
+ throw new ArgumentException(
+ $"{nameof(journalOptions)} and {nameof(snapshotOptions)} could not both be null");
+
+ return (journalOptions, snapshotOptions) switch
+ {
+ (null, null) =>
+ throw new ArgumentException(
+ $"{nameof(journalOptions)} and {nameof(snapshotOptions)} could not both be null"),
+
+ (_, null) =>
+ builder
+ .AddHocon(journalOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(journalOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(MongoDbPersistence.DefaultConfiguration(), HoconAddMode.Append),
+
+ (null, _) =>
+ builder
+ .AddHocon(snapshotOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(snapshotOptions.DefaultConfig, HoconAddMode.Append),
+
+ (_, _) =>
+ builder
+ .AddHocon(journalOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(snapshotOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(journalOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(snapshotOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(MongoDbPersistence.DefaultConfiguration(), HoconAddMode.Append),
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Hosting/MongoDbJournalOptions.cs b/src/Akka.Persistence.MongoDb.Hosting/MongoDbJournalOptions.cs
new file mode 100644
index 0000000..1984dbf
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Hosting/MongoDbJournalOptions.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Text;
+using Akka.Configuration;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.MongoDb.Hosting;
+
+public class MongoDbJournalOptions : JournalOptions
+{
+ private static readonly Config Default = MongoDbPersistence.DefaultConfiguration()
+ .GetConfig(MongoDbJournalSettings.JournalConfigPath);
+
+ public MongoDbJournalOptions() : this(true)
+ {
+ }
+
+ public MongoDbJournalOptions(bool isDefaultPlugin, string identifier = "mongodb") : base(isDefaultPlugin)
+ {
+ Identifier = identifier;
+ AutoInitialize = true;
+ }
+
+ ///
+ /// Connection string used to access the MongoDb, also specifies the database.
+ ///
+ public string ConnectionString { get; set; } = "";
+
+ ///
+ /// Name of the collection for the event journal or snapshots
+ ///
+ public string? Collection { get; set; }
+
+ ///
+ /// Name of the collection for the event journal metadata
+ ///
+ public string? MetadataCollection { get; set; }
+
+ ///
+ /// Transaction
+ ///
+ public bool? UseWriteTransaction { get; set; }
+
+ ///
+ /// When true, enables BSON serialization (which breaks features like Akka.Cluster.Sharding, AtLeastOnceDelivery, and so on.)
+ ///
+ public bool? LegacySerialization { get; set; }
+
+ ///
+ /// Timeout for individual database operations.
+ ///
+ ///
+ /// Defaults to 10s.
+ ///
+ public TimeSpan? CallTimeout { get; set; }
+
+ public override string Identifier { get; set; }
+ protected override Config InternalDefaultConfig { get; } = Default;
+
+ protected override StringBuilder Build(StringBuilder sb)
+ {
+ sb.AppendLine($"connection-string = {ConnectionString.ToHocon()}");
+
+ if(Collection is not null)
+ sb.AppendLine($"collection = {Collection.ToHocon()}");
+
+ if(MetadataCollection is not null)
+ sb.AppendLine($"metadata-collection = {MetadataCollection.ToHocon()}");
+
+ if(CallTimeout is not null)
+ sb.AppendLine($"call-timeout = {CallTimeout.ToHocon()}");
+
+ if(UseWriteTransaction is not null)
+ sb.AppendLine($"use-write-transaction = {UseWriteTransaction.ToHocon()}");
+
+ if(LegacySerialization is not null)
+ sb.AppendLine($"legacy-serialization = {LegacySerialization.ToHocon()}");
+
+ return base.Build(sb);
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Hosting/MongoDbSnapshotOptions.cs b/src/Akka.Persistence.MongoDb.Hosting/MongoDbSnapshotOptions.cs
new file mode 100644
index 0000000..d5ca43d
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Hosting/MongoDbSnapshotOptions.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Text;
+using Akka.Configuration;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.MongoDb.Hosting;
+
+public class MongoDbSnapshotOptions : SnapshotOptions
+{
+ private static readonly Config Default = MongoDbPersistence.DefaultConfiguration()
+ .GetConfig(MongoDbSnapshotSettings.SnapshotStoreConfigPath);
+
+ public MongoDbSnapshotOptions() : this(true)
+ {
+ }
+
+ public MongoDbSnapshotOptions(bool isDefault, string identifier = "mongodb") : base(isDefault)
+ {
+ Identifier = identifier;
+ AutoInitialize = true;
+ }
+
+ ///
+ /// Connection string used to access the MongoDb, also specifies the database.
+ ///
+ public string ConnectionString { get; set; } = "";
+
+ ///
+ /// Name of the collection for the event journal or snapshots
+ ///
+ public string? Collection { get; set; }
+
+ ///
+ /// Transaction
+ ///
+ public bool? UseWriteTransaction { get; set; }
+
+ ///
+ /// When true, enables BSON serialization (which breaks features like Akka.Cluster.Sharding, AtLeastOnceDelivery, and so on.)
+ ///
+ public bool? LegacySerialization { get; set; }
+
+ ///
+ /// Timeout for individual database operations.
+ ///
+ ///
+ /// Defaults to 10s.
+ ///
+ public TimeSpan? CallTimeout { get; set; }
+
+ public override string Identifier { get; set; }
+ protected override Config InternalDefaultConfig { get; } = Default;
+
+ protected override StringBuilder Build(StringBuilder sb)
+ {
+ sb.AppendLine($"connection-string = {ConnectionString.ToHocon()}");
+
+ if(UseWriteTransaction is not null)
+ sb.AppendLine($"use-write-transaction = {UseWriteTransaction.ToHocon()}");
+
+ if(Collection is not null)
+ sb.AppendLine($"collection = {Collection.ToHocon()}");
+
+ if(LegacySerialization is not null)
+ sb.AppendLine($"legacy-serialization = {LegacySerialization.ToHocon()}");
+
+ if(CallTimeout is not null)
+ sb.AppendLine($"call-timeout = {CallTimeout.ToHocon()}");
+
+ return base.Build(sb);
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Hosting/README.md b/src/Akka.Persistence.MongoDb.Hosting/README.md
new file mode 100644
index 0000000..8305f8d
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Hosting/README.md
@@ -0,0 +1,87 @@
+# Akka.Persistence.MongoDb.Hosting
+
+Akka.Hosting extension methods to add Akka.Persistence.MongoDb to an ActorSystem
+
+# Akka.Persistence.MongoDb Extension Methods
+
+## WithMongoDbPersistence() Method
+
+```csharp
+public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ string connectionString,
+ PersistenceMode mode = PersistenceMode.Both,
+ bool autoInitialize = true,
+ Action? journalBuilder = null,
+ string pluginIdentifier = "mongodb",
+ bool isDefaultPlugin = true);
+```
+
+```csharp
+public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ Action? journalOptionConfigurator = null,
+ Action? snapshotOptionConfigurator = null,
+ bool isDefaultPlugin = true)
+```
+
+```csharp
+public static AkkaConfigurationBuilder WithMongoDbPersistence(
+ this AkkaConfigurationBuilder builder,
+ MongoDbJournalOptions? journalOptions = null,
+ MongoDbSnapshotOptions? snapshotOptions = null)
+```
+
+### Parameters
+
+* `connectionString` __string__
+
+ Connection string used for database access.
+
+* `mode` __PersistenceMode__
+
+ Determines which settings should be added by this method call. __Default__: `PersistenceMode.Both`
+
+ * `PersistenceMode.Journal`: Only add the journal settings
+ * `PersistenceMode.SnapshotStore`: Only add the snapshot store settings
+ * `PersistenceMode.Both`: Add both journal and snapshot store settings
+
+* `autoInitialize` __bool__
+
+ Should the Mongo Db store collection be initialized automatically. __Default__: `false`
+
+* `journalBuilder` __Action\__
+
+ An Action delegate used to configure an `AkkaPersistenceJournalBuilder` instance. Used to configure [Event Adapters](https://getakka.net/articles/persistence/event-adapters.html)
+
+* `journalConfigurator` __Action\__
+
+ An Action delegate to configure a `MongoDbJournalOptions` instance.
+
+* `snapshotConfigurator` __Action\__
+
+ An Action delegate to configure a `MongoDbSnapshotOptions` instance.
+
+* `journalOptions` __MongoDbJournalOptions__
+
+ An `MongoDbJournalOptions` instance to configure the SqlServer journal.
+
+* `snapshotOptions` __MongoDbSnapshotOptions__
+
+ An `MongoDbSnapshotOptions` instance to configure the SqlServer snapshot store.
+
+## Example
+
+```csharp
+using var host = new HostBuilder()
+ .ConfigureServices((context, services) =>
+ {
+ services.AddAkka("mongoDbDemo", (builder, provider) =>
+ {
+ builder
+ .WithMongoDbPersistence("your-mongodb-connection-string");
+ });
+ }).Build();
+
+await host.RunAsync();
+```
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Tests/Akka.Persistence.MongoDb.Tests.csproj b/src/Akka.Persistence.MongoDb.Tests/Akka.Persistence.MongoDb.Tests.csproj
index b132dfd..7d7bdd5 100644
--- a/src/Akka.Persistence.MongoDb.Tests/Akka.Persistence.MongoDb.Tests.csproj
+++ b/src/Akka.Persistence.MongoDb.Tests/Akka.Persistence.MongoDb.Tests.csproj
@@ -1,11 +1,12 @@
-
+
-
+
$(NetFrameworkTestVersion);$(NetTestVersion);$(NetCoreTestVersion)
+
all
@@ -18,6 +19,7 @@
+
diff --git a/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbJournalOptionsSpec.cs b/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbJournalOptionsSpec.cs
new file mode 100644
index 0000000..4defb24
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbJournalOptionsSpec.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+using System.Text;
+using Akka.Configuration;
+using Akka.Persistence.MongoDb.Hosting;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using Microsoft.Extensions.Configuration;
+using Xunit;
+
+namespace Akka.Persistence.MongoDb.Tests.Hosting
+{
+ public class MongoDbJournalOptionsSpec
+ {
+ [Fact(DisplayName = "MongoDbJournalOptions as default plugin should generate plugin setting")]
+ public void DefaultPluginJournalOptionsTest()
+ {
+ var options = new MongoDbJournalOptions(true);
+ var config = options.ToConfig();
+
+ config.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.mongodb");
+ config.HasPath("akka.persistence.journal.mongodb").Should().BeTrue();
+ }
+
+ [Fact(DisplayName = "Empty MongoDbJournalOptions should equal empty config with default fallback")]
+ public void DefaultJournalOptionsTest()
+ {
+ var options = new MongoDbJournalOptions(false);
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(MongoDbPersistence.DefaultConfiguration());
+
+ emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.journal.mongodb");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.mongodb");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
+ config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
+ config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
+ config.GetString("metadata-collection").Should().Be(baseConfig.GetString("metadata-collection"));
+ config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
+ config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
+ }
+
+ [Fact(DisplayName = "Empty MongoDbJournalOptions with custom identifier should equal empty config with default fallback")]
+ public void CustomIdJournalOptionsTest()
+ {
+ var options = new MongoDbJournalOptions(false, "custom");
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(MongoDbPersistence.DefaultConfiguration());
+
+ emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.journal.custom");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.mongodb");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
+ config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
+ config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
+ config.GetString("metadata-collection").Should().Be(baseConfig.GetString("metadata-collection"));
+ config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
+ config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
+ }
+
+ [Fact(DisplayName = "MongoDbJournalOptions should generate proper config")]
+ public void JournalOptionsTest()
+ {
+ var options = new MongoDbJournalOptions(true)
+ {
+ Identifier = "custom",
+ AutoInitialize = true,
+ ConnectionString = "testConnection",
+ Collection = "testCollection",
+ MetadataCollection = "metadataCollection",
+ UseWriteTransaction = true,
+ LegacySerialization = true,
+ CallTimeout = TimeSpan.FromHours(2)
+ };
+
+ var baseConfig = options.ToConfig();
+
+ baseConfig.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.custom");
+
+ var config = baseConfig.GetConfig("akka.persistence.journal.custom");
+ config.Should().NotBeNull();
+ config.GetString("connection-string").Should().Be(options.ConnectionString);
+ config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
+ config.GetString("collection").Should().Be(options.Collection);
+ config.GetString("metadata-collection").Should().Be(options.MetadataCollection);
+ config.GetBoolean("use-write-transaction").Should().Be(options.UseWriteTransaction.Value);
+ config.GetBoolean("legacy-serialization").Should().Be(options.LegacySerialization.Value);
+ config.GetTimeSpan("call-timeout").Should().Be(options.CallTimeout.Value);
+ }
+
+ const string Json = @"
+ {
+ ""Logging"": {
+ ""LogLevel"": {
+ ""Default"": ""Information"",
+ ""Microsoft.AspNetCore"": ""Warning""
+ }
+ },
+ ""Akka"": {
+ ""JournalOptions"": {
+ ""ConnectionString"": ""mongodb://localhost:27017"",
+ ""UseWriteTransaction"": ""true"",
+ ""Identifier"": ""custommongodb"",
+ ""AutoInitialize"": true,
+ ""IsDefaultPlugin"": false,
+ ""Collection"": ""CustomEnventJournalCollection"",
+ ""MetadataCollection"": ""CustomMetadataCollection"",
+ ""LegacySerialization"" : ""true"",
+ ""CallTimeout"": ""00:10:00"",
+ ""Serializer"": ""hyperion"",
+ }
+ }
+ }";
+
+ [Fact(DisplayName = "MongoDbJournalOptions should be bindable to IConfiguration")]
+ public void JournalOptionsIConfigurationBindingTest()
+ {
+ var stream = new MemoryStream(Encoding.UTF8.GetBytes(Json));
+ var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
+
+ var options = jsonConfig.GetSection("Akka:JournalOptions").Get();
+ options.ConnectionString.Should().Be("mongodb://localhost:27017");
+ options.UseWriteTransaction.Should().BeTrue();
+ options.Identifier.Should().Be("custommongodb");
+ options.AutoInitialize.Should().BeTrue();
+ options.IsDefaultPlugin.Should().BeFalse();
+ options.Collection.Should().Be("CustomEnventJournalCollection");
+ options.MetadataCollection.Should().Be("CustomMetadataCollection");
+ options.LegacySerialization.Should().BeTrue();
+ options.CallTimeout.Should().Be(10.Minutes());
+ options.Serializer.Should().Be("hyperion");
+
+ // Dispose called here as project not using latest language features.
+ stream.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbSnapshotOptionsSpec.cs b/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbSnapshotOptionsSpec.cs
new file mode 100644
index 0000000..fd02cd7
--- /dev/null
+++ b/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbSnapshotOptionsSpec.cs
@@ -0,0 +1,148 @@
+using System;
+using System.IO;
+using System.Text;
+using Akka.Configuration;
+using Akka.Persistence.MongoDb.Hosting;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using Microsoft.Extensions.Configuration;
+using Xunit;
+
+namespace Akka.Persistence.MongoDb.Tests.Hosting
+{
+ public class MongoDbSnapshotOptionsSpec
+ {
+ [Fact(DisplayName = "MongoDbSnapshotOptions as default plugin should generate plugin setting")]
+ public void DefaultPluginSnapshotOptionsTest()
+ {
+ var options = new MongoDbSnapshotOptions(true);
+ var config = options.ToConfig();
+
+ config.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.mongodb");
+ config.HasPath("akka.persistence.snapshot-store.mongodb").Should().BeTrue();
+ }
+
+ [Fact(DisplayName = "Empty MongoDbSnapshotOptions with default fallback should return default config")]
+ public void DefaultSnapshotOptionsTest()
+ {
+ var options = new MongoDbSnapshotOptions(false);
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(MongoDbPersistence.DefaultConfiguration());
+
+ emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
+ config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
+ config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
+ config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
+ config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
+ }
+
+ [Fact(DisplayName = "Empty MongoDbSnapshotOptions with custom identifier should equal empty config with default fallback")]
+ public void CustomIdSnapshotOptionsTest()
+ {
+ var options = new MongoDbSnapshotOptions(false, "custom");
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(MongoDbPersistence.DefaultConfiguration());
+
+ emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.custom");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
+ config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
+ config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
+ config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
+ config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
+ }
+
+ [Fact(DisplayName = "MongoDbSnapshotOptions should generate proper config")]
+ public void SnapshotOptionsTest()
+ {
+ var options = new MongoDbSnapshotOptions(true)
+ {
+ Identifier = "custom",
+ AutoInitialize = true,
+ ConnectionString = "testConnection",
+ Collection = "testCollection",
+ UseWriteTransaction = true,
+ LegacySerialization = true,
+ CallTimeout = TimeSpan.FromHours(2)
+ };
+
+ var baseConfig = options.ToConfig()
+ .WithFallback(MongoDbPersistence.DefaultConfiguration());
+
+ baseConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.custom");
+
+ var config = baseConfig.GetConfig("akka.persistence.snapshot-store.custom");
+ config.Should().NotBeNull();
+ config.GetString("connection-string").Should().Be(options.ConnectionString);
+ config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
+ config.GetString("collection").Should().Be(options.Collection);
+ config.GetBoolean("use-write-transaction").Should().Be(options.UseWriteTransaction.Value);
+ config.GetBoolean("legacy-serialization").Should().Be(options.LegacySerialization.Value);
+ config.GetTimeSpan("call-timeout").Should().Be(options.CallTimeout.Value);
+ }
+
+ [Fact(DisplayName = "MongoDbSnapshotOptions should be bindable to IConfiguration")]
+ public void SnapshotOptionsIConfigurationBindingTest()
+ {
+ const string json = @"
+ {
+ ""Logging"": {
+ ""LogLevel"": {
+ ""Default"": ""Information"",
+ ""Microsoft.AspNetCore"": ""Warning""
+ }
+ },
+ ""Akka"": {
+ ""SnapshotOptions"": {
+ ""ConnectionString"": ""mongodb://localhost:27017"",
+ ""UseWriteTransaction"": ""true"",
+ ""Identifier"": ""custommongodb"",
+ ""AutoInitialize"": true,
+ ""IsDefaultPlugin"": false,
+ ""Collection"": ""CustomEnventJournalCollection"",
+ ""LegacySerialization"" : ""true"",
+ ""CallTimeout"": ""00:10:00"",
+ ""Serializer"": ""hyperion"",
+ }
+ }
+ }";
+
+ var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
+ var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
+
+ var options = jsonConfig.GetSection("Akka:SnapshotOptions").Get();
+ options.ConnectionString.Should().Be("mongodb://localhost:27017");
+ options.UseWriteTransaction.Should().BeTrue();
+ options.Identifier.Should().Be("custommongodb");
+ options.AutoInitialize.Should().BeTrue();
+ options.IsDefaultPlugin.Should().BeFalse();
+ options.Collection.Should().Be("CustomEnventJournalCollection");
+ options.LegacySerialization.Should().BeTrue();
+ options.CallTimeout.Should().Be(10.Minutes());
+ options.Serializer.Should().Be("hyperion");
+
+ // Dispose called here as project not using latest language features.
+ stream.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.MongoDb/MongoDbSettings.cs b/src/Akka.Persistence.MongoDb/MongoDbSettings.cs
index 82003c8..59d4490 100644
--- a/src/Akka.Persistence.MongoDb/MongoDbSettings.cs
+++ b/src/Akka.Persistence.MongoDb/MongoDbSettings.cs
@@ -64,6 +64,8 @@ protected MongoDbSettings(Config config)
///
public class MongoDbJournalSettings : MongoDbSettings
{
+ public const string JournalConfigPath = "akka.persistence.journal.mongodb";
+
public string MetadataCollection { get; private set; }
public MongoDbJournalSettings(Config config) : base(config)
@@ -81,6 +83,8 @@ public MongoDbJournalSettings(Config config) : base(config)
///
public class MongoDbSnapshotSettings : MongoDbSettings
{
+ public const string SnapshotStoreConfigPath = "akka.persistence.snapshot-store.mongodb";
+
public MongoDbSnapshotSettings(Config config) : base(config)
{
if (config == null)
diff --git a/src/common.props b/src/common.props
index cac50d1..8a10f41 100644
--- a/src/common.props
+++ b/src/common.props
@@ -22,6 +22,7 @@
2.4.2
17.6.3
1.5.8
+ 6.11.0