diff --git a/.gitignore b/.gitignore index fd3586545..9d4a1671b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,347 @@ -!.gitkeep -!.gitignore -!*.dll -[Oo]bj -[Bb]in -*.user +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files *.suo -*.[Cc]ache -*.bak -*.ncb -*.DS_Store +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) *.userprefs -*.iml -*.ncrunch* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* .*crunch*.local.xml -.idea -[Tt]humbs.db -*.tgz -*.sublime-* - -node_modules -bower_components -npm-debug.log +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# .tfvars files +*.tfvars + +# Backend configuration and environment variables +backend*.cfg +azure-env*.ps1 +azure_env*.ps1 + +.DS_Store \ No newline at end of file diff --git a/jobs/Backend/Task/Dockerfile b/jobs/Backend/Task/Dockerfile new file mode 100644 index 000000000..cc3f9f60f --- /dev/null +++ b/jobs/Backend/Task/Dockerfile @@ -0,0 +1,17 @@ + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /app + +COPY ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj ./ExchangeRateUpdater.Host/ +RUN dotnet restore ./ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj + +COPY . . + +RUN dotnet publish ./ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj -c Release -o out + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app + +COPY --from=build /app/out . + +ENTRYPOINT ["dotnet", "ExchangeRateUpdater.Host.dll"] \ No newline at end of file diff --git a/jobs/Backend/Task/Dtos/CurrencyRequestDto.cs b/jobs/Backend/Task/Dtos/CurrencyRequestDto.cs new file mode 100644 index 000000000..04b1e46d6 --- /dev/null +++ b/jobs/Backend/Task/Dtos/CurrencyRequestDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Dtos +{ + public class CurrencyRequestDto + { + public string CurrencyCode { get; set; } + } +} diff --git a/jobs/Backend/Task/Dtos/ExchangeRateResponseDto.cs b/jobs/Backend/Task/Dtos/ExchangeRateResponseDto.cs new file mode 100644 index 000000000..074dfd962 --- /dev/null +++ b/jobs/Backend/Task/Dtos/ExchangeRateResponseDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Dtos +{ + public class ExchangeRateResponseDto + { + public string SourceCurrencyCode { get; set; } + public string TargetCurrencyCode { get; set; } + public decimal Rate { get; set; } + } +} diff --git a/jobs/Backend/Task/Dtos/Profiles/MappingProfile.cs b/jobs/Backend/Task/Dtos/Profiles/MappingProfile.cs new file mode 100644 index 000000000..b5c082b23 --- /dev/null +++ b/jobs/Backend/Task/Dtos/Profiles/MappingProfile.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using ExchangeRateUpdater.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Dtos.Profiles +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap(); + + CreateMap() + .ForMember(dest => dest.SourceCurrencyCode, opt => opt.MapFrom(src => src.SourceCurrency.Code)) + .ForMember(dest => dest.TargetCurrencyCode, opt => opt.MapFrom(src => src.TargetCurrency.Code)) + .ForMember(dest => dest.Rate, opt => opt.MapFrom(src => src.Value)); + } + } +} 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.Api/ApiConfiguration.cs b/jobs/Backend/Task/ExchangeRateUpdater.Api/ApiConfiguration.cs new file mode 100644 index 000000000..fb30d9f57 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/ApiConfiguration.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Api +{ + public static class ApiConfiguration + { + public static IServiceCollection AddApiLayer(this IServiceCollection services) + { + services.AddAutoMapper(typeof(ApiConfiguration).Assembly); + + return services; + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Api/Controllers/ExchangeRateController.cs b/jobs/Backend/Task/ExchangeRateUpdater.Api/Controllers/ExchangeRateController.cs new file mode 100644 index 000000000..75e6e15ab --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/Controllers/ExchangeRateController.cs @@ -0,0 +1,46 @@ +using AutoMapper; +using ExchangeRateUpdater.API.Dtos; +using ExchangeRateUpdater.Domain; +using ExchangeRateUpdater.Service.Services.Interface; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace ExchangeRateUpdater.Presentation.Controllers +{ + [ApiController] + [Route("api/exchangeRates")] + public class ExchangeRateController : ControllerBase + { + private readonly IExchangeRateService _exchangeRateService; + private readonly IMapper _mapper; + private readonly ILogger _logger; + + public ExchangeRateController( + IExchangeRateService exchangeRateService, + IMapper mapper, + ILogger logger) + { + _exchangeRateService = exchangeRateService; + _mapper = mapper; + _logger = logger; + } + + /// + /// Returns exchange rates. + /// + /// List of currencies to check exchange rates for + /// Exchange rates from CZK to requested currencies + [HttpGet] + public async Task>> GetExchangeRates( + [FromQuery]IEnumerable currencies) + { + _logger.LogInformation("Received request for exchange rates"); + var exchangeRates = await _exchangeRateService.GetExchangeRatesAsync(_mapper.Map>(currencies)); + var result = _mapper.Map>(exchangeRates); + + _logger.LogInformation("Returning request for exchange rates"); + + return Ok(result); + } + } +} \ No newline at end of file diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/CurrencyRequestDto.cs b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/CurrencyRequestDto.cs new file mode 100644 index 000000000..478153cb7 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/CurrencyRequestDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.API.Dtos +{ + public class CurrencyRequestDto + { + public string CurrencyCode { get; set; } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/ExchangeRateResponseDto.cs b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/ExchangeRateResponseDto.cs new file mode 100644 index 000000000..3c8b21c32 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/ExchangeRateResponseDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.API.Dtos +{ + public class ExchangeRateResponseDto + { + public string SourceCurrencyCode { get; set; } + public string TargetCurrencyCode { get; set; } + public decimal Rate { get; set; } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/Profiles/MappingProfile.cs b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/Profiles/MappingProfile.cs new file mode 100644 index 000000000..23c7cfa3d --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/Dtos/Profiles/MappingProfile.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using ExchangeRateUpdater.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.API.Dtos.Profiles +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap(); + + CreateMap() + .ForMember(dest => dest.SourceCurrencyCode, opt => opt.MapFrom(src => src.SourceCurrency.Code)) + .ForMember(dest => dest.TargetCurrencyCode, opt => opt.MapFrom(src => src.TargetCurrency.Code)) + .ForMember(dest => dest.Rate, opt => opt.MapFrom(src => src.Value)); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Api/ExchangeRateUpdater.Api.csproj b/jobs/Backend/Task/ExchangeRateUpdater.Api/ExchangeRateUpdater.Api.csproj new file mode 100644 index 000000000..dd52fc025 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Api/ExchangeRateUpdater.Api.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Domain/Base/ValueObject.cs b/jobs/Backend/Task/ExchangeRateUpdater.Domain/Base/ValueObject.cs new file mode 100644 index 000000000..9ce516931 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Domain/Base/ValueObject.cs @@ -0,0 +1,18 @@ +using System; + +namespace ExchangeRateUpdater.Domain.Base +{ + public abstract class ValueObject + { + public override bool Equals(object obj) + { + return obj is ValueObject other && GetType() == other.GetType() && + EqualityComparer.Default.Equals(ToString(), other.ToString()); + } + + public override int GetHashCode() + { + return ToString()?.GetHashCode() ?? 0; + } + } +} \ No newline at end of file diff --git a/jobs/Backend/Task/Currency.cs b/jobs/Backend/Task/ExchangeRateUpdater.Domain/Currency.cs similarity index 51% rename from jobs/Backend/Task/Currency.cs rename to jobs/Backend/Task/ExchangeRateUpdater.Domain/Currency.cs index f375776f2..c105e9a22 100644 --- a/jobs/Backend/Task/Currency.cs +++ b/jobs/Backend/Task/ExchangeRateUpdater.Domain/Currency.cs @@ -1,12 +1,19 @@ -namespace ExchangeRateUpdater +using ExchangeRateUpdater.Domain.Base; + +namespace ExchangeRateUpdater.Domain { - public class Currency + public class Currency : ValueObject { - public Currency(string code) + private Currency(string code) { Code = code; } + public static Currency Create(string code) + { + return new Currency(code); + } + /// /// Three-letter ISO 4217 code of the currency. /// diff --git a/jobs/Backend/Task/ExchangeRate.cs b/jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRate.cs similarity index 51% rename from jobs/Backend/Task/ExchangeRate.cs rename to jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRate.cs index 58c5bb10e..ddf5b7e4e 100644 --- a/jobs/Backend/Task/ExchangeRate.cs +++ b/jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRate.cs @@ -1,23 +1,23 @@ -namespace ExchangeRateUpdater +namespace ExchangeRateUpdater.Domain { public class ExchangeRate { - public ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value) + private ExchangeRate(Currency sourceCurrency, Currency targetCurrency, decimal value) { SourceCurrency = sourceCurrency; TargetCurrency = targetCurrency; Value = value; } + public static ExchangeRate Create(Currency sourceCurrency, Currency targetCurrency, decimal value) + { + return new ExchangeRate(sourceCurrency, targetCurrency, value); + } + public Currency SourceCurrency { get; } public Currency TargetCurrency { get; } public decimal Value { get; } - - public override string ToString() - { - return $"{SourceCurrency}/{TargetCurrency}={Value}"; - } } } diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj b/jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj new file mode 100644 index 000000000..fa71b7ae6 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Domain/ExchangeRateUpdater.Domain.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj b/jobs/Backend/Task/ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj new file mode 100644 index 000000000..140810221 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Host/ExchangeRateUpdater.Host.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Host/Program.cs b/jobs/Backend/Task/ExchangeRateUpdater.Host/Program.cs new file mode 100644 index 000000000..8a7ea6a14 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Host/Program.cs @@ -0,0 +1,30 @@ +using ExchangeRateUpdater.Infrastructure; +using ExchangeRateUpdater.Service; +using ExchangeRateUpdater.Api; +using ExchangeRateUpdater.Presentation.Controllers; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddApiLayer(); +builder.Services.AddInfrastructureLayer(builder.Configuration); +builder.Services.AddServiceLayer(); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); + +public partial class Program { } diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.Development.json b/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.json b/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.json new file mode 100644 index 000000000..6af1dc680 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Host/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CnbApi": { + "BaseUrl": "https://api.cnb.cz/cnbapi/", + "Methods": { + "Exrates": "exrates/daily" + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiMethods.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiMethods.cs new file mode 100644 index 000000000..af0dcab73 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiMethods.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Infrastructure.Configuration +{ + public class CnbApiMethods + { + public string Exrates { get; set; } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiOptions.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiOptions.cs new file mode 100644 index 000000000..68130420f --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Configuration/CnbApiOptions.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Infrastructure.Configuration +{ + public class CnbApiOptions + { + public string BaseUrl { get; set; } + public CnbApiMethods Methods { get; set; } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateDto.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateDto.cs new file mode 100644 index 000000000..612448aa3 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Infrastructure.Dtos +{ + public class ExrateDto + { + public int Amount { get; set; } + public string Country { get; set; } + public string Currency { get; set; } + public string CurrencyCode { get; set; } + public int Order { get; set; } + public decimal Rate { get; set; } + public DateOnly ValidFor { get; set; } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateResponse.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateResponse.cs new file mode 100644 index 000000000..62ef24960 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/Dtos/ExrateResponse.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Infrastructure.Dtos +{ + public class ExrateResponse + { + public List Rates { get; set; } = new(); + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/ExchangeRateUpdater.Infrastructure.csproj b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/ExchangeRateUpdater.Infrastructure.csproj new file mode 100644 index 000000000..cd7bab5fe --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/ExchangeRateUpdater.Infrastructure.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/InfrastructureConfiguration.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/InfrastructureConfiguration.cs new file mode 100644 index 000000000..831c4f815 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/InfrastructureConfiguration.cs @@ -0,0 +1,23 @@ +using ExchangeRateUpdater.Infrastructure.cnbConector; +using ExchangeRateUpdater.Infrastructure.Configuration; +using ExchangeRateUpdater.Service.Infrastructure.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace ExchangeRateUpdater.Infrastructure +{ + public static class InfrastructureConfiguration + { + public static IServiceCollection AddInfrastructureLayer( + this IServiceCollection services, + IConfiguration configuration) + { + services.AddHttpClient(); + + services.Configure( + configuration.GetSection("CnbApi")); + + return services; + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/cnbConector/CnbService.cs b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/cnbConector/CnbService.cs new file mode 100644 index 000000000..8c04614b4 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Infrastructure/cnbConector/CnbService.cs @@ -0,0 +1,68 @@ +using ExchangeRateUpdater.Domain; +using ExchangeRateUpdater.Infrastructure.Configuration; +using ExchangeRateUpdater.Infrastructure.Dtos; +using ExchangeRateUpdater.Service.Infrastructure.Interfaces; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Infrastructure.cnbConector +{ + public class CnbService : ICnbService + { + private readonly HttpClient _httpClient; + private readonly CnbApiOptions _options; + private readonly ILogger _logger; + private static readonly Currency czkCurrency = Currency.Create("CZK"); + + public CnbService(HttpClient httpClient, IOptions options, ILogger logger) + { + _httpClient = httpClient; + _options = options.Value; + _logger = logger; + } + + public async Task> GetExchangeRatesByCurrencyAsync(DateTime date, IEnumerable currencies) + { + var url = $"{_options.BaseUrl}{_options.Methods.Exrates}?date={date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}&lang=EN"; + + _logger.LogInformation("Calling cnb api"); + _logger.LogDebug("Request URL: {Url}", url); + + using var response = await _httpClient.GetAsync(url); + + if (!response.IsSuccessStatusCode) + { + _logger.LogError("Failed to fetch exchange rates from CNB. StatusCode: {StatusCode}", response.StatusCode); + return Enumerable.Empty(); + } + + _logger.LogInformation("Cnb api response was successful"); + + var json = await response.Content.ReadAsStringAsync(); + + var exrateResponse = JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + if (exrateResponse == null) + return Enumerable.Empty(); + + return exrateResponse.Rates + .Where(dto => currencies.Any(c => c.Code.Equals(dto.CurrencyCode))) + .Select(dto => + { + var targetCurrency = currencies.First(c => c.Code.Equals(dto.CurrencyCode)); + return ExchangeRate.Create(czkCurrency, targetCurrency, dto.Rate); + }) + .ToList(); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/CustomWebApplicationFactory .cs b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/CustomWebApplicationFactory .cs new file mode 100644 index 000000000..89a68c1a3 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/CustomWebApplicationFactory .cs @@ -0,0 +1,41 @@ +using ExchangeRateUpdater.Domain; +using ExchangeRateUpdater.Service.Infrastructure.Interfaces; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Moq; + +namespace ExchangeRateUpdater.IntegrationTests +{ + public class CustomWebApplicationFactory : WebApplicationFactory + { + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(services => + { + var descriptor = services.SingleOrDefault( + d => d.ServiceType == typeof(ICnbService)); + + if (descriptor != null) + services.Remove(descriptor); + + var mock = new Mock(); + + mock.Setup(s => s.GetExchangeRatesByCurrencyAsync(It.IsAny(), It.IsAny>())) + .ReturnsAsync((DateTime date, IEnumerable currencies) => + { + var czk = Currency.Create("CZK"); + + var rates = new List(); + foreach (var currency in currencies) + { + rates.Add(ExchangeRate.Create(czk, currency, 24.0m)); + } + return rates; + }); + + services.AddSingleton(mock.Object); + }); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/ExchangeRateUpdater.IntegrationTests.csproj b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/ExchangeRateUpdater.IntegrationTests.csproj new file mode 100644 index 000000000..18c5ddb96 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/ExchangeRateUpdater.IntegrationTests.csproj @@ -0,0 +1,40 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/UseCase/ExchangeRateTests.cs b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/UseCase/ExchangeRateTests.cs new file mode 100644 index 000000000..bb210b8af --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.IntegrationTests/UseCase/ExchangeRateTests.cs @@ -0,0 +1,41 @@ +using ExchangeRateUpdater.API.Dtos; +using System.Net; +using System.Net.Http.Json; +using Xunit; +using Assert = Xunit.Assert; + +namespace ExchangeRateUpdater.IntegrationTests.UseCase +{ + public class ExchangeRateApiTests : IClassFixture + { + private readonly HttpClient _client; + + public ExchangeRateApiTests(CustomWebApplicationFactory factory) + { + _client = factory.CreateClient(); + } + + [Fact] + public async Task GetExchangeRates_ShouldReturnCorrectResult_WhenEverythingIsOk() + { + // Arrange + var url = "/api/exchangeRates?currencies=EUR"; + + // Act + var response = await _client.GetAsync(url); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result); + Assert.Equal(1, result.Count); + + Assert.True(result.Any(r => + r.TargetCurrencyCode == "EUR" && + r.SourceCurrencyCode == "CZK" && + r.Rate == 24.0m), "EUR/CZK not found"); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj b/jobs/Backend/Task/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj new file mode 100644 index 000000000..115e0ee5d --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Service/ExchangeRateUpdater.Service.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Service/Infrastructure/Interfaces/ICnbService.cs b/jobs/Backend/Task/ExchangeRateUpdater.Service/Infrastructure/Interfaces/ICnbService.cs new file mode 100644 index 000000000..98d6151fe --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Service/Infrastructure/Interfaces/ICnbService.cs @@ -0,0 +1,14 @@ +using ExchangeRateUpdater.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Service.Infrastructure.Interfaces +{ + public interface ICnbService + { + Task> GetExchangeRatesByCurrencyAsync(DateTime date, IEnumerable currencies); + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Service/ServiceConfiguration.cs b/jobs/Backend/Task/ExchangeRateUpdater.Service/ServiceConfiguration.cs new file mode 100644 index 000000000..342d5b921 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Service/ServiceConfiguration.cs @@ -0,0 +1,16 @@ +using ExchangeRateUpdater.Service.Services; +using ExchangeRateUpdater.Service.Services.Interface; +using Microsoft.Extensions.DependencyInjection; + +namespace ExchangeRateUpdater.Service +{ + public static class ServiceConfiguration + { + public static IServiceCollection AddServiceLayer(this IServiceCollection services) + { + services.AddScoped(); + + return services; + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/ExchangeRateService.cs b/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/ExchangeRateService.cs new file mode 100644 index 000000000..17a5f81e2 --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/ExchangeRateService.cs @@ -0,0 +1,26 @@ +using ExchangeRateUpdater.Domain; +using ExchangeRateUpdater.Service.Infrastructure.Interfaces; +using ExchangeRateUpdater.Service.Services.Interface; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Service.Services +{ + public class ExchangeRateService : IExchangeRateService + { + private readonly ICnbService _cnbService; + + public ExchangeRateService(ICnbService cnbService) + { + _cnbService = cnbService; + } + + public async Task> GetExchangeRatesAsync(IEnumerable currencies) + { + return await _cnbService.GetExchangeRatesByCurrencyAsync(DateTime.Now, currencies ); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/Interface/IExchangeRateService.cs b/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/Interface/IExchangeRateService.cs new file mode 100644 index 000000000..5681d43af --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.Service/Services/Interface/IExchangeRateService.cs @@ -0,0 +1,14 @@ +using ExchangeRateUpdater.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExchangeRateUpdater.Service.Services.Interface +{ + public interface IExchangeRateService + { + Task> GetExchangeRatesAsync(IEnumerable currencies); + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/Domain/CurrencyTests.cs b/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/Domain/CurrencyTests.cs new file mode 100644 index 000000000..2f6fa12ee --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/Domain/CurrencyTests.cs @@ -0,0 +1,38 @@ +using ExchangeRateUpdater.Domain; +using Xunit; +using Assert = Xunit.Assert; + +namespace ExchangeRateUpdater.UnitTests.Domain +{ + + public class CurrencyTests + { + [Fact] + public void Create_ShouldReturnCurrency_WhenIsOk() + { + var code = "EUR"; + + var currency = Currency.Create(code); + + Assert.Equal(code, currency.Code); + } + + [Fact] + public void Compare_ShouldReturnEqual_WhenSameCode() + { + var c1 = Currency.Create("GBP"); + var c2 = Currency.Create("GBP"); + + Assert.Equal(c1, c2); + } + + [Fact] + public void Compare_ShouldReturnNotEqual_WhenDifferentCode() + { + var c1 = Currency.Create("JPY"); + var c2 = Currency.Create("AUD"); + + Assert.NotEqual(c1, c2); + } + } +} diff --git a/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/ExchangeRateUpdater.UnitTests.csproj b/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/ExchangeRateUpdater.UnitTests.csproj new file mode 100644 index 000000000..5c7ae90ba --- /dev/null +++ b/jobs/Backend/Task/ExchangeRateUpdater.UnitTests/ExchangeRateUpdater.UnitTests.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + 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..2bd16a4a6 100644 --- a/jobs/Backend/Task/ExchangeRateUpdater.sln +++ b/jobs/Backend/Task/ExchangeRateUpdater.sln @@ -1,9 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34616.47 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater", "ExchangeRateUpdater.csproj", "{7B2695D6-D24C-4460-A58E-A10F08550CE0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeRateUpdater.Service", "ExchangeRateUpdater.Service\ExchangeRateUpdater.Service.csproj", "{F1D180AE-C5BA-480F-919E-F82F3ED88C27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeRateUpdater.Domain", "ExchangeRateUpdater.Domain\ExchangeRateUpdater.Domain.csproj", "{9CC87D08-598D-4E9C-B14D-01D1DA5FA982}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeRateUpdater.Infrastructure", "ExchangeRateUpdater.Infrastructure\ExchangeRateUpdater.Infrastructure.csproj", "{99F36EAE-4B83-4445-BFB1-2799AF1E8A4D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{98F22A73-6750-4EDC-9075-EAA01534606E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeRateUpdater.UnitTests", "ExchangeRateUpdater.UnitTests\ExchangeRateUpdater.UnitTests.csproj", "{60476D90-22C3-4E89-805B-74014F6DC272}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeRateUpdater.IntegrationTests", "ExchangeRateUpdater.IntegrationTests\ExchangeRateUpdater.IntegrationTests.csproj", "{AC7689A6-AB66-4D27-8601-A2FD4B30C6BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Host", "ExchangeRateUpdater.Host\ExchangeRateUpdater.Host.csproj", "{02CDEC43-41F0-4EED-97FB-2D277D434E86}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeRateUpdater.Api", "ExchangeRateUpdater.Api\ExchangeRateUpdater.Api.csproj", "{6D21329E-CE50-4EBF-AFF8-A4CB4C5DF9FF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,12 +25,43 @@ Global 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 + {F1D180AE-C5BA-480F-919E-F82F3ED88C27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1D180AE-C5BA-480F-919E-F82F3ED88C27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1D180AE-C5BA-480F-919E-F82F3ED88C27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1D180AE-C5BA-480F-919E-F82F3ED88C27}.Release|Any CPU.Build.0 = Release|Any CPU + {9CC87D08-598D-4E9C-B14D-01D1DA5FA982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9CC87D08-598D-4E9C-B14D-01D1DA5FA982}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CC87D08-598D-4E9C-B14D-01D1DA5FA982}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9CC87D08-598D-4E9C-B14D-01D1DA5FA982}.Release|Any CPU.Build.0 = Release|Any CPU + {99F36EAE-4B83-4445-BFB1-2799AF1E8A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99F36EAE-4B83-4445-BFB1-2799AF1E8A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99F36EAE-4B83-4445-BFB1-2799AF1E8A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99F36EAE-4B83-4445-BFB1-2799AF1E8A4D}.Release|Any CPU.Build.0 = Release|Any CPU + {60476D90-22C3-4E89-805B-74014F6DC272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60476D90-22C3-4E89-805B-74014F6DC272}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60476D90-22C3-4E89-805B-74014F6DC272}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60476D90-22C3-4E89-805B-74014F6DC272}.Release|Any CPU.Build.0 = Release|Any CPU + {AC7689A6-AB66-4D27-8601-A2FD4B30C6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC7689A6-AB66-4D27-8601-A2FD4B30C6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC7689A6-AB66-4D27-8601-A2FD4B30C6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC7689A6-AB66-4D27-8601-A2FD4B30C6BD}.Release|Any CPU.Build.0 = Release|Any CPU + {02CDEC43-41F0-4EED-97FB-2D277D434E86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02CDEC43-41F0-4EED-97FB-2D277D434E86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02CDEC43-41F0-4EED-97FB-2D277D434E86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02CDEC43-41F0-4EED-97FB-2D277D434E86}.Release|Any CPU.Build.0 = Release|Any CPU + {6D21329E-CE50-4EBF-AFF8-A4CB4C5DF9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D21329E-CE50-4EBF-AFF8-A4CB4C5DF9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D21329E-CE50-4EBF-AFF8-A4CB4C5DF9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D21329E-CE50-4EBF-AFF8-A4CB4C5DF9FF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {60476D90-22C3-4E89-805B-74014F6DC272} = {98F22A73-6750-4EDC-9075-EAA01534606E} + {AC7689A6-AB66-4D27-8601-A2FD4B30C6BD} = {98F22A73-6750-4EDC-9075-EAA01534606E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F488060C-98E0-437F-BAA6-A27FE61B951E} + EndGlobalSection EndGlobal 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(); - } - } -}