From 6ae49615966b02ba9a70606680cd8bcbe67ead6e Mon Sep 17 00:00:00 2001 From: Markog19 Date: Sun, 19 Jan 2025 14:42:33 +0100 Subject: [PATCH 1/6] Restructured codebase, created exchange rate service and provider and started working on initial state of them. --- .gitignore | 63 ++++++++++++------- jobs/Backend/Task/ExchangeRateProvider.cs | 19 ------ jobs/Backend/Task/ExchangeRateUpdater.csproj | 8 --- jobs/Backend/Task/ExchangeRateUpdater.sln | 4 +- jobs/Backend/Task/Program.cs | 43 ------------- .../ExchangeRateProviderSettings.cs | 8 +++ .../DTO/ExchangeRateDTO.cs | 28 +++++++++ .../ExchangeRateUpdater.Domain.csproj | 11 ++++ .../Models}/Currency.cs | 2 +- .../Models}/ExchangeRate.cs | 3 +- .../Task/src/ExchangeRateUpdater/.gitignore | 44 +++++++++++++ .../ExchangeRateUpdater.csproj | 20 ++++++ .../ExchangeRateUpdater.sln | 28 +++++++++ .../Task/src/ExchangeRateUpdater/Program.cs | 40 ++++++++++++ .../Providers/ExchangeRateProvider.cs | 44 +++++++++++++ .../Providers/IExchangeRateProvider.cs | 16 +++++ .../Services/ExchangeRateService.cs | 30 +++++++++ .../Services/IExchangeRateService.cs | 15 +++++ .../src/ExchangeRateUpdater/appsettings.json | 20 ++++++ 19 files changed, 351 insertions(+), 95 deletions(-) delete mode 100644 jobs/Backend/Task/ExchangeRateProvider.cs delete mode 100644 jobs/Backend/Task/ExchangeRateUpdater.csproj delete mode 100644 jobs/Backend/Task/Program.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRateDTO.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj rename jobs/Backend/Task/{ => src/ExchangeRateUpdater.Domain/Models}/Currency.cs (88%) rename jobs/Backend/Task/{ => src/ExchangeRateUpdater.Domain/Models}/ExchangeRate.cs (91%) create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json diff --git a/.gitignore b/.gitignore index fd3586545..e3f1a1f9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,44 @@ -!.gitkeep -!.gitignore -!*.dll -[Oo]bj -[Bb]in -*.user +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode/ + +# Rider +.idea/ + +# Visual Studio +.vs/ + +# Fleet +.fleet/ + +# Code Rush +.cr/ + +# User-specific files *.suo -*.[Cc]ache -*.bak -*.ncb -*.DS_Store -*.userprefs -*.iml -*.ncrunch* -.*crunch*.local.xml -.idea -[Tt]humbs.db -*.tgz -*.sublime-* +*.user +*.userosscache +*.sln.docstates -node_modules -bower_components -npm-debug.log +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn +appsettings.Development.json \ No newline at end of file diff --git a/jobs/Backend/Task/ExchangeRateProvider.cs b/jobs/Backend/Task/ExchangeRateProvider.cs deleted file mode 100644 index 6f82a97fb..000000000 --- a/jobs/Backend/Task/ExchangeRateProvider.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace ExchangeRateUpdater -{ - public class ExchangeRateProvider - { - /// - /// Should return exchange rates among the specified currencies that are defined by the source. But only those defined - /// by the source, do not return calculated exchange rates. E.g. if the source contains "CZK/USD" but not "USD/CZK", - /// do not return exchange rate "USD/CZK" with value calculated as 1 / "CZK/USD". If the source does not provide - /// some of the currencies, ignore them. - /// - public IEnumerable GetExchangeRates(IEnumerable currencies) - { - return Enumerable.Empty(); - } - } -} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.csproj b/jobs/Backend/Task/ExchangeRateUpdater.csproj deleted file mode 100644 index 2fc654a12..000000000 --- a/jobs/Backend/Task/ExchangeRateUpdater.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net6.0 - - - \ No newline at end of file diff --git a/jobs/Backend/Task/ExchangeRateUpdater.sln b/jobs/Backend/Task/ExchangeRateUpdater.sln index 89be84daf..d85c9bb56 100644 --- a/jobs/Backend/Task/ExchangeRateUpdater.sln +++ b/jobs/Backend/Task/ExchangeRateUpdater.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" EndProject diff --git a/jobs/Backend/Task/Program.cs b/jobs/Backend/Task/Program.cs deleted file mode 100644 index 379a69b1f..000000000 --- a/jobs/Backend/Task/Program.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ExchangeRateUpdater -{ - public static class Program - { - private static IEnumerable currencies = new[] - { - new Currency("USD"), - new Currency("EUR"), - new Currency("CZK"), - new Currency("JPY"), - new Currency("KES"), - new Currency("RUB"), - new Currency("THB"), - new Currency("TRY"), - new Currency("XYZ") - }; - - public static void Main(string[] args) - { - try - { - var provider = new ExchangeRateProvider(); - var rates = provider.GetExchangeRates(currencies); - - Console.WriteLine($"Successfully retrieved {rates.Count()} exchange rates:"); - foreach (var rate in rates) - { - Console.WriteLine(rate.ToString()); - } - } - catch (Exception e) - { - Console.WriteLine($"Could not retrieve exchange rates: '{e.Message}'."); - } - - Console.ReadLine(); - } - } -} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs new file mode 100644 index 000000000..a24adf946 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs @@ -0,0 +1,8 @@ +namespace ExchangeRateUpdater.Domain.Configurations +{ + public class ExchangeRateProviderSettings + { + public string UrlBaseAPI { get; set; } + public string UrlExchangeRate { get; set; } + } +} \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRateDTO.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRateDTO.cs new file mode 100644 index 000000000..53108a0e7 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRateDTO.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Domain.DTO +{ + public class ExchangeRateDTO + { + [JsonPropertyName("amount")] + public int Amount { get; init; } + + [JsonPropertyName("currencyCode")] + public string CurrencyCode { get; init; } + + [JsonPropertyName("currency")] + public string Currency { get; init; } + + [JsonPropertyName("country")] + public string Country { get; init; } + + [JsonPropertyName("rate")] + public decimal Rate { get; init; } + + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj new file mode 100644 index 000000000..0a8343153 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj @@ -0,0 +1,11 @@ + + + + net8.0 + enable + enable + + + + + diff --git a/jobs/Backend/Task/Currency.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/Currency.cs similarity index 88% rename from jobs/Backend/Task/Currency.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/Currency.cs index f375776f2..a46a1e902 100644 --- a/jobs/Backend/Task/Currency.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/Currency.cs @@ -1,4 +1,4 @@ -namespace ExchangeRateUpdater +namespace ExchangeRateUpdater.Domain.Models { public class Currency { diff --git a/jobs/Backend/Task/ExchangeRate.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs similarity index 91% rename from jobs/Backend/Task/ExchangeRate.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs index 58c5bb10e..969a319c5 100644 --- a/jobs/Backend/Task/ExchangeRate.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs @@ -1,5 +1,6 @@ -namespace ExchangeRateUpdater +namespace ExchangeRateUpdater.Domain.Models { + public class ExchangeRate { public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value) diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore b/jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore new file mode 100644 index 000000000..e3f1a1f9a --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore @@ -0,0 +1,44 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode/ + +# Rider +.idea/ + +# Visual Studio +.vs/ + +# Fleet +.fleet/ + +# Code Rush +.cr/ + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn +appsettings.Development.json \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj new file mode 100644 index 000000000..7a7743cb5 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln new file mode 100644 index 000000000..9efbc0a8c --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Domain", "..\ExchangeRateUpdater.Domain\ExchangeRateUpdater.Domain.csproj", "{09FB3A65-45DF-4F1A-8856-829FBDB792AA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.Build.0 = Release|Any CPU + {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs new file mode 100644 index 000000000..e7db1c779 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using ExchangeRateUpdater.Domain.Configurations; +using ExchangeRateUpdater.Providers; +using ExchangeRateUpdater.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + + + + var host = new HostBuilder() + .ConfigureServices((services) => + { + + + services.AddHttpClient( + (serviceProvider, client) => + { + var settings = serviceProvider.GetRequiredService>().Value; + client.BaseAddress = new Uri($"{settings.UrlBaseAPI}/{settings.UrlExchangeRate}"); + }) + .ConfigurePrimaryHttpMessageHandler(() => + { + return new SocketsHttpHandler() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(15) + }; + }) + .SetHandlerLifetime(Timeout.InfiniteTimeSpan); + services.AddTransient(); + services.AddSingleton(TimeProvider.System); + }) + .Build(); + var exchangeRateProvider = host.Services.GetRequiredService(); + + diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs new file mode 100644 index 000000000..488bc97f4 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using System.Net.Http; +using ExchangeRateUpdater.Domain.Models; +using Microsoft.Extensions.Logging; +using ExchangeRateUpdater.Services; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.DTO; +namespace ExchangeRateUpdater.Providers +{ + public class ExchangeRateProvider(ILogger _logger, IExchangeRateService exchangeRateService) : IExchangeRateProvider + { + /// + /// Should return exchange rates among the specified currencies that are defined by the source. But only those defined + /// by the source, do not return calculated exchange rates. E.g. if the source contains "CZK/USD" but not "USD/CZK", + /// do not return exchange rate "USD/CZK" with value calculated as 1 / "CZK/USD". If the source does not provide + /// some of the currencies, ignore them. + /// + public async Task> GetExchangeRatesAsync(IEnumerable currencies) + { + var result = new List(); + try + { + _logger.LogInformation("Started fetching exchange rates"); + foreach (var currency in currencies) + { + _logger.LogInformation($"Fetching exchange rate for {currency.Code}"); + var rates = await exchangeRateService.GetExchangeRateAsync(currency.Code); + + _logger.LogInformation($"Fetched exchange rate for {currency.Code}"); + result.AddRange(rates); + } + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + return new List(); + } + return result; + } + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs new file mode 100644 index 000000000..5bd36a22b --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.DTO; +using ExchangeRateUpdater.Domain.Models; +using ExchangeRateUpdater.Services; + +namespace ExchangeRateUpdater.Providers +{ + public interface IExchangeRateProvider + { + public Task> GetExchangeRatesAsync(IEnumerable currencies); + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs new file mode 100644 index 000000000..c773cd939 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.DTO; +using ExchangeRateUpdater.Domain.Models; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; + +namespace ExchangeRateUpdater.Services +{ + public class ExchangeRateService(IHttpClientFactory clientFactory, ILogger logger, IMemoryCache memoryCache): IExchangeRateService + { + public async Task> GetExchangeRateAsync(string code) + { + using var client = clientFactory.CreateClient(); + var response = await client.GetAsync(client.BaseAddress + $"?date={DateTime.Now:yyyy-MM-dd}"); + if (!response.IsSuccessStatusCode) + { + logger.LogError($"Status code: {response.StatusCode}; Failed to fetch rates for {code}"); + throw new Exception($"Status code: {response.StatusCode}; Failed to fetch rates for {code}"); + } + var result = await response.Content.ReadFromJsonAsync>(); + return result; + } + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs new file mode 100644 index 000000000..16d8a0f43 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.DTO; +using ExchangeRateUpdater.Domain.Models; + +namespace ExchangeRateUpdater.Services +{ + public interface IExchangeRateService + { + public Task> GetExchangeRateAsync(string code); + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json new file mode 100644 index 000000000..c073f170f --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "MemoryCache": { + "SizeLimit": 1000, + "AbsoluteExpirationInMinutes": "20" + }, + "ExchangeRateProviders": { + "Cnb": { + "UrlBaseAPI": "https://api.cnb.cz", + "UrlExchangeRate": "cnbapi/exrates/daily" + } + }, + "ApiKey": "" +} \ No newline at end of file From 7f8531982eddbeef66d7eebf6bfc375e1c1b930e Mon Sep 17 00:00:00 2001 From: Markog19 Date: Sun, 19 Jan 2025 15:41:23 +0100 Subject: [PATCH 2/6] Finished base setup for provider. Requires adding additional requirements such as memory cache --- .../ExchangeRateProviderSettings.cs | 4 +- .../DTO/ExchangeRatesDTO.cs | 15 +++++++ .../ExchangeRateUpdater.csproj | 6 +++ .../Task/src/ExchangeRateUpdater/Program.cs | 43 ++++++++++++++----- .../Providers/ExchangeRateProvider.cs | 11 +++-- .../Services/ExchangeRateService.cs | 11 ++--- .../Services/IExchangeRateService.cs | 2 +- .../src/ExchangeRateUpdater/appsettings.json | 11 ++--- 8 files changed, 72 insertions(+), 31 deletions(-) create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs index a24adf946..546180279 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs @@ -2,7 +2,7 @@ { public class ExchangeRateProviderSettings { - public string UrlBaseAPI { get; set; } - public string UrlExchangeRate { get; set; } + public string? UrlBaseAPI { get; set; } + public string? UrlExchangeRate { get; set; } } } \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs new file mode 100644 index 000000000..57df527b5 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Domain.DTO +{ + public class ExchangeRatesDTO + { + [JsonPropertyName("rates")] + public IEnumerable Rates { get; init; } + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj index 7a7743cb5..cac15c788 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj @@ -17,4 +17,10 @@ + + + Always + + + \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs index e7db1c779..c254beaf1 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs @@ -3,24 +3,29 @@ using System.Net.Http; using System.Threading; using ExchangeRateUpdater.Domain.Configurations; +using ExchangeRateUpdater.Domain.Models; using ExchangeRateUpdater.Providers; using ExchangeRateUpdater.Services; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; - - - var host = new HostBuilder() - .ConfigureServices((services) => +var basePath = System.IO.Directory.GetCurrentDirectory(); +var config = new ConfigurationBuilder() + .SetBasePath(basePath) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build(); +var host = new HostBuilder() + .ConfigureServices((configuration, services) => { - - - services.AddHttpClient( + var appsettings = config.GetSection("ExchangeRateProviderSettings"); + var settings = new ExchangeRateProviderSettings(); + appsettings.Bind(settings); + services.AddHttpClient("exchange", (serviceProvider, client) => { - var settings = serviceProvider.GetRequiredService>().Value; client.BaseAddress = new Uri($"{settings.UrlBaseAPI}/{settings.UrlExchangeRate}"); }) .ConfigurePrimaryHttpMessageHandler(() => @@ -31,10 +36,28 @@ }; }) .SetHandlerLifetime(Timeout.InfiniteTimeSpan); - services.AddTransient(); - services.AddSingleton(TimeProvider.System); + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); }) .Build(); var exchangeRateProvider = host.Services.GetRequiredService(); + IEnumerable currencies = new[] + { + new Currency("USD"), + new Currency("EUR"), + new Currency("CZK"), + new Currency("JPY"), + new Currency("KES"), + new Currency("RUB"), + new Currency("THB"), + new Currency("TRY"), + new Currency("XYZ") + }; + var rates = await exchangeRateProvider.GetExchangeRatesAsync(currencies); + foreach(var rate in rates) + { + Console.WriteLine(rate.Rate); + } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs index 488bc97f4..94665a42e 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs @@ -24,19 +24,18 @@ public async Task> GetExchangeRatesAsync(IEnumerabl try { _logger.LogInformation("Started fetching exchange rates"); + var exchangeRates = await exchangeRateService.GetExchangeRateAsync(); foreach (var currency in currencies) { - _logger.LogInformation($"Fetching exchange rate for {currency.Code}"); - var rates = await exchangeRateService.GetExchangeRateAsync(currency.Code); - - _logger.LogInformation($"Fetched exchange rate for {currency.Code}"); - result.AddRange(rates); + _logger.LogInformation($"Adding exchange rate for {currency.Code}"); + result.AddRange(exchangeRates.Rates.Where(e => e.CurrencyCode == currency.Code)); + _logger.LogInformation($"Added exchange rate for {currency.Code}"); } } catch (Exception ex) { _logger.LogError(ex, ex.Message); - return new List(); + return result; } return result; } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs index c773cd939..09584ca2a 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs @@ -14,16 +14,17 @@ namespace ExchangeRateUpdater.Services { public class ExchangeRateService(IHttpClientFactory clientFactory, ILogger logger, IMemoryCache memoryCache): IExchangeRateService { - public async Task> GetExchangeRateAsync(string code) + public async Task GetExchangeRateAsync() { - using var client = clientFactory.CreateClient(); + using var client = clientFactory.CreateClient("exchange"); + logger.LogInformation($"Sending GET request for getting exchange rates foon {DateTime.Now:yyyy-MM-dd}"); var response = await client.GetAsync(client.BaseAddress + $"?date={DateTime.Now:yyyy-MM-dd}"); if (!response.IsSuccessStatusCode) { - logger.LogError($"Status code: {response.StatusCode}; Failed to fetch rates for {code}"); - throw new Exception($"Status code: {response.StatusCode}; Failed to fetch rates for {code}"); + logger.LogError($"Status code: {response.StatusCode}; Failed to fetch rates on {DateTime.Now:yyyy-MM-dd}"); + throw new Exception($"Status code: {response.StatusCode}; Failed to fetch rates on{DateTime.Now:yyyy-MM-dd}"); } - var result = await response.Content.ReadFromJsonAsync>(); + var result = await response.Content.ReadFromJsonAsync(); return result; } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs index 16d8a0f43..e4b0e1be7 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs @@ -10,6 +10,6 @@ namespace ExchangeRateUpdater.Services { public interface IExchangeRateService { - public Task> GetExchangeRateAsync(string code); + public Task GetExchangeRateAsync(); } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json index c073f170f..32a900997 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json @@ -10,11 +10,8 @@ "SizeLimit": 1000, "AbsoluteExpirationInMinutes": "20" }, - "ExchangeRateProviders": { - "Cnb": { - "UrlBaseAPI": "https://api.cnb.cz", - "UrlExchangeRate": "cnbapi/exrates/daily" - } - }, - "ApiKey": "" + "ExchangeRateProviderSettings": { + "UrlBaseAPI": "https://api.cnb.cz", + "UrlExchangeRate": "cnbapi/exrates/daily" + } } \ No newline at end of file From 537bdffb33ce2065b98a1fb8e1b961cf6954cbbd Mon Sep 17 00:00:00 2001 From: Markog19 Date: Mon, 20 Jan 2025 12:45:19 +0100 Subject: [PATCH 3/6] Caching results of GET request was added, introduced constants for caching names, introducing serilog for logging to files and console --- jobs/Backend/Task/ExchangeRateUpdater.sln | 22 ------ .../Const/MemoryCacheConstants.cs | 17 +++++ .../Models/ExchangeRate.cs | 2 +- .../ExchangeRateUpdater.csproj | 42 +++++++----- .../ExchangeRateUpdater.sln | 28 -------- .../Extensions/ConverterExtension.cs | 18 +++++ .../Task/src/ExchangeRateUpdater/Program.cs | 67 +++++++++---------- .../Providers/ExchangeRateProvider.cs | 6 +- .../Providers/IExchangeRateProvider.cs | 2 +- .../Services/ExchangeRateService.cs | 22 ++++-- .../Services/IExchangeRateService.cs | 2 +- .../src/ExchangeRateUpdater/TestingData.cs | 25 +++++++ .../src/ExchangeRateUpdater/appsettings.json | 25 +++++-- 13 files changed, 159 insertions(+), 119 deletions(-) delete mode 100644 jobs/Backend/Task/ExchangeRateUpdater.sln create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs delete mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs diff --git a/jobs/Backend/Task/ExchangeRateUpdater.sln b/jobs/Backend/Task/ExchangeRateUpdater.sln deleted file mode 100644 index d85c9bb56..000000000 --- a/jobs/Backend/Task/ExchangeRateUpdater.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35506.116 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs new file mode 100644 index 000000000..a4837284c --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Domain.Const +{ + + public class MemoryCacheConstants + { + public static string ExchangeRateKey(DateTime? date) + { + return $"DATE_{date.GetValueOrDefault().ToString("yyyy-MM-dd")}_KEY"; + } + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs index 969a319c5..514571875 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs @@ -18,7 +18,7 @@ public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal va public override string ToString() { - return $"{SourceCurrency}/{TargetCurrency}={Value}"; + return $"{SourceCurrency.Code}/{TargetCurrency.Code}={Value}"; } } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj index cac15c788..c810b0358 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj @@ -3,24 +3,30 @@ Exe net8.0 + enable + enable + + + + + + + + + + + + + - - - - - - - + + + - - - - - - - Always - - - - \ No newline at end of file + + + Always + + + diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln b/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln deleted file mode 100644 index 9efbc0a8c..000000000 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35506.116 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Domain", "..\ExchangeRateUpdater.Domain\ExchangeRateUpdater.Domain.csproj", "{09FB3A65-45DF-4F1A-8856-829FBDB792AA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7B2695D6-D24C-4460-A58E-A10F08550CE0}.Release|Any CPU.Build.0 = Release|Any CPU - {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09FB3A65-45DF-4F1A-8856-829FBDB792AA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs new file mode 100644 index 000000000..ccf9ddc3f --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.DTO; +using ExchangeRateUpdater.Domain.Models; + +namespace ExchangeRateUpdater.Extensions +{ + public static class ConverterExtension + { + public static IEnumerable ToExchangeRates(this ExchangeRatesDTO exchangeRates) + { + return exchangeRates.Rates.Select(r => new ExchangeRate(new Currency(r.CurrencyCode), new Currency("CZK"), r.Rate / r.Amount)); + } + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs index c254beaf1..970526777 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading; +using ExchangeRateUpdater; using ExchangeRateUpdater.Domain.Configurations; using ExchangeRateUpdater.Domain.Models; using ExchangeRateUpdater.Providers; @@ -12,52 +13,48 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Serilog; +using Serilog.Core; +using Serilog.Extensions.Logging; var basePath = System.IO.Directory.GetCurrentDirectory(); var config = new ConfigurationBuilder() .SetBasePath(basePath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build(); + var host = new HostBuilder() - .ConfigureServices((configuration, services) => - { - var appsettings = config.GetSection("ExchangeRateProviderSettings"); - var settings = new ExchangeRateProviderSettings(); - appsettings.Bind(settings); - services.AddHttpClient("exchange", - (serviceProvider, client) => - { - client.BaseAddress = new Uri($"{settings.UrlBaseAPI}/{settings.UrlExchangeRate}"); - }) + .ConfigureServices((services) => + { + + var settings = new ExchangeRateProviderSettings(); + config.GetSection("ExchangeRateProviderSettings").Bind(settings); + services.AddHttpClient("exchange", + (serviceProvider, client) => + { + client.BaseAddress = new Uri($"{settings.UrlBaseAPI}/{settings.UrlExchangeRate}"); + }) .ConfigurePrimaryHttpMessageHandler(() => { return new SocketsHttpHandler() - { - PooledConnectionLifetime = TimeSpan.FromMinutes(15) - }; + { + PooledConnectionLifetime = TimeSpan.FromMinutes(15) + }; }) .SetHandlerLifetime(Timeout.InfiniteTimeSpan); - services.AddTransient(); - services.AddTransient(); - services.AddSingleton(); - }) + + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + }) + .UseSerilog((context, configuration) => configuration.ReadFrom.Configuration(config)) .Build(); - var exchangeRateProvider = host.Services.GetRequiredService(); - IEnumerable currencies = new[] - { - new Currency("USD"), - new Currency("EUR"), - new Currency("CZK"), - new Currency("JPY"), - new Currency("KES"), - new Currency("RUB"), - new Currency("THB"), - new Currency("TRY"), - new Currency("XYZ") - }; - var rates = await exchangeRateProvider.GetExchangeRatesAsync(currencies); - foreach(var rate in rates) - { - Console.WriteLine(rate.Rate); - } + +var exchangeRateProvider = host.Services.GetRequiredService(); + +var rates = await exchangeRateProvider.GetExchangeRatesAsync(TestingData.currencies); +foreach(var rate in rates) +{ + Console.WriteLine(rate.ToString()); +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs index 94665a42e..f6b2ece43 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs @@ -18,9 +18,9 @@ public class ExchangeRateProvider(ILogger _logger, IExchan /// do not return exchange rate "USD/CZK" with value calculated as 1 / "CZK/USD". If the source does not provide /// some of the currencies, ignore them. /// - public async Task> GetExchangeRatesAsync(IEnumerable currencies) + public async Task> GetExchangeRatesAsync(IEnumerable currencies) { - var result = new List(); + var result = new List(); try { _logger.LogInformation("Started fetching exchange rates"); @@ -28,7 +28,7 @@ public async Task> GetExchangeRatesAsync(IEnumerabl foreach (var currency in currencies) { _logger.LogInformation($"Adding exchange rate for {currency.Code}"); - result.AddRange(exchangeRates.Rates.Where(e => e.CurrencyCode == currency.Code)); + result.AddRange(exchangeRates.Where(e => e.TargetCurrency.Code == currency.Code)); _logger.LogInformation($"Added exchange rate for {currency.Code}"); } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs index 5bd36a22b..7807667e0 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs @@ -11,6 +11,6 @@ namespace ExchangeRateUpdater.Providers { public interface IExchangeRateProvider { - public Task> GetExchangeRatesAsync(IEnumerable currencies); + public Task> GetExchangeRatesAsync(IEnumerable currencies); } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs index 09584ca2a..1b1f366eb 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs @@ -5,26 +5,38 @@ using System.Net.Http.Json; using System.Text; using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.Const; using ExchangeRateUpdater.Domain.DTO; using ExchangeRateUpdater.Domain.Models; +using ExchangeRateUpdater.Extensions; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; namespace ExchangeRateUpdater.Services { - public class ExchangeRateService(IHttpClientFactory clientFactory, ILogger logger, IMemoryCache memoryCache): IExchangeRateService + public class ExchangeRateService(IHttpClientFactory clientFactory, ILogger _logger, IMemoryCache _memoryCache): IExchangeRateService { - public async Task GetExchangeRateAsync() + public async Task> GetExchangeRateAsync() { + var cacheConstant = MemoryCacheConstants.ExchangeRateKey(DateTime.Now.Date); + if (_memoryCache.TryGetValue(cacheConstant, out IEnumerable result)) + { + _logger.LogInformation("Exchange rates retrieved from cache"); + return result; + } using var client = clientFactory.CreateClient("exchange"); - logger.LogInformation($"Sending GET request for getting exchange rates foon {DateTime.Now:yyyy-MM-dd}"); + _logger.LogInformation($"Sending GET request for getting exchange rates for {DateTime.Now:yyyy-MM-dd}"); var response = await client.GetAsync(client.BaseAddress + $"?date={DateTime.Now:yyyy-MM-dd}"); if (!response.IsSuccessStatusCode) { - logger.LogError($"Status code: {response.StatusCode}; Failed to fetch rates on {DateTime.Now:yyyy-MM-dd}"); + _logger.LogError($"Status code: {response.StatusCode}; Failed to fetch rates on {DateTime.Now:yyyy-MM-dd}"); throw new Exception($"Status code: {response.StatusCode}; Failed to fetch rates on{DateTime.Now:yyyy-MM-dd}"); } - var result = await response.Content.ReadFromJsonAsync(); + result = (await response.Content.ReadFromJsonAsync()).ToExchangeRates(); + if (result.Any()) + { + _memoryCache.Set(cacheConstant, result, TimeSpan.FromSeconds(60)); + } return result; } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs index e4b0e1be7..4bfe297e1 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs @@ -10,6 +10,6 @@ namespace ExchangeRateUpdater.Services { public interface IExchangeRateService { - public Task GetExchangeRateAsync(); + public Task> GetExchangeRateAsync(); } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs b/jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs new file mode 100644 index 000000000..66983c952 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ExchangeRateUpdater.Domain.Models; + +namespace ExchangeRateUpdater +{ + public class TestingData + { + public static IEnumerable currencies = new[] + { + new Currency("USD"), + new Currency("EUR"), + new Currency("CZK"), + new Currency("JPY"), + new Currency("KES"), + new Currency("RUB"), + new Currency("THB"), + new Currency("TRY"), + new Currency("XYZ") + }; + } +} diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json index 32a900997..68d764570 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json +++ b/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json @@ -6,12 +6,27 @@ } }, "AllowedHosts": "*", - "MemoryCache": { - "SizeLimit": 1000, - "AbsoluteExpirationInMinutes": "20" - }, + "ExchangeRateProviderSettings": { "UrlBaseAPI": "https://api.cnb.cz", "UrlExchangeRate": "cnbapi/exrates/daily" + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { + "Default": "Information" + }, + "WriteTo": [ + { + "Name": "Console" + }, + { + "Name": "File", + "Args": { + "path": ".../logs/log.txt", + "rollingInterval": "Day" + } + } + ] } -} \ No newline at end of file + } \ No newline at end of file From 98f9fdb6758e5a55b16d4de4d94cbd358f5e6773 Mon Sep 17 00:00:00 2001 From: Markog19 Date: Mon, 20 Jan 2025 12:59:50 +0100 Subject: [PATCH 4/6] Fixed issue which caused Visual studio to crash on opening the csproj file --- .../.gitignore | 2 +- .../ExchangeRateUpdater.App.csproj | 26 +++++++++++++++++++ .../Extensions/ConverterExtension.cs | 0 .../Program.cs | 4 +-- .../Providers/ExchangeRateProvider.cs | 0 .../Providers/IExchangeRateProvider.cs | 0 .../Services/ExchangeRateService.cs | 0 .../Services/IExchangeRateService.cs | 0 .../TestingData.cs | 0 .../appsettings.json | 0 .../ExchangeRateUpdater.csproj | 0 11 files changed, 28 insertions(+), 4 deletions(-) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/.gitignore (98%) create mode 100644 jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Extensions/ConverterExtension.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Program.cs (96%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Providers/ExchangeRateProvider.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Providers/IExchangeRateProvider.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Services/ExchangeRateService.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/Services/IExchangeRateService.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/TestingData.cs (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => ConsoleApp1}/appsettings.json (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater => }/ExchangeRateUpdater.csproj (100%) diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore b/jobs/Backend/Task/src/ConsoleApp1/.gitignore similarity index 98% rename from jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore rename to jobs/Backend/Task/src/ConsoleApp1/.gitignore index e3f1a1f9a..005d62573 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/.gitignore +++ b/jobs/Backend/Task/src/ConsoleApp1/.gitignore @@ -25,7 +25,7 @@ nupkg/ *.user *.userosscache *.sln.docstates - +*.sln # Build results [Dd]ebug/ [Dd]ebugPublic/ diff --git a/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj b/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj new file mode 100644 index 000000000..e132fe1e4 --- /dev/null +++ b/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + + + + Always + + + diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs b/jobs/Backend/Task/src/ConsoleApp1/Extensions/ConverterExtension.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Extensions/ConverterExtension.cs rename to jobs/Backend/Task/src/ConsoleApp1/Extensions/ConverterExtension.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs b/jobs/Backend/Task/src/ConsoleApp1/Program.cs similarity index 96% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs rename to jobs/Backend/Task/src/ConsoleApp1/Program.cs index 970526777..bccf8bd67 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater/Program.cs +++ b/jobs/Backend/Task/src/ConsoleApp1/Program.cs @@ -13,9 +13,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Serilog; -using Serilog.Core; -using Serilog.Extensions.Logging; + var basePath = System.IO.Directory.GetCurrentDirectory(); var config = new ConfigurationBuilder() diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs b/jobs/Backend/Task/src/ConsoleApp1/Providers/ExchangeRateProvider.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Providers/ExchangeRateProvider.cs rename to jobs/Backend/Task/src/ConsoleApp1/Providers/ExchangeRateProvider.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs b/jobs/Backend/Task/src/ConsoleApp1/Providers/IExchangeRateProvider.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Providers/IExchangeRateProvider.cs rename to jobs/Backend/Task/src/ConsoleApp1/Providers/IExchangeRateProvider.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs b/jobs/Backend/Task/src/ConsoleApp1/Services/ExchangeRateService.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Services/ExchangeRateService.cs rename to jobs/Backend/Task/src/ConsoleApp1/Services/ExchangeRateService.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs b/jobs/Backend/Task/src/ConsoleApp1/Services/IExchangeRateService.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/Services/IExchangeRateService.cs rename to jobs/Backend/Task/src/ConsoleApp1/Services/IExchangeRateService.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs b/jobs/Backend/Task/src/ConsoleApp1/TestingData.cs similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/TestingData.cs rename to jobs/Backend/Task/src/ConsoleApp1/TestingData.cs diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json b/jobs/Backend/Task/src/ConsoleApp1/appsettings.json similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/appsettings.json rename to jobs/Backend/Task/src/ConsoleApp1/appsettings.json diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater.csproj similarity index 100% rename from jobs/Backend/Task/src/ExchangeRateUpdater/ExchangeRateUpdater.csproj rename to jobs/Backend/Task/src/ExchangeRateUpdater.csproj From 115ec9093d1381f461ff845aac4c41742cf91ec4 Mon Sep 17 00:00:00 2001 From: Markog19 Date: Mon, 20 Jan 2025 19:11:51 +0100 Subject: [PATCH 5/6] Fixed issue causing Visual Studio crashing, log file was not allowed to be created Wrote tests which covered all major parts of the solution Wrote converter extension which converts from ExchangeRateDTO to ExchangeRate type Introduced static method for creating memory cache contants Wrote documentation in Readme file --- .../ExchangeRateUpdater.App.csproj | 26 -------- .../.gitignore | 0 .../ExchangeRateUpdater.App.csproj} | 6 +- .../Extensions/ConverterExtension.cs | 0 .../Program.cs | 48 +++++++------- .../Providers/ExchangeRateProvider.cs | 2 +- .../Providers/IExchangeRateProvider.cs | 0 .../Services/ExchangeRateService.cs | 2 +- .../Services/IExchangeRateService.cs | 0 .../TestingData.cs | 0 .../appsettings.json | 2 +- .../Const/MemoryCacheConstants.cs | 4 +- .../DTO/ExchangeRatesDTO.cs | 2 +- .../Models/ExchangeRate.cs | 2 +- .../ExchangeRateProviderTests.cs | 45 +++++++++++++ .../ExchangeRateServiceTests.cs | 65 +++++++++++++++++++ .../ExchangeRateUpdater.Tests.csproj | 30 +++++++++ jobs/Backend/Task/src/Readme.md | 41 ++++++++++++ 18 files changed, 216 insertions(+), 59 deletions(-) delete mode 100644 jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/.gitignore (100%) rename jobs/Backend/Task/src/{ExchangeRateUpdater.csproj => ExchangeRateUpdater.App/ExchangeRateUpdater.App.csproj} (87%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Extensions/ConverterExtension.cs (100%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Program.cs (56%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Providers/ExchangeRateProvider.cs (98%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Providers/IExchangeRateProvider.cs (100%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Services/ExchangeRateService.cs (97%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/Services/IExchangeRateService.cs (100%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/TestingData.cs (100%) rename jobs/Backend/Task/src/{ConsoleApp1 => ExchangeRateUpdater.App}/appsettings.json (92%) create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateProviderTests.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateServiceTests.cs create mode 100644 jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj create mode 100644 jobs/Backend/Task/src/Readme.md diff --git a/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj b/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj deleted file mode 100644 index e132fe1e4..000000000 --- a/jobs/Backend/Task/src/ConsoleApp1/ExchangeRateUpdater.App.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - - - - - - - - - - Always - - - diff --git a/jobs/Backend/Task/src/ConsoleApp1/.gitignore b/jobs/Backend/Task/src/ExchangeRateUpdater.App/.gitignore similarity index 100% rename from jobs/Backend/Task/src/ConsoleApp1/.gitignore rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/.gitignore diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater.App/ExchangeRateUpdater.App.csproj similarity index 87% rename from jobs/Backend/Task/src/ExchangeRateUpdater.csproj rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/ExchangeRateUpdater.App.csproj index c810b0358..73906c041 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.csproj +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.App/ExchangeRateUpdater.App.csproj @@ -7,18 +7,18 @@ enable - + - - + + diff --git a/jobs/Backend/Task/src/ConsoleApp1/Extensions/ConverterExtension.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Extensions/ConverterExtension.cs similarity index 100% rename from jobs/Backend/Task/src/ConsoleApp1/Extensions/ConverterExtension.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/Extensions/ConverterExtension.cs diff --git a/jobs/Backend/Task/src/ConsoleApp1/Program.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Program.cs similarity index 56% rename from jobs/Backend/Task/src/ConsoleApp1/Program.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/Program.cs index bccf8bd67..e93b228bb 100644 --- a/jobs/Backend/Task/src/ConsoleApp1/Program.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Program.cs @@ -1,25 +1,21 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using ExchangeRateUpdater; +using ExchangeRateUpdater; using ExchangeRateUpdater.Domain.Configurations; -using ExchangeRateUpdater.Domain.Models; using ExchangeRateUpdater.Providers; using ExchangeRateUpdater.Services; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; +using Serilog; -var basePath = System.IO.Directory.GetCurrentDirectory(); -var config = new ConfigurationBuilder() - .SetBasePath(basePath) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build(); +var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .Build(); +Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(config) + .CreateLogger(); var host = new HostBuilder() .ConfigureServices((services) => { @@ -34,25 +30,31 @@ .ConfigurePrimaryHttpMessageHandler(() => { return new SocketsHttpHandler() - { - PooledConnectionLifetime = TimeSpan.FromMinutes(15) - }; + { + PooledConnectionLifetime = TimeSpan.FromMinutes(15) + }; }) .SetHandlerLifetime(Timeout.InfiniteTimeSpan); - services.AddTransient(); - services.AddTransient(); - services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); }) - .UseSerilog((context, configuration) => configuration.ReadFrom.Configuration(config)) + .UseSerilog() .Build(); var exchangeRateProvider = host.Services.GetRequiredService(); - -var rates = await exchangeRateProvider.GetExchangeRatesAsync(TestingData.currencies); -foreach(var rate in rates) + +for (int i = 0; i < 3; i++) { - Console.WriteLine(rate.ToString()); + var rates = await exchangeRateProvider.GetExchangeRatesAsync(TestingData.currencies); + foreach (var rate in rates) + { + Console.WriteLine(rate.ToString()); + } + Console.WriteLine("-----------------"); + } + diff --git a/jobs/Backend/Task/src/ConsoleApp1/Providers/ExchangeRateProvider.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Providers/ExchangeRateProvider.cs similarity index 98% rename from jobs/Backend/Task/src/ConsoleApp1/Providers/ExchangeRateProvider.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/Providers/ExchangeRateProvider.cs index f6b2ece43..5b78dbd2f 100644 --- a/jobs/Backend/Task/src/ConsoleApp1/Providers/ExchangeRateProvider.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Providers/ExchangeRateProvider.cs @@ -35,7 +35,7 @@ public async Task> GetExchangeRatesAsync(IEnumerable> GetExchangeRateAsync() { - var cacheConstant = MemoryCacheConstants.ExchangeRateKey(DateTime.Now.Date); + var cacheConstant = MemoryCacheConstants.ExchangeRateKey(nameof(ExchangeRate) ,DateTime.Now.Date); if (_memoryCache.TryGetValue(cacheConstant, out IEnumerable result)) { _logger.LogInformation("Exchange rates retrieved from cache"); diff --git a/jobs/Backend/Task/src/ConsoleApp1/Services/IExchangeRateService.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.App/Services/IExchangeRateService.cs similarity index 100% rename from jobs/Backend/Task/src/ConsoleApp1/Services/IExchangeRateService.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/Services/IExchangeRateService.cs diff --git a/jobs/Backend/Task/src/ConsoleApp1/TestingData.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.App/TestingData.cs similarity index 100% rename from jobs/Backend/Task/src/ConsoleApp1/TestingData.cs rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/TestingData.cs diff --git a/jobs/Backend/Task/src/ConsoleApp1/appsettings.json b/jobs/Backend/Task/src/ExchangeRateUpdater.App/appsettings.json similarity index 92% rename from jobs/Backend/Task/src/ConsoleApp1/appsettings.json rename to jobs/Backend/Task/src/ExchangeRateUpdater.App/appsettings.json index 68d764570..4db5ccc0e 100644 --- a/jobs/Backend/Task/src/ConsoleApp1/appsettings.json +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.App/appsettings.json @@ -23,7 +23,7 @@ { "Name": "File", "Args": { - "path": ".../logs/log.txt", + "path": "C:\\Temp\\logs\\log-.txt", "rollingInterval": "Day" } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs index a4837284c..dbaffe3ac 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Const/MemoryCacheConstants.cs @@ -9,9 +9,9 @@ namespace ExchangeRateUpdater.Domain.Const public class MemoryCacheConstants { - public static string ExchangeRateKey(DateTime? date) + public static string ExchangeRateKey(string className, DateTime? date) { - return $"DATE_{date.GetValueOrDefault().ToString("yyyy-MM-dd")}_KEY"; + return $"{className}_DATE_{date.GetValueOrDefault().ToString("yyyy-MM-dd")}_KEY"; } } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs index 57df527b5..dc23019a4 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/DTO/ExchangeRatesDTO.cs @@ -10,6 +10,6 @@ namespace ExchangeRateUpdater.Domain.DTO public class ExchangeRatesDTO { [JsonPropertyName("rates")] - public IEnumerable Rates { get; init; } + public IEnumerable Rates { get; set; } = new List(); } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs index 514571875..969a319c5 100644 --- a/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Domain/Models/ExchangeRate.cs @@ -18,7 +18,7 @@ public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal va public override string ToString() { - return $"{SourceCurrency.Code}/{TargetCurrency.Code}={Value}"; + return $"{SourceCurrency}/{TargetCurrency}={Value}"; } } } diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateProviderTests.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateProviderTests.cs new file mode 100644 index 000000000..161d71ead --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateProviderTests.cs @@ -0,0 +1,45 @@ +using ExchangeRateUpdater.Domain.Models; +using ExchangeRateUpdater.Providers; +using ExchangeRateUpdater.Services; +using Microsoft.Extensions.Logging; +using Moq; + + +namespace ExchangeRateUpdater.Tests +{ + public class ExchangeRateProviderTests + { + + private Mock> _loggerMock; + public readonly Mock exchangeRateServiceMock; + public ExchangeRateProvider exchangeRateProvider; + + public IEnumerable testExchangeRatesData { get; set; } + public IEnumerable testCurrenciesData { get; set; } + + public ExchangeRateProviderTests() + { + testExchangeRatesData = new List() + { + new ExchangeRate(new Currency("CZK"), new Currency("USD"), 0.89m), + new ExchangeRate(new Currency("CZK"), new Currency("EUR"), 0.86m), + new ExchangeRate(new Currency("CZK"), new Currency("AUD"), 1.37m), + new ExchangeRate(new Currency("CZK"), new Currency("GBP"), 0.0075m), + }; + testCurrenciesData = new List(TestingData.currencies); + + _loggerMock = new Mock>(); + exchangeRateServiceMock = new Mock(); + exchangeRateServiceMock.Setup(s => s.GetExchangeRateAsync()).ReturnsAsync(testExchangeRatesData); + } + + [Fact] + public async Task GetExchangeRatesProvider_NotNullAsync() + { + exchangeRateProvider = new ExchangeRateProvider(_loggerMock.Object, exchangeRateServiceMock.Object); + var result = await exchangeRateProvider.GetExchangeRatesAsync(testCurrenciesData); + Assert.NotNull(result); + Assert.True(result.Count() > 0); + } + } +} \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateServiceTests.cs b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateServiceTests.cs new file mode 100644 index 000000000..03eb2154a --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateServiceTests.cs @@ -0,0 +1,65 @@ +using Castle.Core.Logging; +using ExchangeRateUpdater.Domain.DTO; +using ExchangeRateUpdater.Domain.Models; +using ExchangeRateUpdater.Extensions; +using ExchangeRateUpdater.Providers; +using ExchangeRateUpdater.Services; +using Microsoft.Extensions.Logging; +using Moq; + + +namespace ExchangeRateUpdater.Tests +{ + public class ExchangeRateServiceTests + { + + public readonly Mock exchangeRateServiceMock; + public ExchangeRatesDTO ExchangeRateDTOTestData { get; set; } = new ExchangeRatesDTO(); + public IEnumerable testExchangeRatesData { get; set; } + public IEnumerable exchangeRatesDTO { get; set; } + public IEnumerable testCurrenciesData { get; set; } + + public ExchangeRateServiceTests() + { + testExchangeRatesData = new List() + { + new ExchangeRate(new Currency("CZK"), new Currency("USD"), 0.89m), + new ExchangeRate(new Currency("CZK"), new Currency("EUR"), 0.86m), + new ExchangeRate(new Currency("CZK"), new Currency("AUD"), 1.37m), + new ExchangeRate(new Currency("CZK"), new Currency("GBP"), 0.0075m), + }; + + exchangeRatesDTO = new List() + { + new ExchangeRateDTO { Amount = 100, CurrencyCode = "USD", Currency = "US Dollar", Country = "United States", Rate = 1.0m}, + new ExchangeRateDTO { Amount = 200, CurrencyCode = "EUR", Currency = "Euro", Country = "European Union", Rate = 0.89m }, + new ExchangeRateDTO { Amount = 300, CurrencyCode = "GBP", Currency = "British Pound", Country = "United Kingdom", Rate = 0.75m}, + new ExchangeRateDTO { Amount = 1000, CurrencyCode = "JPY", Currency = "Japanese Yen", Country = "Japan", Rate = 109.65m}, + }; + ExchangeRateDTOTestData.Rates = exchangeRatesDTO; + testCurrenciesData = new List(TestingData.currencies); + + exchangeRateServiceMock = new Mock(); + exchangeRateServiceMock.Setup(s => s.GetExchangeRateAsync()).ReturnsAsync(testExchangeRatesData); + } + + + [Fact] + public async Task GetExchangeRates_NotNullAsync() + { + var result = await exchangeRateServiceMock.Object.GetExchangeRateAsync(); + + Assert.NotNull(result); + Assert.True(result.Count() > 0); + } + [Fact] + + public async Task ConvertsExchangeRates_OkAsync() + { + var result = ConverterExtension.ToExchangeRates(ExchangeRateDTOTestData); + + Assert.NotNull(result); + Assert.True(result.Count() > 0); + } + } +} \ No newline at end of file diff --git a/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj new file mode 100644 index 000000000..6dcbc0047 --- /dev/null +++ b/jobs/Backend/Task/src/ExchangeRateUpdater.Tests/ExchangeRateUpdater.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/jobs/Backend/Task/src/Readme.md b/jobs/Backend/Task/src/Readme.md new file mode 100644 index 000000000..6ec391ace --- /dev/null +++ b/jobs/Backend/Task/src/Readme.md @@ -0,0 +1,41 @@ +# Mews backend task - Exchange Rate Updater solution +This is a repository with solution for Mews backend task. Said task is supposed to fetch exchange rates from Czech National banks for their national currency. +## Requirements +For sucessfull running of the solution certain packages are required: +* Serilog - consists of several packages such as Serilog sinks for different kinds of outputs (console and file in this solution), Serilog hosting and configuration for configuring logging via appsettings json file +* Microsoft.Extensions - extensions package that consists of different package needed for setting up of the program.cs class: + * Microsoft.Extensions.Caching.Memory - package for in memory caching of returned exchange rates + * Microsoft.Extensions.Configuration - package for binding appsettings json to a custom settings class + * Microsoft.Extensions.Logging - package for logger used in Serilog + * Microsoft.Extensions.Http - package for http client for sending get requests to the bank API +## Logging +Logging in this solution is done via Serilog which allows the user to setup a logger with just few lines of code. It allows the user to have logging done directly to different outpouts such as: +* Message broker +* Console +* File + +``` +var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .Build(); +Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(config) + .CreateLogger(); +var host = new HostBuilder().UseSerilog(); +``` +## How To Run +Use dotnet run to run the solution +## Projects +### [App](ExchangeRateUpdater.App) + +### [Domain](ExchangeRateUpdater.Domain) +### [Tests](ExchangeRateUpdater.Tests) + +## Configuration +Configuration is stored in [appsettings.json](ExchangeRateUpdater.App/appsettings.json) file which is parsed inside [settings](ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs) file. It allows the user to enter the API endpoint where exchange rates can be found. + +## Testing + +Running dotnet test inside [Tests](ExchangeRateUpdater.Tests/) directory will execute the application tests. + + From a0552dcb48ffe45481010cd08634bd682a54441c Mon Sep 17 00:00:00 2001 From: Markog19 Date: Mon, 20 Jan 2025 19:12:46 +0100 Subject: [PATCH 6/6] Updated Readme file --- jobs/Backend/Task/src/Readme.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jobs/Backend/Task/src/Readme.md b/jobs/Backend/Task/src/Readme.md index 6ec391ace..98a3a8d39 100644 --- a/jobs/Backend/Task/src/Readme.md +++ b/jobs/Backend/Task/src/Readme.md @@ -26,10 +26,13 @@ var host = new HostBuilder().UseSerilog(); ## How To Run Use dotnet run to run the solution ## Projects -### [App](ExchangeRateUpdater.App) +### [App](ExchangeRateUpdater.App) +Main part of the project. Inside of it are [service](ExchangeRateUpdater.App/Services/ExchangeRateService.cs) and [provider](ExchangeRateUpdater.App/Providers/ExchangeRateProvider.cs) alongside their interfaces for the fetching of exchange rates. ### [Domain](ExchangeRateUpdater.Domain) +Consists of all supporting classes ExchangeRateUpdater.App requires such as configuration, DTO, model, etc classes. ### [Tests](ExchangeRateUpdater.Tests) +Consits of tests for ExchangeRateUpdater.App service and provider. ## Configuration Configuration is stored in [appsettings.json](ExchangeRateUpdater.App/appsettings.json) file which is parsed inside [settings](ExchangeRateUpdater.Domain/Configurations/ExchangeRateProviderSettings.cs) file. It allows the user to enter the API endpoint where exchange rates can be found. @@ -38,4 +41,9 @@ Configuration is stored in [appsettings.json](ExchangeRateUpdater.App/appsetting Running dotnet test inside [Tests](ExchangeRateUpdater.Tests/) directory will execute the application tests. +## Possible improvements +* Caching - Caching could be implemented in Redis instead of in memory caching +* Docker, CI/CD - Was not required in the task but it is a must have thing in every modern production environment +* Performance tracing and metrics - Enterprise solutions require tracing of how solution is performing, Grafana could be used for creating easy to read dashboards for tracking. +