diff --git a/.editorconfig b/.editorconfig index 4701ee2..b664d32 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,13 @@ root = true # C# files [*.cs] +# this should be causing namespace missmatch to fail the linting workflow and it does +# however, auto fix of formatting accoring to this rule is broken since 2022 :( +# thus, it is validated but needs manual fix as for now +# https://github.com/dotnet/format/issues/1623 +dotnet_style_namespace_match_folder = true:warning +dotnet_diagnostic.IDE0130.severity = warning + #### Core EditorConfig Options #### # Indentation and spacing diff --git a/Api/Controllers/ItemsController.cs b/Api/Controllers/ItemsController.cs deleted file mode 100644 index 74446ed..0000000 --- a/Api/Controllers/ItemsController.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Api.EnternalDeps.EmployeesApi; -using Api.Mappers; -using Api.Requests; -using Api.Responses; -using Application.Commands; -using Application.Queries; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using TourmalineCore.AspNetCore.JwtAuthentication.Core.Filters; - -namespace Api.Controllers; - -[Authorize] -[ApiController] -[Route("api/items")] -public class ItemsController : ControllerBase -{ - /// - /// Get all items - /// - [RequiresPermission(UserClaimsProvider.CanViewItems)] - [HttpGet] - public async Task GetAllItemsAsync( - [FromServices] AllItemsQuery allItemsQuery, - [FromServices] EmployeesApi employeesApi - ) - { - var items = await allItemsQuery.GetAsync(); - - var allEmployeesResponse = await employeesApi.GetAllEmployeesAsync(); - - return new ItemsResponse - { - Items = items - .Select(x => new ItemDto - { - Id = x.Id, - Name = x.Name, - SerialNumber = x.SerialNumber, - ItemType = new ItemTypeDto - { - Id = x.ItemType.Id, - Name = x.ItemType.Name - }, - Price = x.Price, - Description = x.Description, - PurchaseDate = x.PurchaseDate, - HolderEmployee = HolderEmployeeMapper.MapToEmployeeDto(x.HolderEmployeeId, allEmployeesResponse) - }) - .ToList() - }; - } - - /// - /// Add an item - /// - /// - [RequiresPermission(UserClaimsProvider.CanManageItems)] - [HttpPost] - public async Task CreateItemAsync( - [FromServices] CreateItemCommand createItemCommand, - [Required][FromBody] CreateItemRequest createItemRequest - ) - { - var createItemCommandParams = new CreateItemCommandParams - { - Name = createItemRequest.Name, - SerialNumber = createItemRequest.SerialNumber, - ItemTypeId = createItemRequest.ItemTypeId, - Price = createItemRequest.Price, - Description = createItemRequest.Description, - PurchaseDate = createItemRequest.PurchaseDate, - HolderEmployeeId = createItemRequest.HolderEmployeeId - }; - - var newItemId = await createItemCommand.ExecuteAsync(createItemCommandParams); - - return new CreateItemResponse() - { - NewItemId = newItemId - }; - } - - /// - /// Deletes specific item - /// - /// - [RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsItemsHardDeleteAllowed)] - [HttpDelete("{itemId}/hard-delete")] - public async Task HardDeleteItemAsync( - [FromServices] HardDeleteItemCommand hardDeleteItemCommand, - [Required][FromRoute] long itemId - ) - { - return new - { - isDeleted = await hardDeleteItemCommand.ExecuteAsync(itemId) - }; - } -} diff --git a/Api/DependencyInjection.cs b/Api/DependencyInjection.cs index b94215c..ecd0766 100644 --- a/Api/DependencyInjection.cs +++ b/Api/DependencyInjection.cs @@ -1,4 +1,6 @@ -using Api.EnternalDeps.EmployeesApi; +using Api.ExternalDeps.EmployeesApi; +using Api.Features.Items.CreateItem; +using Api.Features.Items.GetAllItems; using Application; using Application.Commands; using Application.Queries; @@ -36,5 +38,8 @@ public static void AddApplication(this IServiceCollection services, IConfigurati services.AddTransient(); services.AddTransient(); services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); } } diff --git a/Api/EnternalDeps/EmployeesApi/EmployeesApi.cs b/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs similarity index 94% rename from Api/EnternalDeps/EmployeesApi/EmployeesApi.cs rename to Api/ExternalDeps/EmployeesApi/EmployeesApi.cs index 7036fff..ad8b306 100644 --- a/Api/EnternalDeps/EmployeesApi/EmployeesApi.cs +++ b/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs @@ -1,8 +1,8 @@ -using Api.EnternalDeps.EmployeesApi.Responses; +using Api.ExternalDeps.EmployeesApi.Responses; using Microsoft.Extensions.Options; using TourmalineCore.AspNetCore.JwtAuthentication.Core.Options; -namespace Api.EnternalDeps.EmployeesApi; +namespace Api.ExternalDeps.EmployeesApi; public class EmployeesApi { diff --git a/Api/EnternalDeps/EmployeesApi/Responses/EmployeesResponse.cs b/Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs similarity index 86% rename from Api/EnternalDeps/EmployeesApi/Responses/EmployeesResponse.cs rename to Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs index e4c693c..411123a 100644 --- a/Api/EnternalDeps/EmployeesApi/Responses/EmployeesResponse.cs +++ b/Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs @@ -1,4 +1,4 @@ -namespace Api.EnternalDeps.EmployeesApi.Responses; +namespace Api.ExternalDeps.EmployeesApi.Responses; public class EmployeesResponse { diff --git a/Api/EnternalDeps/ExternalDepsUrls.cs b/Api/ExternalDeps/ExternalDepsUrls.cs similarity index 100% rename from Api/EnternalDeps/ExternalDepsUrls.cs rename to Api/ExternalDeps/ExternalDepsUrls.cs diff --git a/Api/Features/Items/CreateItem/CreateItemHandler.cs b/Api/Features/Items/CreateItem/CreateItemHandler.cs new file mode 100644 index 0000000..6246b54 --- /dev/null +++ b/Api/Features/Items/CreateItem/CreateItemHandler.cs @@ -0,0 +1,34 @@ +using Application.Commands; + +namespace Api.Features.Items.CreateItem; + +public class CreateItemHandler +{ + private readonly CreateItemCommand _createItemCommand; + + public CreateItemHandler(CreateItemCommand createItemCommand) + { + _createItemCommand = createItemCommand; + } + + public async Task HandleAsync(CreateItemRequest createItemRequest) + { + var createItemCommandParams = new CreateItemCommandParams + { + Name = createItemRequest.Name, + SerialNumber = createItemRequest.SerialNumber, + ItemTypeId = createItemRequest.ItemTypeId, + Price = createItemRequest.Price, + Description = createItemRequest.Description, + PurchaseDate = createItemRequest.PurchaseDate, + HolderEmployeeId = createItemRequest.HolderEmployeeId + }; + + var newItemId = await _createItemCommand.ExecuteAsync(createItemCommandParams); + + return new CreateItemResponse() + { + NewItemId = newItemId + }; + } +} diff --git a/Api/Requests/CreateItemRequest.cs b/Api/Features/Items/CreateItem/CreateItemRequest.cs similarity index 92% rename from Api/Requests/CreateItemRequest.cs rename to Api/Features/Items/CreateItem/CreateItemRequest.cs index 4f427ca..bebc464 100644 --- a/Api/Requests/CreateItemRequest.cs +++ b/Api/Features/Items/CreateItem/CreateItemRequest.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Api.Requests; +namespace Api.Features.Items.CreateItem; public class CreateItemRequest { diff --git a/Api/Responses/CreateItemResponse.cs b/Api/Features/Items/CreateItem/CreateItemResponse.cs similarity index 67% rename from Api/Responses/CreateItemResponse.cs rename to Api/Features/Items/CreateItem/CreateItemResponse.cs index 0fac7f6..40467ea 100644 --- a/Api/Responses/CreateItemResponse.cs +++ b/Api/Features/Items/CreateItem/CreateItemResponse.cs @@ -1,4 +1,4 @@ -namespace Api.Responses; +namespace Api.Features.Items.CreateItem; public class CreateItemResponse { diff --git a/Api/Features/Items/GetAllItems/GetAllItemsHandler.cs b/Api/Features/Items/GetAllItems/GetAllItemsHandler.cs new file mode 100644 index 0000000..4d20ba4 --- /dev/null +++ b/Api/Features/Items/GetAllItems/GetAllItemsHandler.cs @@ -0,0 +1,48 @@ +using Api.ExternalDeps.EmployeesApi; +using Api.Responses; +using Application.Queries; + +namespace Api.Features.Items.GetAllItems; + +public class GetAllItemsHandler +{ + private readonly AllItemsQuery _allItemsQuery; + private readonly EmployeesApi _employeesApi; + + public GetAllItemsHandler( + AllItemsQuery allItemsQuery, + EmployeesApi employeesApi + ) + { + _allItemsQuery = allItemsQuery; + _employeesApi = employeesApi; + } + + public async Task HandleAsync() + { + var items = await _allItemsQuery.GetAsync(); + + var allEmployeesResponse = await _employeesApi.GetAllEmployeesAsync(); + + return new GetAllItemsResponse + { + Items = items + .Select(x => new ItemDto + { + Id = x.Id, + Name = x.Name, + SerialNumber = x.SerialNumber, + ItemType = new ItemTypeDto + { + Id = x.ItemType.Id, + Name = x.ItemType.Name + }, + Price = x.Price, + Description = x.Description, + PurchaseDate = x.PurchaseDate, + HolderEmployee = HolderEmployeeMapper.MapToEmployeeDto(x.HolderEmployeeId, allEmployeesResponse) + }) + .ToList() + }; + } +} diff --git a/Api/Responses/ItemsResponse.cs b/Api/Features/Items/GetAllItems/GetAllItemsResponse.cs similarity index 86% rename from Api/Responses/ItemsResponse.cs rename to Api/Features/Items/GetAllItems/GetAllItemsResponse.cs index 2bf4cf3..207971d 100644 --- a/Api/Responses/ItemsResponse.cs +++ b/Api/Features/Items/GetAllItems/GetAllItemsResponse.cs @@ -1,6 +1,8 @@ -namespace Api.Responses; +using Api.Responses; -public class ItemsResponse +namespace Api.Features.Items.GetAllItems; + +public class GetAllItemsResponse { public required List Items { get; set; } } diff --git a/Api/Mappers/HolderEmployeeMapper.cs b/Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs similarity index 86% rename from Api/Mappers/HolderEmployeeMapper.cs rename to Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs index 14643b6..0c3eb12 100644 --- a/Api/Mappers/HolderEmployeeMapper.cs +++ b/Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs @@ -1,7 +1,6 @@ -using Api.EnternalDeps.EmployeesApi.Responses; -using Api.Responses; +using Api.ExternalDeps.EmployeesApi.Responses; -namespace Api.Mappers; +namespace Api.Features.Items.GetAllItems; public class HolderEmployeeMapper { diff --git a/Api/Mappers/HolderEmployeeMapperTests.cs b/Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs similarity index 87% rename from Api/Mappers/HolderEmployeeMapperTests.cs rename to Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs index cbe5d84..af563f9 100644 --- a/Api/Mappers/HolderEmployeeMapperTests.cs +++ b/Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs @@ -1,7 +1,7 @@ -using Api.EnternalDeps.EmployeesApi.Responses; +using Api.ExternalDeps.EmployeesApi.Responses; using Xunit; -namespace Api.Mappers; +namespace Api.Features.Items.GetAllItems; public class HolderEmployeeMapperTests { diff --git a/Api/Features/Items/ItemsController.cs b/Api/Features/Items/ItemsController.cs new file mode 100644 index 0000000..df2235c --- /dev/null +++ b/Api/Features/Items/ItemsController.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations; +using Api.Features.Items.CreateItem; +using Api.Features.Items.GetAllItems; +using Application.Commands; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using TourmalineCore.AspNetCore.JwtAuthentication.Core.Filters; + +namespace Api.Features.Items; + +[Authorize] +[ApiController] +[Route("api/items")] +public class ItemsController : ControllerBase +{ + /// + /// Get all items + /// + [RequiresPermission(UserClaimsProvider.CanViewItems)] + [HttpGet] + public Task GetAllItemsAsync( + [FromServices] GetAllItemsHandler getAllItemsHandler + ) + { + return getAllItemsHandler.HandleAsync(); + } + + /// + /// Add an item + /// + /// + [RequiresPermission(UserClaimsProvider.CanManageItems)] + [HttpPost] + public Task CreateItemAsync( + [FromServices] CreateItemHandler createItemHandler, + [Required][FromBody] CreateItemRequest createItemRequest + ) + { + return createItemHandler.HandleAsync(createItemRequest); + } + + /// + /// Deletes specific item + /// + /// + [RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsItemsHardDeleteAllowed)] + [HttpDelete("{itemId}/hard-delete")] + public async Task HardDeleteItemAsync( + [FromServices] HardDeleteItemCommand hardDeleteItemCommand, + [Required][FromRoute] long itemId + ) + { + return new + { + isDeleted = await hardDeleteItemCommand.ExecuteAsync(itemId) + }; + } +} diff --git a/README.md b/README.md index dc12873..af5bae1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # inner-circle-items-api -[![coverage](https://img.shields.io/badge/e2e_coverage-81.90%25-olivedrab)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) -[![coverage](https://img.shields.io/badge/units_coverage-17.65%25-crimson)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) -[![coverage](https://img.shields.io/badge/full_coverage-89.78%25-olivedrab)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) +[![coverage](https://img.shields.io/badge/e2e_coverage-82.22%25-olivedrab)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) +[![coverage](https://img.shields.io/badge/units_coverage-17.34%25-crimson)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) +[![coverage](https://img.shields.io/badge/full_coverage-89.96%25-olivedrab)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) This repo contains Inner Circle Items API.