Skip to content

Commit

Permalink
Create new component (Movie) & fix SonarCloud issues (#2)
Browse files Browse the repository at this point in the history
* Fix issues raised by SonarCloud

* Create Api with CarHistory CRUD

* Activate authentication with Firebase

* Add MovieComponent

* Delete .csproj.user and add it in gitignore

* Fix SonarCloud issues
  • Loading branch information
devpro committed Oct 24, 2019
1 parent 76485ce commit 22c46c3
Show file tree
Hide file tree
Showing 42 changed files with 1,350 additions and 51 deletions.
2 changes: 2 additions & 0 deletions dotnet/.gitignore
Expand Up @@ -2,3 +2,5 @@
Debug/
Release/
obj/
appsettings.Development.json
*.csproj.user
46 changes: 40 additions & 6 deletions dotnet/KeepTrack.sln
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
}
}
}

0 comments on commit 22c46c3

Please sign in to comment.