diff --git a/Api/Controllers/ItemTypesController.cs b/Api/Controllers/ItemTypesController.cs deleted file mode 100644 index 5c86dba..0000000 --- a/Api/Controllers/ItemTypesController.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.ComponentModel.DataAnnotations; -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/item-types")] -public class ItemTypesController : ControllerBase -{ - /// - /// Get all item types - /// - [RequiresPermission(UserClaimsProvider.CanViewItemsTypes)] - [HttpGet] - public async Task GetAllItemTypesAsync( - [FromServices] AllItemTypesQuery allItemTypesQuery - ) - { - var itemTypes = await allItemTypesQuery.GetAsync(); - - return new ItemTypesResponse - { - ItemTypes = itemTypes - .Select(x => new ItemTypeDto - { - Id = x.Id, - Name = x.Name, - }) - .ToList() - }; - } - - /// - /// Adds item type - /// - /// - [RequiresPermission(UserClaimsProvider.CanManageItemsTypes)] - [HttpPost] - public async Task CreateItemTypeAsync( - [FromServices] CreateItemTypeCommand createItemTypeCommand, - [Required][FromBody] CreateItemTypeRequest createItemTypeRequest - ) - { - var createItemTypeCommandParams = new CreateItemTypeCommandParams - { - Name = createItemTypeRequest.Name - }; - - var newItemTypeId = await createItemTypeCommand.ExecuteAsync(createItemTypeCommandParams); - - return new CreateItemTypeResponse() - { - NewItemTypeId = newItemTypeId - }; - } - - /// - /// Deletes specific item type - /// - /// - [RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsItemTypesHardDeleteAllowed)] - [HttpDelete("{itemTypeId}/hard-delete")] - public async Task HardDeleteItemTypeAsync( - [FromServices] HardDeleteItemTypeCommand hardDeleteItemTypeCommand, - [Required][FromRoute] long itemTypeId - ) - { - return new - { - isDeleted = await hardDeleteItemTypeCommand.ExecuteAsync(itemTypeId) - }; - } -} diff --git a/Api/DependencyInjection.cs b/Api/DependencyInjection.cs index ecd0766..05cd46c 100644 --- a/Api/DependencyInjection.cs +++ b/Api/DependencyInjection.cs @@ -1,9 +1,12 @@ using Api.ExternalDeps.EmployeesApi; -using Api.Features.Items.CreateItem; -using Api.Features.Items.GetAllItems; using Application; -using Application.Commands; -using Application.Queries; +using Application.ExternalDeps.EmployeesApi; +using Application.Features.Items.CreateItem; +using Application.Features.Items.GetAllItems; +using Application.Features.Items.HardDeleteItem; +using Application.Features.ItemTypes.CreateItemType; +using Application.Features.ItemTypes.GetAllItemTypes; +using Application.Features.ItemTypes.HardDeleteItemType; using Microsoft.EntityFrameworkCore; namespace Api; @@ -29,17 +32,22 @@ public static void AddApplication(this IServiceCollection services, IConfigurati services.Configure(configuration.GetSection(nameof(ExternalDepsUrls))); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + + services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); } } diff --git a/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs b/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs index ad8b306..f20af56 100644 --- a/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs +++ b/Api/ExternalDeps/EmployeesApi/EmployeesApi.cs @@ -1,10 +1,10 @@ -using Api.ExternalDeps.EmployeesApi.Responses; +using Application.ExternalDeps.EmployeesApi; using Microsoft.Extensions.Options; using TourmalineCore.AspNetCore.JwtAuthentication.Core.Options; namespace Api.ExternalDeps.EmployeesApi; -public class EmployeesApi +public class EmployeesApi : IEmployeesApi { private readonly ExternalDepsUrls _externalDepsUrls; private readonly AuthenticationOptions _authenticationOptions; diff --git a/Api/Features/ItemTypes/ItemTypesController.cs b/Api/Features/ItemTypes/ItemTypesController.cs new file mode 100644 index 0000000..611c658 --- /dev/null +++ b/Api/Features/ItemTypes/ItemTypesController.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations; +using Application.Features.ItemTypes.CreateItemType; +using Application.Features.ItemTypes.GetAllItemTypes; +using Application.Features.ItemTypes.HardDeleteItemType; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using TourmalineCore.AspNetCore.JwtAuthentication.Core.Filters; + +namespace Api.Features.ItemTypes; + +[Authorize] +[ApiController] +[Route("api/item-types")] +public class ItemTypesController : ControllerBase +{ + /// + /// Get all item types + /// + [RequiresPermission(UserClaimsProvider.CanViewItemsTypes)] + [HttpGet] + public Task GetAllItemTypesAsync( + [FromServices] GetAllItemTypesHandler getAllItemTypesHandler + ) + { + return getAllItemTypesHandler.HandleAsync(); + } + + /// + /// Adds item type + /// + /// + [RequiresPermission(UserClaimsProvider.CanManageItemsTypes)] + [HttpPost] + public Task CreateItemTypeAsync( + [FromServices] CreateItemTypeHandler createItemTypeHandler, + [Required][FromBody] CreateItemTypeRequest createItemTypeRequest + ) + { + return createItemTypeHandler.HandleAsync(createItemTypeRequest); + } + + /// + /// Deletes specific item type + /// + /// + [RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsItemTypesHardDeleteAllowed)] + [HttpDelete("{itemTypeId}/hard-delete")] + public async Task HardDeleteItemTypeAsync( + [FromServices] HardDeleteItemTypeHandler hardDeleteItemTypeHandler, + [Required][FromRoute] long itemTypeId + ) + { + return new + { + isDeleted = await hardDeleteItemTypeHandler.HandleAsync(itemTypeId) + }; + } +} diff --git a/Api/Features/Items/CreateItem/CreateItemHandler.cs b/Api/Features/Items/CreateItem/CreateItemHandler.cs deleted file mode 100644 index 6246b54..0000000 --- a/Api/Features/Items/CreateItem/CreateItemHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -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/Features/Items/ItemsController.cs b/Api/Features/Items/ItemsController.cs index df2235c..a89ee36 100644 --- a/Api/Features/Items/ItemsController.cs +++ b/Api/Features/Items/ItemsController.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; -using Api.Features.Items.CreateItem; -using Api.Features.Items.GetAllItems; -using Application.Commands; +using Application.Features.Items.CreateItem; +using Application.Features.Items.GetAllItems; +using Application.Features.Items.HardDeleteItem; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using TourmalineCore.AspNetCore.JwtAuthentication.Core.Filters; @@ -46,13 +46,13 @@ [Required][FromBody] CreateItemRequest createItemRequest [RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsItemsHardDeleteAllowed)] [HttpDelete("{itemId}/hard-delete")] public async Task HardDeleteItemAsync( - [FromServices] HardDeleteItemCommand hardDeleteItemCommand, + [FromServices] HardDeleteItemHandler hardDeleteItemHandler, [Required][FromRoute] long itemId ) { return new { - isDeleted = await hardDeleteItemCommand.ExecuteAsync(itemId) + isDeleted = await hardDeleteItemHandler.HandleAsync(itemId) }; } } diff --git a/Api/Responses/ItemTypesResponse.cs b/Api/Responses/ItemTypesResponse.cs deleted file mode 100644 index e4389bf..0000000 --- a/Api/Responses/ItemTypesResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Api.Responses; - -public class ItemTypesResponse -{ - public required List ItemTypes { get; set; } -} diff --git a/Application/Commands/CreateItemCommand.cs b/Application/Commands/CreateItemCommand.cs deleted file mode 100644 index 91ba52e..0000000 --- a/Application/Commands/CreateItemCommand.cs +++ /dev/null @@ -1,69 +0,0 @@ - -using Core.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Application.Commands; - -public class CreateItemCommandParams -{ - public required string Name { get; set; } - - public required string SerialNumber { get; set; } - - public required long ItemTypeId { get; set; } - - public required decimal Price { get; set; } - - public required string Description { get; set; } - - public required DateOnly? PurchaseDate { get; set; } - - public required long? HolderEmployeeId { get; set; } -} - -public class CreateItemCommand -{ - private readonly TenantAppDbContext _context; - private readonly IClaimsProvider _claimsProvider; - - public CreateItemCommand( - TenantAppDbContext context, - IClaimsProvider claimsProvider - ) - { - _context = context; - _claimsProvider = claimsProvider; - } - - public async Task ExecuteAsync(CreateItemCommandParams createItemCommandParams) - { - var itemTypeIdDoesNotExistWithinTenant = await _context - .QueryableWithinTenant() - .AllAsync(x => x.Id != createItemCommandParams.ItemTypeId); - - if (itemTypeIdDoesNotExistWithinTenant) - { - throw new Exception($"Passed item type where id={createItemCommandParams.ItemTypeId} is not found within tenant where id={_claimsProvider.TenantId}"); - } - - var item = new Item - { - TenantId = _claimsProvider.TenantId, - Name = createItemCommandParams.Name, - SerialNumber = createItemCommandParams.SerialNumber, - ItemTypeId = createItemCommandParams.ItemTypeId, - Price = createItemCommandParams.Price, - Description = createItemCommandParams.Description, - PurchaseDate = createItemCommandParams.PurchaseDate, - HolderEmployeeId = createItemCommandParams.HolderEmployeeId - }; - - await _context - .Items - .AddAsync(item); - - await _context.SaveChangesAsync(); - - return item.Id; - } -} diff --git a/Application/Commands/HardDeleteItemCommand.cs b/Application/Commands/HardDeleteItemCommand.cs deleted file mode 100644 index 8f62646..0000000 --- a/Application/Commands/HardDeleteItemCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Core.Entities; - -namespace Application.Commands; - -public class HardDeleteItemCommand -{ - private readonly HardDeleteEntityCommand _hardDeleteEntityCommand; - - public HardDeleteItemCommand(TenantAppDbContext context) - { - _hardDeleteEntityCommand = new HardDeleteEntityCommand(context); - } - - public Task ExecuteAsync(long itemId) - { - return _hardDeleteEntityCommand.ExecuteAsync(itemId); - } -} diff --git a/Application/Commands/HardDeleteItemTypeCommand.cs b/Application/Commands/HardDeleteItemTypeCommand.cs deleted file mode 100644 index 4d1ed21..0000000 --- a/Application/Commands/HardDeleteItemTypeCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Core.Entities; - -namespace Application.Commands; - -public class HardDeleteItemTypeCommand -{ - private readonly HardDeleteEntityCommand _hardDeleteEntityCommand; - - public HardDeleteItemTypeCommand(TenantAppDbContext context) - { - _hardDeleteEntityCommand = new HardDeleteEntityCommand(context); - } - - public Task ExecuteAsync(long itemTypeId) - { - return _hardDeleteEntityCommand.ExecuteAsync(itemTypeId); - } -} diff --git a/Application/Commands/HardDeleteItemTypeCommandTests.cs b/Application/Commands/HardDeleteItemTypeCommandTests.cs deleted file mode 100644 index 4181362..0000000 --- a/Application/Commands/HardDeleteItemTypeCommandTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Core.Entities; -using Microsoft.EntityFrameworkCore; -using Xunit; - -namespace Application.Commands; - -public class HardDeleteItemTypeCommandTests -{ - [Fact] - public async Task DeleteItemTypeThatHasRelatedItem_ShouldDeleteItemAsWell() - { - var context = TenantAppDbContextExtensionsTestsRelated.CteateInMemoryTenantContextForTests(); - - await context.AddEntityAndSaveAsync(new ItemType - { - Id = 1 - }); - - await context.AddEntityAndSaveAsync(new Item - { - Id = 2, - ItemTypeId = 1 - }); - - var deleteItemTypeCommand = new HardDeleteItemTypeCommand(context); - - await deleteItemTypeCommand.ExecuteAsync(1); - - var itemDoesNotExist = await context - .Items - .AllAsync(x => x.Id != 2); - - Assert.True(itemDoesNotExist); - } -} diff --git a/Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs b/Application/ExternalDeps/EmployeesApi/EmployeesResponse.cs similarity index 86% rename from Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs rename to Application/ExternalDeps/EmployeesApi/EmployeesResponse.cs index 411123a..fe9b1e8 100644 --- a/Api/ExternalDeps/EmployeesApi/Responses/EmployeesResponse.cs +++ b/Application/ExternalDeps/EmployeesApi/EmployeesResponse.cs @@ -1,4 +1,4 @@ -namespace Api.ExternalDeps.EmployeesApi.Responses; +namespace Application.ExternalDeps.EmployeesApi; public class EmployeesResponse { diff --git a/Application/ExternalDeps/EmployeesApi/IEmployeesApi.cs b/Application/ExternalDeps/EmployeesApi/IEmployeesApi.cs new file mode 100644 index 0000000..ef0eeed --- /dev/null +++ b/Application/ExternalDeps/EmployeesApi/IEmployeesApi.cs @@ -0,0 +1,6 @@ +namespace Application.ExternalDeps.EmployeesApi; + +public interface IEmployeesApi +{ + Task GetAllEmployeesAsync(); +} diff --git a/Application/Commands/CreateItemTypeCommand.cs b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeCommand.cs similarity index 69% rename from Application/Commands/CreateItemTypeCommand.cs rename to Application/Features/ItemTypes/CreateItemType/CreateItemTypeCommand.cs index fbcf765..4c89a2b 100644 --- a/Application/Commands/CreateItemTypeCommand.cs +++ b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeCommand.cs @@ -1,11 +1,6 @@ using Core.Entities; -namespace Application.Commands; - -public class CreateItemTypeCommandParams -{ - public required string Name { get; set; } -} +namespace Application.Features.ItemTypes.CreateItemType; public class CreateItemTypeCommand { @@ -21,12 +16,12 @@ IClaimsProvider claimsProvider _claimsProvider = claimsProvider; } - public async Task ExecuteAsync(CreateItemTypeCommandParams createItemTypeCommandParams) + public async Task ExecuteAsync(CreateItemTypeRequest createItemTypeRequest) { var itemType = new ItemType { TenantId = _claimsProvider.TenantId, - Name = createItemTypeCommandParams.Name + Name = createItemTypeRequest.Name }; await _context diff --git a/Application/Features/ItemTypes/CreateItemType/CreateItemTypeHandler.cs b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeHandler.cs new file mode 100644 index 0000000..7f0e2db --- /dev/null +++ b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeHandler.cs @@ -0,0 +1,23 @@ +namespace Application.Features.ItemTypes.CreateItemType; + +public class CreateItemTypeHandler +{ + private readonly CreateItemTypeCommand _createItemTypeCommand; + + public CreateItemTypeHandler( + CreateItemTypeCommand createItemTypeCommand + ) + { + _createItemTypeCommand = createItemTypeCommand; + } + + public async Task HandleAsync(CreateItemTypeRequest createItemTypeRequest) + { + var newItemTypeId = await _createItemTypeCommand.ExecuteAsync(createItemTypeRequest); + + return new CreateItemTypeResponse() + { + NewItemTypeId = newItemTypeId + }; + } +} diff --git a/Api/Requests/CreateItemTypeRequest.cs b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeRequest.cs similarity index 74% rename from Api/Requests/CreateItemTypeRequest.cs rename to Application/Features/ItemTypes/CreateItemType/CreateItemTypeRequest.cs index 46643ba..038ff61 100644 --- a/Api/Requests/CreateItemTypeRequest.cs +++ b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeRequest.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Api.Requests; +namespace Application.Features.ItemTypes.CreateItemType; public class CreateItemTypeRequest { diff --git a/Api/Responses/CreateItemTypeResponse.cs b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeResponse.cs similarity index 62% rename from Api/Responses/CreateItemTypeResponse.cs rename to Application/Features/ItemTypes/CreateItemType/CreateItemTypeResponse.cs index 81ab6ca..1634dc7 100644 --- a/Api/Responses/CreateItemTypeResponse.cs +++ b/Application/Features/ItemTypes/CreateItemType/CreateItemTypeResponse.cs @@ -1,4 +1,4 @@ -namespace Api.Responses; +namespace Application.Features.ItemTypes.CreateItemType; public class CreateItemTypeResponse { diff --git a/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesHandler.cs b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesHandler.cs new file mode 100644 index 0000000..d7e5d0f --- /dev/null +++ b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesHandler.cs @@ -0,0 +1,31 @@ +using Application.SharedDtos; + +namespace Application.Features.ItemTypes.GetAllItemTypes; + +public class GetAllItemTypesHandler +{ + private readonly GetAllItemTypesQuery _getAllItemTypesQuery; + + public GetAllItemTypesHandler( + GetAllItemTypesQuery getAllItemTypesQuery + ) + { + _getAllItemTypesQuery = getAllItemTypesQuery; + } + + public async Task HandleAsync() + { + var itemTypes = await _getAllItemTypesQuery.GetAsync(); + + return new GetAllItemTypesResponse + { + ItemTypes = itemTypes + .Select(x => new ItemTypeDto + { + Id = x.Id, + Name = x.Name, + }) + .ToList() + }; + } +} diff --git a/Application/Queries/AllItemTypesQuery.cs b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesQuery.cs similarity index 68% rename from Application/Queries/AllItemTypesQuery.cs rename to Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesQuery.cs index be19827..6be7079 100644 --- a/Application/Queries/AllItemTypesQuery.cs +++ b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesQuery.cs @@ -1,13 +1,13 @@ using Core.Entities; using Microsoft.EntityFrameworkCore; -namespace Application.Queries; +namespace Application.Features.ItemTypes.GetAllItemTypes; -public class AllItemTypesQuery +public class GetAllItemTypesQuery { private readonly TenantAppDbContext _context; - public AllItemTypesQuery(TenantAppDbContext context) + public GetAllItemTypesQuery(TenantAppDbContext context) { _context = context; } diff --git a/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesResponse.cs b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesResponse.cs new file mode 100644 index 0000000..1a25b62 --- /dev/null +++ b/Application/Features/ItemTypes/GetAllItemTypes/GetAllItemTypesResponse.cs @@ -0,0 +1,8 @@ +using Application.SharedDtos; + +namespace Application.Features.ItemTypes.GetAllItemTypes; + +public class GetAllItemTypesResponse +{ + public required List ItemTypes { get; set; } +} diff --git a/Application/Features/ItemTypes/HardDeleteItemType/HardDeleteItemTypeHandler.cs b/Application/Features/ItemTypes/HardDeleteItemType/HardDeleteItemTypeHandler.cs new file mode 100644 index 0000000..2db0768 --- /dev/null +++ b/Application/Features/ItemTypes/HardDeleteItemType/HardDeleteItemTypeHandler.cs @@ -0,0 +1,18 @@ +using Application.SharedCommands; +using Core.Entities; + +namespace Application.Features.ItemTypes.HardDeleteItemType; + +public class HardDeleteItemTypeHandler +{ + private readonly HardDeleteEntityCommand _hardDeleteEntityCommand; + + public HardDeleteItemTypeHandler(TenantAppDbContext context) + { + _hardDeleteEntityCommand = new HardDeleteEntityCommand(context); + } + public Task HandleAsync(long itemTypeId) + { + return _hardDeleteEntityCommand.ExecuteAsync(itemTypeId); + } +} diff --git a/Application/Features/Items/CreateItem/CreateItemCommand.cs b/Application/Features/Items/CreateItem/CreateItemCommand.cs new file mode 100644 index 0000000..a92fddf --- /dev/null +++ b/Application/Features/Items/CreateItem/CreateItemCommand.cs @@ -0,0 +1,51 @@ +using Core.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Application.Features.Items.CreateItem; + +public class CreateItemCommand +{ + private readonly TenantAppDbContext _context; + private readonly IClaimsProvider _claimsProvider; + + public CreateItemCommand( + TenantAppDbContext context, + IClaimsProvider claimsProvider + ) + { + _context = context; + _claimsProvider = claimsProvider; + } + + public async Task ExecuteAsync(CreateItemRequest createItemRequest) + { + var itemTypeIdDoesNotExistWithinTenant = await _context + .QueryableWithinTenant() + .AllAsync(x => x.Id != createItemRequest.ItemTypeId); + + if (itemTypeIdDoesNotExistWithinTenant) + { + throw new Exception($"Passed item type where id={createItemRequest.ItemTypeId} is not found within tenant where id={_claimsProvider.TenantId}"); + } + + var item = new Item + { + TenantId = _claimsProvider.TenantId, + Name = createItemRequest.Name, + SerialNumber = createItemRequest.SerialNumber, + ItemTypeId = createItemRequest.ItemTypeId, + Price = createItemRequest.Price, + Description = createItemRequest.Description, + PurchaseDate = createItemRequest.PurchaseDate, + HolderEmployeeId = createItemRequest.HolderEmployeeId + }; + + await _context + .Items + .AddAsync(item); + + await _context.SaveChangesAsync(); + + return item.Id; + } +} diff --git a/Application/Features/Items/CreateItem/CreateItemHandler.cs b/Application/Features/Items/CreateItem/CreateItemHandler.cs new file mode 100644 index 0000000..fcb4925 --- /dev/null +++ b/Application/Features/Items/CreateItem/CreateItemHandler.cs @@ -0,0 +1,21 @@ +namespace Application.Features.Items.CreateItem; + +public class CreateItemHandler +{ + private readonly CreateItemCommand _createItemCommand; + + public CreateItemHandler( + CreateItemCommand createItemCommand + ) + { + _createItemCommand = createItemCommand; + } + + public async Task HandleAsync(CreateItemRequest createItemRequest) + { + return new CreateItemResponse + { + NewItemId = await _createItemCommand.ExecuteAsync(createItemRequest) + }; + } +} diff --git a/Api/Features/Items/CreateItem/CreateItemRequest.cs b/Application/Features/Items/CreateItem/CreateItemRequest.cs similarity index 91% rename from Api/Features/Items/CreateItem/CreateItemRequest.cs rename to Application/Features/Items/CreateItem/CreateItemRequest.cs index bebc464..278a963 100644 --- a/Api/Features/Items/CreateItem/CreateItemRequest.cs +++ b/Application/Features/Items/CreateItem/CreateItemRequest.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Api.Features.Items.CreateItem; +namespace Application.Features.Items.CreateItem; public class CreateItemRequest { diff --git a/Api/Features/Items/CreateItem/CreateItemResponse.cs b/Application/Features/Items/CreateItem/CreateItemResponse.cs similarity index 63% rename from Api/Features/Items/CreateItem/CreateItemResponse.cs rename to Application/Features/Items/CreateItem/CreateItemResponse.cs index 40467ea..0ef53b9 100644 --- a/Api/Features/Items/CreateItem/CreateItemResponse.cs +++ b/Application/Features/Items/CreateItem/CreateItemResponse.cs @@ -1,4 +1,4 @@ -namespace Api.Features.Items.CreateItem; +namespace Application.Features.Items.CreateItem; public class CreateItemResponse { diff --git a/Api/Features/Items/GetAllItems/GetAllItemsHandler.cs b/Application/Features/Items/GetAllItems/GetAllItemsHandler.cs similarity index 71% rename from Api/Features/Items/GetAllItems/GetAllItemsHandler.cs rename to Application/Features/Items/GetAllItems/GetAllItemsHandler.cs index 4d20ba4..722f066 100644 --- a/Api/Features/Items/GetAllItems/GetAllItemsHandler.cs +++ b/Application/Features/Items/GetAllItems/GetAllItemsHandler.cs @@ -1,29 +1,28 @@ -using Api.ExternalDeps.EmployeesApi; -using Api.Responses; -using Application.Queries; +using Application.ExternalDeps.EmployeesApi; +using Application.SharedDtos; -namespace Api.Features.Items.GetAllItems; +namespace Application.Features.Items.GetAllItems; public class GetAllItemsHandler { - private readonly AllItemsQuery _allItemsQuery; - private readonly EmployeesApi _employeesApi; + private readonly IEmployeesApi _employeesApi; + private readonly GetAllItemsQuery _getAllItemsQuery; public GetAllItemsHandler( - AllItemsQuery allItemsQuery, - EmployeesApi employeesApi + IEmployeesApi employeesApi, + GetAllItemsQuery getAllItemsQuery ) { - _allItemsQuery = allItemsQuery; _employeesApi = employeesApi; + _getAllItemsQuery = getAllItemsQuery; } public async Task HandleAsync() { - var items = await _allItemsQuery.GetAsync(); - var allEmployeesResponse = await _employeesApi.GetAllEmployeesAsync(); + var items = await _getAllItemsQuery.GetAsync(); + return new GetAllItemsResponse { Items = items diff --git a/Application/Queries/AllItemsQuery.cs b/Application/Features/Items/GetAllItems/GetAllItemsQuery.cs similarity index 72% rename from Application/Queries/AllItemsQuery.cs rename to Application/Features/Items/GetAllItems/GetAllItemsQuery.cs index cc273bb..24c314e 100644 --- a/Application/Queries/AllItemsQuery.cs +++ b/Application/Features/Items/GetAllItems/GetAllItemsQuery.cs @@ -1,13 +1,13 @@ using Core.Entities; using Microsoft.EntityFrameworkCore; -namespace Application.Queries; +namespace Application.Features.Items.GetAllItems; -public class AllItemsQuery +public class GetAllItemsQuery { private readonly TenantAppDbContext _context; - public AllItemsQuery(TenantAppDbContext context) + public GetAllItemsQuery(TenantAppDbContext context) { _context = context; } diff --git a/Api/Features/Items/GetAllItems/GetAllItemsResponse.cs b/Application/Features/Items/GetAllItems/GetAllItemsResponse.cs similarity index 89% rename from Api/Features/Items/GetAllItems/GetAllItemsResponse.cs rename to Application/Features/Items/GetAllItems/GetAllItemsResponse.cs index 207971d..5ba99dd 100644 --- a/Api/Features/Items/GetAllItems/GetAllItemsResponse.cs +++ b/Application/Features/Items/GetAllItems/GetAllItemsResponse.cs @@ -1,6 +1,6 @@ -using Api.Responses; +using Application.SharedDtos; -namespace Api.Features.Items.GetAllItems; +namespace Application.Features.Items.GetAllItems; public class GetAllItemsResponse { diff --git a/Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs b/Application/Features/Items/GetAllItems/HolderEmployeeMapper.cs similarity index 86% rename from Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs rename to Application/Features/Items/GetAllItems/HolderEmployeeMapper.cs index 0c3eb12..ccc4827 100644 --- a/Api/Features/Items/GetAllItems/HolderEmployeeMapper.cs +++ b/Application/Features/Items/GetAllItems/HolderEmployeeMapper.cs @@ -1,6 +1,6 @@ -using Api.ExternalDeps.EmployeesApi.Responses; +using Application.ExternalDeps.EmployeesApi; -namespace Api.Features.Items.GetAllItems; +namespace Application.Features.Items.GetAllItems; public class HolderEmployeeMapper { diff --git a/Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs b/Application/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs similarity index 86% rename from Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs rename to Application/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs index af563f9..2c7b68e 100644 --- a/Api/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs +++ b/Application/Features/Items/GetAllItems/HolderEmployeeMapperTests.cs @@ -1,7 +1,7 @@ -using Api.ExternalDeps.EmployeesApi.Responses; +using Application.ExternalDeps.EmployeesApi; using Xunit; -namespace Api.Features.Items.GetAllItems; +namespace Application.Features.Items.GetAllItems; public class HolderEmployeeMapperTests { diff --git a/Application/Features/Items/HardDeleteItem/HardDeleteItemHandler.cs b/Application/Features/Items/HardDeleteItem/HardDeleteItemHandler.cs new file mode 100644 index 0000000..c2fead4 --- /dev/null +++ b/Application/Features/Items/HardDeleteItem/HardDeleteItemHandler.cs @@ -0,0 +1,19 @@ +using Application.SharedCommands; +using Core.Entities; + +namespace Application.Features.Items.HardDeleteItem; + +public class HardDeleteItemHandler +{ + private readonly HardDeleteEntityCommand _hardDeleteEntityCommand; + + public HardDeleteItemHandler(TenantAppDbContext context) + { + _hardDeleteEntityCommand = new HardDeleteEntityCommand(context); + } + + public Task HandleAsync(long itemId) + { + return _hardDeleteEntityCommand.ExecuteAsync(itemId); + } +} diff --git a/Application/Commands/HardDeleteEntityCommand.cs b/Application/SharedCommands/HardDeleteEntityCommand.cs similarity index 94% rename from Application/Commands/HardDeleteEntityCommand.cs rename to Application/SharedCommands/HardDeleteEntityCommand.cs index 7699f12..52be655 100644 --- a/Application/Commands/HardDeleteEntityCommand.cs +++ b/Application/SharedCommands/HardDeleteEntityCommand.cs @@ -1,7 +1,7 @@ using Core; using Microsoft.EntityFrameworkCore; -namespace Application.Commands; +namespace Application.SharedCommands; public class HardDeleteEntityCommand { diff --git a/Application/Commands/HardDeleteEntityCommandTests.cs b/Application/SharedCommands/HardDeleteEntityCommandTests.cs similarity index 68% rename from Application/Commands/HardDeleteEntityCommandTests.cs rename to Application/SharedCommands/HardDeleteEntityCommandTests.cs index 615a0fa..1a2cb0a 100644 --- a/Application/Commands/HardDeleteEntityCommandTests.cs +++ b/Application/SharedCommands/HardDeleteEntityCommandTests.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; using Xunit; -namespace Application.Commands; +namespace Application.SharedCommands; public class HardDeleteEntityCommandTests { @@ -52,4 +52,31 @@ await context.AddEntityAndSaveAsync(new Item Assert.Null(await Record.ExceptionAsync(async () => wasNonExistedDeleted = await hardDeleteEntityCommand.ExecuteAsync(2))); Assert.False(wasNonExistedDeleted); } + + [Fact] + public async Task DeleteItemTypeThatHasRelatedItem_ShouldDeleteItemAsWell() + { + var context = TenantAppDbContextExtensionsTestsRelated.CteateInMemoryTenantContextForTests(); + + await context.AddEntityAndSaveAsync(new ItemType + { + Id = 1 + }); + + await context.AddEntityAndSaveAsync(new Item + { + Id = 2, + ItemTypeId = 1 + }); + + var hardDeleteEntityCommand = new HardDeleteEntityCommand(context); + + await hardDeleteEntityCommand.ExecuteAsync(1); + + var itemDoesNotExist = await context + .Items + .AllAsync(x => x.Id != 2); + + Assert.True(itemDoesNotExist); + } } diff --git a/Api/Responses/ItemTypeDto.cs b/Application/SharedDtos/ItemTypeDto.cs similarity index 76% rename from Api/Responses/ItemTypeDto.cs rename to Application/SharedDtos/ItemTypeDto.cs index e5729e4..f1d8d2b 100644 --- a/Api/Responses/ItemTypeDto.cs +++ b/Application/SharedDtos/ItemTypeDto.cs @@ -1,4 +1,4 @@ -namespace Api.Responses; +namespace Application.SharedDtos; public class ItemTypeDto { diff --git a/Application/TenantAppDbContextExtensionsTestsRelated.cs b/Application/TenantAppDbContextExtensionsTestsRelated.cs index cd14e65..30a431d 100644 --- a/Application/TenantAppDbContextExtensionsTestsRelated.cs +++ b/Application/TenantAppDbContextExtensionsTestsRelated.cs @@ -13,7 +13,7 @@ public static TenantAppDbContext CteateInMemoryTenantContextForTests() .UseInMemoryDatabase( // we need a unique db name so that tests of the same collection can run in isolation // otherwise they inrefere and see each others data - new Random(100500).Next().ToString(), + new Random().Next().ToString(), // we want to provide as little setup data as possible to check a certain piece of a flow // thus, we don't want to specify all properties of seeded data when it isn't used by the logic // for instance, I need to check that an entity exists by Id, I don't need to setup its required Name property diff --git a/README.md b/README.md index 4e07e78..b8a63d7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # inner-circle-items-api -[![coverage](https://img.shields.io/badge/e2e_coverage-82.81%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-16.77%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-90.30%25-forestgreen)](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.79%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-16.18%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-90.29%25-forestgreen)](https://github.com/TourmalineCore/inner-circle-items-api/actions/workflows/calculate-tests-coverage-on-pull-request.yml) This repo contains Inner Circle Items API. diff --git a/js-client/index.ts b/js-client/index.ts index 016b5d9..777362c 100644 --- a/js-client/index.ts +++ b/js-client/index.ts @@ -52,6 +52,10 @@ export interface GetAllItemsResponse { items: ItemDto[]; } +export interface GetAllItemTypesResponse { + itemTypes: ItemTypeDto[]; +} + export interface ItemDto { /** @format int64 */ id: number; @@ -72,10 +76,6 @@ export interface ItemTypeDto { name: string; } -export interface ItemTypesResponse { - itemTypes: ItemTypeDto[]; -} - import type { AxiosInstance, AxiosRequestConfig, @@ -253,7 +253,7 @@ export class HttpClient { /** * @title inner-circle-items-api - * @version 1.0.8 + * @version 1.0.9 * @baseUrl http://localhost:6501/ */ export class Api< @@ -263,13 +263,13 @@ export class Api< /** * No description * - * @tags Items - * @name ItemsGetAllItems - * @request GET:/api/items + * @tags ItemTypes + * @name ItemTypesGetAllItemTypes + * @request GET:/api/item-types */ - itemsGetAllItems: (params: RequestParams = {}) => - this.request({ - path: `/api/items`, + itemTypesGetAllItemTypes: (params: RequestParams = {}) => + this.request({ + path: `/api/item-types`, method: "GET", format: "json", ...params, @@ -278,13 +278,16 @@ export class Api< /** * No description * - * @tags Items - * @name ItemsCreateItem - * @request POST:/api/items + * @tags ItemTypes + * @name ItemTypesCreateItemType + * @request POST:/api/item-types */ - itemsCreateItem: (data: CreateItemRequest, params: RequestParams = {}) => - this.request({ - path: `/api/items`, + itemTypesCreateItemType: ( + data: CreateItemTypeRequest, + params: RequestParams = {}, + ) => + this.request({ + path: `/api/item-types`, method: "POST", body: data, type: ContentType.Json, @@ -295,13 +298,16 @@ export class Api< /** * No description * - * @tags Items - * @name ItemsHardDeleteItem - * @request DELETE:/api/items/{itemId}/hard-delete + * @tags ItemTypes + * @name ItemTypesHardDeleteItemType + * @request DELETE:/api/item-types/{itemTypeId}/hard-delete */ - itemsHardDeleteItem: (itemId: number, params: RequestParams = {}) => + itemTypesHardDeleteItemType: ( + itemTypeId: number, + params: RequestParams = {}, + ) => this.request({ - path: `/api/items/${itemId}/hard-delete`, + path: `/api/item-types/${itemTypeId}/hard-delete`, method: "DELETE", ...params, }), @@ -309,13 +315,13 @@ export class Api< /** * No description * - * @tags ItemTypes - * @name ItemTypesGetAllItemTypes - * @request GET:/api/item-types + * @tags Items + * @name ItemsGetAllItems + * @request GET:/api/items */ - itemTypesGetAllItemTypes: (params: RequestParams = {}) => - this.request({ - path: `/api/item-types`, + itemsGetAllItems: (params: RequestParams = {}) => + this.request({ + path: `/api/items`, method: "GET", format: "json", ...params, @@ -324,16 +330,13 @@ export class Api< /** * No description * - * @tags ItemTypes - * @name ItemTypesCreateItemType - * @request POST:/api/item-types + * @tags Items + * @name ItemsCreateItem + * @request POST:/api/items */ - itemTypesCreateItemType: ( - data: CreateItemTypeRequest, - params: RequestParams = {}, - ) => - this.request({ - path: `/api/item-types`, + itemsCreateItem: (data: CreateItemRequest, params: RequestParams = {}) => + this.request({ + path: `/api/items`, method: "POST", body: data, type: ContentType.Json, @@ -344,16 +347,13 @@ export class Api< /** * No description * - * @tags ItemTypes - * @name ItemTypesHardDeleteItemType - * @request DELETE:/api/item-types/{itemTypeId}/hard-delete + * @tags Items + * @name ItemsHardDeleteItem + * @request DELETE:/api/items/{itemId}/hard-delete */ - itemTypesHardDeleteItemType: ( - itemTypeId: number, - params: RequestParams = {}, - ) => + itemsHardDeleteItem: (itemId: number, params: RequestParams = {}) => this.request({ - path: `/api/item-types/${itemTypeId}/hard-delete`, + path: `/api/items/${itemId}/hard-delete`, method: "DELETE", ...params, }),