From 638489a8425f168148c3d976a9abfcef9770358f Mon Sep 17 00:00:00 2001 From: Mikhail Shabanov Date: Fri, 4 Jun 2021 23:04:03 +0200 Subject: [PATCH] feat: introduced caching in the server + Added serilog --- .../graphql/schema.graphql | 2 +- .../Mutation/WotBlitzStatisticsMutation.cs | 5 +- WotBlitzStatisticsPro.GraphQl/Program.cs | 34 +++++++- .../Query/WotBlitzStatisticsQuery.cs | 1 + WotBlitzStatisticsPro.GraphQl/Startup.cs | 6 +- .../WotBlitzStatisticsPro.GraphQl.csproj | 1 + .../appsettings.Development.json | 28 +++++-- .../appsettings.Production.json | 17 ++++ .../appsettings.json | 29 ++++++- .../Operations/GetAccountInfoOperation.cs | 24 +++++- .../Operations/GetTanksInfoOperation.cs | 20 ++++- .../IWargamingAccounts.cs | 2 +- .../Model/AccountDataCache.cs | 17 ++++ .../Model/CacheItem.cs | 28 +++++++ .../Model/StatisticsCache.cs | 84 +++++++++++++++++++ .../Model/TanksDataCache.cs | 18 ++++ .../WargamingAccounts.cs | 6 +- .../WotBlitzStatisticsLogicInstaller.cs | 2 + .../GetAccountInfoOperationTest.cs | 21 ++++- .../GetTanksInfoOperationTest.cs | 23 ++++- .../OperationsStepsTestBase.cs | 6 +- .../WagramingApiClientBase.cs | 13 ++- .../WargamingAchievementsApiClient.cs | 6 +- .../WargamingApiClient.cs | 4 +- .../WotBlitzStatisticsPro.WgApiClient.csproj | 1 + 25 files changed, 365 insertions(+), 33 deletions(-) create mode 100644 WotBlitzStatisticsPro.GraphQl/appsettings.Production.json create mode 100644 WotBlitzStatisticsPro.Logic/Model/AccountDataCache.cs create mode 100644 WotBlitzStatisticsPro.Logic/Model/CacheItem.cs create mode 100644 WotBlitzStatisticsPro.Logic/Model/StatisticsCache.cs create mode 100644 WotBlitzStatisticsPro.Logic/Model/TanksDataCache.cs diff --git a/WotBlitzStatisticsPro.Blazor/graphql/schema.graphql b/WotBlitzStatisticsPro.Blazor/graphql/schema.graphql index 7ad93a5..7d2573b 100644 --- a/WotBlitzStatisticsPro.Blazor/graphql/schema.graphql +++ b/WotBlitzStatisticsPro.Blazor/graphql/schema.graphql @@ -24,7 +24,7 @@ type Mutation { "Updates the necessary dictionaries in database.\n\n\n**Returns:**\nInformation about what was updated." updateDictionaries("Determines what dictionaries should be updated." updateDictionariesRequest: UpdateDictionariesRequestInput!): [UpdateDictionariesResponseItem!]! "Gathers all account information" - gatherAccountInformation("Account region" realmType: RealmType! "AccountId" accountId: Long! "Request language" requestLanguage: RequestLanguage!): AccountInfoResponse! + gatherAccountInformation("Account region" realmType: RealmType! "AccountId" accountId: Long! "Request language" requestLanguage: RequestLanguage!): DateTime! "This method accepts GitHub oauth code, and returns JWT token with GutHub user as claim" gitHubAuthenticate("OAuth code can be generated by this URL https:\/\/github.com\/login\/oauth\/authorize?client_id={yourClientId}" oauthCode: String!): String! } diff --git a/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs b/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs index 645e8d7..15e9b19 100644 --- a/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs +++ b/WotBlitzStatisticsPro.GraphQl/Mutation/WotBlitzStatisticsMutation.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using HotChocolate; using HotChocolate.AspNetCore.Authorization; using HotChocolate.Types; @@ -43,7 +44,7 @@ namespace WotBlitzStatisticsPro.GraphQl.Mutation /// Request language /// Wargaming authentication token /// - public Task GatherAccountInformation( + public Task GatherAccountInformation( RealmType realmType, long accountId, RequestLanguage requestLanguage, diff --git a/WotBlitzStatisticsPro.GraphQl/Program.cs b/WotBlitzStatisticsPro.GraphQl/Program.cs index 5548266..afeeb1b 100644 --- a/WotBlitzStatisticsPro.GraphQl/Program.cs +++ b/WotBlitzStatisticsPro.GraphQl/Program.cs @@ -1,5 +1,9 @@ +using System; +using System.IO; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; +using Serilog; namespace WotBlitzStatisticsPro.GraphQl { @@ -7,12 +11,36 @@ namespace WotBlitzStatisticsPro.GraphQl { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true) + .AddEnvironmentVariables() + .Build(); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger(); + + try + { + Log.Information("Starting web host"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + } + finally + { + Log.CloseAndFlush(); + } } - public static IHostBuilder CreateHostBuilder(string[] args) => + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => + .UseSerilog() + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); diff --git a/WotBlitzStatisticsPro.GraphQl/Query/WotBlitzStatisticsQuery.cs b/WotBlitzStatisticsPro.GraphQl/Query/WotBlitzStatisticsQuery.cs index e2f5bc3..3dcc365 100644 --- a/WotBlitzStatisticsPro.GraphQl/Query/WotBlitzStatisticsQuery.cs +++ b/WotBlitzStatisticsPro.GraphQl/Query/WotBlitzStatisticsQuery.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using HotChocolate.Types; +using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.Logic; diff --git a/WotBlitzStatisticsPro.GraphQl/Startup.cs b/WotBlitzStatisticsPro.GraphQl/Startup.cs index b6622fa..b1731c9 100644 --- a/WotBlitzStatisticsPro.GraphQl/Startup.cs +++ b/WotBlitzStatisticsPro.GraphQl/Startup.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; +using Serilog; using WotBlitzStatisticsPro.Common; using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.GraphQl.GitHubOauth; @@ -37,7 +38,10 @@ namespace WotBlitzStatisticsPro.GraphQl public void ConfigureServices(IServiceCollection services) { - var wgApiConfig = new WargamingApiSettings(); + //services.AddLogging(loggingBuilder => + // loggingBuilder.AddSerilog(dispose: true)); + + var wgApiConfig = new WargamingApiSettings(); Configuration.GetSection("WargamingApi").Bind(wgApiConfig); var mongoConfig = new MongoSettings(); diff --git a/WotBlitzStatisticsPro.GraphQl/WotBlitzStatisticsPro.GraphQl.csproj b/WotBlitzStatisticsPro.GraphQl/WotBlitzStatisticsPro.GraphQl.csproj index 8b0f42b..4663a2c 100644 --- a/WotBlitzStatisticsPro.GraphQl/WotBlitzStatisticsPro.GraphQl.csproj +++ b/WotBlitzStatisticsPro.GraphQl/WotBlitzStatisticsPro.GraphQl.csproj @@ -17,6 +17,7 @@ + diff --git a/WotBlitzStatisticsPro.GraphQl/appsettings.Development.json b/WotBlitzStatisticsPro.GraphQl/appsettings.Development.json index 8983e0f..d5a3436 100644 --- a/WotBlitzStatisticsPro.GraphQl/appsettings.Development.json +++ b/WotBlitzStatisticsPro.GraphQl/appsettings.Development.json @@ -1,9 +1,25 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console" ], + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + + }, + "WriteTo": [ + { "Name": "Console" } + ], + "Enrich": [ "FromLogContext" ] } - } } diff --git a/WotBlitzStatisticsPro.GraphQl/appsettings.Production.json b/WotBlitzStatisticsPro.GraphQl/appsettings.Production.json new file mode 100644 index 0000000..1d1fae0 --- /dev/null +++ b/WotBlitzStatisticsPro.GraphQl/appsettings.Production.json @@ -0,0 +1,17 @@ +{ + "Serilog": { + "Using": [ "Serilog.Sinks.Console" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + }, + "WriteTo": [ + { "Name": "Console" } + ], + "Enrich": [ "FromLogContext" ] + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.GraphQl/appsettings.json b/WotBlitzStatisticsPro.GraphQl/appsettings.json index ad559c2..59cb4b7 100644 --- a/WotBlitzStatisticsPro.GraphQl/appsettings.json +++ b/WotBlitzStatisticsPro.GraphQl/appsettings.json @@ -14,9 +14,30 @@ "Mongo": { "ConnectionString": "*", "DatabaseName": "*" - }, + }, "GitHubOAuth": { "ClientId": "YourClientId", - "ClientSecret": "YourSecret" - } -} + "ClientSecret": "YourSecret" + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", + "outputTemplate": "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {Message:lj} |{SourceContext}|{NewLine}{Exception}" + } + } + ], + "Enrich": [ "FromLogContext" ] + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs index 65601ff..f0aa607 100644 --- a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetAccountInfoOperation.cs @@ -4,6 +4,7 @@ using AutoMapper; using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.DataAccess.Model.Accounts; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext; +using WotBlitzStatisticsPro.Logic.Model; using WotBlitzStatisticsPro.Logic.Pipeline; using WotBlitzStatisticsPro.WgApiClient; using WotBlitzStatisticsPro.WgApiClient.Model; @@ -15,19 +16,36 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations private readonly IWargamingApiClient _wargamingApi; private readonly IMapper _mapper; private readonly ILogger _logger; + private readonly StatisticsCache _cache; public GetAccountInfoOperation( IWargamingApiClient wargamingApi, IMapper mapper, - ILogger logger) + ILogger logger, + StatisticsCache cache) { _wargamingApi = wargamingApi; _mapper = mapper; _logger = logger; + _cache = cache; } public async Task Invoke(IOperationContext context, Func? next) { + // Store info to AccountInfo and in AccountInfoHistory + var contextData = context.Get(); + + var cache = _cache.GetAccountData(context.Request.AccountId); + if (cache != null) + { + contextData.AccountInfo = cache.AccountInfo; + contextData.AccountInfoHistory = cache.AccountInfoHistory; + _logger.LogInformation($"Account data for account {context.Request.AccountId} got from cache"); + + if (next != null) await next.Invoke(context); + return; + } + var accountInfo = await _wargamingApi.GetPlayerAccountInfo(context.Request.AccountId, context.Request.RealmType, context.Request.RequestLanguage, context.Request.AuthenticationToken); @@ -38,14 +56,14 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations return; } - // Store info to AccountInfo and in AccountInfoHistory - var contextData = context.Get(); contextData.AccountInfo = _mapper.Map(accountInfo); contextData.AccountInfo.Realm = context.Request.RealmType; contextData.AccountInfoHistory = new AccountInfoHistory(context.Request.AccountId, contextData.AccountInfo.LastBattleTime); _mapper.Map(accountInfo.Statistics?.All, contextData.AccountInfoHistory); + _cache.SetAccountData(context.Request.AccountId, new AccountDataCache(contextData.AccountInfo, contextData.AccountInfoHistory)); + if (next != null) await next.Invoke(context); } } diff --git a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs index ccb1851..86fd343 100644 --- a/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs +++ b/WotBlitzStatisticsPro.Logic/AccountInformationPipeline/Operations/GetTanksInfoOperation.cs @@ -6,6 +6,7 @@ using AutoMapper; using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.DataAccess.Model.Accounts; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext; +using WotBlitzStatisticsPro.Logic.Model; using WotBlitzStatisticsPro.Logic.Pipeline; using WotBlitzStatisticsPro.WgApiClient; @@ -16,21 +17,36 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations private readonly IWargamingTanksApiClient _wargamingApi; private readonly IMapper _mapper; private readonly ILogger _logger; + private readonly StatisticsCache _cache; public GetTanksInfoOperation( IWargamingTanksApiClient wargamingApi, IMapper mapper, - ILogger logger) + ILogger logger, + StatisticsCache cache) { _wargamingApi = wargamingApi; _mapper = mapper; _logger = logger; + _cache = cache; } public async Task Invoke(IOperationContext context, Func? next) { var contextData = context.Get(); + var cache = _cache.GetTanksData(context.Request.AccountId); + if (cache != null) + { + contextData.Tanks = cache.TankInfo; + contextData.TanksHistory = cache.TankInfoHistory; + _logger.LogInformation($"Account tanks data for account {context.Request.AccountId} got from cache"); + + if (next != null) await next.Invoke(context); + return; + } + + var tanksInfo = await _wargamingApi.GetPlayerAccountTanksInfo(context.Request.AccountId, context.Request.RealmType, context.Request.RequestLanguage); @@ -54,6 +70,8 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations contextData.TanksHistory[stat.TankId] = stat; }); + _cache.SetTanksData(context.Request.AccountId, new TanksDataCache(contextData.Tanks, contextData.TanksHistory)); + if (next != null) await next.Invoke(context); } } diff --git a/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs b/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs index 8c57f63..4bf0b67 100644 --- a/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs +++ b/WotBlitzStatisticsPro.Logic/IWargamingAccounts.cs @@ -8,7 +8,7 @@ namespace WotBlitzStatisticsPro.Logic { Task GatherAccountInformation(RealmType realm, long accountId, RequestLanguage requestLanguage); - Task GatherAndSaveAccountInformation(RealmType realm, long accountId, RequestLanguage requestLanguage, string wargamingToken); + Task GatherAndSaveAccountInformation(RealmType realm, long accountId, RequestLanguage requestLanguage, string wargamingToken); Task GetAccountInfoHistory(RealmType realm, long accountId, DateTime startDate, RequestLanguage requestLanguage); diff --git a/WotBlitzStatisticsPro.Logic/Model/AccountDataCache.cs b/WotBlitzStatisticsPro.Logic/Model/AccountDataCache.cs new file mode 100644 index 0000000..593383f --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Model/AccountDataCache.cs @@ -0,0 +1,17 @@ +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; + +namespace WotBlitzStatisticsPro.Logic.Model +{ + public class AccountDataCache + { + public AccountDataCache(AccountInfo accountInfo, AccountInfoHistory accountInfoHistory) + { + AccountInfo = accountInfo; + AccountInfoHistory = accountInfoHistory; + } + + public AccountInfo AccountInfo { get; } + + public AccountInfoHistory AccountInfoHistory { get; } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Model/CacheItem.cs b/WotBlitzStatisticsPro.Logic/Model/CacheItem.cs new file mode 100644 index 0000000..01e35ee --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Model/CacheItem.cs @@ -0,0 +1,28 @@ +using System; + +namespace WotBlitzStatisticsPro.Logic.Model +{ + public class CacheItem + { + private const int ExpirationTimeoutInMinutes = 60; + private DateTime? _lastRefreshed = null; + + private T? _itemData = default(T); + + public long AccountId { get; set; } + + public bool IsExpired => _lastRefreshed != null && + (DateTime.Now - _lastRefreshed.Value).TotalMinutes > ExpirationTimeoutInMinutes; + + public T? Data + { + get => _itemData; + set + { + _itemData = value; + _lastRefreshed = DateTime.Now; + + } + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Model/StatisticsCache.cs b/WotBlitzStatisticsPro.Logic/Model/StatisticsCache.cs new file mode 100644 index 0000000..634e781 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Model/StatisticsCache.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; + +namespace WotBlitzStatisticsPro.Logic.Model +{ + public class StatisticsCache + { + private readonly Dictionary> _accountsCache = new(); + private readonly Dictionary> _tanksCache = new(); + + public AccountDataCache? GetAccountData(long accountId) + { + if (!_accountsCache.ContainsKey(accountId) || _accountsCache[accountId].IsExpired) + { + return null; + } + + return _accountsCache[accountId].Data; + } + + public TanksDataCache? GetTanksData(long accountId) + { + if (!_tanksCache.ContainsKey(accountId) || _tanksCache[accountId].IsExpired) + { + return null; + } + + return _tanksCache[accountId].Data; + } + + public void SetAccountData(long accountId, AccountDataCache data) + { + if (!_accountsCache.ContainsKey(accountId)) + { + _accountsCache.Add(accountId, new CacheItem()); + ClearExpiredAccounts(); + } + _accountsCache[accountId].Data = data; + } + + public void SetTanksData(long accountId, TanksDataCache data) + { + if (!_tanksCache.ContainsKey(accountId)) + { + _tanksCache.Add(accountId, new CacheItem()); + ClearExpiredTanks(); + } + _tanksCache[accountId].Data = data; + } + + private void ClearExpiredAccounts() + { + var expired = new List(); + foreach (var cacheItem in _accountsCache) + { + if (cacheItem.Value.IsExpired) + { + expired.Add(cacheItem.Key); + } + } + + foreach (var expiredKey in expired) + { + _accountsCache.Remove(expiredKey); + } + } + + private void ClearExpiredTanks() + { + var expired = new List(); + foreach (var cacheItem in _tanksCache) + { + if (cacheItem.Value.IsExpired) + { + expired.Add(cacheItem.Key); + } + } + + foreach (var expiredKey in expired) + { + _accountsCache.Remove(expiredKey); + } + } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/Model/TanksDataCache.cs b/WotBlitzStatisticsPro.Logic/Model/TanksDataCache.cs new file mode 100644 index 0000000..d419a32 --- /dev/null +++ b/WotBlitzStatisticsPro.Logic/Model/TanksDataCache.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using WotBlitzStatisticsPro.DataAccess.Model.Accounts; + +namespace WotBlitzStatisticsPro.Logic.Model +{ + public class TanksDataCache + { + public TanksDataCache(List tankInfo, Dictionary tankInfoHistory) + { + TankInfo = tankInfo; + TankInfoHistory = tankInfoHistory; + } + + public List TankInfo { get; } + + public Dictionary TankInfoHistory { get; } + } +} \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs index 0421b1e..26ee05e 100644 --- a/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs +++ b/WotBlitzStatisticsPro.Logic/WargamingAccounts.cs @@ -5,6 +5,7 @@ using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; +using WotBlitzStatisticsPro.Logic.Calculations; using WotBlitzStatisticsPro.Logic.Pipeline; namespace WotBlitzStatisticsPro.Logic @@ -58,7 +59,7 @@ namespace WotBlitzStatisticsPro.Logic return contextData?.Response ?? new AccountInfoResponse(); } - public async Task GatherAndSaveAccountInformation( + public async Task GatherAndSaveAccountInformation( RealmType realm, long accountId, RequestLanguage requestLanguage, @@ -78,7 +79,6 @@ namespace WotBlitzStatisticsPro.Logic .AddOperation() .AddOperation() .AddOperation() - .AddOperation() ; var firstOperation = pipeline.Build(); @@ -95,7 +95,7 @@ namespace WotBlitzStatisticsPro.Logic throw; } - return contextData?.Response ?? new AccountInfoResponse(); + return contextData?.AccountInfo?.LastBattleTime.ToDateTime() ?? new DateTime(1970,1,1); } public async Task GetAccountInfoHistory(RealmType realm, long accountId, DateTime startDate, RequestLanguage requestLanguage) diff --git a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs index 915eefb..dee84f1 100644 --- a/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs +++ b/WotBlitzStatisticsPro.Logic/WotBlitzStatisticsLogicInstaller.cs @@ -7,6 +7,7 @@ using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.DataAccess; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; using WotBlitzStatisticsPro.Logic.Dictionaries; +using WotBlitzStatisticsPro.Logic.Model; using WotBlitzStatisticsPro.Logic.Pipeline; using WotBlitzStatisticsPro.WgApiClient; @@ -19,6 +20,7 @@ namespace WotBlitzStatisticsPro.Logic public static void ConfigureServices(IServiceCollection services) { + services.AddSingleton(); ConfigureDictionariesFactory(services); ConfigureOperationsFactory(services); WotBlitzStatisticsDataAccessInstaller.ConfigureServices(services); diff --git a/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetAccountInfoOperationTest.cs b/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetAccountInfoOperationTest.cs index ba9f1a6..35b9805 100644 --- a/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetAccountInfoOperationTest.cs +++ b/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetAccountInfoOperationTest.cs @@ -9,6 +9,7 @@ using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; +using WotBlitzStatisticsPro.Logic.Model; namespace WotBlitzStatisticsPro.Tests.OperationStepsTests { @@ -17,16 +18,19 @@ namespace WotBlitzStatisticsPro.Tests.OperationStepsTests { private GetAccountInfoOperation _operation; private AccountInformationPipelineContextData _contextData; + private StatisticsCache _cache; [SetUp] public void Init() { InitAutoMapper(); InitWgApiClient(); + _cache = new StatisticsCache(); _operation = new GetAccountInfoOperation( WargamingApiClient, Mapper, - (new Mock>()).Object); + (new Mock>()).Object, + _cache); _contextData = new AccountInformationPipelineContextData(); } @@ -51,6 +55,21 @@ namespace WotBlitzStatisticsPro.Tests.OperationStepsTests var expectedAccountInfoHistory = await File.ReadAllTextAsync(GetFixturePath("MappedAccountHistory.json")); accountInfoHistorySerialized.Should().Be(expectedAccountInfoHistory); + + // ToDo: Check cache + Assert.Fail("Implement test"); + } + + [Test] + public async Task ShouldGetAndSaveAccountInfoInformationIfCacheExpired() + { + Assert.Fail("Implement test"); + } + + [Test] + public async Task ShouldReadDataFromCacheIfItIsNotEmpty() + { + Assert.Fail("Implement test"); } } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetTanksInfoOperationTest.cs b/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetTanksInfoOperationTest.cs index 3d6d7e1..54c7344 100644 --- a/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetTanksInfoOperationTest.cs +++ b/WotBlitzStatisticsPro.Tests/OperationStepsTests/GetTanksInfoOperationTest.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.OperationContext; using WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations; +using WotBlitzStatisticsPro.Logic.Model; namespace WotBlitzStatisticsPro.Tests.OperationStepsTests { @@ -16,16 +17,21 @@ namespace WotBlitzStatisticsPro.Tests.OperationStepsTests { private GetTanksInfoOperation _operation; private AccountInformationPipelineContextData _contextData; + private StatisticsCache _cache; + [SetUp] public void Init() { InitAutoMapper(); InitWgApiClient(); + _cache = new StatisticsCache(); + _operation = new GetTanksInfoOperation( WargamingTanksApiClient, Mapper, - (new Mock>()).Object); + (new Mock>()).Object, + _cache); _contextData = new AccountInformationPipelineContextData(); } @@ -50,6 +56,21 @@ namespace WotBlitzStatisticsPro.Tests.OperationStepsTests var expectedTanksHistory = await File.ReadAllTextAsync(GetFixturePath("MappedTanksHistory.json")); tanksHistorySerialized.Should().Be(expectedTanksHistory); + + // ToDo: Check cache + Assert.Fail("Implement test"); + } + + [Test] + public async Task ShouldGetAndSaveTankInfoInformationIfCacheExpired() + { + Assert.Fail("Implement test"); + } + + [Test] + public async Task ShouldReadDataFromCacheIfItIsNotEmpty() + { + Assert.Fail("Implement test"); } } diff --git a/WotBlitzStatisticsPro.Tests/OperationStepsTests/OperationsStepsTestBase.cs b/WotBlitzStatisticsPro.Tests/OperationStepsTests/OperationsStepsTestBase.cs index be3607f..d44a680 100644 --- a/WotBlitzStatisticsPro.Tests/OperationStepsTests/OperationsStepsTestBase.cs +++ b/WotBlitzStatisticsPro.Tests/OperationStepsTests/OperationsStepsTestBase.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; using AutoMapper; +using Microsoft.Extensions.Logging; using Moq; using Moq.Protected; using Newtonsoft.Json; @@ -97,7 +98,10 @@ namespace WotBlitzStatisticsPro.Tests.OperationStepsTests ItExpr.IsAny()) .ReturnsAsync(tanksInfoResponse); - var client = new WargamingApiClient(new HttpClient(handlerMock.Object), settingsMock.Object); + var client = new WargamingApiClient( + new HttpClient(handlerMock.Object), + settingsMock.Object, + (new Mock>()).Object); WargamingApiClient = client; WargamingTanksApiClient = client; } diff --git a/WotBlitzStatisticsPro.WgApiClient/WagramingApiClientBase.cs b/WotBlitzStatisticsPro.WgApiClient/WagramingApiClientBase.cs index 830cc9d..e917fe9 100644 --- a/WotBlitzStatisticsPro.WgApiClient/WagramingApiClientBase.cs +++ b/WotBlitzStatisticsPro.WgApiClient/WagramingApiClientBase.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.Common; using WotBlitzStatisticsPro.Common.Model; @@ -19,14 +20,17 @@ namespace WotBlitzStatisticsPro.WgApiClient private readonly HttpClient _httpClient; private readonly IWargamingApiSettings _wargamingApiSettings; + private readonly ILogger _logger; - public WagramingApiClientBase( + public WagramingApiClientBase( HttpClient httpClient, - IWargamingApiSettings wargamingApiSettings) + IWargamingApiSettings wargamingApiSettings, + ILogger logger) { _httpClient = httpClient; _wargamingApiSettings = wargamingApiSettings; - _blitzApiUrls[RealmType.Eu] = "https://api.wotblitz.eu/wotb/"; + _logger = logger; + _blitzApiUrls[RealmType.Eu] = "https://api.wotblitz.eu/wotb/"; _blitzApiUrls[RealmType.Ru] = "https://api.wotblitz.ru/wotb/"; _blitzApiUrls[RealmType.Na] = "https://api.wotblitz.com/wotb/"; _blitzApiUrls[RealmType.Asia] = "https://api.wotblitz.asia/wotb/"; @@ -53,6 +57,9 @@ namespace WotBlitzStatisticsPro.WgApiClient var responseString = await response.Content.ReadAsStringAsync(); var responseBody = JsonConvert.DeserializeObject>(responseString); + + _logger.LogInformation($"HTTP GET {uri} - {responseBody.Status}"); + switch (responseBody.Status) { case "ok": diff --git a/WotBlitzStatisticsPro.WgApiClient/WargamingAchievementsApiClient.cs b/WotBlitzStatisticsPro.WgApiClient/WargamingAchievementsApiClient.cs index 11caa26..83becbb 100644 --- a/WotBlitzStatisticsPro.WgApiClient/WargamingAchievementsApiClient.cs +++ b/WotBlitzStatisticsPro.WgApiClient/WargamingAchievementsApiClient.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.Common; using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.WgApiClient.Model; @@ -10,7 +11,10 @@ namespace WotBlitzStatisticsPro.WgApiClient public class WargamingAchievementsApiClient: WagramingApiClientBase, IWargamingAchievementsApiClient { - public WargamingAchievementsApiClient(HttpClient httpClient, IWargamingApiSettings wargamingApiSettings) : base(httpClient, wargamingApiSettings) + public WargamingAchievementsApiClient( + HttpClient httpClient, + IWargamingApiSettings wargamingApiSettings, + ILogger logger) : base(httpClient, wargamingApiSettings, logger) { } diff --git a/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs b/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs index fbc8b8e..2e2ab6f 100644 --- a/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs +++ b/WotBlitzStatisticsPro.WgApiClient/WargamingApiClient.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using WotBlitzStatisticsPro.Common; using WotBlitzStatisticsPro.Common.Model; using WotBlitzStatisticsPro.WgApiClient.Model; @@ -15,7 +16,8 @@ namespace WotBlitzStatisticsPro.WgApiClient { public WargamingApiClient( HttpClient httpClient, - IWargamingApiSettings wargamingApiSettings) : base(httpClient, wargamingApiSettings) + IWargamingApiSettings wargamingApiSettings, + ILogger logger) : base(httpClient, wargamingApiSettings, logger) { } diff --git a/WotBlitzStatisticsPro.WgApiClient/WotBlitzStatisticsPro.WgApiClient.csproj b/WotBlitzStatisticsPro.WgApiClient/WotBlitzStatisticsPro.WgApiClient.csproj index 04a9ff5..9bfd599 100644 --- a/WotBlitzStatisticsPro.WgApiClient/WotBlitzStatisticsPro.WgApiClient.csproj +++ b/WotBlitzStatisticsPro.WgApiClient/WotBlitzStatisticsPro.WgApiClient.csproj @@ -6,6 +6,7 @@ +