Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
23d5d07
added a client class with basic fields and configured the structure o…
Amitroki Nov 20, 2025
63be3a9
added an autopark class with basic fields
Amitroki Nov 23, 2025
fc0420e
added a rent class with basic fields
Amitroki Nov 27, 2025
24f6b52
added summary comments to domain classes and internal components
Amitroki Nov 27, 2025
3f9d2f4
added a data seed for completing unit tests and made ome minor changes
Amitroki Nov 28, 2025
d5fd5f0
added summaries for CarRental.Domain.DataSeed.DataSeed.cs and CarRent…
Amitroki Dec 5, 2025
09fa874
the .net version has been fixed from 9.0 to 8.0
Amitroki Dec 10, 2025
6dab5db
was added the first repository for storing cars
Amitroki Dec 11, 2025
1c8cc84
minor changes: have the brackets in the files been changed, the type …
Amitroki Dec 11, 2025
df26361
made minor change in field name of the CarModelGeneration class, adde…
Amitroki Dec 15, 2025
842d9ed
made minor change in data seed field name, added abstract class for r…
Amitroki Dec 15, 2025
f5f343d
added an interface of base repository for better abstraction, fixed s…
Amitroki Dec 19, 2025
758e4b4
modified the structure of solution the interfaces have been moved to …
Amitroki Dec 19, 2025
7f28c55
added DTO's for exchange information about cars, class for correct ma…
Amitroki Dec 21, 2025
d02573c
fixed links between projects, fixed names of imports, fixed type para…
Amitroki Dec 21, 2025
28990aa
added correct working services for client, rent, car model, car model…
Amitroki Dec 22, 2025
fb25401
added all essential files for analytics methods (DTOs, Service, Contr…
Amitroki Dec 22, 2025
440ae7f
added summaries for all existing essential files and changed construc…
Amitroki Dec 22, 2025
3505b49
added CarRental.ServiceDefaults and CarRental.AppHost projects, added…
Amitroki Dec 22, 2025
ebe7790
added attributes with response codes, fixed the return types in the c…
Amitroki Dec 23, 2025
e410704
fixed troubles with dissapeared patronymic when user tries to create …
Amitroki Dec 23, 2025
1a96f6d
fixed problem with summaries and CarCreateUpdateDto
Amitroki Dec 24, 2025
0f99de8
merged with branch for second lab
Amitroki Dec 24, 2025
49642d7
type of the ID was changed from uint to int for better communication …
Amitroki Dec 24, 2025
72aa8bf
made services and controllers asynchronous
Amitroki Dec 24, 2025
3479fc4
updated MongoDBDriver and Aspire.Hosting.AppHost, now Aspire can runs…
Amitroki Dec 24, 2025
c7312cf
tried to made good communication between database and server
Amitroki Dec 25, 2025
985cb56
changed type for all ID fields and remaked repositories for working w…
Amitroki Dec 30, 2025
a47f911
removed unnecessary commentaries, added logging for car model and car…
Amitroki Jan 28, 2026
3ca8b7f
remade of analytics requests
Amitroki Jan 28, 2026
e919da0
added necessary xml-commentaries
Amitroki Jan 30, 2026
26c1ff9
implemented some changes in logging of events, exception handling, as…
Amitroki Feb 10, 2026
4d2ef49
changed summary for Mapster config, changed response codes for analyt…
Amitroki Feb 12, 2026
d64858d
added a service for generating fake rents
Amitroki Feb 15, 2026
b3c57ef
divided the CarRental.Application structure to avoid circular depende…
Amitroki Feb 17, 2026
bade9a1
optimized work with Guid in Lists, added control for the number of re…
Amitroki Feb 18, 2026
65c81d4
made changes in GeneratorController.cs, KafkaProducer.cs, GenerateRen…
Amitroki Feb 19, 2026
2f3ef59
the error message for incorrect delay time was corrected
Amitroki Feb 20, 2026
7c529d1
minor changes
Amitroki Feb 20, 2026
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
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches: [ "*" ]
pull_request:
branches: [ "main" ]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Run unit tests
run: dotnet test --no-build --configuration Release --verbosity normal
22 changes: 22 additions & 0 deletions CarRental.Api/CarRental.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.MongoDB.Driver" Version="13.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CarRental.Application\CarRental.Application.csproj" />
<ProjectReference Include="..\CarRental.Domain\CarRental.Domain.csproj" />
<ProjectReference Include="..\CarRental.Infrastructure\CarRental.Infrastructure.csproj" />
<ProjectReference Include="..\CarRental.ServiceDefaults\CarRental.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
126 changes: 126 additions & 0 deletions CarRental.Api/Controllers/AnalyticsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using CarRental.Application.Contracts.Client;
using CarRental.Application.Contracts.Analytics;
using CarRental.Application.Contracts.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace CarRental.Api.Controllers;

/// <summary>
/// Provides specialized API endpoints for data analytics and business reporting
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class AnalyticsController(IAnalyticsService analyticsService, ILogger<AnalyticsController> logger) : ControllerBase
{
/// <summary>
/// Retrieves a list of clients who have rented cars associated with a specific model name
/// </summary>
/// <param name="modelName">The name of the car model to filter by</param>
[HttpGet("clients-by-model")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<ClientDto>>> GetClientsByModel([FromQuery] string modelName)
{
logger.LogInformation("{method} method of {controller} is called with {string} parameter", nameof(GetClientsByModel), GetType().Name, modelName);
try
{
var result = await analyticsService.ReadClientsByModelName(modelName);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetClientsByModel), GetType().Name);
return Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetClientsByModel), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Returns details of cars that are currently on lease at the specified date and time
/// </summary>
/// <param name="atTime">The point in time to check for active rentals</param>
[HttpGet("cars-in-rent")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<CarInRentDto>>> GetCarsInRent([FromQuery] DateTime atTime)
{
logger.LogInformation("{method} method of {controller} is called with {parameterName} = {parameterValue}", nameof(GetCarsInRent), GetType().Name, nameof(atTime), atTime);
try
{
var result = await analyticsService.ReadCarsInRent(atTime);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetCarsInRent), GetType().Name);
return Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetCarsInRent), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Returns the top 5 most popular cars based on total rental frequency
/// </summary>
[HttpGet("top-5-rented-cars")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<CarWithRentalCountDto>>> GetTop5Cars()
{
logger.LogInformation("{method} method of {controller} is called", nameof(GetTop5Cars), GetType().Name);
try
{
var result = await analyticsService.ReadTop5MostRentedCars();
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetTop5Cars), GetType().Name);
return Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetTop5Cars), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Provides a comprehensive list of all cars and how many times each has been rented
/// </summary>
[HttpGet("all-cars-with-rental-count")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<CarWithRentalCountDto>>> GetAllCarsWithCount()
{
logger.LogInformation("{method} method of {controller} is called", nameof(GetAllCarsWithCount), GetType().Name);
try
{
var result = await analyticsService.ReadAllCarsWithRentalCount();
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetAllCarsWithCount), GetType().Name);
return Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetAllCarsWithCount), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Returns the top 5 clients who have contributed the most to total revenue
/// </summary>
[HttpGet("top-5-clients-by-money")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<ClientWithTotalAmountDto>>> GetTop5Clients()
{
logger.LogInformation("{method} method of {controller} is called", nameof(GetTop5Clients), GetType().Name);
try
{
var result = await analyticsService.ReadTop5ClientsByTotalAmount();
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetTop5Clients), GetType().Name);
return Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetTop5Clients), GetType().Name);
return StatusCode(500);
}
}
}
135 changes: 135 additions & 0 deletions CarRental.Api/Controllers/CarControllers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using CarRental.Application.Contracts.Car;
using CarRental.Application.Contracts.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace CarRental.Api.Controllers;

/// <summary>
/// API controller for managing the car fleet (CRUD operations)
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class CarController(IApplicationService<CarDto, CarCreateUpdateDto, Guid> carService, ILogger<CarController> logger) : ControllerBase
{
/// <summary>
/// Retrieves a list of all cars available in the system
/// </summary>
[HttpGet]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<List<CarDto>>> GetAll()
{
logger.LogInformation("{method} method of {controller} is called", nameof(GetAll), GetType().Name);
try
{
var cars = await carService.ReadAll();
logger.LogInformation("{method} method of {controller} executed successfully", nameof(GetAll), GetType().Name);
return Ok(cars);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(GetAll), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Retrieves details of a specific car by its identifier
/// </summary>
/// <param name="id">The unique identifier of the car</param>
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<CarDto>> Get(Guid id)
{
logger.LogInformation("{method} method of {controller} is called with {id} parameter", nameof(Get), GetType().Name, id);
try
{
var car = await carService.Read(id);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(Get), GetType().Name);
return Ok(car);
}
catch (KeyNotFoundException ex)
{
logger.LogWarning(ex, "An exception happened during {method} method of {controller}", nameof(Get), GetType().Name);
return NotFound();
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(Get), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Registers a new car in the fleet
/// </summary>
/// <param name="dto">The data for the new car record</param>
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(500)]
public async Task<ActionResult<CarDto>> Create([FromBody] CarCreateUpdateDto dto)
{
logger.LogInformation("{method} method of {controller} is called with {dto} parameter", nameof(Create), GetType().Name, dto);
try
{
var createdCar = await carService.Create(dto);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(Create), GetType().Name);
return CreatedAtAction(nameof(this.Create), createdCar);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(Create), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Updates an existing car's information
/// </summary>
/// <param name="id">The unique identifier of the car to update</param>
/// <param name="dto">The updated data</param>
[HttpPut("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(500)]
public async Task<ActionResult<CarDto>> Update(Guid id, [FromBody] CarCreateUpdateDto dto)
{
logger.LogInformation("{method} method of {controller} is called with {key},{dto} parameters", nameof(Update), GetType().Name, id, dto);
try
{
var updatedCar = await carService.Update(dto, id);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(Update), GetType().Name);
return Ok(updatedCar);
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(Update), GetType().Name);
return StatusCode(500);
}
}

/// <summary>
/// Removes a car from the system
/// </summary>
/// <param name="id">The unique identifier of the car to delete</param>
[HttpDelete("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(204)]
[ProducesResponseType(500)]
public async Task<ActionResult> Delete(Guid id)
{
logger.LogInformation("{method} method of {controller} is called with {id} parameter", nameof(Delete), GetType().Name, id);
try
{
var result = await carService.Delete(id);
logger.LogInformation("{method} method of {controller} executed successfully", nameof(Delete), GetType().Name);
return result ? Ok() : NoContent();
}
catch (Exception ex)
{
logger.LogError(ex, "An exception happened during {method} method of {controller}", nameof(Delete), GetType().Name);
return StatusCode(500);
}
}
}
Loading