diff --git a/README.md b/README.md index 26825e5..d1df18a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,16 @@ akka.persistence.snapshot-store.azure-blob-store.connection-string = "Your Azure akka.persistence.snapshot-store.azure-blob-store.container-name = "Your container name" ``` +## Using the plugin in local development environment + +You can use this plugin with Azure Storage Emulator in a local development environment by setting the development flag in the configuration file: +``` +akka.persistence.journal.azure-table.development = on +akka.persistence.snapshot-store.azure-blob-store.development = on +``` + +you do **not** need to provide a connection string for this to work, it is handled automatically by the Microsoft Azure SDK. + ## Building this solution To run the build script associated with this solution, execute the following: diff --git a/src/Akka.Persistence.Azure.Tests/AzurePersistenceConfigSpec.cs b/src/Akka.Persistence.Azure.Tests/AzurePersistenceConfigSpec.cs index b6685c6..e71043a 100644 --- a/src/Akka.Persistence.Azure.Tests/AzurePersistenceConfigSpec.cs +++ b/src/Akka.Persistence.Azure.Tests/AzurePersistenceConfigSpec.cs @@ -86,25 +86,6 @@ public void ShouldProvideDefaultTableNameValue() tableSettings.TableName.Should().Be("AkkaPersistenceDefaultTable"); } - [Fact] - public void ShouldParseQueryConfig() - { - var querySettings = - AzureTableStorageQuerySettings.Create( - ConfigurationFactory.ParseString(@"akka.persistence.query.journal.azure-table{ - class = ""classname"" - write-plugin = foo - max-buffer-size = 100 - refresh-interval = 3s - }").WithFallback(AzurePersistence.DefaultConfig) - .GetConfig("akka.persistence.query.journal.azure-table")); - - querySettings.Class.Should().Be("classname"); - querySettings.WritePlugin.Should().Be("foo"); - querySettings.MaxBufferSize.Should().Be("100"); - querySettings.RefreshInterval.Should().Be(new TimeSpan(0, 0, 3)); - } - [Theory] [InlineData("fo", "Invalid table name length")] [InlineData("1foo", "Invalid table name")] diff --git a/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournal.cs b/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournal.cs index e24e28e..94f4d88 100644 --- a/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournal.cs +++ b/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournal.cs @@ -19,6 +19,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Akka.Configuration; using Debug = System.Diagnostics.Debug; namespace Akka.Persistence.Azure.Journal @@ -54,11 +55,17 @@ public class AzureTableStorageJournal : AsyncWriteJournal private readonly Lazy _tableStorage; private readonly Dictionary> _tagSubscribers = new Dictionary>(); - public AzureTableStorageJournal() + public AzureTableStorageJournal(Config config = null) { - _settings = AzurePersistence.Get(Context.System).TableSettings; + _settings = config is null ? + AzurePersistence.Get(Context.System).TableSettings : + AzureTableStorageJournalSettings.Create(config); + _serialization = new SerializationHelper(Context.System); - _storageAccount = CloudStorageAccount.Parse(_settings.ConnectionString); + _storageAccount = _settings.Development ? + CloudStorageAccount.DevelopmentStorageAccount : + CloudStorageAccount.Parse(_settings.ConnectionString); + _tableStorage = new Lazy(() => InitCloudStorage(5).Result); } diff --git a/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournalSettings.cs b/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournalSettings.cs index 1d7f1bc..7bc239b 100644 --- a/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournalSettings.cs +++ b/src/Akka.Persistence.Azure/Journal/AzureTableStorageJournalSettings.cs @@ -23,8 +23,12 @@ public AzureTableStorageJournalSettings( string tableName, TimeSpan connectTimeout, TimeSpan requestTimeout, - bool verboseLogging) + bool verboseLogging, + bool development) { + if(string.IsNullOrWhiteSpace(tableName)) + throw new ConfigurationException("[AzureTableStorageJournal] Table name is null or empty."); + NameValidator.ValidateTableName(tableName); if (ReservedTableNames.Contains(tableName)) @@ -38,6 +42,7 @@ public AzureTableStorageJournalSettings( ConnectTimeout = connectTimeout; RequestTimeout = requestTimeout; VerboseLogging = verboseLogging; + Development = development; } /// @@ -65,6 +70,8 @@ public AzureTableStorageJournalSettings( /// public bool VerboseLogging { get; } + public bool Development { get; } + /// /// Creates an instance using the /// `akka.persistence.journal.azure-table` HOCON configuration section. @@ -78,12 +85,15 @@ public static AzureTableStorageJournalSettings Create(Config config) var connectTimeout = config.GetTimeSpan("connect-timeout", TimeSpan.FromSeconds(3)); var requestTimeout = config.GetTimeSpan("request-timeout", TimeSpan.FromSeconds(3)); var verbose = config.GetBoolean("verbose-logging", false); + var development = config.GetBoolean("development", false); + return new AzureTableStorageJournalSettings( connectionString, tableName, connectTimeout, requestTimeout, - verbose); + verbose, + development); } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Azure/Query/AzureTableStorageQuerySettings.cs b/src/Akka.Persistence.Azure/Query/AzureTableStorageQuerySettings.cs deleted file mode 100644 index 7543fd5..0000000 --- a/src/Akka.Persistence.Azure/Query/AzureTableStorageQuerySettings.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Akka.Configuration; -using System; - -namespace Akka.Persistence.Azure.Query -{ - public sealed class AzureTableStorageQuerySettings - { - private AzureTableStorageQuerySettings( - string @class, - string writePlugin, - string maxBufferSize, - TimeSpan refreshInterval) - { - Class = @class; - MaxBufferSize = maxBufferSize; - RefreshInterval = refreshInterval; - WritePlugin = writePlugin; - } - - public string Class { get; } - - public string MaxBufferSize { get; } - - public TimeSpan RefreshInterval { get; } - - public string WritePlugin { get; } - - public static AzureTableStorageQuerySettings Create(Config config) - { - var @class = config.GetString("class"); - var writePlugin = config.GetString("write-plugin"); - var maxBufferSize = config.GetString("max-buffer-size"); - var refreshInterval = config.GetTimeSpan("refresh-interval"); - - return new AzureTableStorageQuerySettings( - @class, - writePlugin, - maxBufferSize, - refreshInterval); - } - } -} \ No newline at end of file diff --git a/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStore.cs b/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStore.cs index c1fe15e..6ad24fc 100644 --- a/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStore.cs +++ b/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStore.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Akka.Configuration; using Akka.Event; using Akka.Persistence.Azure.Util; using Akka.Persistence.Snapshot; @@ -32,12 +33,16 @@ public class AzureBlobSnapshotStore : SnapshotStore private readonly AzureBlobSnapshotStoreSettings _settings; private readonly CloudStorageAccount _storageAccount; - public AzureBlobSnapshotStore() + public AzureBlobSnapshotStore(Config config = null) { - _settings = AzurePersistence.Get(Context.System).BlobSettings; _serialization = new SerializationHelper(Context.System); - _storageAccount = CloudStorageAccount.Parse(_settings.ConnectionString); + _settings = config is null + ? AzurePersistence.Get(Context.System).BlobSettings + : AzureBlobSnapshotStoreSettings.Create(config); + _storageAccount = _settings.Development ? + CloudStorageAccount.DevelopmentStorageAccount : + CloudStorageAccount.Parse(_settings.ConnectionString); _container = new Lazy(() => InitCloudStorage().Result); } diff --git a/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStoreSettings.cs b/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStoreSettings.cs index b885911..d40abd7 100644 --- a/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStoreSettings.cs +++ b/src/Akka.Persistence.Azure/Snapshot/AzureBlobSnapshotStoreSettings.cs @@ -17,14 +17,18 @@ namespace Akka.Persistence.Azure.Snapshot public sealed class AzureBlobSnapshotStoreSettings { public AzureBlobSnapshotStoreSettings(string connectionString, string containerName, - TimeSpan connectTimeout, TimeSpan requestTimeout, bool verboseLogging) + TimeSpan connectTimeout, TimeSpan requestTimeout, bool verboseLogging, bool development) { + if (string.IsNullOrWhiteSpace(containerName)) + throw new ConfigurationException("[AzureBlobSnapshotStore] Container name is null or empty."); + NameValidator.ValidateContainerName(containerName); ConnectionString = connectionString; ContainerName = containerName; RequestTimeout = requestTimeout; ConnectTimeout = connectTimeout; VerboseLogging = verboseLogging; + Development = development; } /// @@ -52,6 +56,8 @@ public AzureBlobSnapshotStoreSettings(string connectionString, string containerN /// public bool VerboseLogging { get; } + public bool Development { get; } + /// /// Creates an instance using the /// `akka.persistence.snapshot-store.azure-blob-store` HOCON configuration section. @@ -61,16 +67,19 @@ public AzureBlobSnapshotStoreSettings(string connectionString, string containerN public static AzureBlobSnapshotStoreSettings Create(Config config) { var connectionString = config.GetString("connection-string"); + var containerName = config.GetString("container-name"); var connectTimeout = config.GetTimeSpan("connect-timeout", TimeSpan.FromSeconds(3)); var requestTimeout = config.GetTimeSpan("request-timeout", TimeSpan.FromSeconds(3)); var verbose = config.GetBoolean("verbose-logging", false); - var containerName = config.GetString("container-name"); + var development = config.GetBoolean("development", false); + return new AzureBlobSnapshotStoreSettings( connectionString, containerName, connectTimeout, - requestTimeout, - verbose); + requestTimeout, + verbose, + development); } } } \ No newline at end of file diff --git a/src/Akka.Persistence.Azure/reference.conf b/src/Akka.Persistence.Azure/reference.conf index 07f9475..be83a76 100644 --- a/src/Akka.Persistence.Azure/reference.conf +++ b/src/Akka.Persistence.Azure/reference.conf @@ -27,6 +27,10 @@ akka.persistence { # dispatcher used to drive journal actor plugin-dispatcher = "akka.actor.default-dispatcher" + + # Support for Azure Storage Emulator for local development. + # Will ignore connection string settings if turned on. + development = off } } @@ -78,6 +82,10 @@ akka.persistence { # dispatcher used to drive snapshot storage actor plugin-dispatcher = "akka.actor.default-dispatcher" + + # Support for Azure Storage Emulator for local development. + # Will ignore connection string settings if turned on. + development = off } } } \ No newline at end of file