From 32ed1a54bf94e7df807092c25aa266dfd962df44 Mon Sep 17 00:00:00 2001 From: Yelaman Abdullin Date: Sun, 16 Apr 2023 23:14:13 +1000 Subject: [PATCH 1/3] feat: add MongoDB storage support This commit introduces MongoDB storage support to the project by adding necessary classes, methods, and configurations. Key changes include: Added MongoDbStorage, MongoDbStorageSettings, and MongoDbStorageOptions classes for handling storage operations. Implemented extension methods for configuring and registering MongoDB storage in the IServiceCollection. Provided detailed XML documentation for all new classes and methods. These enhancements enable seamless integration of MongoDB storage into the existing infrastructure, offering a robust and scalable storage solution. --- ...t.Builder.Community.Storage.MongoDB.csproj | 12 +++ .../MongoDbStorageSettings.cs | 87 +++++++++++++++++++ .../ServiceCollectionExtensions.cs | 54 ++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/Bot.Builder.Community.Storage.MongoDB.csproj create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageSettings.cs create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/ServiceCollectionExtensions.cs diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/Bot.Builder.Community.Storage.MongoDB.csproj b/libraries/Bot.Builder.Community.Storage.MongoDB/Bot.Builder.Community.Storage.MongoDB.csproj new file mode 100644 index 00000000..af2f8177 --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/Bot.Builder.Community.Storage.MongoDB.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + + + + + + + + diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageSettings.cs b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageSettings.cs new file mode 100644 index 00000000..12da8384 --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageSettings.cs @@ -0,0 +1,87 @@ +using System; +using Microsoft.Extensions.Configuration; + +namespace Bot.Builder.Community.Storage.MongoDB +{ + public class MongoDbStorageSettings + { + public Type[] AllowedTypes { get; private set; } + + public MongoDbStorageOptions Options { get; private set; } + + /// + /// Registers the specified types for MongoDB storage. + /// + /// An array of objects to be registered for storage. + /// The current instance, allowing method calls to be chained. + /// + /// This method sets the property with the provided types. It is useful for configuring the MongoDB storage settings with the desired types that need to be stored in the database. + /// + + public MongoDbStorageSettings RegisterTypes(params Type[] types) + { + AllowedTypes = types; + return this; + } + + /// + /// Configures the using the provided . + /// + /// The to bind the options to. + /// The current instance, allowing method calls to be chained. + /// + /// This method creates a new instance of and binds the provided configuration section to it. The resulting options are then used to configure the MongoDB storage settings. + /// + /// + /// ... + /// "ConnectionString": "...", + /// "DatabaseName": "...", + /// "CollectionName": "...", + /// ... + /// + public MongoDbStorageSettings ConfigureOptions(IConfigurationSection configuration) + { + Options = new MongoDbStorageOptions(); + configuration.Bind(Options); + return this; + } + + /// + /// Configures the using the provided instance. + /// + /// The instance to use for configuration. + /// The current instance, allowing method calls to be chained. + /// + /// This method sets the property with the provided instance. It is useful for configuring the MongoDB storage settings using a preconfigured options object. + /// + public MongoDbStorageSettings ConfigureOptions(MongoDbStorageOptions options) + { + Options = options; + return this; + } + + /// + /// Configures the using the provided connection string, database name, and optionally, collection name. + /// + /// The connection string for the MongoDB instance. + /// The name of the database to be used for storage. + /// The optional name of the collection to be used for storage (default is null). + /// The current instance, allowing method calls to be chained. + /// + /// This method creates a new instance of and sets the provided connection string, database name, and collection name (if provided). The resulting options are then used to configure the MongoDB storage settings. + /// + public MongoDbStorageSettings ConfigureOptions( + string connectionString, + string databaseName, + string collectionName = null) + { + Options = new MongoDbStorageOptions + { + ConnectionString = connectionString, + DatabaseName = databaseName, + CollectionName = collectionName + }; + return this; + } + } +} \ No newline at end of file diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/ServiceCollectionExtensions.cs b/libraries/Bot.Builder.Community.Storage.MongoDB/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..6f7bf5ee --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/ServiceCollectionExtensions.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using Microsoft.Bot.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; + +namespace Bot.Builder.Community.Storage.MongoDB +{ + public static class ServiceCollectionExtensions + { + /// + /// Adds and configures MongoDB storage support + /// + /// The to add the storage to. + /// A delegate to configure the . + /// The of the storage context (default is ). + /// The same instance so that multiple calls can be chained. + /// Thrown when no types are found to scan or when the connection string or database name is not provided. + /// + /// This extension method configures MongoDB storage support for the specified service collection. It requires the caller to provide at least one type to scan and a valid connection string and database name. + /// + public static IServiceCollection AddMongoDbStorage(this IServiceCollection services, + Action settings, + ServiceLifetime contextLifetime = ServiceLifetime.Singleton) + { + var config = new MongoDbStorageSettings(); + + settings.Invoke(config); + + if (!config.AllowedTypes.Any()) + { + throw new ArgumentException("No types found to scan. Supply at least one type"); + } + + if (config.Options == null + || string.IsNullOrEmpty(config.Options.ConnectionString) + || string.IsNullOrEmpty(config.Options.DatabaseName) + ) + { + throw new ArgumentException("No connection string or database name found."); + } + + var objectSerializer = new ObjectSerializer(type => ObjectSerializer.DefaultAllowedTypes(type) || config.AllowedTypes.Contains(type)); + + BsonSerializer.RegisterSerializer(objectSerializer); + + services.TryAdd(new ServiceDescriptor(typeof(IStorage), provider => new MongoDbStorage(config.Options), contextLifetime)); + + return services; + } + } +} \ No newline at end of file From 9510f784c958ce9d80b2fe3a508c2749769234a0 Mon Sep 17 00:00:00 2001 From: Yelaman Abdullin Date: Sun, 16 Apr 2023 23:14:31 +1000 Subject: [PATCH 2/3] feat: add MongoDB storage support This commit introduces MongoDB storage support to the project by adding necessary classes, methods, and configurations. Key changes include: Added MongoDbStorage, MongoDbStorageSettings, and MongoDbStorageOptions classes for handling storage operations. Implemented extension methods for configuring and registering MongoDB storage in the IServiceCollection. Provided detailed XML documentation for all new classes and methods. These enhancements enable seamless integration of MongoDB storage into the existing infrastructure, offering a robust and scalable storage solution. --- Bot.Builder.Community.sln | 11 ++ .../MongoDbStorage.cs | 114 ++++++++++++++++++ .../MongoDbStorageOptions.cs | 39 ++++++ 3 files changed, 164 insertions(+) create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorage.cs create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageOptions.cs diff --git a/Bot.Builder.Community.sln b/Bot.Builder.Community.sln index 0e3583bc..6cec3361 100644 --- a/Bot.Builder.Community.sln +++ b/Bot.Builder.Community.sln @@ -183,6 +183,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot.Builder.Community.Adapt EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot.Builder.Community.Adapters.Facebook.Tests", "tests\Bot.Builder.Community.Adapters.Facebook.Tests\Bot.Builder.Community.Adapters.Facebook.Tests.csproj", "{8201DC48-763A-4534-9E51-466E15DF01D8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bot.Builder.Community.Storage.MongoDB", "libraries\Bot.Builder.Community.Storage.MongoDB\Bot.Builder.Community.Storage.MongoDB.csproj", "{74BE0FA2-3C6D-4807-8C73-CF87E898276C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug - NuGet Packages|Any CPU = Debug - NuGet Packages|Any CPU @@ -805,6 +807,14 @@ Global {8201DC48-763A-4534-9E51-466E15DF01D8}.Documentation|Any CPU.Build.0 = Debug|Any CPU {8201DC48-763A-4534-9E51-466E15DF01D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {8201DC48-763A-4534-9E51-466E15DF01D8}.Release|Any CPU.Build.0 = Release|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Documentation|Any CPU.Build.0 = Debug|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74BE0FA2-3C6D-4807-8C73-CF87E898276C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -893,6 +903,7 @@ Global {428AD1B4-DF58-4D21-9C19-AB4AB6001A90} = {840D4038-9AB8-4750-9FFE-365386CE47E2} {3348B9A5-E3CE-4AF8-B059-8B4D7971C25A} = {840D4038-9AB8-4750-9FFE-365386CE47E2} {8201DC48-763A-4534-9E51-466E15DF01D8} = {840D4038-9AB8-4750-9FFE-365386CE47E2} + {74BE0FA2-3C6D-4807-8C73-CF87E898276C} = {DC62D60A-2EA2-4DB1-B1BA-C8F38D3940B3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9FE3B75E-BA2B-45BC-BBF0-DDA8BA10C4F0} diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorage.cs b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorage.cs new file mode 100644 index 00000000..7a654a20 --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorage.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Bot.Builder; +using Microsoft.Extensions.Options; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver; +using Newtonsoft.Json.Linq; + +namespace Bot.Builder.Community.Storage.MongoDB +{ + public class MongoDbStorage: IStorage + { + private readonly MongoDbStorageOptions _options; + private readonly MongoClient _mongoClient; + + + /// + /// Initializes a new instance of the class. + /// + /// MongoDb options class. + public MongoDbStorage(MongoDbStorageOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (string.IsNullOrEmpty(options.ConnectionString)) + { + throw new ArgumentNullException(nameof(options.ConnectionString)); + } + + _options = options; + + _mongoClient = new MongoClient(options.ConnectionString); + + } + + /// + /// Reads storage items from storage. + /// + /// keys of the objects to read. + /// A cancellation token that can be used by other objects + /// or threads to receive notice of cancellation. + /// A task that represents the work queued to execute. + /// If the activities are successfully sent, the task result contains + /// the items read, indexed by key. + /// + /// + public async Task> ReadAsync(string[] keys, CancellationToken cancellationToken = new CancellationToken()) + { + var filter = Builders.Filter + .In(o => o.Id, keys); + + var result = await GetCollection().Find(filter).ToListAsync(cancellationToken: cancellationToken); + + return result.ToDictionary(x => x.Id, x => x.Data); + } + + /// + /// Writes storage items to storage. + /// + /// The items to write, indexed by key. + /// A cancellation token that can be used by other objects + /// or threads to receive notice of cancellation. + /// A task that represents the work queued to execute. + /// + /// + public async Task WriteAsync(IDictionary changes, CancellationToken cancellationToken = new CancellationToken()) + { + var collection = GetCollection(); + foreach (var change in changes) + { + var filter = Builders.Filter.Eq(o => o.Id, change.Key); + var update = Builders.Update.Set(o => o.Data, change.Value); + var options = new UpdateOptions { IsUpsert = true }; + await collection.UpdateOneAsync(filter, update, options, cancellationToken); + } + } + + /// + /// Writes storage items to storage. + /// + /// The items to write, indexed by key. + /// A cancellation token that can be used by other objects + /// or threads to receive notice of cancellation. + /// A task that represents the work queued to execute. + /// + /// + public Task DeleteAsync(string[] keys, CancellationToken cancellationToken = new CancellationToken()) + { + + var filter = Builders.Filter + .In(restaurant => restaurant.Id, keys); + + return GetCollection().DeleteManyAsync(filter, cancellationToken); + } + + private IMongoCollection GetCollection() + { + var database = _mongoClient.GetDatabase(_options.DatabaseName); + var collection = database.GetCollection(_options.CollectionName); + return collection; + } + private class StorageEntry + { + public string Id { get; set; } + public object Data { get; set; } + } + } +} \ No newline at end of file diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageOptions.cs b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageOptions.cs new file mode 100644 index 00000000..e5ccbccd --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/MongoDbStorageOptions.cs @@ -0,0 +1,39 @@ +namespace Bot.Builder.Community.Storage.MongoDB +{ + /// + /// Represents the MongoDB storage configuration options. + /// + /// + /// This class is used to configure the settings for connecting to a MongoDB instance, specifying the database and collection to be used for storage. + /// + public class MongoDbStorageOptions + { + /// + /// Gets or sets the connection string for the MongoDB instance. + /// + /// The connection string. + /// + /// The connection string is used to specify the location and authentication details for the MongoDB instance. + /// + public string ConnectionString { get; set; } + + /// + /// Gets or sets the name of the database to be used for storage. + /// + /// The name of the database. + /// + /// The database name is used to determine which database within the MongoDB instance should be used for storing data. + /// + public string DatabaseName { get; set; } + + /// + /// Gets or sets the name of the collection to be used for storage. + /// + /// The name of the collection. Defaults to "StateData". + /// + /// The collection name is used to determine which collection within the specified database should be used for storing data. If not specified, the default value is "StateData". + /// + public string CollectionName { get; set; } = "StateData"; + } + +} \ No newline at end of file From 3741e672329de8015cd389dcf6dbce146be72679 Mon Sep 17 00:00:00 2001 From: Yelaman Abdullin Date: Sun, 16 Apr 2023 23:30:28 +1000 Subject: [PATCH 3/3] added Readme.md --- .../Readme.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 libraries/Bot.Builder.Community.Storage.MongoDB/Readme.md diff --git a/libraries/Bot.Builder.Community.Storage.MongoDB/Readme.md b/libraries/Bot.Builder.Community.Storage.MongoDB/Readme.md new file mode 100644 index 00000000..32cc3f75 --- /dev/null +++ b/libraries/Bot.Builder.Community.Storage.MongoDB/Readme.md @@ -0,0 +1,46 @@ +# MongoDB Storage Integration + +This functionality enables seamless integration of MongoDB storage into your existing project. With this integration, you can easily store and retrieve data using MongoDB as your backend storage system. + +## Features +- Robust and scalable MongoDB storage solution +- Easy configuration and registration of MongoDB storage using extension methods +- Support for custom serialization of allowed types + +## Getting Started + +To get started, follow these steps: + +1. Install the required package `Bot.Builder.Community.Storage.MongoDB` for your project + + +2. Add the following using statements to your project: +```csharp +using Bot.Builder.Community.Storage.MongoDB; +``` + +3. In the ConfigureServices method of your Startup.cs file, add the following code to configure and register MongoDB storage: +```csharp +services.AddMongoDbStorage(settings => +{ + settings.ConfigureOptions(Configuration.GetSection("MongoDb")); + settings.RegisterTypes( + typeof(YourType1), + typeof(YourType2) + ); +}); +``` +Replace YourType with the type(s) you want to store in + +4. Update your appsettings.json file to include the MongoDB configuration settings: + +```json +{ + "MongoDb": { + "ConnectionString": "your_connection_string", + "Database": "your_database_name", + "Collection": "your_collection_name" + } +} +``` +Replace your_connection_string, your_database_name, and your_collection_name with the appropriate values for your MongoDB instance. \ No newline at end of file