diff --git a/README.md b/README.md index 7b63678..4ad9c1f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,74 @@ Detailed player & tank statistics and history for [World of Tanks: Blitz](ht - Frontent - Blazor WebAssembly (coming soon) ### Local MongoDB Installation for debugging ([Docker desktop for Windows](https://www.docker.com/products/docker-desktop)) -(Coming soon) +For the developing and debugging purposes, you have to istall MongoDB locally on your dev machine and run it as a service. Alternatively, if you have istalled [Docker desktop for Windows](https://www.docker.com/products/docker-desktop), you can deploy MongoDB inside the container. To do so, create an empty folder and make 2 files there: ```docker-compose.yml``` and ```.env```. Second one will contain sensitive data. + +
+ Example for docker-compose.yml with mongo-express shell: + +```yml +version: "3" +services: + mongo: + image: mongo + restart: always + container_name: "mein-mongo" + ports: + - "27017:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: "${DB_USERNAME}" + MONGO_INITDB_ROOT_PASSWORD: "${DB_PASSWORD}" + volumes: + - "mongodblvolume:/data/db" + mongo-express: + image: mongo-express + container_name: "mein-mongo-express" + restart: always + ports: + - 8081:8081 + environment: + ME_CONFIG_MONGODB_ADMINUSERNAME: "${DB_USERNAME}" + ME_CONFIG_MONGODB_ADMINPASSWORD: "${DB_PASSWORD}" +volumes: + mongodblvolume: +``` +
+ +
+Example of .env file + +``` +DB_USERNAME=root +DB_PASSWORD=P@SSW0RT! +``` +
+
+To install docker, use shell command: + +```bash +docker-compose up -d +``` + +That's all. You can use mongo shell like this: + +```bash +docker exec -it mein-mongo bash +mongo --username root --password --authenticationDatabase admin +use your_database_name +db.someSampleCollection.insert({}) +``` + +Or you can use mongo-express by this address: http://localhost:8081/ + +To use this mongo instance in application, use this connection string in ```appsettings.json```: + +```json +{ + "Mongo": { + "ConnectionString": "mongodb://root:P@SSW0RT!@localhost:27017/?authSource=admin" + } +} +``` ### Hosting solution (k8s) (Coming soon) diff --git a/WotBlitzStatisticsPro.DataAccess/IWargamingAccountDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/IWargamingAccountDataAccessor.cs new file mode 100644 index 0000000..0af5d0f --- /dev/null +++ b/WotBlitzStatisticsPro.DataAccess/IWargamingAccountDataAccessor.cs @@ -0,0 +1,7 @@ +namespace WotBlitzStatisticsPro.DataAccess +{ + public interface IWargamingAccountDataAccessor + { + + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/Model/Accounts/AccountInfo.cs b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/AccountInfo.cs new file mode 100644 index 0000000..f1ff745 --- /dev/null +++ b/WotBlitzStatisticsPro.DataAccess/Model/Accounts/AccountInfo.cs @@ -0,0 +1,42 @@ +using MongoDB.Bson.Serialization.Attributes; +using WotBlitzStatisticsPro.Common.Model; + +namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts +{ + /// + /// Account information + /// + public class AccountInfo + { + /// + /// Player account identifier + /// + [BsonId] + public long AccountId { get; set; } + + /// + /// Player region + /// + public RealmType Realm { get; set; } + + /// + /// Account creation date + /// + public int CreatedAt { get; set; } + + /// + /// Last battle time + /// + public int LastBattleTime { get; set; } + + /// + /// Player's nick + /// + public string Nickname { get; set; } + + /// + /// Player information update date + /// + public int? UpdatedAt { get; set; } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/WargamingAccountDataAccessor.cs b/WotBlitzStatisticsPro.DataAccess/WargamingAccountDataAccessor.cs new file mode 100644 index 0000000..dfea248 --- /dev/null +++ b/WotBlitzStatisticsPro.DataAccess/WargamingAccountDataAccessor.cs @@ -0,0 +1,44 @@ +using System.Runtime.Serialization.Formatters; +using MongoDB.Driver; +using WotBlitzStatisticsPro.Common; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; + +namespace WotBlitzStatisticsPro.DataAccess +{ + public class WargamingAccountDataAccessor : IWargamingAccountDataAccessor + { + private const string AccountInfoCollectionName = "account-info"; + + + private readonly IMongoDatabase _database; + + public WargamingAccountDataAccessor(IMongoSettings mongoSettings) + { + var client = new MongoClient(mongoSettings.ConnectionString); + _database = client.GetDatabase(mongoSettings.DatabaseName); + + CreateAccountIndexesIfNotExists(); + } + + private void CreateAccountIndexesIfNotExists() + { + var accountInfoBuilder = Builders.IndexKeys; + var accountInfoRealmIndexModel = new CreateIndexModel(accountInfoBuilder.Ascending(a => a.Realm)); + var accountInfoLastBattleTimeIndexModel = new CreateIndexModel(accountInfoBuilder.Descending(a => a.LastBattleTime)); + _database.GetCollection(AccountInfoCollectionName).Indexes + .CreateMany(new[] {accountInfoRealmIndexModel, accountInfoLastBattleTimeIndexModel}); + + + /* + +// Multiple keys +var indexDefinition = Builders.IndexKeys.Combine( +Builders.IndexKeys.Ascending(f => f.Key1), +Builders.IndexKeys.Ascending(f => f.Key2)); + +await collection.Indexes.CreateOneAsync(indexDefinition); + */ + + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.DataAccess/WotBlitzStatisticsDataAccessInstaller.cs b/WotBlitzStatisticsPro.DataAccess/WotBlitzStatisticsDataAccessInstaller.cs index e7a8b7d..568f636 100644 --- a/WotBlitzStatisticsPro.DataAccess/WotBlitzStatisticsDataAccessInstaller.cs +++ b/WotBlitzStatisticsPro.DataAccess/WotBlitzStatisticsDataAccessInstaller.cs @@ -7,6 +7,7 @@ namespace WotBlitzStatisticsPro.DataAccess public static void ConfigureServices(IServiceCollection services) { services.AddTransient(); + services.AddTransient(); } } diff --git a/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs b/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs index a386599..5d0f19e 100644 --- a/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs +++ b/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs @@ -10,10 +10,13 @@ namespace WotBlitzStatisticsPro.GraphQl.Mutation public class WotBlitzStatisticsMutation { private readonly IWargamingDictionaries _wargamingDictionaries; + private readonly IWargamingAccounts _wargamingAccounts; - public WotBlitzStatisticsMutation(IWargamingDictionaries wargamingDictionaries) + public WotBlitzStatisticsMutation(IWargamingDictionaries wargamingDictionaries, + IWargamingAccounts wargamingAccounts) { _wargamingDictionaries = wargamingDictionaries; + _wargamingAccounts = wargamingAccounts; } /// @@ -26,5 +29,17 @@ namespace WotBlitzStatisticsPro.GraphQl.Mutation { return _wargamingDictionaries.UpdateDictionaries(updateDictionariesRequest); } + + /// + /// Gathers all account information + /// + /// Account region + /// AccountId + /// Request language + /// + public Task GatherAccountInformation(RealmType realmType, long accountId, RequestLanguage requestLanguage) + { + return _wargamingAccounts.GatherAccountInformation(realmType, accountId, requestLanguage); + } } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs new file mode 100644 index 0000000..5a111ad --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/AccountInformationPipelineContext.cs @@ -0,0 +1,23 @@ +using WotBlitzStatisticsPro.Common.Model; + +namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline +{ + public class AccountInformationPipelineContext + { + public AccountInformationPipelineContext( + long accountId, + RealmType realmType, + RequestLanguage requestLanguage) + { + AccountId = accountId; + RealmType = realmType; + RequestLanguage = requestLanguage; + } + + + public long AccountId { get; } + public RealmType RealmType { get; } + public RequestLanguage RequestLanguage { get; } + + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs new file mode 100644 index 0000000..56dfc2b --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Logic.Pipeline; + +namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations +{ + public class GetAccountInfoOperation: IOperation + { + public Task Invoke(AccountInformationPipelineContext context, Func next) + { + return next.Invoke(context); + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs new file mode 100644 index 0000000..2f3dbd6 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Logic.Pipeline; + +namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations +{ + public class GetTanksInfoOperation: IOperation + { + public Task Invoke(AccountInformationPipelineContext context, Func next) + { + return next.Invoke(context); + + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs b/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs new file mode 100644 index 0000000..8c80730 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Common.Model; + +namespace WotBlitzStatisticsPro.Logic +{ + public interface IWargamingAccounts + { + Task GatherAccountInformation(RealmType realm, long accountId, RequestLanguage requestLanguage); + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/IOperation.cs b/WotBlitzStatisticsPro.Logic/Pipeline/IOperation.cs new file mode 100644 index 0000000..a820266 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/IOperation.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + /// + /// Represents pipeline single operation + /// + public interface IOperation + { + /// + /// Invokes the pipeline operation + /// + /// Pipeline context + /// Next operation + Task Invoke(TContext context, Func next); + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/IOperationFactory.cs b/WotBlitzStatisticsPro.Logic/Pipeline/IOperationFactory.cs new file mode 100644 index 0000000..d52c285 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/IOperationFactory.cs @@ -0,0 +1,18 @@ +using System; + +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + /// + /// Factory creates the operation instances. + /// + public interface IOperationFactory + { + /// + /// Creates new operation instance + /// + /// + /// + /// + IOperation Create(Type operationType); + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/IPipeline.cs b/WotBlitzStatisticsPro.Logic/Pipeline/IPipeline.cs new file mode 100644 index 0000000..af8885c --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/IPipeline.cs @@ -0,0 +1,21 @@ +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + /// + /// Represents operation pipeline + /// + public interface IPipeline + { + /// + /// Builds the pipeline. + /// + /// First operation in pipeline. + IOperation Build(); + + /// + /// Adds operation type to the pipeline + /// + /// Operation type + /// Pipeline instance + IPipeline AddOperation() where TOperation : IOperation; + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/OperationMediator.cs b/WotBlitzStatisticsPro.Logic/Pipeline/OperationMediator.cs new file mode 100644 index 0000000..2ca2fab --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/OperationMediator.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; + +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + public class OperationMediator: IOperation + { + private readonly IOperation _current; + private readonly IOperation _next; + + public OperationMediator(IOperation current, IOperation next) + { + _current = current ?? throw new ArgumentNullException(nameof(current)); + _next = next; + } + + public Task Invoke(TContext context, Func next) + { + return _current.Invoke(context, c => _next == null ? Task.CompletedTask : _next.Invoke(c, null)); + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/Pipeline.cs b/WotBlitzStatisticsPro.Logic/Pipeline/Pipeline.cs new file mode 100644 index 0000000..14ecd48 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/Pipeline.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + public class Pipeline: IPipeline + { + private readonly IOperationFactory _operationFactory; + private readonly List _operations = new List(); + + public Pipeline(IOperationFactory operationFactory) + { + _operationFactory = operationFactory; + } + + public IOperation Build() + { + var allOperationTypes = _operations.ToArray(); + var lastOperationType = allOperationTypes.LastOrDefault(); + if (lastOperationType == null) + { + return null; + } + var nextOperation = new OperationMediator(_operationFactory.Create(lastOperationType), null); + + for (int i = allOperationTypes.Length - 2; i >= 0; i--) + { + nextOperation = new OperationMediator(_operationFactory.Create(allOperationTypes[i]), nextOperation); + } + + return nextOperation; + } + + public IPipeline AddOperation() where TOperation : IOperation + { + _operations.Add(typeof(TOperation)); + return this; + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Pipeline/ServiceProviderOperationFactory.cs b/WotBlitzStatisticsPro.Logic/Pipeline/ServiceProviderOperationFactory.cs new file mode 100644 index 0000000..f68513b --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Pipeline/ServiceProviderOperationFactory.cs @@ -0,0 +1,19 @@ +using System; + +namespace WotBlitzStatisticsPro.Logic.Pipeline +{ + public class ServiceProviderOperationFactory: IOperationFactory + { + private readonly IServiceProvider _serviceProvider; + + public ServiceProviderOperationFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public IOperation Create(Type operationType) + { + return _serviceProvider.GetService(operationType) as IOperation; + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs new file mode 100644 index 0000000..47c8081 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Common.Model; +using WotBlitzStatisticsPro.Logic.AccountInformationPipeline; +using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; +using WotBlitzStatisticsPro.Logic.Pipeline; + +namespace WotBlitzStatisticsPro.Logic +{ + public class WargamingAccounts: IWargamingAccounts + { + private readonly IOperationFactory _operationFactory; + + public WargamingAccounts(IOperationFactory operationFactory) + { + _operationFactory = operationFactory; + } + + public async Task GatherAccountInformation(RealmType realm, long accountId, RequestLanguage requestLanguage) + { + var context = new AccountInformationPipelineContext(accountId, realm, requestLanguage); + var pipeline = new Pipeline(_operationFactory); + + pipeline.AddOperation() + .AddOperation() + ; + // ToDo: add other operations + + await pipeline.Build() + .Invoke(context, null) + .ConfigureAwait(false); + + // ToDo: return context response + + return true; + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs index a9a652b..4a1b96e 100644 --- a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs +++ b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs @@ -4,7 +4,9 @@ using AutoMapper; using Microsoft.Extensions.DependencyInjection; using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.DataAccess; +using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; using WotBlitzStatisticsPro.Logic.Dictionaries; +using WotBlitzStatisticsPro.Logic.Pipeline; using WotBlitzStatisticsPro.WgApiClient; [assembly: InternalsVisibleTo("WotBlitzStatisticsPro.Tests")] @@ -17,12 +19,14 @@ namespace WotBlitzStatisticsPro.Logic public static void ConfigureServices(IServiceCollection services) { ConfigureDictionariesFactory(services); + ConfigureOperationsFactory(services); WotBlitzStatisticsDataAccessInstaller.ConfigureServices(services); services.AddAutoMapper(typeof(WotBlitzStatisticsLogicInstaller)); services.AddHttpClient(); services.AddHttpClient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } private static void ConfigureDictionariesFactory(IServiceCollection services) @@ -31,10 +35,10 @@ namespace WotBlitzStatisticsPro.Logic services.AddTransient(); services.AddTransient(); - RegisterFactoryMethod(services); + RegisterDictionariesFactoryMethod(services); } - internal static void RegisterFactoryMethod(IServiceCollection services) + internal static void RegisterDictionariesFactoryMethod(IServiceCollection services) { services.AddTransient(serviceProvider => dictionaryType => { @@ -51,5 +55,14 @@ namespace WotBlitzStatisticsPro.Logic } }); } + + private static void ConfigureOperationsFactory(IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(serviceProvider => + new ServiceProviderOperationFactory(serviceProvider)); + } } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Tests/DictionariesTests/DictionariesServiceTests.cs b/WotBlitzStatisticsPro.Tests/DictionariesTests/DictionariesServiceTests.cs index a07c04b..e42cd09 100644 --- a/WotBlitzStatisticsPro.Tests/DictionariesTests/DictionariesServiceTests.cs +++ b/WotBlitzStatisticsPro.Tests/DictionariesTests/DictionariesServiceTests.cs @@ -36,7 +36,7 @@ namespace WotBlitzStatisticsPro.Tests.DictionariesTests serviceProvider.AddSingleton(_achievementsDictionaryUpdaterMock.Object); serviceProvider.AddSingleton(_vehiclesDictionaryUpdaterMock.Object); - WotBlitzStatisticsLogicInstaller.RegisterFactoryMethod(serviceProvider); + WotBlitzStatisticsLogicInstaller.RegisterDictionariesFactoryMethod(serviceProvider); var services = serviceProvider.BuildServiceProvider();