Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1cc8d06
initial commit with standard VS project template
PeterParushev Nov 9, 2021
2df4dcf
add swagger
PeterParushev Nov 9, 2021
f09f47f
add test project
PeterParushev Nov 9, 2021
704cfb4
remove unneeded controller and model; add earthquake controller;
PeterParushev Nov 9, 2021
b35fb39
add initial poc test
PeterParushev Nov 9, 2021
34f7ffb
[TDD commit] add EarthquakeService interface and class; extend initia…
PeterParushev Nov 9, 2021
2687c9c
[TDD] add and pass parameters
PeterParushev Nov 9, 2021
fe71d32
[TDD] add EarthquakeResponseModel; service now returns EarthquakeResp…
PeterParushev Nov 9, 2021
aeec132
[TDD] add initial implementation of EarthquakeService; set internal m…
PeterParushev Nov 9, 2021
e892607
[TDD] add test that filters out earthquake data based on time; genera…
PeterParushev Nov 9, 2021
f3fac00
add constants file for the earth radius
PeterParushev Nov 9, 2021
9c66dbe
enhance EarthquakeServiceTests; add constant for travel distance factor;
PeterParushev Nov 9, 2021
4ee8c30
[TDD] add test to check distance; implement distance check; organize …
PeterParushev Nov 9, 2021
61c8274
[TDD] add ordering and number limit to returned quakes;
PeterParushev Nov 9, 2021
cadc4bb
simplify usgsService; parse csv implemented;
PeterParushev Nov 9, 2021
513409f
improve csv parsing; general improvements;
PeterParushev Nov 9, 2021
bb15037
add query parameters; add 404 response when no quakes found;
PeterParushev Nov 9, 2021
d548cea
convert API to asynchronous execution; add test for 404;
PeterParushev Nov 9, 2021
249d901
add notes and thoughts to readme
PeterParushev Nov 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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*
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;

using topggcsharpchallenge.Controllers;
using topggcsharpchallenge.Models;
using topggcsharpchallenge.Services;

namespace topggcsharpchallengetest.Controllers
{
[TestFixture]
public class EarthquakeControllerTest
{
private readonly Mock<IEarthquakeService> earthquakeServiceMock = new Mock<IEarthquakeService>();

private EarthquakeController sut;

[SetUp]
public void SetUp()
{
sut = new EarthquakeController(earthquakeServiceMock.Object);
}

[Test]
public async Task GetShouldBeSuccessfull()
{
int latitude = 10;
int longitude = 20;
DateTime startDate = DateTime.MinValue;
DateTime endDate = DateTime.Now;
IList<EarthquakeResponseModel> mockedData = getEarthquakeResponseModelMockData();
earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(Task.FromResult(mockedData));

ActionResult<IEnumerable<EarthquakeResponseModel>> response = await sut.Get(latitude, longitude, startDate, endDate);

Assert.IsInstanceOf<OkObjectResult>(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<EarthquakeResponseModel> mockedData = new List<EarthquakeResponseModel>();
earthquakeServiceMock.Setup((x) => x.Get(latitude, longitude, startDate, endDate)).Returns(Task.FromResult(mockedData));

ActionResult<IEnumerable<EarthquakeResponseModel>> response = await sut.Get(latitude, longitude, startDate, endDate);

Assert.IsInstanceOf<NotFoundResult>(response.Result);
earthquakeServiceMock.Verify((x) => x.Get(latitude, longitude, startDate, endDate), Times.Once);
}

private IList<EarthquakeResponseModel> getEarthquakeResponseModelMockData()
{
return new List<EarthquakeResponseModel>()
{
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"
},
};
}

}
}
Loading