Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 87 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,89 @@
!.gitkeep
!.gitignore
!*.dll
[Oo]bj
[Bb]in
*.user
## .NET ##
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
msbuild.log
msbuild.err
msbuild.wrn

# Visual Studio cache/options directory
.vs/
.vscode/

# User-specific files
*.suo
*.[Cc]ache
*.bak
*.ncb
*.DS_Store
*.user
*.userosscache
*.sln.docstates

# Build results
*.DotSettings
*.dotSettings

# ReSharper cache
_ReSharper*/
*.[Rr]e[Ss]harper
*.sln.ide

# Rider cache
.idea/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml

# Packages directory
packages/

# NuGet
[Nn]u[Gg]et.exe
*.nupkg
nuget.exe
.nuget/

# Local development cache
.localhistory/

# Rider/Resharper specific
_ReSharper.*/
*.sln.ide

# MacOS
.DS_Store

# VS Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# User-specific files
*.userprefs
*.iml
*.ncrunch*
.*crunch*.local.xml
.idea
[Tt]humbs.db
*.tgz
*.sublime-*

node_modules
bower_components
npm-debug.log

# IDE files
*.swp
*.swo

# Build output
dotnet-watch.html

# App-specific ignores
/appsettings.Development.json
!/appsettings.json
30 changes: 30 additions & 0 deletions jobs/Backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
19 changes: 19 additions & 0 deletions jobs/Backend/ExchangeRateUpdater.Console/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Use the .NET 6 SDK image for building the application
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app

# Copy the project file and restore dependencies
COPY MyApp.Console.csproj ./
RUN dotnet restore

# Copy the rest of the application files and publish the application
COPY . ./
RUN dotnet publish -c Release -o /app/publish

# Use the .NET 6 runtime image for running the application
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build /app/publish .

# Set the entry point for the application
ENTRYPOINT ["dotnet", "ExchangeRateUpdater.Console.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.5" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ExchangeRateUpdater.Infrastructure\ExchangeRateUpdater.Infrastructure.csproj" />
</ItemGroup>

</Project>
72 changes: 72 additions & 0 deletions jobs/Backend/ExchangeRateUpdater.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using ExchangeRateUpdater.Infrastructure;
using ExchangeRateUpdater.Core.Interfaces;
using ExchangeRateUpdater.Core.Models;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using System.Net.Http.Headers;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace ExchangeRateUpdater
{
public static class Program
{
public static async Task Main(string[] args)
{
// Configure the host builder
var builder = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
})
.ConfigureServices((hostContext, services) =>
{
services.Configure<SettingOptions>(hostContext.Configuration.GetSection("Settings"));
// Register services
services.AddHttpClient<IHttpWebClient, HttpWebClient>();

services.AddTransient<IExchangeRateProvider, ExchangeRateProvider>();
});


// Build the host
using var host = builder.Build();

try
{
var provider = host.Services.GetRequiredService<IExchangeRateProvider>();
var settingOptions = host.Services.GetRequiredService<IOptions<SettingOptions>>().Value;
var allowedCurrenciess = settingOptions.AllowedCurrencies.Select(x => new Currency(x));
var rates = await provider.GetExchangeRatesAsync(allowedCurrenciess);

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();
}
}
}
26 changes: 26 additions & 0 deletions jobs/Backend/ExchangeRateUpdater.Console/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"Settings": {
"AllowedCurrencies": [
"USD",
"EUR",
"CZK",
"JPY",
"KES",
"RUB",
"THB",
"TRY",
"XYZ"
],
"Endpoint": "en/financial-markets/foreign-exchange-market/central-bank-exchange-rate-fixing/central-bank-exchange-rate-fixing/daily.txt",
"BaseUrl": "https://www.cnb.cz"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"ExchangeRateProvider": "Debug"
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.5" />
<PackageReference Include="Polly" Version="8.5.2" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using ExchangeRateUpdater.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExchangeRateUpdater.Core.Interfaces
{
/// <summary>
/// Provides functionality to retrieve currency exchange rates from a specified source.
/// </summary>
public interface IExchangeRateProvider
{
/// <summary>
/// Retrieves exchange rates for the specified currencies from the provider's source.
/// </summary>
/// <param name="currencies">The collection of currencies to get exchange rates for.</param>
/// <returns></returns>
Task<IEnumerable<ExchangeRate>> GetExchangeRatesAsync(IEnumerable<Currency> currencies);
}
}
31 changes: 31 additions & 0 deletions jobs/Backend/ExchangeRateUpdater.Core/Interfaces/IHttpWebClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExchangeRateUpdater.Core.Interfaces
{
/// <summary>
/// Provides an abstraction for making HTTP GET requests with built-in resilience policies.
/// </summary>
/// <remarks>
/// Implementations of this interface should:
/// <list type="bullet">
/// <item><description>Handle transient failures with automatic retries</description></item>
/// <item><description>Maintain configured HTTP client settings (base address, headers, etc.)</description></item>
/// <item><description>Validate responses before returning them</description></item>
/// </list>
/// </remarks>
public interface IHttpWebClient
{
/// <summary>
/// Executes a GET request to the configured endpoint.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains
/// the <see cref="HttpResponseMessage"/> from the successful request.
/// </returns>
Task<HttpResponseMessage> GetAsync();
}
}
17 changes: 17 additions & 0 deletions jobs/Backend/ExchangeRateUpdater.Core/Models/CnbExchangeRate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExchangeRateUpdater.Core.Models
{
public class CnbExchangeRate
{
public string Country { get; set; } = string.Empty;
public string CurrencyName { get; set; } = string.Empty;
public decimal Amount { get; set; }
public string Code { get; set; } = string.Empty;
public decimal Rate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ExchangeRateUpdater
namespace ExchangeRateUpdater.Core.Models
{
public class Currency
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ExchangeRateUpdater
namespace ExchangeRateUpdater.Core.Models
{
public class ExchangeRate
{
Expand Down
Loading