diff --git a/jobs/Backend/Task/ExchangeRateProvider.cs b/jobs/Backend/Task/ExchangeRateProvider.cs
index 6f82a97fb..7f10db30e 100644
--- a/jobs/Backend/Task/ExchangeRateProvider.cs
+++ b/jobs/Backend/Task/ExchangeRateProvider.cs
@@ -1,10 +1,23 @@
-using System.Collections.Generic;
+using ExchangeRateProvider.Models;
+using ExchangeRateUpdater.Interfaces;
+using System;
+using System.Collections.Generic;
using System.Linq;
namespace ExchangeRateUpdater
{
- public class ExchangeRateProvider
- {
+ public class ExchangeRateProvider : IExchangeRateProvider
+ {///
+ /// The data source to fetch exchange rates from.
+ ///
+ private BaseExchangeDataSource _exchangeDataSource;
+
+
+ public ExchangeRateProvider(BaseExchangeDataSource baseExchangeDataSource)
+ {
+ _exchangeDataSource = baseExchangeDataSource;
+ }
+
///
/// 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",
@@ -13,7 +26,25 @@ public class ExchangeRateProvider
///
public IEnumerable GetExchangeRates(IEnumerable currencies)
{
- return Enumerable.Empty();
+ try
+ {
+ var textFormatUrl = _exchangeDataSource.GetExchangeRateDatasetUrl();
+
+ var readerService = ExchangeDataSourceReaderFactory.CreateDataSourceReader(_exchangeDataSource.DataSourceType);
+ // Fetch and parse the data
+ var exchangeRates = readerService.FetchAndParseExchangeRatesAsync(_exchangeDataSource)
+ .GetAwaiter()
+ .GetResult();
+
+ // Filter the exchange rates based on the specified currencies
+ return exchangeRates.Where(rate =>
+ currencies.Any(c => c.Code == rate.TargetCurrency.Code));
+ }
+ catch (Exception ex)
+ {
+ // return a specific error
+ throw new Exception("An error occurred while fetching exchange rates.", ex);
+ }
}
}
}
diff --git a/jobs/Backend/Task/ExchangeRateUpdater.csproj b/jobs/Backend/Task/ExchangeRateUpdater.csproj
index 2fc654a12..d9d8c82d6 100644
--- a/jobs/Backend/Task/ExchangeRateUpdater.csproj
+++ b/jobs/Backend/Task/ExchangeRateUpdater.csproj
@@ -5,4 +5,8 @@
net6.0
+
+
+
+
\ No newline at end of file
diff --git a/jobs/Backend/Task/Interfaces/IExchangeDataSourceFactory.cs b/jobs/Backend/Task/Interfaces/IExchangeDataSourceFactory.cs
new file mode 100644
index 000000000..a4862c270
--- /dev/null
+++ b/jobs/Backend/Task/Interfaces/IExchangeDataSourceFactory.cs
@@ -0,0 +1,9 @@
+using ExchangeRateProvider.Models;
+
+namespace ExchangeRateUpdater.Interfaces
+{
+ public interface IExchangeDataSourceFactory
+ {
+ public BaseExchangeDataSource CreateDataSource(ExchangeRateDataSourceType dataSourceType);
+ }
+}
diff --git a/jobs/Backend/Task/Interfaces/IExchangeRateProvider.cs b/jobs/Backend/Task/Interfaces/IExchangeRateProvider.cs
new file mode 100644
index 000000000..0ac260b67
--- /dev/null
+++ b/jobs/Backend/Task/Interfaces/IExchangeRateProvider.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace ExchangeRateUpdater.Interfaces
+{
+ public interface IExchangeRateProvider
+ {
+ public IEnumerable GetExchangeRates(IEnumerable currencies);
+ }
+}
diff --git a/jobs/Backend/Task/Models/BaseExchangeDataSource.cs b/jobs/Backend/Task/Models/BaseExchangeDataSource.cs
new file mode 100644
index 000000000..1fffa0299
--- /dev/null
+++ b/jobs/Backend/Task/Models/BaseExchangeDataSource.cs
@@ -0,0 +1,34 @@
+namespace ExchangeRateProvider.Models
+{
+ public abstract class BaseExchangeDataSource
+ {
+ ///
+ /// The source currency code.
+ ///
+ public abstract SourceCurrencyCode SourceCurrencyCode { get; }
+
+ ///
+ /// The type of the exchange rate data source.
+ ///
+ public abstract ExchangeRateDataSourceType DataSourceType { get; }
+
+ ///
+ /// The URL to connect to the exchange rate data source.
+ ///
+ public abstract string ConnectionUrl { get; }
+
+ ///
+ /// Returns the URL to fetch the exchange rate dataset.
+ ///
+ ///
+ public abstract string GetExchangeRateDatasetUrl();
+ }
+
+ ///
+ /// The source currency code.
+ ///
+ public enum SourceCurrencyCode
+ {
+ CZK = 1
+ }
+}
diff --git a/jobs/Backend/Task/Models/CnbExchangeRateDataSource.cs b/jobs/Backend/Task/Models/CnbExchangeRateDataSource.cs
new file mode 100644
index 000000000..ae8f8da13
--- /dev/null
+++ b/jobs/Backend/Task/Models/CnbExchangeRateDataSource.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace ExchangeRateProvider.Models
+{
+ public class CnbExchangeRateDataSource : BaseExchangeDataSource
+ {
+ ///
+ /// The base URL of the exchange rate data source.
+ ///
+ private const string BaseUrl = "https://www.cnb.cz";
+
+ ///
+ /// The URL path to fetch the exchange rate dataset.
+ ///
+ private const string DailyTextUrl = "/en/financial-markets/foreign-exchange-market/central-bank-exchange-rate-fixing/central-bank-exchange-rate-fixing/daily.txt";
+
+ ///
+ /// The date of the exchange rate. Default is today.
+ ///
+ private DateTime _exchangeRateDate;
+
+ ///
+ /// The date format.
+ ///
+ private string DATE_FORMAT = "dd.MM.yyyy";
+
+ ///
+ /// The type of the exchange rate data source.
+ ///
+ public override ExchangeRateDataSourceType DataSourceType => ExchangeRateDataSourceType.Cnb;
+
+ ///
+ /// The URL to connect to the exchange rate data source.
+ ///
+ public override string ConnectionUrl => $"{BaseUrl}{DailyTextUrl}";
+
+ ///
+ /// The date of the exchange rate.
+ ///
+ public DateTime ExchangeRateDate { get => _exchangeRateDate; set => _exchangeRateDate = value; }
+
+ ///
+ /// The source currency code.
+ ///
+ public override SourceCurrencyCode SourceCurrencyCode => SourceCurrencyCode.CZK;
+
+
+ public CnbExchangeRateDataSource(DateTime? exchangeRateDate = null) : base()
+ {
+ ExchangeRateDate = exchangeRateDate ?? DateTime.UtcNow;
+ }
+
+ ///
+ /// Returns the URL to fetch the exchange rate dataset.
+ ///
+ ///
+ public override string GetExchangeRateDatasetUrl()
+ {
+ var formattedDate = ExchangeRateDate.ToString(DATE_FORMAT);
+ return $"{ConnectionUrl}?date={formattedDate}";
+ }
+
+ public override string ToString()
+ {
+ return "Czech National Bank (CNB)";
+ }
+ }
+}
diff --git a/jobs/Backend/Task/Models/ExchangeDataSourceFactory.cs b/jobs/Backend/Task/Models/ExchangeDataSourceFactory.cs
new file mode 100644
index 000000000..67ee53036
--- /dev/null
+++ b/jobs/Backend/Task/Models/ExchangeDataSourceFactory.cs
@@ -0,0 +1,33 @@
+using ExchangeRateUpdater.Interfaces;
+using System;
+
+namespace ExchangeRateProvider.Models
+{
+ public class ExchangeDataSourceFactory : IExchangeDataSourceFactory
+ {
+ ///
+ /// Creates a new instance of the exchange rate data source.
+ ///
+ ///
+ ///
+ ///
+ public BaseExchangeDataSource CreateDataSource(ExchangeRateDataSourceType dataSourceType)
+ {
+ switch (dataSourceType)
+ {
+ case ExchangeRateDataSourceType.Cnb:
+ return new CnbExchangeRateDataSource();
+ default:
+ throw new NotSupportedException($"Data source type '{dataSourceType}' is not supported.");
+ }
+ }
+ }
+
+ ///
+ /// The type of the exchange rate data source.
+ ///
+ public enum ExchangeRateDataSourceType
+ {
+ Cnb = 1
+ }
+}
diff --git a/jobs/Backend/Task/Models/ExchangeDataSourceReaderFactory.cs b/jobs/Backend/Task/Models/ExchangeDataSourceReaderFactory.cs
new file mode 100644
index 000000000..902426d6b
--- /dev/null
+++ b/jobs/Backend/Task/Models/ExchangeDataSourceReaderFactory.cs
@@ -0,0 +1,22 @@
+using ExchangeRateProvider.Services;
+using System;
+
+namespace ExchangeRateProvider.Models
+{
+ ///
+ /// The factory for creating the exchange rate data source reader.
+ ///
+ public class ExchangeDataSourceReaderFactory
+ {
+ public static BaseExchangeDataSourceReader CreateDataSourceReader(ExchangeRateDataSourceType dataSourceType)
+ {
+ switch (dataSourceType)
+ {
+ case ExchangeRateDataSourceType.Cnb:
+ return new CnbExchangeDataSourceReader();
+ default:
+ throw new NotSupportedException($"Data source type '{dataSourceType}' is not supported.");
+ }
+ }
+ }
+}
diff --git a/jobs/Backend/Task/Program.cs b/jobs/Backend/Task/Program.cs
index 379a69b1f..a5ae87517 100644
--- a/jobs/Backend/Task/Program.cs
+++ b/jobs/Backend/Task/Program.cs
@@ -1,4 +1,7 @@
-using System;
+using ExchangeRateProvider.Models;
+using ExchangeRateUpdater.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
@@ -23,7 +26,20 @@ public static void Main(string[] args)
{
try
{
- var provider = new ExchangeRateProvider();
+ var serviceCollection = new ServiceCollection();
+
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient(provider =>
+ {
+ var factory = provider.GetRequiredService();
+ return new ExchangeRateProvider(factory.CreateDataSource(ExchangeRateDataSourceType.Cnb));
+
+ });
+
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+
+ var provider = serviceProvider.GetRequiredService();
+
var rates = provider.GetExchangeRates(currencies);
Console.WriteLine($"Successfully retrieved {rates.Count()} exchange rates:");
@@ -32,6 +48,10 @@ public static void Main(string[] args)
Console.WriteLine(rate.ToString());
}
}
+ catch (NotSupportedException e)
+ {
+ Console.WriteLine($"Could not create data source: '{e.Message}'.");
+ }
catch (Exception e)
{
Console.WriteLine($"Could not retrieve exchange rates: '{e.Message}'.");
diff --git a/jobs/Backend/Task/Services/BaseExchangeDataSourceReader.cs b/jobs/Backend/Task/Services/BaseExchangeDataSourceReader.cs
new file mode 100644
index 000000000..71a405b92
--- /dev/null
+++ b/jobs/Backend/Task/Services/BaseExchangeDataSourceReader.cs
@@ -0,0 +1,23 @@
+using ExchangeRateProvider.Models;
+using ExchangeRateUpdater;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace ExchangeRateProvider.Services
+{
+ public abstract class BaseExchangeDataSourceReader
+ {
+ ///
+ /// Fetches and parses the exchange rates from the data source.
+ ///
+ protected BaseExchangeDataSource _baseExchangeDataSource;
+
+ ///
+ /// Fetches and parses the exchange rates from the data source.
+ ///
+ ///
+ ///
+ public abstract Task> FetchAndParseExchangeRatesAsync(BaseExchangeDataSource baseExchangeDataSource);
+
+ }
+}
diff --git a/jobs/Backend/Task/Services/CnbExchangeDataSourceReader.cs b/jobs/Backend/Task/Services/CnbExchangeDataSourceReader.cs
new file mode 100644
index 000000000..9c7fa89a4
--- /dev/null
+++ b/jobs/Backend/Task/Services/CnbExchangeDataSourceReader.cs
@@ -0,0 +1,58 @@
+using ExchangeRateProvider.Models;
+using ExchangeRateUpdater;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace ExchangeRateProvider.Services
+{
+ public class CnbExchangeDataSourceReader : BaseExchangeDataSourceReader
+ {
+ ///
+ /// Fetches and parses the exchange rates from the data source.
+ ///
+ ///
+ ///
+ public override async Task> FetchAndParseExchangeRatesAsync(BaseExchangeDataSource baseExchangeDataSource)
+ {
+ _baseExchangeDataSource = baseExchangeDataSource;
+
+ var httpClient = new HttpClient();
+ var response = await httpClient.GetStringAsync(baseExchangeDataSource.GetExchangeRateDatasetUrl());
+
+ return ParseExchangeRates(response);
+ }
+
+ ///
+ /// Parses the exchange rates from the text data.
+ ///
+ ///
+ ///
+ private List ParseExchangeRates(string textData)
+ {
+ var exchangeRates = new List();
+ var lines = textData.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
+
+
+ // Skip the first two lines (date and header)
+ foreach (var line in lines.Skip(2))
+ {
+ var parts = line.Split('|');
+ if (parts.Length >= 5)
+ {
+ var currencyCode = parts[3];
+ var amount = int.Parse(parts[2]); // Amount indicates the base unit (e.g., 100 HUF)
+ if (decimal.TryParse(parts[4], out var rate))
+ {
+ var sourceCurrency = new Currency(_baseExchangeDataSource.SourceCurrencyCode.ToString());
+ var targetCurrency = new Currency(currencyCode);
+ exchangeRates.Add(new ExchangeRate(sourceCurrency, targetCurrency, rate / amount));
+ }
+ }
+ }
+ return exchangeRates;
+ }
+ }
+}