From 1cc8d06c0ff409c241dbb6febe3936d7c4134743 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 16:28:41 +0200 Subject: [PATCH 01/19] initial commit with standard VS project template --- .gitignore | 135 ++++++++++++++++++ topggcsharpchallenge/topggcsharpchallenge.sln | 25 ++++ .../Controllers/WeatherForecastController.cs | 38 +++++ .../topggcsharpchallenge/Program.cs | 20 +++ .../Properties/launchSettings.json | 30 ++++ .../topggcsharpchallenge/Startup.cs | 44 ++++++ .../topggcsharpchallenge/WeatherForecast.cs | 15 ++ .../appsettings.Development.json | 9 ++ .../topggcsharpchallenge/appsettings.json | 10 ++ .../topggcsharpchallenge.csproj | 8 ++ 10 files changed, 334 insertions(+) create mode 100644 .gitignore create mode 100644 topggcsharpchallenge/topggcsharpchallenge.sln create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Program.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Startup.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/appsettings.Development.json create mode 100644 topggcsharpchallenge/topggcsharpchallenge/appsettings.json create mode 100644 topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70f3938 --- /dev/null +++ b/.gitignore @@ -0,0 +1,135 @@ +## Shamelessly stolen from https://gist.github.com/takekazuomi/10955889 + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.svclog +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +!packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +![Ss]tyle[Cc]op.targets +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml + +*.publishsettings + +# 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 + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store + +_NCrunch* \ No newline at end of file diff --git a/topggcsharpchallenge/topggcsharpchallenge.sln b/topggcsharpchallenge/topggcsharpchallenge.sln new file mode 100644 index 0000000..bb43eba --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31702.278 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "topggcsharpchallenge", "topggcsharpchallenge\topggcsharpchallenge.csproj", "{EFDE0803-2939-4275-92A9-B2C9355FAE16}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {39454A13-FC7F-44EE-A3AE-CEE0FF150ECA} + EndGlobalSection +EndGlobal diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..776fcd3 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace topggcsharpchallenge.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IEnumerable Get() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Program.cs b/topggcsharpchallenge/topggcsharpchallenge/Program.cs new file mode 100644 index 0000000..a9d3cc8 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace topggcsharpchallenge +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json b/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json new file mode 100644 index 0000000..1232838 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52785", + "sslPort": 44379 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "topggcsharpchallenge": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Startup.cs b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs new file mode 100644 index 0000000..c0ee7d9 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace topggcsharpchallenge +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs b/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs new file mode 100644 index 0000000..4223fb6 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs @@ -0,0 +1,15 @@ +using System; + +namespace topggcsharpchallenge +{ + public class WeatherForecast + { + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string Summary { get; set; } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/appsettings.Development.json b/topggcsharpchallenge/topggcsharpchallenge/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/appsettings.json b/topggcsharpchallenge/topggcsharpchallenge/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj new file mode 100644 index 0000000..d12c450 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj @@ -0,0 +1,8 @@ + + + + netcoreapp3.1 + + + + From 2df4dcf235f1f7fc2c60bf8ef6af9deb1a46c751 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 16:38:25 +0200 Subject: [PATCH 02/19] add swagger --- .../topggcsharpchallenge/Startup.cs | 28 +++++++++++++++++++ .../topggcsharpchallenge.csproj | 4 +++ 2 files changed, 32 insertions(+) diff --git a/topggcsharpchallenge/topggcsharpchallenge/Startup.cs b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs index c0ee7d9..5bb7d14 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Startup.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs @@ -1,8 +1,11 @@ +using System; + using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; namespace topggcsharpchallenge { @@ -19,6 +22,22 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddControllers(); + + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Earthquake API", + Version = "v1", + Description = "Provides information on earthquakes.", + Contact = new OpenApiContact + { + Name = "Petar Parushev", + Email = "petergparushev@gmail.com", + Url = new Uri("https://top.gg/"), + }, + }); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -29,6 +48,15 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } + app.UseSwagger(); + + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Earthquake API V1"); + + c.RoutePrefix = string.Empty; + }); + app.UseHttpsRedirection(); app.UseRouting(); diff --git a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj index d12c450..394d531 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj +++ b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj @@ -4,5 +4,9 @@ netcoreapp3.1 + + + + From f09f47fb39785a76964d32f2d349523adaa8f866 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 17:24:22 +0200 Subject: [PATCH 03/19] add test project --- topggcsharpchallenge/topggcsharpchallenge.sln | 6 ++++++ .../topggcsharpchallenge/topggcsharpchallenge.csproj | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/topggcsharpchallenge/topggcsharpchallenge.sln b/topggcsharpchallenge/topggcsharpchallenge.sln index bb43eba..0d899ac 100644 --- a/topggcsharpchallenge/topggcsharpchallenge.sln +++ b/topggcsharpchallenge/topggcsharpchallenge.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.31702.278 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "topggcsharpchallenge", "topggcsharpchallenge\topggcsharpchallenge.csproj", "{EFDE0803-2939-4275-92A9-B2C9355FAE16}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "topggcsharpchallengetest", "topggchallengetest\topggcsharpchallengetest.csproj", "{948C5E86-5E33-410F-8BDA-BC7C666278F0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFDE0803-2939-4275-92A9-B2C9355FAE16}.Release|Any CPU.Build.0 = Release|Any CPU + {948C5E86-5E33-410F-8BDA-BC7C666278F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {948C5E86-5E33-410F-8BDA-BC7C666278F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {948C5E86-5E33-410F-8BDA-BC7C666278F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {948C5E86-5E33-410F-8BDA-BC7C666278F0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj index 394d531..bed57c5 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj +++ b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 From 704cfb49ee6941887b0b0ed213dc93b7428ac22f Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 17:31:53 +0200 Subject: [PATCH 04/19] remove unneeded controller and model; add earthquake controller; --- .../topggcsharpchallengetest.csproj | 20 ++++++++++ .../Controllers/EearthquakeController.cs | 10 +++++ .../Controllers/WeatherForecastController.cs | 38 ------------------- .../Properties/launchSettings.json | 4 +- .../topggcsharpchallenge/WeatherForecast.cs | 15 -------- 5 files changed, 32 insertions(+), 55 deletions(-) create mode 100644 topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs delete mode 100644 topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs delete mode 100644 topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs diff --git a/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj new file mode 100644 index 0000000..3e71057 --- /dev/null +++ b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs new file mode 100644 index 0000000..021df06 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc; + +namespace topggcsharpchallenge.Controllers +{ + [ApiController] + [Route("earthquakes")] + public class EearthquakeController : ControllerBase + { + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs deleted file mode 100644 index 776fcd3..0000000 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace topggcsharpchallenge.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json b/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json index 1232838..58fc29e 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json +++ b/topggcsharpchallenge/topggcsharpchallenge/Properties/launchSettings.json @@ -12,7 +12,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,7 +20,7 @@ "topggcsharpchallenge": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "", "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs b/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs deleted file mode 100644 index 4223fb6..0000000 --- a/topggcsharpchallenge/topggcsharpchallenge/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace topggcsharpchallenge -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} From b35fb39c65d53332b9bbfc844d081e6fa6dc540d Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 17:59:48 +0200 Subject: [PATCH 05/19] add initial poc test --- .../Controllers/EarthquakeControllerTest.cs | 23 +++++++++++++++++++ .../topggcsharpchallengetest.csproj | 2 +- .../Controllers/EearthquakeController.cs | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs new file mode 100644 index 0000000..c0b43d7 --- /dev/null +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using topggcsharpchallenge.Controllers; + +namespace topggcsharpchallengetest.Controllers +{ + [TestFixture] + public class EarthquakeControllerTest + { + EearthquakeController sut; + + [SetUp] + public void SetUp() + { + sut = new EearthquakeController(); + } + + [Test] + public void GetShouldBeSuccessfull() + { + sut.Get(); + } + } +} diff --git a/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj index 3e71057..58d5c0d 100644 --- a/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj +++ b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj @@ -14,7 +14,7 @@ - + diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs index 021df06..3e2baf2 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using System; namespace topggcsharpchallenge.Controllers { @@ -6,5 +7,8 @@ namespace topggcsharpchallenge.Controllers [Route("earthquakes")] public class EearthquakeController : ControllerBase { + public void Get() + { + } } } From 34f7ffb3a746b2617bc1b87c3e41f1cf1fe0a37f Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 18:08:13 +0200 Subject: [PATCH 06/19] [TDD commit] add EarthquakeService interface and class; extend initial test; remove typo --- .../Controllers/EarthquakeControllerTest.cs | 11 ++++++--- .../topggcsharpchallengetest.csproj | 1 + .../Controllers/EarthquakeController.cs | 23 +++++++++++++++++++ .../Controllers/EearthquakeController.cs | 14 ----------- .../Services/EarthquakeService.cs | 10 ++++++++ .../Services/IEarthquakeService.cs | 7 ++++++ 6 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs delete mode 100644 topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index c0b43d7..907d9f2 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -1,23 +1,28 @@ -using NUnit.Framework; +using Moq; +using NUnit.Framework; using topggcsharpchallenge.Controllers; +using topggcsharpchallenge.Services; namespace topggcsharpchallengetest.Controllers { [TestFixture] public class EarthquakeControllerTest { - EearthquakeController sut; + Mock earthquakeServiceMock = new Mock(); + EarthquakeController sut; [SetUp] public void SetUp() { - sut = new EearthquakeController(); + sut = new EarthquakeController(earthquakeServiceMock.Object); } [Test] public void GetShouldBeSuccessfull() { sut.Get(); + + earthquakeServiceMock.Verify((x) => x.Get(), Times.Once); } } } diff --git a/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj index 58d5c0d..4f9dfb7 100644 --- a/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj +++ b/topggcsharpchallenge/topggchallengetest/topggcsharpchallengetest.csproj @@ -8,6 +8,7 @@ + diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs new file mode 100644 index 0000000..12ce922 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Mvc; +using topggcsharpchallenge.Services; + +namespace topggcsharpchallenge.Controllers +{ + [ApiController] + [Route("earthquakes")] + public class EarthquakeController : ControllerBase + { + private IEarthquakeService earthquakeService; + + public EarthquakeController(IEarthquakeService earthquakeService) + { + this.earthquakeService = earthquakeService; + } + + [HttpGet] + public void Get() + { + earthquakeService.Get(); + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs deleted file mode 100644 index 3e2baf2..0000000 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EearthquakeController.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System; - -namespace topggcsharpchallenge.Controllers -{ - [ApiController] - [Route("earthquakes")] - public class EearthquakeController : ControllerBase - { - public void Get() - { - } - } -} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs new file mode 100644 index 0000000..e3889dd --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -0,0 +1,10 @@ +namespace topggcsharpchallenge.Services +{ + public class EarthquakeService : IEarthquakeService + { + public void Get() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs new file mode 100644 index 0000000..a358b9a --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -0,0 +1,7 @@ +namespace topggcsharpchallenge.Services +{ + public interface IEarthquakeService + { + void Get(); + } +} From 2687c9c1b747dcb24788aa6f6820baf90514c15f Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 18:16:01 +0200 Subject: [PATCH 07/19] [TDD] add and pass parameters --- .../Controllers/EarthquakeControllerTest.cs | 10 ++++++++-- .../Controllers/EarthquakeController.cs | 5 +++-- .../topggcsharpchallenge/Services/EarthquakeService.cs | 8 +++++--- .../Services/IEarthquakeService.cs | 6 ++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index 907d9f2..fe69d38 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -1,5 +1,6 @@ using Moq; using NUnit.Framework; +using System; using topggcsharpchallenge.Controllers; using topggcsharpchallenge.Services; @@ -20,9 +21,14 @@ public void SetUp() [Test] public void GetShouldBeSuccessfull() { - sut.Get(); + int latitude = 10; + int longitude = 20; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.Now; - earthquakeServiceMock.Verify((x) => x.Get(), Times.Once); + sut.Get(latitude, longitude, startDate, endDate); + + earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index 12ce922..f1710a9 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using System; using topggcsharpchallenge.Services; namespace topggcsharpchallenge.Controllers @@ -15,9 +16,9 @@ public EarthquakeController(IEarthquakeService earthquakeService) } [HttpGet] - public void Get() + public void Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - earthquakeService.Get(); + earthquakeService.Get(latitude, longitude, startDate, endDate); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index e3889dd..0082318 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -1,10 +1,12 @@ -namespace topggcsharpchallenge.Services +using System; + +namespace topggcsharpchallenge.Services { public class EarthquakeService : IEarthquakeService { - public void Get() + public void Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index a358b9a..20d508c 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -1,7 +1,9 @@ -namespace topggcsharpchallenge.Services +using System; + +namespace topggcsharpchallenge.Services { public interface IEarthquakeService { - void Get(); + void Get(int latitude, int longitude, DateTime startDate, DateTime endDate); } } From fe71d322c925a6d128f7fe5696a6739eb7095009 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 18:45:31 +0200 Subject: [PATCH 08/19] [TDD] add EarthquakeResponseModel; service now returns EarthquakeResponseModels; controller now returns EarthquakeResponseModels; --- .../Controllers/EarthquakeControllerTest.cs | 67 ++++++++++++++++++- .../Controllers/EarthquakeController.cs | 6 +- .../Models/EarthquakeResponseModel.cs | 30 +++++++++ .../Services/EarthquakeService.cs | 4 +- .../Services/IEarthquakeService.cs | 4 +- .../topggcsharpchallenge.csproj | 1 - 6 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index fe69d38..75aa64c 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -1,7 +1,9 @@ using Moq; using NUnit.Framework; using System; +using System.Collections.Generic; using topggcsharpchallenge.Controllers; +using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; namespace topggcsharpchallengetest.Controllers @@ -9,7 +11,7 @@ namespace topggcsharpchallengetest.Controllers [TestFixture] public class EarthquakeControllerTest { - Mock earthquakeServiceMock = new Mock(); + readonly Mock earthquakeServiceMock = new Mock(); EarthquakeController sut; [SetUp] @@ -25,10 +27,71 @@ public void GetShouldBeSuccessfull() int longitude = 20; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.Now; + IEnumerable mockedData = getEarthquakeResponseModelMockData(); + earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(mockedData); - sut.Get(latitude, longitude, startDate, endDate); + IEnumerable actualData = sut.Get(latitude, longitude, startDate, endDate); + Assert.That(actualData, Is.EqualTo(mockedData)); earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once); } + + private IEnumerable getEarthquakeResponseModelMockData() + { + return new List() + { + new EarthquakeResponseModel() + { + Time = DateTime.MinValue, + Latitude = 0.1234, + Longitude = 4.3210, + Depth = 123.456, + Mag = 654.312, + MagType = "md", + Nst = 1, + Gap = 2, + Dmin = 2.34, + Rms = 3.45, + Net = "nc", + Id = "nc73636400", + Updated = DateTime.Now, + Place = "sofia", + Type = "earthquake", + HorizontalError = 0.111, + DepthError = 0.222, + MagError = 0.333, + MagNst = 4, + Status = "automatic", + LocationSource = "nc", + MagSource = "nc" + }, + new EarthquakeResponseModel() + { + Time = DateTime.Now, + Latitude = 0.000001, + Longitude = 0.000002, + Depth = 0.000003, + Mag = 0.000004, + MagType = "dm", + Nst = 200, + Gap = 300, + Dmin = 0.000005, + Rms = 0.000006, + Net = "cn", + Id = "cn73636400", + Updated = DateTime.MaxValue, + Place = "nyc", + Type = "earthquake", + HorizontalError = 0.000007, + DepthError = 0.000008, + MagError = 0.000009, + MagNst = 400, + Status = "reviwed", + LocationSource = "ls", + MagSource = "ms" + }, + }; + } + } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index f1710a9..c516bf6 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Mvc; using System; +using System.Collections.Generic; +using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; namespace topggcsharpchallenge.Controllers @@ -16,9 +18,9 @@ public EarthquakeController(IEarthquakeService earthquakeService) } [HttpGet] - public void Get(int latitude, int longitude, DateTime startDate, DateTime endDate) + public IEnumerable Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - earthquakeService.Get(latitude, longitude, startDate, endDate); + return earthquakeService.Get(latitude, longitude, startDate, endDate); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs new file mode 100644 index 0000000..f802e1e --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs @@ -0,0 +1,30 @@ +using System; + +namespace topggcsharpchallenge.Models +{ + public class EarthquakeResponseModel + { + public DateTime Time { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } + public double Depth { get; set; } + public double Mag { get; set; } + public string MagType { get; set; } + public int Nst { get; set; } + public int Gap { get; set; } + public double Dmin { get; set; } + public double Rms { get; set; } + public string Net { get; set; } + public string Id { get; set; } + public DateTime Updated { get; set; } + public string Place { get; set; } + public string Type { get; set; } + public double HorizontalError { get; set; } + public double DepthError { get; set; } + public double MagError { get; set; } + public int MagNst { get; set; } + public string Status { get; set; } + public string LocationSource { get; set; } + public string MagSource { get; set; } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index 0082318..ee2b31f 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; +using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services { public class EarthquakeService : IEarthquakeService { - public void Get(int latitude, int longitude, DateTime startDate, DateTime endDate) + IEnumerable IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { throw new NotImplementedException(); } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index 20d508c..db314d2 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; +using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services { public interface IEarthquakeService { - void Get(int latitude, int longitude, DateTime startDate, DateTime endDate); + IEnumerable Get(int latitude, int longitude, DateTime startDate, DateTime endDate); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj index bed57c5..90e0820 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj +++ b/topggcsharpchallenge/topggcsharpchallenge/topggcsharpchallenge.csproj @@ -8,5 +8,4 @@ - From aeec132e6497a57e533af29f809a01d1f3ef831d Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 19:07:03 +0200 Subject: [PATCH 09/19] [TDD] add initial implementation of EarthquakeService; set internal modifier to classes; add AssemblyInfo and grant visibility to test project and runner; --- .../Services/EarthquakeServiceTest.cs | 95 +++++++++++++++++++ .../topggcsharpchallenge/AssemblyInfo.cs | 21 ++++ .../Controllers/EarthquakeController.cs | 2 +- .../Services/EarthquakeService.cs | 12 ++- .../Services/IEarthquakeService.cs | 2 +- .../Services/IUsgsService.cs | 10 ++ .../Services/UsgsService.cs | 13 +++ 7 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/AssemblyInfo.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs new file mode 100644 index 0000000..1517271 --- /dev/null +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -0,0 +1,95 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using topggcsharpchallenge.Models; +using topggcsharpchallenge.Services; + +namespace topggcsharpchallengetest.Services +{ + [TestFixture] + public class EarthquakeServiceTest + { + readonly Mock usgsServiceMock = new Mock(); + + IEarthquakeService sut; + + [SetUp] + public void SetUp() + { + sut = new EarthquakeService(usgsServiceMock.Object); + } + + [Test] + public void GetShouldBeSuccessfull() + { + int latitude = 10; + int longitude = 20; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.Now; + IEnumerable expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData, Is.EqualTo(expectedEarthquakeData)); + } + + private IEnumerable getUsgsServiceGetEarthquakeDataMocks() + { + return new List() + { + new EarthquakeResponseModel() + { + Time = DateTime.MinValue, + Latitude = 0.1234, + Longitude = 4.3210, + Depth = 123.456, + Mag = 654.312, + MagType = "md", + Nst = 1, + Gap = 2, + Dmin = 2.34, + Rms = 3.45, + Net = "nc", + Id = "nc73636400", + Updated = DateTime.Now, + Place = "sofia", + Type = "earthquake", + HorizontalError = 0.111, + DepthError = 0.222, + MagError = 0.333, + MagNst = 4, + Status = "automatic", + LocationSource = "nc", + MagSource = "nc" + }, + new EarthquakeResponseModel() + { + Time = DateTime.Now, + Latitude = 0.000001, + Longitude = 0.000002, + Depth = 0.000003, + Mag = 0.000004, + MagType = "dm", + Nst = 200, + Gap = 300, + Dmin = 0.000005, + Rms = 0.000006, + Net = "cn", + Id = "cn73636400", + Updated = DateTime.MaxValue, + Place = "nyc", + Type = "earthquake", + HorizontalError = 0.000007, + DepthError = 0.000008, + MagError = 0.000009, + MagNst = 400, + Status = "reviwed", + LocationSource = "ls", + MagSource = "ms" + }, + }; + } + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/AssemblyInfo.cs b/topggcsharpchallenge/topggcsharpchallenge/AssemblyInfo.cs new file mode 100644 index 0000000..215d955 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("edeaad31-e994-4648-ae4f-346454635958")] + +[assembly: InternalsVisibleTo("topggcsharpchallengetest")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index c516bf6..05c42d6 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -8,7 +8,7 @@ namespace topggcsharpchallenge.Controllers { [ApiController] [Route("earthquakes")] - public class EarthquakeController : ControllerBase + class EarthquakeController : ControllerBase { private IEarthquakeService earthquakeService; diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index ee2b31f..fcfc72c 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -4,11 +4,19 @@ namespace topggcsharpchallenge.Services { - public class EarthquakeService : IEarthquakeService + class EarthquakeService : IEarthquakeService { + private readonly IUsgsService usgsService; + + public EarthquakeService(IUsgsService usgsService) + { + this.usgsService = usgsService; + } + IEnumerable IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - throw new NotImplementedException(); + IEnumerable earthquakeData = usgsService.getEarthquakeData(); + return earthquakeData; } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index db314d2..d153753 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -4,7 +4,7 @@ namespace topggcsharpchallenge.Services { - public interface IEarthquakeService + interface IEarthquakeService { IEnumerable Get(int latitude, int longitude, DateTime startDate, DateTime endDate); } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs new file mode 100644 index 0000000..a1a448a --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using topggcsharpchallenge.Models; + +namespace topggcsharpchallenge.Services +{ + interface IUsgsService + { + IEnumerable getEarthquakeData(); + } +} diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs new file mode 100644 index 0000000..c0d6595 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using topggcsharpchallenge.Models; + +namespace topggcsharpchallenge.Services +{ + class UsgsService : IUsgsService + { + public IEnumerable getEarthquakeData() + { + throw new System.NotImplementedException(); + } + } +} From e892607d229dbabb393897d69a33f704329b15a7 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 19:23:35 +0200 Subject: [PATCH 10/19] [TDD] add test that filters out earthquake data based on time; general code improvements; --- .../Controllers/EarthquakeControllerTest.cs | 9 +-- .../Services/EarthquakeServiceTest.cs | 56 ++++++++++++++----- .../Services/EarthquakeService.cs | 7 ++- .../Services/IEarthquakeService.cs | 2 +- .../Services/IUsgsService.cs | 2 +- .../Services/UsgsService.cs | 2 +- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index 75aa64c..6ae0376 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -11,8 +11,9 @@ namespace topggcsharpchallengetest.Controllers [TestFixture] public class EarthquakeControllerTest { - readonly Mock earthquakeServiceMock = new Mock(); - EarthquakeController sut; + private readonly Mock earthquakeServiceMock = new Mock(); + + private EarthquakeController sut; [SetUp] public void SetUp() @@ -27,7 +28,7 @@ public void GetShouldBeSuccessfull() int longitude = 20; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.Now; - IEnumerable mockedData = getEarthquakeResponseModelMockData(); + IList mockedData = getEarthquakeResponseModelMockData(); earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(mockedData); IEnumerable actualData = sut.Get(latitude, longitude, startDate, endDate); @@ -36,7 +37,7 @@ public void GetShouldBeSuccessfull() earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once); } - private IEnumerable getEarthquakeResponseModelMockData() + private IList getEarthquakeResponseModelMockData() { return new List() { diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index 1517271..cc023d4 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -10,9 +10,9 @@ namespace topggcsharpchallengetest.Services [TestFixture] public class EarthquakeServiceTest { - readonly Mock usgsServiceMock = new Mock(); + private readonly Mock usgsServiceMock = new Mock(); - IEarthquakeService sut; + private IEarthquakeService sut; [SetUp] public void SetUp() @@ -26,8 +26,8 @@ public void GetShouldBeSuccessfull() int latitude = 10; int longitude = 20; DateTime startDate = DateTime.MinValue; - DateTime endDate = DateTime.Now; - IEnumerable expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + DateTime endDate = DateTime.MaxValue; + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -35,15 +35,45 @@ public void GetShouldBeSuccessfull() Assert.That(actualEarthquakeData, Is.EqualTo(expectedEarthquakeData)); } - private IEnumerable getUsgsServiceGetEarthquakeDataMocks() + [Test] + public void GetShouldReturnEmptyWhenIntervalBeforeAnyQuakes() + { + int latitude = 10; + int longitude = 20; + DateTime startDate = new DateTime(1111); + DateTime endDate = new DateTime(1112); + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData, Is.Empty); + } + + [Test] + public void GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() + { + int latitude = 10; + int longitude = 20; + DateTime startDate = new DateTime(2222); + DateTime endDate = new DateTime(2223); + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData, Is.Empty); + } + + private IList getUsgsServiceGetEarthquakeDataMocks() { return new List() { new EarthquakeResponseModel() { - Time = DateTime.MinValue, - Latitude = 0.1234, - Longitude = 4.3210, + Time = new DateTime(1993, 9, 29), + Latitude = 0, + Longitude = 0, Depth = 123.456, Mag = 654.312, MagType = "md", @@ -53,7 +83,7 @@ private IEnumerable getUsgsServiceGetEarthquakeDataMock Rms = 3.45, Net = "nc", Id = "nc73636400", - Updated = DateTime.Now, + Updated = new DateTime(1993, 9, 29), Place = "sofia", Type = "earthquake", HorizontalError = 0.111, @@ -66,9 +96,9 @@ private IEnumerable getUsgsServiceGetEarthquakeDataMock }, new EarthquakeResponseModel() { - Time = DateTime.Now, - Latitude = 0.000001, - Longitude = 0.000002, + Time = new DateTime(1996, 1, 11), + Latitude = 180, + Longitude = 180, Depth = 0.000003, Mag = 0.000004, MagType = "dm", @@ -78,7 +108,7 @@ private IEnumerable getUsgsServiceGetEarthquakeDataMock Rms = 0.000006, Net = "cn", Id = "cn73636400", - Updated = DateTime.MaxValue, + Updated = new DateTime(1996, 1, 11), Place = "nyc", Type = "earthquake", HorizontalError = 0.000007, diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index fcfc72c..68e7133 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services @@ -13,10 +14,10 @@ public EarthquakeService(IUsgsService usgsService) this.usgsService = usgsService; } - IEnumerable IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) + IList IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - IEnumerable earthquakeData = usgsService.getEarthquakeData(); - return earthquakeData; + IList earthquakeData = usgsService.getEarthquakeData(); + return earthquakeData.Where(x => startDate <= x.Time && x.Time <= endDate).ToList(); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index d153753..e1b9cb7 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -6,6 +6,6 @@ namespace topggcsharpchallenge.Services { interface IEarthquakeService { - IEnumerable Get(int latitude, int longitude, DateTime startDate, DateTime endDate); + IList Get(int latitude, int longitude, DateTime startDate, DateTime endDate); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs index a1a448a..a01f4a9 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -5,6 +5,6 @@ namespace topggcsharpchallenge.Services { interface IUsgsService { - IEnumerable getEarthquakeData(); + IList getEarthquakeData(); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs index c0d6595..6b6d9b8 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -5,7 +5,7 @@ namespace topggcsharpchallenge.Services { class UsgsService : IUsgsService { - public IEnumerable getEarthquakeData() + public IList getEarthquakeData() { throw new System.NotImplementedException(); } From f3fac002df9257ac51afd9f1a90c1c57f47357c8 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 19:26:14 +0200 Subject: [PATCH 11/19] add constants file for the earth radius --- topggcsharpchallenge/topggcsharpchallenge/Constants.cs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 topggcsharpchallenge/topggcsharpchallenge/Constants.cs diff --git a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs new file mode 100644 index 0000000..a26d967 --- /dev/null +++ b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs @@ -0,0 +1,7 @@ +namespace topggcsharpchallenge +{ + public static class Constants + { + public const int EARTH_RADIUS_MILES = 3959; + } +} From 9c66dbe088ab17d90d7c57a4e09ec5194b50d725 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 19:41:57 +0200 Subject: [PATCH 12/19] enhance EarthquakeServiceTests; add constant for travel distance factor; --- .../Services/EarthquakeServiceTest.cs | 26 +++++++++++++++---- .../topggcsharpchallenge/Constants.cs | 1 + 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index cc023d4..fd08486 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -40,8 +40,8 @@ public void GetShouldReturnEmptyWhenIntervalBeforeAnyQuakes() { int latitude = 10; int longitude = 20; - DateTime startDate = new DateTime(1111); - DateTime endDate = new DateTime(1112); + DateTime startDate = new DateTime(1111, 1, 1); + DateTime endDate = new DateTime(1112, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); @@ -55,16 +55,32 @@ public void GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() { int latitude = 10; int longitude = 20; - DateTime startDate = new DateTime(2222); - DateTime endDate = new DateTime(2223); + DateTime startDate = new DateTime(2222, 1, 1); + DateTime endDate = new DateTime(2223, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); - IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.Empty); } + [Test] + public void GetShouldReturnOnlyQuakesWithValidTime() + { + int latitude = 10; + int longitude = 20; + DateTime startDate = new DateTime(1995, 1, 1); + DateTime endDate = new DateTime(2020, 1, 1); + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData.Count, Is.EqualTo(1)); + Assert.That(actualEarthquakeData[0], Is.EqualTo(expectedEarthquakeData[1])); + } + private IList getUsgsServiceGetEarthquakeDataMocks() { return new List() diff --git a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs index a26d967..ecc7359 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs @@ -3,5 +3,6 @@ public static class Constants { public const int EARTH_RADIUS_MILES = 3959; + public const int TRAVEL_DISTANCE_FACTOR = 1000; } } From 4ee8c3044eb0c6b7c8740fae2d8698d458cc65f4 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 19:59:37 +0200 Subject: [PATCH 13/19] [TDD] add test to check distance; implement distance check; organize imports and general improvements; --- .../Controllers/EarthquakeControllerTest.cs | 8 +++-- .../Services/EarthquakeServiceTest.cs | 35 ++++++++++++++----- .../topggcsharpchallenge/Constants.cs | 2 +- .../Controllers/EarthquakeController.cs | 6 ++-- .../Services/EarthquakeService.cs | 19 +++++++++- .../Services/IEarthquakeService.cs | 1 + .../Services/IUsgsService.cs | 1 + .../Services/UsgsService.cs | 1 + 8 files changed, 57 insertions(+), 16 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index 6ae0376..f92a08a 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -1,7 +1,9 @@ -using Moq; -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; + +using Moq; +using NUnit.Framework; + using topggcsharpchallenge.Controllers; using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index fd08486..f21e308 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -1,7 +1,9 @@ -using Moq; -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; + +using Moq; +using NUnit.Framework; + using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; @@ -23,8 +25,8 @@ public void SetUp() [Test] public void GetShouldBeSuccessfull() { - int latitude = 10; - int longitude = 20; + int latitude = 0; + int longitude = 0; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); @@ -68,8 +70,8 @@ public void GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() [Test] public void GetShouldReturnOnlyQuakesWithValidTime() { - int latitude = 10; - int longitude = 20; + int latitude = 0; + int longitude = 0; DateTime startDate = new DateTime(1995, 1, 1); DateTime endDate = new DateTime(2020, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); @@ -81,6 +83,21 @@ public void GetShouldReturnOnlyQuakesWithValidTime() Assert.That(actualEarthquakeData[0], Is.EqualTo(expectedEarthquakeData[1])); } + [Test] + public void GetShouldReturnNoQuakesWhenThereAreNoneInRange() + { + int latitude = 90; + int longitude = 90; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.MaxValue; + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData, Is.Empty); + } + private IList getUsgsServiceGetEarthquakeDataMocks() { return new List() @@ -91,7 +108,7 @@ private IList getUsgsServiceGetEarthquakeDataMocks() Latitude = 0, Longitude = 0, Depth = 123.456, - Mag = 654.312, + Mag = 1, MagType = "md", Nst = 1, Gap = 2, @@ -116,7 +133,7 @@ private IList getUsgsServiceGetEarthquakeDataMocks() Latitude = 180, Longitude = 180, Depth = 0.000003, - Mag = 0.000004, + Mag = 2, MagType = "dm", Nst = 200, Gap = 300, diff --git a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs index ecc7359..8526660 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs @@ -3,6 +3,6 @@ public static class Constants { public const int EARTH_RADIUS_MILES = 3959; - public const int TRAVEL_DISTANCE_FACTOR = 1000; + public const int TRAVEL_DISTANCE_FACTOR = 100; } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index 05c42d6..7fbaee9 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -1,6 +1,8 @@ -using Microsoft.AspNetCore.Mvc; -using System; +using System; using System.Collections.Generic; + +using Microsoft.AspNetCore.Mvc; + using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index 68e7133..bafd56c 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; + using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services @@ -17,7 +18,23 @@ public EarthquakeService(IUsgsService usgsService) IList IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { IList earthquakeData = usgsService.getEarthquakeData(); - return earthquakeData.Where(x => startDate <= x.Time && x.Time <= endDate).ToList(); + return earthquakeData + .Where(x => startDate <= x.Time && x.Time <= endDate) + .Where(x => CalculateDistance(x.Latitude, x.Longitude, latitude, longitude) < x.Mag * Constants.TRAVEL_DISTANCE_FACTOR) + .ToList(); + } + + private double CalculateDistance(double lat1, double long1, double lat2, double long2, int sphereRadius = Constants.EARTH_RADIUS_MILES) + { + int degreesInACircle = 180; + double lat1rad = lat1 * Math.PI / degreesInACircle; + double lat2rad = lat2 * Math.PI / degreesInACircle; + double deltaLat = (lat1 - lat2) * Math.PI / degreesInACircle; + double deltaLong = (long1 - long2) * Math.PI / degreesInACircle; + + double a = Math.Pow(Math.Sin(deltaLat / 2), 2) + Math.Pow(Math.Sin(deltaLong / 2), 2) * Math.Cos(lat1rad) * Math.Cos(lat2rad); + double c = 2 * Math.Asin(Math.Sqrt(a)); + return sphereRadius * c; } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index e1b9cb7..f5268e8 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs index a01f4a9..613efb9 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; + using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs index 6b6d9b8..5e171ca 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; + using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services From 61c8274b87c7a0745c3ecc5d79e15cd3a3df2972 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 20:18:22 +0200 Subject: [PATCH 14/19] [TDD] add ordering and number limit to returned quakes; --- .../Services/EarthquakeServiceTest.cs | 72 +++++++++++++++++-- .../topggcsharpchallenge/Constants.cs | 1 + .../Services/EarthquakeService.cs | 6 +- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index f21e308..22655c7 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; - +using System.Linq; using Moq; using NUnit.Framework; - +using topggcsharpchallenge; using topggcsharpchallenge.Models; using topggcsharpchallenge.Services; @@ -29,12 +29,18 @@ public void GetShouldBeSuccessfull() int longitude = 0; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; - IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + IList mockedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(mockedEarthquakeData); + IList expectedEarthquakeData = mockedEarthquakeData.OrderByDescending(x => x.Time).ToList(); - IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.EqualTo(expectedEarthquakeData)); + IList dates = actualEarthquakeData.Select(x => x.Time).ToList(); + for (int i = 0; i < dates.Count - 1; i++) + { + Assert.That(dates[i] >= dates[i + 1]); + } } [Test] @@ -98,6 +104,37 @@ public void GetShouldReturnNoQuakesWhenThereAreNoneInRange() Assert.That(actualEarthquakeData, Is.Empty); } + [Test] + public void GetShouldReturnSomeQuakesWhenTheyAreInRange() + { + int latitude = 10; + int longitude = 10; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.MaxValue; + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData.Count, Is.EqualTo(1)); + Assert.That(actualEarthquakeData[0], Is.EqualTo(expectedEarthquakeData[1])); + } + + [Test] + public void GetShouldReturnNoMoreThanTheLimitOfResults() + { + int latitude = 0; + int longitude = 0; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.MaxValue; + IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocksMany(); + usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + + IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + + Assert.That(actualEarthquakeData.Count, Is.EqualTo(Constants.EARTHQUAKE_COUNT_LIMIT)); + } + private IList getUsgsServiceGetEarthquakeDataMocks() { return new List() @@ -108,7 +145,7 @@ private IList getUsgsServiceGetEarthquakeDataMocks() Latitude = 0, Longitude = 0, Depth = 123.456, - Mag = 1, + Mag = 0.00000001, MagType = "md", Nst = 1, Gap = 2, @@ -133,7 +170,7 @@ private IList getUsgsServiceGetEarthquakeDataMocks() Latitude = 180, Longitude = 180, Depth = 0.000003, - Mag = 2, + Mag = 20, MagType = "dm", Nst = 200, Gap = 300, @@ -154,5 +191,26 @@ private IList getUsgsServiceGetEarthquakeDataMocks() }, }; } + + private IList getUsgsServiceGetEarthquakeDataMocksMany() + { + return new List() + { + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + new EarthquakeResponseModel(), + }; + } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs index 8526660..77a1dea 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs @@ -4,5 +4,6 @@ public static class Constants { public const int EARTH_RADIUS_MILES = 3959; public const int TRAVEL_DISTANCE_FACTOR = 100; + public const int EARTHQUAKE_COUNT_LIMIT = 10; } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index bafd56c..6f5b3ff 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -20,11 +20,13 @@ IList IEarthquakeService.Get(int latitude, int longitud IList earthquakeData = usgsService.getEarthquakeData(); return earthquakeData .Where(x => startDate <= x.Time && x.Time <= endDate) - .Where(x => CalculateDistance(x.Latitude, x.Longitude, latitude, longitude) < x.Mag * Constants.TRAVEL_DISTANCE_FACTOR) + .Where(x => CalculateDistanceInSphere(x.Latitude, x.Longitude, latitude, longitude) <= x.Mag * Constants.TRAVEL_DISTANCE_FACTOR) + .Take(Constants.EARTHQUAKE_COUNT_LIMIT) + .OrderByDescending(x => x.Time) .ToList(); } - private double CalculateDistance(double lat1, double long1, double lat2, double long2, int sphereRadius = Constants.EARTH_RADIUS_MILES) + private double CalculateDistanceInSphere(double lat1, double long1, double lat2, double long2, int sphereRadius = Constants.EARTH_RADIUS_MILES) { int degreesInACircle = 180; double lat1rad = lat1 * Math.PI / degreesInACircle; From cadc4bb2b81f35fcecec20939a2faee508db5de5 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 22:01:51 +0200 Subject: [PATCH 15/19] simplify usgsService; parse csv implemented; --- .../Services/EarthquakeServiceTest.cs | 33 +++++-- .../topggcsharpchallenge/Constants.cs | 7 ++ .../Models/EarthquakeResponseModel.cs | 93 +++++++++++++++++++ .../Services/EarthquakeService.cs | 51 +++++++++- .../Services/IUsgsService.cs | 8 +- .../Services/UsgsService.cs | 13 ++- 6 files changed, 183 insertions(+), 22 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index 22655c7..b6c08fc 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Moq; using NUnit.Framework; using topggcsharpchallenge; @@ -30,7 +31,8 @@ public void GetShouldBeSuccessfull() DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList mockedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(mockedEarthquakeData); + string csv = createCsv(mockedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(csv); IList expectedEarthquakeData = mockedEarthquakeData.OrderByDescending(x => x.Time).ToList(); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -51,7 +53,7 @@ public void GetShouldReturnEmptyWhenIntervalBeforeAnyQuakes() DateTime startDate = new DateTime(1111, 1, 1); DateTime endDate = new DateTime(1112, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -66,7 +68,7 @@ public void GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() DateTime startDate = new DateTime(2222, 1, 1); DateTime endDate = new DateTime(2223, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -81,7 +83,7 @@ public void GetShouldReturnOnlyQuakesWithValidTime() DateTime startDate = new DateTime(1995, 1, 1); DateTime endDate = new DateTime(2020, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -97,7 +99,7 @@ public void GetShouldReturnNoQuakesWhenThereAreNoneInRange() DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -112,7 +114,7 @@ public void GetShouldReturnSomeQuakesWhenTheyAreInRange() DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -128,7 +130,7 @@ public void GetShouldReturnNoMoreThanTheLimitOfResults() DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocksMany(); - usgsServiceMock.Setup((x) => x.getEarthquakeData()).Returns(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); @@ -185,7 +187,7 @@ private IList getUsgsServiceGetEarthquakeDataMocks() DepthError = 0.000008, MagError = 0.000009, MagNst = 400, - Status = "reviwed", + Status = "reviewed", LocationSource = "ls", MagSource = "ms" }, @@ -209,8 +211,21 @@ private IList getUsgsServiceGetEarthquakeDataMocksMany( new EarthquakeResponseModel(), new EarthquakeResponseModel(), new EarthquakeResponseModel(), - new EarthquakeResponseModel(), }; } + + private string createCsv(IList quakes) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource"); + foreach (EarthquakeResponseModel quake in quakes) + { + stringBuilder.Append("\n"); + stringBuilder.Append(quake.ToString()); + } + + + return stringBuilder.ToString(); + } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs index 77a1dea..c318360 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Constants.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Constants.cs @@ -4,6 +4,13 @@ public static class Constants { public const int EARTH_RADIUS_MILES = 3959; public const int TRAVEL_DISTANCE_FACTOR = 100; + + // Count could/should be configurable public const int EARTHQUAKE_COUNT_LIMIT = 10; + + // Url could/should be configurable + public const string USGS_LATEST_REPORT_URL = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv"; + + public const string DATE_FORMAT = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"; } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs index f802e1e..9ba8cb1 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace topggcsharpchallenge.Models { @@ -26,5 +27,97 @@ public class EarthquakeResponseModel public string Status { get; set; } public string LocationSource { get; set; } public string MagSource { get; set; } + + public override bool Equals(object obj) + { + if (!(obj is EarthquakeResponseModel)) + { + return false; + } + + EarthquakeResponseModel model = (EarthquakeResponseModel)obj; + + return Time == model.Time && + Latitude == model.Latitude && + Longitude == model.Longitude && + Depth == model.Depth && + Mag == model.Mag && + MagType == model.MagType && + Nst == model.Nst && + Gap == model.Gap && + Dmin == model.Dmin && + Rms == model.Rms && + Net == model.Net && + Id == model.Id && + Updated == model.Updated && + Place == model.Place && + Type == model.Type && + HorizontalError == model.HorizontalError && + DepthError == model.DepthError && + MagError == model.MagError && + MagNst == model.MagNst && + Status == model.Status && + LocationSource == model.LocationSource && + MagSource == model.MagSource; + } + + public override int GetHashCode() + { + HashCode hash = new HashCode(); + hash.Add(Time); + hash.Add(Latitude); + hash.Add(Longitude); + hash.Add(Depth); + hash.Add(Mag); + hash.Add(MagType); + hash.Add(Nst); + hash.Add(Gap); + hash.Add(Dmin); + hash.Add(Rms); + hash.Add(Net); + hash.Add(Id); + hash.Add(Updated); + hash.Add(Place); + hash.Add(Type); + hash.Add(HorizontalError); + hash.Add(DepthError); + hash.Add(MagError); + hash.Add(MagNst); + hash.Add(Status); + hash.Add(LocationSource); + hash.Add(MagSource); + return hash.ToHashCode(); + } + + public override string ToString() + { + IList data = new List() + { + Time.ToUniversalTime().ToString(Constants.DATE_FORMAT), + Latitude.ToString(), + Longitude.ToString(), + Depth.ToString(), + Mag.ToString(), + MagType, + Nst.ToString(), + Gap.ToString(), + Dmin.ToString(), + Rms.ToString(), + Net, + Id, + Updated.ToUniversalTime().ToString(Constants.DATE_FORMAT), + Place, + Type, + HorizontalError.ToString(), + DepthError.ToString(), + MagError.ToString(), + MagNst.ToString(), + Status, + LocationSource, + MagSource + }; + + return string.Join(',', data); + } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index 6f5b3ff..084850a 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -17,8 +17,11 @@ public EarthquakeService(IUsgsService usgsService) IList IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) { - IList earthquakeData = usgsService.getEarthquakeData(); - return earthquakeData + string earthquakeCsv = usgsService.GetEarthquakeData(); + + IList earthquakes = ParseEarthquakes(earthquakeCsv); + + return earthquakes .Where(x => startDate <= x.Time && x.Time <= endDate) .Where(x => CalculateDistanceInSphere(x.Latitude, x.Longitude, latitude, longitude) <= x.Mag * Constants.TRAVEL_DISTANCE_FACTOR) .Take(Constants.EARTHQUAKE_COUNT_LIMIT) @@ -38,5 +41,49 @@ private double CalculateDistanceInSphere(double lat1, double long1, double lat2, double c = 2 * Math.Asin(Math.Sqrt(a)); return sphereRadius * c; } + + private IList ParseEarthquakes(string csv) + { + string[] allRows = csv.Split('\n'); + IList result = new List(); + IEnumerable dataRows = allRows.TakeLast(allRows.Length - 1); + foreach(string row in dataRows) + { + result.Add(ParseEarthquake(row)); + } + + return result; + } + + private EarthquakeResponseModel ParseEarthquake(string row) + { + string[] columnData = row.Split(','); ; + + return new EarthquakeResponseModel() + { + Time = DateTime.Parse(columnData[0]), + Latitude = double.Parse(columnData[1]), + Longitude = double.Parse(columnData[2]), + Depth = double.Parse(columnData[3]), + Mag = double.Parse(columnData[4]), + MagType = columnData[5], + Nst = int.Parse(columnData[6]), + Gap = int.Parse(columnData[7]), + Dmin = double.Parse(columnData[8]), + Rms = double.Parse(columnData[9]), + Net = columnData[10], + Id = columnData[11], + Updated = DateTime.Parse(columnData[12]), + Place = columnData[13], + Type = columnData[14], + HorizontalError = double.Parse(columnData[15]), + DepthError = double.Parse(columnData[16]), + MagError = double.Parse(columnData[17]), + MagNst = int.Parse(columnData[18]), + Status = columnData[19], + LocationSource = columnData[20], + MagSource = columnData[21], + }; + } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs index 613efb9..4b781a8 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -1,11 +1,7 @@ -using System.Collections.Generic; - -using topggcsharpchallenge.Models; - -namespace topggcsharpchallenge.Services +namespace topggcsharpchallenge.Services { interface IUsgsService { - IList getEarthquakeData(); + string GetEarthquakeData(); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs index 5e171ca..7ca5aa9 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -1,14 +1,17 @@ -using System.Collections.Generic; - -using topggcsharpchallenge.Models; +using System.Net; namespace topggcsharpchallenge.Services { class UsgsService : IUsgsService { - public IList getEarthquakeData() + public string GetEarthquakeData() { - throw new System.NotImplementedException(); + string latestReportUrl = Constants.USGS_LATEST_REPORT_URL; + + using (var client = new WebClient()) + { + return client.DownloadString(latestReportUrl); + } } } } From 513409f6724702e00ac25effda7a45b0b47684ab Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 22:42:16 +0200 Subject: [PATCH 16/19] improve csv parsing; general improvements; --- .../Services/EarthquakeServiceTest.cs | 6 +- .../Controllers/EarthquakeController.cs | 4 +- .../Models/EarthquakeResponseModel.cs | 2 +- .../Services/EarthquakeService.cs | 93 ++++++++++++------- .../Services/IEarthquakeService.cs | 4 +- .../Services/IUsgsService.cs | 4 +- .../Services/UsgsService.cs | 6 +- .../topggcsharpchallenge/Startup.cs | 4 + 8 files changed, 77 insertions(+), 46 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index b6c08fc..a92764a 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -31,7 +31,7 @@ public void GetShouldBeSuccessfull() DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList mockedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - string csv = createCsv(mockedEarthquakeData); + byte[] csv = createCsv(mockedEarthquakeData); usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(csv); IList expectedEarthquakeData = mockedEarthquakeData.OrderByDescending(x => x.Time).ToList(); @@ -214,7 +214,7 @@ private IList getUsgsServiceGetEarthquakeDataMocksMany( }; } - private string createCsv(IList quakes) + private byte[] createCsv(IList quakes) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource"); @@ -225,7 +225,7 @@ private string createCsv(IList quakes) } - return stringBuilder.ToString(); + return Encoding.ASCII.GetBytes(stringBuilder.ToString()); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index 7fbaee9..4bf9e89 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -10,7 +10,7 @@ namespace topggcsharpchallenge.Controllers { [ApiController] [Route("earthquakes")] - class EarthquakeController : ControllerBase + public class EarthquakeController : ControllerBase { private IEarthquakeService earthquakeService; @@ -20,7 +20,7 @@ public EarthquakeController(IEarthquakeService earthquakeService) } [HttpGet] - public IEnumerable Get(int latitude, int longitude, DateTime startDate, DateTime endDate) + public IEnumerable Get(double latitude, double longitude, DateTime startDate, DateTime endDate) { return earthquakeService.Get(latitude, longitude, startDate, endDate); } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs index 9ba8cb1..c5824ce 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Models/EarthquakeResponseModel.cs @@ -12,7 +12,7 @@ public class EarthquakeResponseModel public double Mag { get; set; } public string MagType { get; set; } public int Nst { get; set; } - public int Gap { get; set; } + public double Gap { get; set; } public double Dmin { get; set; } public double Rms { get; set; } public string Net { get; set; } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index 084850a..c391c94 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using Microsoft.VisualBasic.FileIO; + using topggcsharpchallenge.Models; namespace topggcsharpchallenge.Services { - class EarthquakeService : IEarthquakeService + public class EarthquakeService : IEarthquakeService { private readonly IUsgsService usgsService; @@ -15,9 +18,9 @@ public EarthquakeService(IUsgsService usgsService) this.usgsService = usgsService; } - IList IEarthquakeService.Get(int latitude, int longitude, DateTime startDate, DateTime endDate) + IList IEarthquakeService.Get(double latitude, double longitude, DateTime startDate, DateTime endDate) { - string earthquakeCsv = usgsService.GetEarthquakeData(); + byte[] earthquakeCsv = usgsService.GetEarthquakeData(); IList earthquakes = ParseEarthquakes(earthquakeCsv); @@ -42,48 +45,72 @@ private double CalculateDistanceInSphere(double lat1, double long1, double lat2, return sphereRadius * c; } - private IList ParseEarthquakes(string csv) + private IList ParseEarthquakes(byte[] csv) { - string[] allRows = csv.Split('\n'); IList result = new List(); - IEnumerable dataRows = allRows.TakeLast(allRows.Length - 1); - foreach(string row in dataRows) + using (TextFieldParser parser = new TextFieldParser(new MemoryStream(csv))) { - result.Add(ParseEarthquake(row)); + parser.TextFieldType = FieldType.Delimited; + parser.SetDelimiters(","); + bool headerSkipped = false; + while (!parser.EndOfData) + { + string[] fields = parser.ReadFields(); + if (!headerSkipped) + { + headerSkipped = true; + continue; + } + + result.Add(ParseEarthquake(fields)); + } } return result; } - private EarthquakeResponseModel ParseEarthquake(string row) + private EarthquakeResponseModel ParseEarthquake(string[] fields) { - string[] columnData = row.Split(','); ; - return new EarthquakeResponseModel() { - Time = DateTime.Parse(columnData[0]), - Latitude = double.Parse(columnData[1]), - Longitude = double.Parse(columnData[2]), - Depth = double.Parse(columnData[3]), - Mag = double.Parse(columnData[4]), - MagType = columnData[5], - Nst = int.Parse(columnData[6]), - Gap = int.Parse(columnData[7]), - Dmin = double.Parse(columnData[8]), - Rms = double.Parse(columnData[9]), - Net = columnData[10], - Id = columnData[11], - Updated = DateTime.Parse(columnData[12]), - Place = columnData[13], - Type = columnData[14], - HorizontalError = double.Parse(columnData[15]), - DepthError = double.Parse(columnData[16]), - MagError = double.Parse(columnData[17]), - MagNst = int.Parse(columnData[18]), - Status = columnData[19], - LocationSource = columnData[20], - MagSource = columnData[21], + Time = parseToDateTimeOrDefault(fields[0]), + Latitude = parseToDoubleOrDefault(fields[1]), + Longitude = parseToDoubleOrDefault(fields[2]), + Depth = parseToDoubleOrDefault(fields[3]), + Mag = parseToDoubleOrDefault(fields[4]), + MagType = fields[5], + Nst = parseToIntegerOrDefault(fields[6]), + Gap = parseToDoubleOrDefault(fields[7]), + Dmin = parseToDoubleOrDefault(fields[8]), + Rms = parseToDoubleOrDefault(fields[9]), + Net = fields[10], + Id = fields[11], + Updated = parseToDateTimeOrDefault(fields[12]), + Place = fields[13], + Type = fields[14], + HorizontalError = parseToDoubleOrDefault(fields[15]), + DepthError = parseToDoubleOrDefault(fields[16]), + MagError = parseToDoubleOrDefault(fields[17]), + MagNst = parseToIntegerOrDefault(fields[18]), + Status = fields[19], + LocationSource = fields[20], + MagSource = fields[21] }; } + + private double parseToDoubleOrDefault(string i) + { + return i != string.Empty ? double.Parse(i) : 0; + } + + private int parseToIntegerOrDefault(string i) + { + return i != string.Empty ? int.Parse(i) : 0; + } + + private DateTime parseToDateTimeOrDefault(string date) + { + return date != string.Empty ? DateTime.Parse(date) : new DateTime(); + } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index f5268e8..52f0c3b 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -5,8 +5,8 @@ namespace topggcsharpchallenge.Services { - interface IEarthquakeService + public interface IEarthquakeService { - IList Get(int latitude, int longitude, DateTime startDate, DateTime endDate); + IList Get(double latitude, double longitude, DateTime startDate, DateTime endDate); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs index 4b781a8..4d80ae3 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -1,7 +1,7 @@ namespace topggcsharpchallenge.Services { - interface IUsgsService + public interface IUsgsService { - string GetEarthquakeData(); + byte[] GetEarthquakeData(); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs index 7ca5aa9..38cfed6 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -2,15 +2,15 @@ namespace topggcsharpchallenge.Services { - class UsgsService : IUsgsService + public class UsgsService : IUsgsService { - public string GetEarthquakeData() + public byte[] GetEarthquakeData() { string latestReportUrl = Constants.USGS_LATEST_REPORT_URL; using (var client = new WebClient()) { - return client.DownloadString(latestReportUrl); + return client.DownloadData(latestReportUrl); } } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Startup.cs b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs index 5bb7d14..98f9680 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Startup.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Startup.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; +using topggcsharpchallenge.Services; namespace topggcsharpchallenge { @@ -23,6 +24,9 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); + services.AddScoped(); + services.AddScoped(); + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo From bb15037a6c24993c63ca5f5bb50fc7b989ed74c1 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 22:56:09 +0200 Subject: [PATCH 17/19] add query parameters; add 404 response when no quakes found; --- .../Controllers/EarthquakeController.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index 4bf9e89..627d869 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; - +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using topggcsharpchallenge.Models; @@ -20,9 +20,21 @@ public EarthquakeController(IEarthquakeService earthquakeService) } [HttpGet] - public IEnumerable Get(double latitude, double longitude, DateTime startDate, DateTime endDate) + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult> Get( + [FromQuery(Name = "lat")] double latitude, + [FromQuery(Name = "long")] double longitude, + [FromQuery(Name = "start_date")] DateTime startDate, + [FromQuery(Name = "end_date")] DateTime endDate) { - return earthquakeService.Get(latitude, longitude, startDate, endDate); + IList quakes = earthquakeService.Get(latitude, longitude, startDate, endDate); + if (quakes.Count == 0) + { + return NotFound(); + } + + return new OkObjectResult(quakes); } } } From d548ceaa67283636fc1c39234df0613192b3edd8 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 23:34:05 +0200 Subject: [PATCH 18/19] convert API to asynchronous execution; add test for 404; --- .../Controllers/EarthquakeControllerTest.cs | 29 +++++++++-- .../Services/EarthquakeServiceTest.cs | 49 +++++++++++-------- .../Controllers/EarthquakeController.cs | 5 +- .../Services/EarthquakeService.cs | 5 +- .../Services/IEarthquakeService.cs | 3 +- .../Services/IUsgsService.cs | 6 ++- .../Services/UsgsService.cs | 5 +- 7 files changed, 68 insertions(+), 34 deletions(-) diff --git a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs index f92a08a..2e998d7 100644 --- a/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Controllers/EarthquakeControllerTest.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; using Moq; using NUnit.Framework; @@ -24,18 +26,37 @@ public void SetUp() } [Test] - public void GetShouldBeSuccessfull() + public async Task GetShouldBeSuccessfull() { int latitude = 10; int longitude = 20; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.Now; IList mockedData = getEarthquakeResponseModelMockData(); - earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(mockedData); + earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(Task.FromResult(mockedData)); - IEnumerable actualData = sut.Get(latitude, longitude, startDate, endDate); + ActionResult> response = await sut.Get(latitude, longitude, startDate, endDate); - Assert.That(actualData, Is.EqualTo(mockedData)); + Assert.IsInstanceOf(response.Result); + OkObjectResult result = (OkObjectResult) response.Result; + Assert.That(result.Value, Is.EqualTo(mockedData)); + earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once); + } + + + [Test] + public async Task GetShouldReturn404WhenNoQuakesFound() + { + int latitude = 10; + int longitude = 20; + DateTime startDate = DateTime.MinValue; + DateTime endDate = DateTime.Now; + IList mockedData = new List(); + earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(Task.FromResult(mockedData)); + + ActionResult> response = await sut.Get(latitude, longitude, startDate, endDate); + + Assert.IsInstanceOf(response.Result); earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once); } diff --git a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs index a92764a..9102f95 100644 --- a/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs +++ b/topggcsharpchallenge/topggchallengetest/Services/EarthquakeServiceTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; using Moq; using NUnit.Framework; using topggcsharpchallenge; @@ -24,7 +25,7 @@ public void SetUp() } [Test] - public void GetShouldBeSuccessfull() + public async Task GetShouldBeSuccessfull() { int latitude = 0; int longitude = 0; @@ -32,10 +33,10 @@ public void GetShouldBeSuccessfull() DateTime endDate = DateTime.MaxValue; IList mockedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); byte[] csv = createCsv(mockedEarthquakeData); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(csv); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); IList expectedEarthquakeData = mockedEarthquakeData.OrderByDescending(x => x.Time).ToList(); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.EqualTo(expectedEarthquakeData)); IList dates = actualEarthquakeData.Select(x => x.Time).ToList(); @@ -46,93 +47,99 @@ public void GetShouldBeSuccessfull() } [Test] - public void GetShouldReturnEmptyWhenIntervalBeforeAnyQuakes() + public async Task GetShouldReturnEmptyWhenIntervalBeforeAnyQuakes() { int latitude = 10; int longitude = 20; DateTime startDate = new DateTime(1111, 1, 1); DateTime endDate = new DateTime(1112, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IEnumerable actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IEnumerable actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.Empty); } [Test] - public void GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() + public async Task GetShouldReturnEmptyWhenIntervalAfterAnyQuakes() { int latitude = 10; int longitude = 20; DateTime startDate = new DateTime(2222, 1, 1); DateTime endDate = new DateTime(2223, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut .Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.Empty); } [Test] - public void GetShouldReturnOnlyQuakesWithValidTime() + public async Task GetShouldReturnOnlyQuakesWithValidTime() { int latitude = 0; int longitude = 0; DateTime startDate = new DateTime(1995, 1, 1); DateTime endDate = new DateTime(2020, 1, 1); IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData.Count, Is.EqualTo(1)); Assert.That(actualEarthquakeData[0], Is.EqualTo(expectedEarthquakeData[1])); } [Test] - public void GetShouldReturnNoQuakesWhenThereAreNoneInRange() + public async Task GetShouldReturnNoQuakesWhenThereAreNoneInRange() { int latitude = 90; int longitude = 90; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData, Is.Empty); } [Test] - public void GetShouldReturnSomeQuakesWhenTheyAreInRange() + public async Task GetShouldReturnSomeQuakesWhenTheyAreInRange() { int latitude = 10; int longitude = 10; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocks(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData.Count, Is.EqualTo(1)); Assert.That(actualEarthquakeData[0], Is.EqualTo(expectedEarthquakeData[1])); } [Test] - public void GetShouldReturnNoMoreThanTheLimitOfResults() + public async Task GetShouldReturnNoMoreThanTheLimitOfResults() { int latitude = 0; int longitude = 0; DateTime startDate = DateTime.MinValue; DateTime endDate = DateTime.MaxValue; IList expectedEarthquakeData = getUsgsServiceGetEarthquakeDataMocksMany(); - usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(createCsv(expectedEarthquakeData)); + byte[] csv = createCsv(expectedEarthquakeData); + usgsServiceMock.Setup((x) => x.GetEarthquakeData()).Returns(Task.FromResult(csv)); - IList actualEarthquakeData = sut.Get(latitude, longitude, startDate, endDate); + IList actualEarthquakeData = await sut.Get(latitude, longitude, startDate, endDate); Assert.That(actualEarthquakeData.Count, Is.EqualTo(Constants.EARTHQUAKE_COUNT_LIMIT)); } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs index 627d869..24b9dba 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Controllers/EarthquakeController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -22,13 +23,13 @@ public EarthquakeController(IEarthquakeService earthquakeService) [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> Get( + public async Task>> Get( [FromQuery(Name = "lat")] double latitude, [FromQuery(Name = "long")] double longitude, [FromQuery(Name = "start_date")] DateTime startDate, [FromQuery(Name = "end_date")] DateTime endDate) { - IList quakes = earthquakeService.Get(latitude, longitude, startDate, endDate); + IList quakes = await earthquakeService.Get(latitude, longitude, startDate, endDate); if (quakes.Count == 0) { return NotFound(); diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs index c391c94..e968908 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/EarthquakeService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using Microsoft.VisualBasic.FileIO; @@ -18,9 +19,9 @@ public EarthquakeService(IUsgsService usgsService) this.usgsService = usgsService; } - IList IEarthquakeService.Get(double latitude, double longitude, DateTime startDate, DateTime endDate) + async Task> IEarthquakeService.Get(double latitude, double longitude, DateTime startDate, DateTime endDate) { - byte[] earthquakeCsv = usgsService.GetEarthquakeData(); + byte[] earthquakeCsv = await usgsService.GetEarthquakeData(); IList earthquakes = ParseEarthquakes(earthquakeCsv); diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs index 52f0c3b..bf5e233 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IEarthquakeService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using topggcsharpchallenge.Models; @@ -7,6 +8,6 @@ namespace topggcsharpchallenge.Services { public interface IEarthquakeService { - IList Get(double latitude, double longitude, DateTime startDate, DateTime endDate); + Task> Get(double latitude, double longitude, DateTime startDate, DateTime endDate); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs index 4d80ae3..71b700f 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/IUsgsService.cs @@ -1,7 +1,9 @@ -namespace topggcsharpchallenge.Services +using System.Threading.Tasks; + +namespace topggcsharpchallenge.Services { public interface IUsgsService { - byte[] GetEarthquakeData(); + Task GetEarthquakeData(); } } diff --git a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs index 38cfed6..f6eadeb 100644 --- a/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs +++ b/topggcsharpchallenge/topggcsharpchallenge/Services/UsgsService.cs @@ -1,16 +1,17 @@ using System.Net; +using System.Threading.Tasks; namespace topggcsharpchallenge.Services { public class UsgsService : IUsgsService { - public byte[] GetEarthquakeData() + public async Task GetEarthquakeData() { string latestReportUrl = Constants.USGS_LATEST_REPORT_URL; using (var client = new WebClient()) { - return client.DownloadData(latestReportUrl); + return await client.DownloadDataTaskAsync(new System.Uri(latestReportUrl)); } } } From 249d90195ac35cbfe5731c20b312536918ab8718 Mon Sep 17 00:00:00 2001 From: Petar Parushev Date: Tue, 9 Nov 2021 23:43:11 +0200 Subject: [PATCH 19/19] add notes and thoughts to readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index df21dc8..a14bb6f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ +# Petar Parushev + +## Notes and thoughts +Took me a while longer that I had hoped it would but I am generally pleased with how this task turned out. My C# is super rusty and I also forgot all of VS's shortcuts. +I developed it mostly using TDD and I have added unit tests for more than the controller classes. I do not have proper endpoint tests - I would add a in-memory API that I call using actual HTTP calls to check routes, parameters, validation and etc. +I am missing validation in controllers. I should add attributres on the query parameters to make sure that they are not null and that the start date should be less than the end date + +## API performance and scale +I converted the API to asynchronos execution in one of the last commits which should make it easier on the threadpool. +The solution is stateless so it can scale indefinetely behind a load balancer. +I am making a call to USGS on every request which can be improved by setting up some sort of cache-ing. Surely there is an earthquake every second but most of them are insignificant and the use case of this system means that we don't need that fresh of data. + + + # Earthquake Challenge ## Was that an Earthquake?