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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dotnet/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
Debug/
Release/
obj/
appsettings.Development.json
*.csproj.user
46 changes: 40 additions & 6 deletions dotnet/KeepTrack.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,26 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Car", "Car", "{A52EF787-A7D
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Movie", "Movie", "{A6A8275A-6360-43E7-AB5A-D8A1E96BED06}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "src\ConsoleApp\ConsoleApp.csproj", "{09DBB876-546F-40EF-B1D8-D0FDAE02E80D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "src\ConsoleApp\ConsoleApp.csproj", "{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CarComponent.Domain", "src\CarComponent.Domain\CarComponent.Domain.csproj", "{A2193B19-8191-4A28-9E64-23D9083A0531}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CarComponent.Infrastructure.MongoDb", "src\CarComponent.Infrastructure.MongoDb\CarComponent.Infrastructure.MongoDb.csproj", "{F7250DAA-718B-44FB-990B-AD7DFC488AA7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{73A074B8-D41E-4CCD-AC4B-95DB716EC6C6}"
ProjectSection(SolutionItems) = preProject
CodeCoverage.runsettings = CodeCoverage.runsettings
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "src\Api\Api.csproj", "{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CarComponent.Domain.UnitTests", "test\CarComponent.Domain.UnitTests\CarComponent.Domain.UnitTests.csproj", "{2079D00A-8489-4A43-BC2E-0B7CFB772CE7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovieComponent.Domain", "src\MovieComponent.Domain\MovieComponent.Domain.csproj", "{A08241ED-8F29-4DF7-9BCF-2E310A9C8187}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovieComponent.Infrastructure.MongoDb", "src\MovieComponent.Infrastructure.MongoDb\MovieComponent.Infrastructure.MongoDb.csproj", "{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -31,10 +45,10 @@ Global
{6A10979B-FBAE-488A-B5BC-0E45716DFE88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A10979B-FBAE-488A-B5BC-0E45716DFE88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A10979B-FBAE-488A-B5BC-0E45716DFE88}.Release|Any CPU.Build.0 = Release|Any CPU
{09DBB876-546F-40EF-B1D8-D0FDAE02E80D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09DBB876-546F-40EF-B1D8-D0FDAE02E80D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09DBB876-546F-40EF-B1D8-D0FDAE02E80D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09DBB876-546F-40EF-B1D8-D0FDAE02E80D}.Release|Any CPU.Build.0 = Release|Any CPU
{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57}.Release|Any CPU.Build.0 = Release|Any CPU
{A2193B19-8191-4A28-9E64-23D9083A0531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2193B19-8191-4A28-9E64-23D9083A0531}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2193B19-8191-4A28-9E64-23D9083A0531}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -43,6 +57,22 @@ Global
{F7250DAA-718B-44FB-990B-AD7DFC488AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7250DAA-718B-44FB-990B-AD7DFC488AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7250DAA-718B-44FB-990B-AD7DFC488AA7}.Release|Any CPU.Build.0 = Release|Any CPU
{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560}.Release|Any CPU.Build.0 = Release|Any CPU
{2079D00A-8489-4A43-BC2E-0B7CFB772CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2079D00A-8489-4A43-BC2E-0B7CFB772CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2079D00A-8489-4A43-BC2E-0B7CFB772CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2079D00A-8489-4A43-BC2E-0B7CFB772CE7}.Release|Any CPU.Build.0 = Release|Any CPU
{A08241ED-8F29-4DF7-9BCF-2E310A9C8187}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A08241ED-8F29-4DF7-9BCF-2E310A9C8187}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A08241ED-8F29-4DF7-9BCF-2E310A9C8187}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A08241ED-8F29-4DF7-9BCF-2E310A9C8187}.Release|Any CPU.Build.0 = Release|Any CPU
{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -51,9 +81,13 @@ Global
{6A10979B-FBAE-488A-B5BC-0E45716DFE88} = {215584EC-8DFC-4DAD-B3A0-D419CEC24260}
{A52EF787-A7DD-4F2F-99B1-9A62CBE1DE5B} = {59B2BDBC-F7DE-47F8-880E-248202630B1A}
{A6A8275A-6360-43E7-AB5A-D8A1E96BED06} = {59B2BDBC-F7DE-47F8-880E-248202630B1A}
{09DBB876-546F-40EF-B1D8-D0FDAE02E80D} = {61C528CD-816E-4FC5-ADFA-C5C25A3C4052}
{3C4E6452-A0A7-4860-83F1-4AF7A0CF1E57} = {61C528CD-816E-4FC5-ADFA-C5C25A3C4052}
{A2193B19-8191-4A28-9E64-23D9083A0531} = {A52EF787-A7DD-4F2F-99B1-9A62CBE1DE5B}
{F7250DAA-718B-44FB-990B-AD7DFC488AA7} = {A52EF787-A7DD-4F2F-99B1-9A62CBE1DE5B}
{5B65EF04-3F7A-4042-8D8B-DFB3AD7CC560} = {61C528CD-816E-4FC5-ADFA-C5C25A3C4052}
{2079D00A-8489-4A43-BC2E-0B7CFB772CE7} = {A52EF787-A7DD-4F2F-99B1-9A62CBE1DE5B}
{A08241ED-8F29-4DF7-9BCF-2E310A9C8187} = {A6A8275A-6360-43E7-AB5A-D8A1E96BED06}
{E7BCC402-E50E-4AA1-AA7C-1A9C95DBDB50} = {A6A8275A-6360-43E7-AB5A-D8A1E96BED06}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {876333E7-72D4-4F43-B6F9-9432CA2B7425}
Expand Down
21 changes: 21 additions & 0 deletions dotnet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Keep track .NET solution

[![Build Status](https://dev.azure.com/devprofr/open-source/_apis/build/status/keeptrack-CI?branchName=master)](https://dev.azure.com/devprofr/open-source/_build/latest?definitionId=18&branchName=master)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=devpro.keep-track&metric=alert_status)](https://sonarcloud.io/dashboard?id=devpro.keep-track)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=devpro.keep-track&metric=coverage)](https://sonarcloud.io/dashboard?id=devpro.keep-track)

## Dependencies

- SDK: .NET Core 3.0
- DB: MongoDB 4.2

## Configuration

- Value for key `KeepTrack_MongoDbConnectionString`: .NET connection string to access MongoDB cluster, ideally set as an environment variable.

## Local run

- Clone the solution: `git clone ...`
- Build the solution: `dotnet build`
- Run the console: `dotnet dotnet src\ConsoleApp\bin\Debug\netcoreapp3.0\KeepTrack.ConsoleApp.dll ...`
- Run the web api: `dotnet run --project src\Api`
35 changes: 35 additions & 0 deletions dotnet/src/Api/Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>KeepTrack.Api</AssemblyName>
<RootNamespace>KeepTrack.Api</RootNamespace>
<ProjectGuid>{651432D5-8481-4807-A422-552EEB4DE8C2}</ProjectGuid>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.0.0-rc4" />
<PackageReference Include="Withywoods.Configuration" Version="1.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CarComponent.Domain\CarComponent.Domain.csproj" />
<ProjectReference Include="..\CarComponent.Infrastructure.MongoDb\CarComponent.Infrastructure.MongoDb.csproj" />
<ProjectReference Include="..\MovieComponent.Domain\MovieComponent.Domain.csproj" />
<ProjectReference Include="..\MovieComponent.Infrastructure.MongoDb\MovieComponent.Infrastructure.MongoDb.csproj" />
</ItemGroup>

</Project>
75 changes: 75 additions & 0 deletions dotnet/src/Api/AppConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models;
using Withywoods.Configuration;
using Withywoods.Dal.MongoDb;
using Withywoods.Dal.MongoDb.Serialization;

namespace KeepTrack.Api
{
/// <summary>
/// Web application configuration.
/// This class implements the interface from the libraries that are used in the application.
/// </summary>
public class AppConfiguration : IMongoDbConfiguration
{
#region Constructor & private fields

/// <summary>
/// Create a new instance of <see cref="AppConfiguration"/>
/// </summary>
/// <param name="configurationRoot"></param>
public AppConfiguration(IConfiguration configurationRoot)
{
ConfigurationRoot = configurationRoot;
}

/// <summary>
/// Configuration root.
/// </summary>
public IConfiguration ConfigurationRoot { get; set; }

#endregion

#region IMongoDbConfiguration properties

/// <summary>
/// MongoDB connection string => secret!
/// This is really a sensitive information so better defined as an environment variable.
/// </summary>
public string ConnectionString => ConfigurationRoot.TryGetSection("KeepTrack_MongoDbConnectionString").Value;

/// <summary>
/// MongoDB collection name.
/// </summary>
public string DatabaseName => ConfigurationRoot.TryGetSection("Infrastructure:MongoDB:DatabaseName").Value;

/// <summary>
/// MongoDB serialization conventions.
/// </summary>
public List<string> SerializationConventions =>
new List<string>
{
ConventionValues.CamelCaseElementName,
ConventionValues.EnumAsString,
ConventionValues.IgnoreExtraElements,
ConventionValues.IgnoreNullValues
};

#endregion

#region General properties

/// <summary>
/// Open API information.
/// </summary>
public OpenApiInfo OpenApiInfo =>
new OpenApiInfo
{
Title = "Keep Track API",
Version = "1.0"
};

#endregion
}
}
135 changes: 135 additions & 0 deletions dotnet/src/Api/Controllers/CarHistoryController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using KeepTrack.Api.Dto;
using KeepTrack.CarComponent.Domain;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Api.Controllers
{
/// <summary>
/// Car history controller.
/// </summary>
[ApiController]
[Authorize]
[Route("api/car-history")]
public class CarHistoryController : KeepTrack.Api.Controllers.ControllerBase
{
private readonly IMapper _mapper;
private readonly ICarHistoryRepository _carHistoryRepository;

/// <summary>
/// Creates a new instance of <see cref="CarHistoryController"/>.
/// </summary>
/// <param name="mapper"></param>
/// <param name="carHistoryRepository"></param>
public CarHistoryController(IMapper mapper, ICarHistoryRepository carHistoryRepository)
{
_mapper = mapper;
_carHistoryRepository = carHistoryRepository;
}

/// <summary>
/// Gets all the history for a given car.
/// </summary>
/// <param name="carId">Car ID</param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(200, Type = typeof(List<CarHistoryDto>))]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public async Task<IActionResult> Get(string carId)
{
if (string.IsNullOrEmpty(carId))
{
return BadRequest();
}

var models = await _carHistoryRepository.FindAllAsync(carId, GetUserId());
return Ok(_mapper.Map<List<CarHistoryDto>>(models));
}

/// <summary>
/// Gets information from a single car history.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}", Name = "GetCarHistoryById")]
[ProducesResponseType(200, Type = typeof(CarHistoryDto))]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<IActionResult> GetById(string id)
{
if (string.IsNullOrEmpty(id))
{
return BadRequest();
}

var model = await _carHistoryRepository.FindOneAsync(id, GetUserId());
if (model == null)
{
return NotFound();
}

return Ok(_mapper.Map<CarHistoryDto>(model));
}

/// <summary>
/// Creates a new car history.
/// </summary>
/// <param name="dto"></param>
[HttpPost]
[ProducesResponseType(201)]
public async Task<IActionResult> Post([FromBody] CarHistoryDto dto)
{
var input = _mapper.Map<CarHistoryModel>(dto);
input.OwnerId = GetUserId();
var model = await _carHistoryRepository.CreateAsync(input);
return CreatedAtRoute("GetCarHistoryById", new { id = model.Id }, _mapper.Map<CarHistoryDto>(model));
}

/// <summary>
/// Updates a car history.
/// </summary>
/// <param name="id"></param>
/// <param name="dto"></param>
[HttpPut("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public async Task<IActionResult> Put(string id, [FromBody] CarHistoryDto dto)
{
if (string.IsNullOrEmpty(id))
{
return BadRequest();
}

var input = _mapper.Map<CarHistoryModel>(dto);
input.OwnerId = GetUserId();
await _carHistoryRepository.UpdateAsync(id, input, GetUserId());
return NoContent();
}

/// <summary>
/// Deletes a car history.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public async Task<IActionResult> Delete(string id)
{
if (string.IsNullOrEmpty(id))
{
return BadRequest();
}

await _carHistoryRepository.DeleteAsync(id, GetUserId());
return NoContent();
}
}
}
26 changes: 26 additions & 0 deletions dotnet/src/Api/Controllers/ControllerBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Linq;

namespace KeepTrack.Api.Controllers
{
/// <summary>
/// Base controller for the web application.
/// </summary>
public abstract class ControllerBase : Microsoft.AspNetCore.Mvc.ControllerBase
{
/// <summary>
/// Get authenticated user id.
/// </summary>
/// <returns></returns>
protected string GetUserId()
{
var userId = User.Claims.FirstOrDefault(x => x.Type == "user_id")?.Value;
if (string.IsNullOrEmpty(userId))
{
throw new UnauthorizedAccessException();
}

return userId;
}
}
}
Loading