diff --git a/docker-compose.test.yml b/docker-compose.test.yml index d7d44b92..993b5ad1 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -8,7 +8,7 @@ services: ports: - '8888:80' environment: - - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_ENVIRONMENT=Testing - PROFILE_DatabaseSettings__ConnectionString=Server=database;Port=5432;Database=mytestdb;User ID=profiletester;Password=supasupasecured; depends_on: database: diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index 09720f26..11e16ecd 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -4,6 +4,7 @@ net6.0 enable enable + true diff --git a/src/Api/ConfigureServices.cs b/src/Api/ConfigureServices.cs index 6dea0c7d..39dabb10 100644 --- a/src/Api/ConfigureServices.cs +++ b/src/Api/ConfigureServices.cs @@ -1,6 +1,8 @@ +using System.Reflection; using Api.Middlewares; using Api.Policies; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.OpenApi.Models; namespace Api; @@ -31,7 +33,18 @@ public static IServiceCollection AddApiServices(this IServiceCollection services // For swagger services.AddEndpointsApiExplorer(); - services.AddSwaggerGen(); + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "ProFile API", + Description = "An ASP.NET Core Web API for managing documents", + }); + + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + }); return services; } diff --git a/src/Api/Controllers/AuthController.cs b/src/Api/Controllers/AuthController.cs index efdd1591..a770a036 100644 --- a/src/Api/Controllers/AuthController.cs +++ b/src/Api/Controllers/AuthController.cs @@ -1,6 +1,6 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Authentication; -using Api.Controllers.Payload.Requests; +using Api.Controllers.Payload.Requests.Auth; using Api.Controllers.Payload.Responses; using Application.Common.Interfaces; using Application.Common.Models; @@ -23,6 +23,11 @@ public AuthController(IIdentityService identityService) _identityService = identityService; } + /// + /// Login + /// + /// Login credentials + /// A LoginResult indicating the result of logging in [AllowAnonymous] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] @@ -48,23 +53,11 @@ public async Task>> Login([FromBody] LoginModel return Ok(Result.Succeed(loginResult)); } - - [HttpPost] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task Logout() - { - var refreshToken = Request.Cookies[nameof(RefreshToken)]; - var jweToken = Request.Cookies["JweToken"]; - - RemoveJweToken(); - RemoveRefreshToken(); - - await _identityService.LogoutAsync(jweToken!, refreshToken!); - - return Ok(); - } - + + /// + /// Refresh session and token + /// + /// An IActionResult indicating the result of refreshing token [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -90,6 +83,10 @@ public async Task Refresh() return Ok(); } + /// + /// Validate current user + /// + /// An IActionResult indicating the result of validating the user [Authorize] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] @@ -99,11 +96,32 @@ public IActionResult Validate() return Ok(); } + /// + /// Logout of the system + /// + /// An IActionResult indicating the result of logging out of the system + [HttpPost] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task Logout() + { + var refreshToken = Request.Cookies[nameof(RefreshToken)]; + var jweToken = Request.Cookies["JweToken"]; + + RemoveJweToken(); + RemoveRefreshToken(); + + await _identityService.LogoutAsync(jweToken!, refreshToken!); + + return Ok(); + } + private void SetJweToken(SecurityToken jweToken, RefreshTokenDto newRefreshToken) { var cookieOptions = new CookieOptions { HttpOnly = true, + Expires = newRefreshToken.ExpiryDateTime }; var handler = new JwtSecurityTokenHandler(); Response.Cookies.Append("JweToken", handler.WriteToken(jweToken), cookieOptions); diff --git a/src/Api/Controllers/DepartmentsController.cs b/src/Api/Controllers/DepartmentsController.cs index 98045cf7..bf01c28a 100644 --- a/src/Api/Controllers/DepartmentsController.cs +++ b/src/Api/Controllers/DepartmentsController.cs @@ -1,10 +1,8 @@ -using Api.Controllers.Payload.Requests; +using Api.Controllers.Payload.Requests.Departments; using Application.Common.Models; -using Application.Departments.Commands.DeleteDepartment; -using Application.Departments.Commands.UpdateDepartment; -using Application.Departments.Commands.AddDepartment; -using Application.Departments.Queries.GetAllDepartments; -using Application.Departments.Queries.GetDepartmentById; +using Application.Common.Models.Dtos; +using Application.Departments.Commands; +using Application.Departments.Queries; using Application.Identity; using Application.Users.Queries; using Infrastructure.Identity.Authorization; @@ -15,52 +13,57 @@ namespace Api.Controllers; public class DepartmentsController : ApiControllerBase { /// - /// Create a department + /// Get back a department based on its id /// - /// command parameter to create a department - /// Result[DepartmentDto] - [RequiresRole(IdentityData.Roles.Admin)] - [HttpPost] + /// id of the department to be retrieved + /// A DepartmentDto of the retrieved department + [HttpGet("{departmentId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> AddDepartment([FromBody] AddDepartmentCommand command) + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task>> GetById([FromRoute] Guid departmentId) { - var result = await Mediator.Send(command); + var query = new GetDepartmentById.Query() + { + DepartmentId = departmentId + }; + var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); } - + /// /// Get all documents /// - /// a Result of an IEnumerable of DepartmentDto + /// A list of DocumentDto [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task>>> GetAllDepartments() + public async Task>>> GetAll() { - var result = await Mediator.Send(new GetAllDepartmentsQuery()); + var result = await Mediator.Send(new GetAllDepartments.Query()); return Ok(Result>.Succeed(result)); } /// - /// Get back a department based on its id + /// Add a department /// - /// id of the department to be retrieved - /// A DepartmentDto of the retrieved department - [HttpGet("{departmentId:guid}")] + /// Add department details + /// A DepartmentDto of the the added department + [RequiresRole(IdentityData.Roles.Admin)] + [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task>> GetDepartmentById([FromRoute] Guid departmentId) + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task>> Add([FromBody] AddDepartmentRequest request) { - var query = new GetDepartmentByIdQuery() + var command = new AddDepartment.Command() { - DepartmentId = departmentId + Name = request.Name, }; - var result = await Mediator.Send(query); + var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + /// /// Update a department /// @@ -72,9 +75,9 @@ public async Task>> GetDepartmentById([FromRo [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> UpdateDepartment([FromRoute] Guid departmentId, [FromBody] UpdateDepartmentRequest request) + public async Task>> Update([FromRoute] Guid departmentId, [FromBody] UpdateDepartmentRequest request) { - var command = new UpdateDepartmentCommand() + var command = new UpdateDepartment.Command() { DepartmentId = departmentId, Name = request.Name @@ -93,9 +96,9 @@ public async Task>> UpdateDepartment([FromRou [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> DeleteDepartment([FromRoute] Guid departmentId) + public async Task>> Delete([FromRoute] Guid departmentId) { - var command = new DeleteDepartmentCommand() + var command = new DeleteDepartment.Command() { DepartmentId = departmentId, }; diff --git a/src/Api/Controllers/DocumentsController.cs b/src/Api/Controllers/DocumentsController.cs index 41f8fb3d..5c56986b 100644 --- a/src/Api/Controllers/DocumentsController.cs +++ b/src/Api/Controllers/DocumentsController.cs @@ -1,12 +1,8 @@ using Api.Controllers.Payload.Requests.Documents; using Application.Common.Models; using Application.Common.Models.Dtos.Physical; -using Application.Documents.Commands.DeleteDocument; -using Application.Documents.Commands.ImportDocument; -using Application.Documents.Commands.UpdateDocument; -using Application.Documents.Queries.GetAllDocumentsPaginated; -using Application.Documents.Queries.GetDocumentById; -using Application.Documents.Queries.GetDocumentTypes; +using Application.Documents.Commands; +using Application.Documents.Queries; using Application.Identity; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; @@ -15,65 +11,94 @@ namespace Api.Controllers; public class DocumentsController : ApiControllerBase { - [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpPost] + /// + /// Get a document by id + /// + /// Id of the document to be retrieved + /// A DocumentDto of the retrieved document + [HttpGet("{documentId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> ImportDocument([FromBody] ImportDocumentCommand command) + public async Task>> GetById([FromRoute] Guid documentId) { - var result = await Mediator.Send(command); + var query = new GetDocumentById.Query() + { + DocumentId = documentId, + }; + var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); } - [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpGet("types")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task>>> GetAllDocumentTypes() - { - var result = await Mediator.Send(new GetAllDocumentTypesQuery()); - return Ok(Result>.Succeed(result)); - } - + /// + /// Get all documents paginated + /// + /// Get all documents query parameters + /// A paginated list of DocumentDto [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>>> GetAllDocuments(Guid? roomId, Guid? lockerId, Guid? folderId, int? page, int? size, string? sortBy, string? sortOrder) + public async Task>>> GetAllPaginated( + [FromQuery] GetAllDocumentsPaginatedQueryParameters queryParameters) { - var query = new GetAllDocumentsPaginatedQuery() + var query = new GetAllDocumentsPaginated.Query() { - RoomId = roomId, - LockerId = lockerId, - FolderId = folderId, - Page = page, - Size = size, - SortBy = sortBy, - SortOrder = sortOrder + RoomId = queryParameters.RoomId, + LockerId = queryParameters.LockerId, + FolderId = queryParameters.FolderId, + SearchTerm = queryParameters.SearchTerm, + Page = queryParameters.Page, + Size = queryParameters.Size, + SortBy = queryParameters.SortBy, + SortOrder = queryParameters.SortOrder, }; var result = await Mediator.Send(query); return Ok(Result>.Succeed(result)); } - [HttpGet("{id:guid}")] + /// + /// Get all document types + /// + /// A list of document types + [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] + [HttpGet("types")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task>>> GetAllDocumentTypes() + { + var result = await Mediator.Send(new GetAllDocumentTypes.Query()); + return Ok(Result>.Succeed(result)); + } + + /// + /// Import a document + /// + /// Import document details + /// A DocumentDto of the imported document + [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] + [HttpPost] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetDocumentById(Guid id) + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task>> Import([FromBody] ImportDocumentRequest request) { - var query = new GetDocumentByIdQuery() + var command = new ImportDocument.Command() { - Id = id + Title = request.Title, + Description = request.Description, + DocumentType = request.DocumentType, + FolderId = request.FolderId, + ImporterId = request.ImporterId, }; - var result = await Mediator.Send(query); + var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + /// /// Update a document /// @@ -86,9 +111,9 @@ public async Task>> GetDocumentById(Guid id) [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> UpdateDocument([FromRoute] Guid documentId, [FromBody] UpdateDocumentRequest request) + public async Task>> Update([FromRoute] Guid documentId, [FromBody] UpdateDocumentRequest request) { - var query = new UpdateDocumentCommand() + var query = new UpdateDocument.Command() { DocumentId = documentId, Title = request.Title, @@ -98,7 +123,7 @@ public async Task>> UpdateDocument([FromRoute] var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); } - + /// /// Delete a document /// @@ -108,9 +133,9 @@ public async Task>> UpdateDocument([FromRoute] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> UpdateDocument([FromRoute] Guid documentId) + public async Task>> Delete([FromRoute] Guid documentId) { - var query = new DeleteDocumentCommand() + var query = new DeleteDocument.Command() { DocumentId = documentId, }; diff --git a/src/Api/Controllers/FoldersController.cs b/src/Api/Controllers/FoldersController.cs index 8ea03dd6..3365ec81 100644 --- a/src/Api/Controllers/FoldersController.cs +++ b/src/Api/Controllers/FoldersController.cs @@ -1,13 +1,8 @@ using Api.Controllers.Payload.Requests.Folders; using Application.Common.Models; using Application.Common.Models.Dtos.Physical; -using Application.Folders.Commands.AddFolder; -using Application.Folders.Commands.DisableFolder; -using Application.Folders.Commands.EnableFolder; -using Application.Folders.Commands.RemoveFolder; -using Application.Folders.Commands.UpdateFolder; -using Application.Folders.Queries.GetAllFoldersPaginated; -using Application.Folders.Queries.GetFolderById; +using Application.Folders.Commands; +using Application.Folders.Queries; using Application.Identity; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; @@ -28,9 +23,9 @@ public class FoldersController : ApiControllerBase [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetById([FromRoute] Guid folderId) { - var query = new GetFolderByIdQuery() + var query = new GetFolderById.Query() { - FolderId = folderId + FolderId = folderId, }; var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); @@ -39,7 +34,7 @@ public async Task>> GetById([FromRoute] Guid fold /// /// Get all folders paginated /// - /// Get all folders query parameters + /// Get all folders paginated query parameters /// A paginated list of FolderDto [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] [HttpGet] @@ -48,7 +43,7 @@ public async Task>> GetById([FromRoute] Guid fold public async Task>>> GetAllPaginated( [FromQuery] GetAllFoldersPaginatedQueryParameters queryParameters) { - var query = new GetAllFoldersPaginatedQuery() + var query = new GetAllFoldersPaginated.Query() { RoomId = queryParameters.RoomId, LockerId = queryParameters.LockerId, @@ -64,7 +59,7 @@ public async Task>>> GetAllPaginate /// /// Add a folder /// - /// Add folder details + /// Add folder details /// A FolderDto of the added folder [RequiresRole(IdentityData.Roles.Admin)] [HttpPost] @@ -73,8 +68,15 @@ public async Task>>> GetAllPaginate [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> AddFolder([FromBody] AddFolderCommand command) + public async Task>> AddFolder([FromBody] AddFolderRequest request) { + var command = new AddFolder.Command() + { + Name = request.Name, + Description = request.Description, + Capacity = request.Capacity, + LockerId = request.LockerId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } @@ -93,7 +95,7 @@ public async Task>> AddFolder([FromBody] AddFolde [ProducesResponseType(StatusCodes.Status409Conflict)] public async Task>> RemoveFolder([FromRoute] Guid folderId) { - var command = new RemoveFolderCommand() + var command = new RemoveFolder.Command() { FolderId = folderId, }; @@ -104,17 +106,21 @@ public async Task>> RemoveFolder([FromRoute] Guid /// /// Enable a folder /// - /// Enable folder details + /// Id of the folder to be enabled /// A FolderDto of the enabled folder [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpPut("enable")] - [ProducesResponseType(StatusCodes.Status200OK)] + [HttpPut("enable/{folderId:guid}")] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> EnableFolder([FromBody] EnableFolderCommand command) + public async Task>> EnableFolder([FromRoute] Guid folderId) { + var command = new EnableFolder.Command() + { + FolderId = folderId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } @@ -122,17 +128,21 @@ public async Task>> EnableFolder([FromBody] Enabl /// /// Disable a folder /// - /// Disable folder details + /// Id of the disabled folder /// A FolderDto of the disabled folder [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpPut("disable")] + [HttpPut("disable/{folderId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> DisableFolder([FromBody] DisableFolderCommand command) + public async Task>> DisableFolder([FromRoute] Guid folderId) { + var command = new DisableFolder.Command() + { + FolderId = folderId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } @@ -150,12 +160,12 @@ public async Task>> DisableFolder([FromBody] Disa [ProducesResponseType(StatusCodes.Status409Conflict)] public async Task>> Update([FromRoute] Guid folderId, [FromBody] UpdateFolderRequest request) { - var command = new UpdateFolderCommand() + var command = new UpdateFolder.Command() { FolderId = folderId, Name = request.Name, Description = request.Description, - Capacity = request.Capacity + Capacity = request.Capacity, }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); diff --git a/src/Api/Controllers/LockersController.cs b/src/Api/Controllers/LockersController.cs index f064090e..373a0cee 100644 --- a/src/Api/Controllers/LockersController.cs +++ b/src/Api/Controllers/LockersController.cs @@ -1,15 +1,9 @@ -using Api.Controllers.Payload.Requests; -using Api.Controllers.Payload.Requests.Lockers; +using Api.Controllers.Payload.Requests.Lockers; using Application.Common.Models; using Application.Common.Models.Dtos.Physical; using Application.Identity; -using Application.Lockers.Commands.AddLocker; -using Application.Lockers.Commands.DisableLocker; -using Application.Lockers.Commands.EnableLocker; -using Application.Lockers.Commands.RemoveLocker; -using Application.Lockers.Commands.UpdateLocker; -using Application.Lockers.Queries.GetAllLockersPaginated; -using Application.Lockers.Queries.GetLockerById; +using Application.Lockers.Commands; +using Application.Lockers.Queries; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; @@ -28,9 +22,9 @@ public class LockersController : ApiControllerBase [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetById([FromRoute] Guid lockerId) { - var query = new GetLockerByIdQuery() + var query = new GetLockerById.Query() { - LockerId = lockerId + LockerId = lockerId, }; var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); @@ -39,7 +33,7 @@ public async Task>> GetById([FromRoute] Guid lock /// /// Get all lockers paginated /// - /// Get all lockers query parameters + /// Get all lockers paginated query parameters /// A paginated list of LockerDto [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] @@ -47,7 +41,7 @@ public async Task>> GetById([FromRoute] Guid lock public async Task>>> GetAllPaginated( [FromQuery] GetAllLockersPaginatedQueryParameters queryParameters) { - var query = new GetAllLockersPaginatedQuery() + var query = new GetAllLockersPaginated.Query() { RoomId = queryParameters.RoomId, Page = queryParameters.Page, @@ -59,84 +53,114 @@ public async Task>>> GetAllPaginate return Ok(Result>.Succeed(result)); } - [RequiresRole(IdentityData.Roles.Staff)] + /// + /// Add a locker + /// + /// Add locker details + /// A LockerDto of the added locker + [RequiresRole(IdentityData.Roles.Admin)] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> AddLocker([FromBody] AddLockerCommand command) + public async Task>> Add([FromBody] AddLockerRequest request) { + var command = new AddLocker.Command() + { + Name = request.Name, + Description = request.Description, + Capacity = request.Capacity, + RoomId = request.RoomId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpPut("disable")] + /// + /// Remove a locker + /// + /// Id of the locker to be removed + /// A LockerDto of the removed locker + [HttpDelete("{lockerId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> DisableLocker([FromBody] DisableLockerCommand command) + public async Task>> Remove([FromRoute] Guid lockerId) { + var command = new RemoveLocker.Command() + { + LockerId = lockerId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } + /// + /// Enable a locker + /// + /// Id of the locker to be enabled + /// A LockerDto of the enabled locker [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] - [HttpPut("enable")] + [HttpPut("enable/{lockerId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> EnableLocker([FromBody] EnableLockerCommand command) + public async Task>> Enable([FromRoute] Guid lockerId) { + var command = new EnableLocker.Command() + { + LockerId = lockerId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } /// - /// Update a locker + /// Disable a locker /// - /// Id of the locker to be updated - /// Update locker details - /// A LockerDto of the updated locker - [HttpPut("{lockerId:guid}")] + /// Id of the locker to be disabled + /// A LockerDto of the disabled locker + [RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)] + [HttpPut("disable/{lockerId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> Update([FromRoute] Guid lockerId, [FromBody] UpdateLockerRequest request) + public async Task>> Disable([FromRoute] Guid lockerId) { - var command = new UpdateLockerCommand() + var command = new DisableLocker.Command() { LockerId = lockerId, - Name = request.Name, - Description = request.Description, - Capacity = request.Capacity }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + /// - /// Remove a locker + /// Update a locker /// - /// Id of the locker to be removed - /// A LockerDto of the removed locker - [HttpDelete("{lockerId:guid}")] + /// Id of the locker to be updated + /// Update locker details + /// A LockerDto of the updated locker + [HttpPut("{lockerId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> Remove([FromRoute] Guid lockerId) + public async Task>> Update([FromRoute] Guid lockerId, [FromBody] UpdateLockerRequest request) { - var command = new RemoveLockerCommand() + var command = new UpdateLocker.Command() { - LockerId = lockerId + LockerId = lockerId, + Name = request.Name, + Description = request.Description, + Capacity = request.Capacity, }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); diff --git a/src/Api/Controllers/Payload/Requests/Auth/LoginModel.cs b/src/Api/Controllers/Payload/Requests/Auth/LoginModel.cs new file mode 100644 index 00000000..99f49b03 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Auth/LoginModel.cs @@ -0,0 +1,16 @@ +namespace Api.Controllers.Payload.Requests.Auth; + +/// +/// Login credentials to login +/// +public class LoginModel +{ + /// + /// Email of user + /// + public string Email { get; set; } = null!; + /// + /// Password of user + /// + public string Password { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Auth/RefreshTokenRequest.cs b/src/Api/Controllers/Payload/Requests/Auth/RefreshTokenRequest.cs new file mode 100644 index 00000000..979dc447 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Auth/RefreshTokenRequest.cs @@ -0,0 +1,16 @@ +namespace Api.Controllers.Payload.Requests.Auth; + +/// +/// Request details to refresh token +/// +public class RefreshTokenRequest +{ + /// + /// Access token + /// + public string Token { get; set; } = null!; + /// + /// Refresh token + /// + public string RefreshToken { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Departments/AddDepartmentRequest.cs b/src/Api/Controllers/Payload/Requests/Departments/AddDepartmentRequest.cs new file mode 100644 index 00000000..2f6444f7 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Departments/AddDepartmentRequest.cs @@ -0,0 +1,12 @@ +namespace Api.Controllers.Payload.Requests.Departments; + +/// +/// Request details to add a department +/// +public class AddDepartmentRequest +{ + /// + /// Name of the department to be added + /// + public string Name { get; init; } = null!; +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Departments/UpdateDepartmentRequest.cs b/src/Api/Controllers/Payload/Requests/Departments/UpdateDepartmentRequest.cs new file mode 100644 index 00000000..48ad1052 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Departments/UpdateDepartmentRequest.cs @@ -0,0 +1,12 @@ +namespace Api.Controllers.Payload.Requests.Departments; + +/// +/// Request details to update a department +/// +public class UpdateDepartmentRequest +{ + /// + /// New name of the department to be updated + /// + public string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Documents/GetAllDocumentsPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Documents/GetAllDocumentsPaginatedQueryParameters.cs new file mode 100644 index 00000000..7f134c04 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Documents/GetAllDocumentsPaginatedQueryParameters.cs @@ -0,0 +1,40 @@ +namespace Api.Controllers.Payload.Requests.Documents; + +/// +/// Query parameters for getting all documents with pagination +/// +public class GetAllDocumentsPaginatedQueryParameters +{ + /// + /// Id of the room to find documents in + /// + public Guid? RoomId { get; set; } + /// + /// Id of the locker to find documents in + /// + public Guid? LockerId { get; set; } + /// + /// Id of the folder to find documents in + /// + public Guid? FolderId { get; set; } + /// + /// Search term + /// + public string? SearchTerm { get; set; } + /// + /// Page number + /// + public int? Page { get; set; } + /// + /// Size number + /// + public int? Size { get; set; } + /// + /// Sort criteria + /// + public string? SortBy { get; set; } + /// + /// Sort direction + /// + public string? SortOrder { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Documents/ImportDocumentRequest.cs b/src/Api/Controllers/Payload/Requests/Documents/ImportDocumentRequest.cs new file mode 100644 index 00000000..0bbb2723 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Documents/ImportDocumentRequest.cs @@ -0,0 +1,28 @@ +namespace Api.Controllers.Payload.Requests.Documents; + +/// +/// Request details to import a document +/// +public class ImportDocumentRequest +{ + /// + /// Title of the document to be imported + /// + public string Title { get; set; } = null!; + /// + /// Description of the document to be imported + /// + public string? Description { get; set; } + /// + /// Document type of the document to be imported + /// + public string DocumentType { get; set; } = null!; + /// + /// Id of the importer + /// + public Guid ImporterId { get; set; } + /// + /// Id of the folder that this document will be in + /// + public Guid FolderId { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Documents/UpdateDocumentRequest.cs b/src/Api/Controllers/Payload/Requests/Documents/UpdateDocumentRequest.cs index e6a0885b..a31cec88 100644 --- a/src/Api/Controllers/Payload/Requests/Documents/UpdateDocumentRequest.cs +++ b/src/Api/Controllers/Payload/Requests/Documents/UpdateDocumentRequest.cs @@ -1,8 +1,20 @@ namespace Api.Controllers.Payload.Requests.Documents; +/// +/// Request details to update a document +/// public class UpdateDocumentRequest { + /// + /// New title of the document to be updated + /// public string Title { get; set; } = null!; + /// + /// New description of the document to be updated + /// public string? Description { get; set; } + /// + /// New document type of the document to be updated + /// public string DocumentType { get; set; } = null!; } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Folders/AddFolderRequest.cs b/src/Api/Controllers/Payload/Requests/Folders/AddFolderRequest.cs new file mode 100644 index 00000000..1dd731c0 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Folders/AddFolderRequest.cs @@ -0,0 +1,23 @@ +namespace Api.Controllers.Payload.Requests.Folders; +/// +/// Request details to add a folder +/// +public class AddFolderRequest +{ + /// + /// Name of the folder to be added + /// + public string Name { get; init; } = null!; + /// + /// Description of the folder to be added + /// + public string? Description { get; init; } + /// + /// Number of documents this folder can hold + /// + public int Capacity { get; init; } + /// + /// Id of the locker that this folder will be in + /// + public Guid LockerId { get; init; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Folders/GetAllFoldersPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Folders/GetAllFoldersPaginatedQueryParameters.cs index db642122..1be3d84f 100644 --- a/src/Api/Controllers/Payload/Requests/Folders/GetAllFoldersPaginatedQueryParameters.cs +++ b/src/Api/Controllers/Payload/Requests/Folders/GetAllFoldersPaginatedQueryParameters.cs @@ -1,11 +1,32 @@ namespace Api.Controllers.Payload.Requests.Folders; +/// +/// Query parameters for getting all folders with pagination +/// public class GetAllFoldersPaginatedQueryParameters { + /// + /// Id of the room to find folders in + /// public Guid? RoomId { get; set; } + /// + /// Id of the locker to find folders in + /// public Guid? LockerId { get; set; } + /// + /// Page number + /// public int? Page { get; set; } + /// + /// Size number + /// public int? Size { get; set; } + /// + /// Sort criteria + /// public string? SortBy { get; set; } + /// + /// Sort direction + /// public string? SortOrder { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Folders/UpdateFolderRequest.cs b/src/Api/Controllers/Payload/Requests/Folders/UpdateFolderRequest.cs index 97e9691f..e26cf33b 100644 --- a/src/Api/Controllers/Payload/Requests/Folders/UpdateFolderRequest.cs +++ b/src/Api/Controllers/Payload/Requests/Folders/UpdateFolderRequest.cs @@ -1,8 +1,20 @@ namespace Api.Controllers.Payload.Requests.Folders; +/// +/// Request details to update a folder +/// public class UpdateFolderRequest { + /// + /// New name of the folder to be updated + /// public string Name { get; set; } = null!; + /// + /// New description of the folder to be updated + /// public string? Description { get; set; } + /// + /// New capacity of the folder to be updated + /// public int Capacity { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Lockers/AddLockerRequest.cs b/src/Api/Controllers/Payload/Requests/Lockers/AddLockerRequest.cs new file mode 100644 index 00000000..67988e72 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Lockers/AddLockerRequest.cs @@ -0,0 +1,24 @@ +namespace Api.Controllers.Payload.Requests.Lockers; + +/// +/// Request details to add a locker +/// +public class AddLockerRequest +{ + /// + /// Name of the locker to be updated + /// + public string Name { get; init; } = null!; + /// + /// Description of the locker to be updated + /// + public string? Description { get; init; } + /// + /// Id of the room that this locker will be in + /// + public Guid RoomId { get; init; } + /// + /// Number of folders this locker can hold + /// + public int Capacity { get; init; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs index b4b7f837..656b96f6 100644 --- a/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs +++ b/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs @@ -2,12 +2,29 @@ namespace Api.Controllers.Payload.Requests.Lockers; - +/// +/// Query parameters for getting all lockers with pagination +/// public class GetAllLockersPaginatedQueryParameters { + /// + /// Id of the room to find lockers in + /// public Guid? RoomId { get; set; } + /// + /// Page number + /// public int? Page { get; set; } + /// + /// Size number + /// public int? Size { get; set; } + /// + /// Sort criteria + /// public string? SortBy { get; set; } + /// + /// Sort direction + /// public string? SortOrder { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Lockers/UpdateLockerRequest.cs b/src/Api/Controllers/Payload/Requests/Lockers/UpdateLockerRequest.cs index a7f69036..5558c1ce 100644 --- a/src/Api/Controllers/Payload/Requests/Lockers/UpdateLockerRequest.cs +++ b/src/Api/Controllers/Payload/Requests/Lockers/UpdateLockerRequest.cs @@ -1,8 +1,20 @@ namespace Api.Controllers.Payload.Requests.Lockers; +/// +/// Request details to update a locker +/// public class UpdateLockerRequest { + /// + /// New name of the locker to be updated + /// public string Name { get; set; } = null!; + /// + /// New description of the locker to be updated + /// public string? Description { get; set; } + /// + /// New capacity of the locker to be updated + /// public int Capacity { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/LoginModel.cs b/src/Api/Controllers/Payload/Requests/LoginModel.cs deleted file mode 100644 index d5bd6c61..00000000 --- a/src/Api/Controllers/Payload/Requests/LoginModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Api.Controllers.Payload.Requests; - -public class LoginModel -{ - public string Email { get; set; } - public string Password { get; set; } -} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/RefreshTokenRequest.cs b/src/Api/Controllers/Payload/Requests/RefreshTokenRequest.cs deleted file mode 100644 index bcc3d8e2..00000000 --- a/src/Api/Controllers/Payload/Requests/RefreshTokenRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Api.Controllers.Payload.Requests; - -public class RefreshTokenRequest -{ - public string Token { get; set; } - public string RefreshToken { get; set; } -} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Rooms/AddRoomRequest.cs b/src/Api/Controllers/Payload/Requests/Rooms/AddRoomRequest.cs new file mode 100644 index 00000000..4228f4da --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Rooms/AddRoomRequest.cs @@ -0,0 +1,24 @@ +namespace Api.Controllers.Payload.Requests.Rooms; + +/// +/// Request details to add a room +/// +public class AddRoomRequest +{ + /// + /// Name of the room to be added + /// + public string Name { get; set; } = null!; + /// + /// Description of the room to be added + /// + public string? Description { get; set; } + /// + /// Number of lockers this room can hold + /// + public int Capacity { get; set; } + /// + /// Id of the department this room belongs to + /// + public Guid DepartmentId { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs new file mode 100644 index 00000000..34721c39 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs @@ -0,0 +1,24 @@ +namespace Api.Controllers.Payload.Requests.Rooms; + +/// +/// Query parameters for getting all rooms with pagination +/// +public class GetAllRoomsPaginatedQueryParameters +{ + /// + /// Page number + /// + public int? Page { get; set; } + /// + /// Size number + /// + public int? Size { get; set; } + /// + /// Sort criteria + /// + public string? SortBy { get; set; } + /// + /// Sort direction + /// + public string? SortOrder { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Rooms/GetEmptyContainersPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Rooms/GetEmptyContainersPaginatedQueryParameters.cs new file mode 100644 index 00000000..2d60bb6d --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Rooms/GetEmptyContainersPaginatedQueryParameters.cs @@ -0,0 +1,16 @@ +namespace Api.Controllers.Payload.Requests.Rooms; + +/// +/// Query parameters for getting all empty containers in a room +/// +public class GetEmptyContainersPaginatedQueryParameters +{ + /// + /// Page number + /// + public int? Page { get; set; } + /// + /// Size number + /// + public int? Size { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Rooms/UpdateRoomRequest.cs b/src/Api/Controllers/Payload/Requests/Rooms/UpdateRoomRequest.cs new file mode 100644 index 00000000..40d4965c --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Rooms/UpdateRoomRequest.cs @@ -0,0 +1,20 @@ +namespace Api.Controllers.Payload.Requests.Rooms; + +/// +/// Request details to update a room +/// +public class UpdateRoomRequest +{ + /// + /// New name of the room to be updated + /// + public string Name { get; set; } = null!; + /// + /// New description of the room to be updated + /// + public string? Description { get; set; } + /// + /// New capacity of the room to be updated + /// + public int Capacity { get; set; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Staffs/AddStaffRequest.cs b/src/Api/Controllers/Payload/Requests/Staffs/AddStaffRequest.cs new file mode 100644 index 00000000..47ebbc06 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Staffs/AddStaffRequest.cs @@ -0,0 +1,16 @@ +namespace Api.Controllers.Payload.Requests.Staffs; + +/// +/// Request details to add a staff +/// +public class AddStaffRequest +{ + /// + /// User id of the new staff + /// + public Guid UserId { get; init; } + /// + /// Id of the room this staff will be in + /// + public Guid? RoomId { get; init; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Staffs/GetAllStaffsPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Staffs/GetAllStaffsPaginatedQueryParameters.cs index bcb47cb4..53fcbb87 100644 --- a/src/Api/Controllers/Payload/Requests/Staffs/GetAllStaffsPaginatedQueryParameters.cs +++ b/src/Api/Controllers/Payload/Requests/Staffs/GetAllStaffsPaginatedQueryParameters.cs @@ -1,10 +1,28 @@ namespace Api.Controllers.Payload.Requests.Staffs; +/// +/// Query parameters for getting all staffs with pagination +/// public class GetAllStaffsPaginatedQueryParameters { + /// + /// Search term + /// public string? SearchTerm { get; set; } + /// + /// Page number + /// public int? Page { get; set; } + /// + /// Size number + /// public int? Size { get; set; } + /// + /// Sort criteria + /// public string? SortBy { get; set; } + /// + /// Sort direction + /// public string? SortOrder { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Staffs/RemoveStaffFromRoomRequest.cs b/src/Api/Controllers/Payload/Requests/Staffs/RemoveStaffFromRoomRequest.cs deleted file mode 100644 index 811608df..00000000 --- a/src/Api/Controllers/Payload/Requests/Staffs/RemoveStaffFromRoomRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Api.Controllers.Payload.Requests.Staffs; - -public class RemoveStaffFromRoomRequest -{ - public Guid RoomId { get; set; } -} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/UpdateDepartmentRequest.cs b/src/Api/Controllers/Payload/Requests/UpdateDepartmentRequest.cs deleted file mode 100644 index 467ceeb5..00000000 --- a/src/Api/Controllers/Payload/Requests/UpdateDepartmentRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Api.Controllers.Payload.Requests; - -public class UpdateDepartmentRequest -{ - public string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/UpdateRoomRequest.cs b/src/Api/Controllers/Payload/Requests/UpdateRoomRequest.cs deleted file mode 100644 index a579a447..00000000 --- a/src/Api/Controllers/Payload/Requests/UpdateRoomRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Api.Controllers.Payload.Requests; - -public class UpdateRoomRequest -{ - public string Name { get; set; } = null!; - public string? Description { get; set; } - public int Capacity { get; set; } -} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Users/AddUserRequest.cs b/src/Api/Controllers/Payload/Requests/Users/AddUserRequest.cs new file mode 100644 index 00000000..2c449aff --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Users/AddUserRequest.cs @@ -0,0 +1,40 @@ +namespace Api.Controllers.Payload.Requests.Users; + +/// +/// Request details to add a user +/// +public class AddUserRequest +{ + /// + /// Username of the user to be added + /// + public string Username { get; init; } = null!; + /// + /// Email of the user to be added + /// + public string Email { get; init; } = null!; + /// + /// Password of the user to be added + /// + public string Password { get; init; } = null!; + /// + /// First name of the user to be added + /// + public string? FirstName { get; init; } + /// + /// Last name of the user to be added + /// + public string? LastName { get; init; } + /// + /// Department of the user to be added + /// + public Guid DepartmentId { get; init; } + /// + /// Role of the user to be added + /// + public string Role { get; init; } = null!; + /// + /// Position of the user to be added + /// + public string? Position { get; init; } +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Users/GetAllUsersPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Users/GetAllUsersPaginatedQueryParameters.cs index d3484660..9f47d022 100644 --- a/src/Api/Controllers/Payload/Requests/Users/GetAllUsersPaginatedQueryParameters.cs +++ b/src/Api/Controllers/Payload/Requests/Users/GetAllUsersPaginatedQueryParameters.cs @@ -1,11 +1,32 @@ namespace Api.Controllers.Payload.Requests.Users; +/// +/// Query parameters for getting all users with pagination +/// public class GetAllUsersPaginatedQueryParameters { + /// + /// Id of the department to find users in + /// public Guid? DepartmentId { get; set; } + /// + /// Search term + /// public string? SearchTerm { get; set; } + /// + /// Page number + /// public int? Page { get; set; } + /// + /// Size number + /// public int? Size { get; set; } + /// + /// Sort criteria + /// public string? SortBy { get; set; } + /// + /// Sort direction + /// public string? SortOrder { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Users/UpdateUserRequest.cs b/src/Api/Controllers/Payload/Requests/Users/UpdateUserRequest.cs index 77bcb0e4..d162c28a 100644 --- a/src/Api/Controllers/Payload/Requests/Users/UpdateUserRequest.cs +++ b/src/Api/Controllers/Payload/Requests/Users/UpdateUserRequest.cs @@ -1,11 +1,24 @@ namespace Api.Controllers.Payload.Requests.Users; +/// +/// Request details to update a user +/// public class UpdateUserRequest { - public string Username { get; set; } = null!; - public string Email { get; set; } = null!; + /// + /// New first name of the user to be updated + /// public string? FirstName { get; set; } + /// + /// New last name of the user to be updated + /// public string? LastName { get; set; } + /// + /// New role of the user to be updated + /// public string Role { get; set; } = null!; + /// + /// New position of the user to be updated + /// public string? Position { get; set; } } \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Responses/LoginResult.cs b/src/Api/Controllers/Payload/Responses/LoginResult.cs index 29fb21c4..f731ac4a 100644 --- a/src/Api/Controllers/Payload/Responses/LoginResult.cs +++ b/src/Api/Controllers/Payload/Responses/LoginResult.cs @@ -1,3 +1,4 @@ +using Application.Common.Models.Dtos; using Application.Users.Queries; namespace Api.Controllers.Payload.Responses; diff --git a/src/Api/Controllers/RoomsController.cs b/src/Api/Controllers/RoomsController.cs index 3cbeb8dd..d46390cc 100644 --- a/src/Api/Controllers/RoomsController.cs +++ b/src/Api/Controllers/RoomsController.cs @@ -1,15 +1,10 @@ -using Api.Controllers.Payload.Requests; +using Api.Controllers.Payload.Requests.Lockers; +using Api.Controllers.Payload.Requests.Rooms; using Application.Common.Models; using Application.Common.Models.Dtos.Physical; using Application.Identity; -using Application.Rooms.Commands.AddRoom; -using Application.Rooms.Commands.DisableRoom; -using Application.Rooms.Commands.EnableRoom; -using Application.Rooms.Commands.RemoveRoom; -using Application.Rooms.Commands.UpdateRoom; -using Application.Rooms.Queries.GetAllRoomPaginated; -using Application.Rooms.Queries.GetEmptyContainersPaginated; -using Application.Rooms.Queries.GetRoomById; +using Application.Rooms.Commands; +using Application.Rooms.Queries; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; @@ -17,135 +12,179 @@ namespace Api.Controllers; public class RoomsController : ApiControllerBase { - [RequiresRole(IdentityData.Roles.Admin)] - [HttpPost] + /// + /// Get a room by id + /// + /// Id of the room to be retrieved + /// A RoomDto of the retrieved room + [HttpGet("{roomId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> AddRoom(AddRoomCommand command) + public async Task>> GetById([FromRoute] Guid roomId) { - var result = await Mediator.Send(command); + var query = new GetRoomById.Query() + { + RoomId = roomId, + }; + var result = await Mediator.Send(query); return Ok(Result.Succeed(result)); } - + + /// + /// Get all rooms paginated + /// + /// Get all rooms paginated details + /// A paginated list of rooms + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task>>> GetAllPaginated( + [FromQuery] GetAllLockersPaginatedQueryParameters queryParameters) + { + var query = new GetAllRoomsPaginated.Query() + { + Page = queryParameters.Page, + Size = queryParameters.Size, + SortBy = queryParameters.SortBy, + SortOrder = queryParameters.SortOrder, + }; + var result = await Mediator.Send(query); + return Ok(Result>.Succeed(result)); + } + + /// + /// Get empty containers in a room + /// + /// Get empty containers paginated details + /// A paginated list of EmptyLockerDto [RequiresRole(IdentityData.Roles.Staff)] [HttpPost("empty-containers")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetEmptyContainers(GetEmptyContainersPaginatedQuery query) + public async Task>> GetEmptyContainers( + [FromRoute] Guid roomId, + [FromQuery] GetEmptyContainersPaginatedQueryParameters queryParameters) { + var query = new GetEmptyContainersPaginated.Query() + { + RoomId = roomId, + Page = queryParameters.Page, + Size = queryParameters.Size, + }; var result = await Mediator.Send(query); return Ok(Result>.Succeed(result)); } - + + /// + /// Add a room + /// + /// Add room details + /// A RoomDto of the added room [RequiresRole(IdentityData.Roles.Admin)] - [HttpPut("disable")] + [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> DisableRoom(DisableRoomCommand command) + public async Task>> AddRoom([FromBody] AddRoomRequest request) { + var command = new AddRoom.Command() + { + Name = request.Name, + Description = request.Description, + Capacity = request.Capacity, + DepartmentId = request.DepartmentId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + + /// + /// Remove a room + /// + /// Id of the room to be removed + /// A RoomDto of the removed room [RequiresRole(IdentityData.Roles.Admin)] - [HttpDelete] + [HttpDelete("{roomId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> RemoveRoom(RemoveRoomCommand command) + public async Task>> RemoveRoom([FromRoute] Guid roomId) { + var command = new RemoveRoom.Command() + { + RoomId = roomId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + /// /// Enable a room /// - /// Enable room details + /// Id of the room to be enabled /// A RoomDto of the enabled room - [HttpPut("enable")] + [HttpPut("enable/{roomId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> EnableRoom([FromBody] EnableRoomCommand command) + public async Task>> EnableRoom([FromRoute] Guid roomId) { + var command = new EnableRoom.Command() + { + RoomId = roomId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } /// - /// Update a room + /// Disable a room /// - /// Id of the room to be updated - /// Update room details - /// A RoomDto of the updated room - [HttpPut("{roomId:guid}")] + /// Id of the room to be disabled + /// A RoomDto of the disabled room + [RequiresRole(IdentityData.Roles.Admin)] + [HttpPut("disable/{roomId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> Update([FromRoute] Guid roomId, [FromBody] UpdateRoomRequest request) + public async Task>> DisableRoom([FromRoute] Guid roomId) { - Console.WriteLine(request.Description); - var command = new UpdateRoomCommand() + var command = new DisableRoom.Command() { RoomId = roomId, - Name = request.Name, - Description = request.Description, - Capacity = request.Capacity, }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - + /// - /// Get a room by id + /// Update a room /// - /// Id of the room to be retrieved - /// A RoomDto of the retrieved room - [HttpGet("{roomId:guid}")] + /// Id of the room to be updated + /// Update room details + /// A RoomDto of the updated room + [HttpPut("{roomId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetById([FromRoute] Guid roomId) + [ProducesResponseType(StatusCodes.Status409Conflict)] + public async Task>> Update([FromRoute] Guid roomId, [FromBody] UpdateRoomRequest request) { - var query = new GetRoomByIdQuery() + var command = new UpdateRoom.Command() { RoomId = roomId, + Name = request.Name, + Description = request.Description, + Capacity = request.Capacity, }; - var result = await Mediator.Send(query); + var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - - /// - /// Get all rooms paginated - /// - /// The page index - /// The size number - /// Criteria - /// The order in which the rooms are sorted - /// A paginated list of rooms - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task>>> GetAllPaginated(int? page, int? size, string? sortBy, string? sortOrder) - { - var query = new GetAllRoomsPaginatedQuery() - { - Page = page, - Size = size, - SortBy = sortBy, - SortOrder = sortOrder - }; - var result = await Mediator.Send(query); - return Ok(Result>.Succeed(result)); - } } \ No newline at end of file diff --git a/src/Api/Controllers/StaffsController.cs b/src/Api/Controllers/StaffsController.cs index 1ad6fff1..048f1b4c 100644 --- a/src/Api/Controllers/StaffsController.cs +++ b/src/Api/Controllers/StaffsController.cs @@ -1,12 +1,9 @@ using Api.Controllers.Payload.Requests.Staffs; using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; using Application.Identity; -using Application.Staffs.Commands.RemoveStaffFromRoom; -using Application.Staffs.Queries.GetAllStaffsPaginated; -using Application.Staffs.Queries.GetStaffById; -using Application.Staffs.Queries.GetStaffByRoom; -using Application.Staffs.Commands.AddStaff; -using Application.Users.Queries.Physical; +using Application.Staffs.Commands; +using Application.Staffs.Queries; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; @@ -25,7 +22,7 @@ public class StaffsController : ApiControllerBase [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetById([FromRoute] Guid staffId) { - var query = new GetStaffByIdQuery() + var query = new GetStaffById.Query() { StaffId = staffId }; @@ -44,7 +41,7 @@ public async Task>> GetById([FromRoute] Guid staff [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetByRoom([FromRoute] Guid roomId) { - var query = new GetStaffByRoomQuery() + var query = new GetStaffByRoom.Query() { RoomId = roomId }; @@ -63,8 +60,9 @@ public async Task>> GetByRoom([FromRoute] Guid roo public async Task>>> GetAllPaginated( [FromQuery] GetAllStaffsPaginatedQueryParameters queryParameters) { - var query = new GetAllStaffsPaginatedQuery() + var query = new GetAllStaffsPaginated.Query() { + SearchTerm = queryParameters.SearchTerm, Page = queryParameters.Page, Size = queryParameters.Size, SortBy = queryParameters.SortBy, @@ -74,13 +72,23 @@ public async Task>>> GetAllPaginated return Ok(Result>.Succeed(result)); } + /// + /// Add a staff + /// + /// Add staff details + /// A StaffDto of the added staff [RequiresRole(IdentityData.Roles.Admin)] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> AddStaff([FromBody] AddStaffCommand command) + public async Task>> Add([FromBody] AddStaffRequest request) { + var command = new AddStaff.Command() + { + RoomId = request.RoomId, + UserId = request.UserId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } @@ -89,19 +97,17 @@ public async Task>> AddStaff([FromBody] AddStaffCo /// Remove a staff from room /// /// Id of the staff to be removed from room - /// Remove details /// A StaffDto of the removed staff [HttpPut("{staffId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> RemoveStaffFromRoom([FromRoute] Guid staffId, - [FromBody] RemoveStaffFromRoomRequest request) + public async Task>> RemoveFromRoom( + [FromRoute] Guid staffId) { - var command = new RemoveStaffFromRoomCommand() + var command = new RemoveStaffFromRoom.Command() { StaffId = staffId, - RoomId = request.RoomId, }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); diff --git a/src/Api/Controllers/UsersController.cs b/src/Api/Controllers/UsersController.cs index f7d93173..dc2b406e 100644 --- a/src/Api/Controllers/UsersController.cs +++ b/src/Api/Controllers/UsersController.cs @@ -1,17 +1,9 @@ using Api.Controllers.Payload.Requests.Users; using Application.Common.Models; -using Application.Common.Models.Dtos.Physical; using Application.Identity; -using Application.Users.Commands.AddUser; -using Application.Users.Commands.DisableUser; -using Application.Users.Commands.EnableUser; -using Application.Users.Commands.UpdateUser; +using Application.Users.Commands; using Application.Users.Queries; -using Application.Users.Queries.GetAllUsersPaginated; -using Application.Users.Queries.GetUserById; -using Application.Users.Queries.GetUsersByName; using Infrastructure.Identity.Authorization; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Api.Controllers; @@ -27,9 +19,9 @@ public class UsersController : ApiControllerBase [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetUserById([FromRoute] Guid userId) + public async Task>> GetById([FromRoute] Guid userId) { - var query = new GetUserByIdQuery + var query = new GetUserById.Query { UserId = userId, }; @@ -40,7 +32,7 @@ public async Task>> GetUserById([FromRoute] Guid us /// /// Get all users paginated /// - /// Get all users query parameters + /// Get all users query parameters /// A paginated list of UserDto [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] @@ -48,7 +40,7 @@ public async Task>> GetUserById([FromRoute] Guid us public async Task>>> GetAllPaginated( [FromQuery] GetAllUsersPaginatedQueryParameters queryParameters) { - var query = new GetAllUsersPaginatedQuery() + var query = new GetAllUsersPaginated.Query() { DepartmentId = queryParameters.DepartmentId, SearchTerm = queryParameters.SearchTerm, @@ -61,37 +53,37 @@ public async Task>>> GetAllPaginated( return Ok(Result>.Succeed(result)); } + /// + /// Add a user + /// + /// Add user details + /// A UserDto of the added user [RequiresRole(IdentityData.Roles.Admin)] [HttpPost] - [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> AddUser([FromBody] AddUserCommand command) + public async Task>> Add([FromBody] AddUserRequest request) { + var command = new AddUser.Command() + { + Username = request.Username, + Email = request.Email, + Password = request.Password, + FirstName = request.FirstName, + LastName = request.LastName, + Role = request.Role, + Position = request.Position, + DepartmentId = request.DepartmentId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } - - // [RequiresRole(IdentityData.Roles.Admin)] - // [HttpGet] - // [ProducesResponseType(StatusCodes.Status200OK)] - // [ProducesResponseType(StatusCodes.Status403Forbidden)] - // public async Task>>> GetUsersByName(string? searchTerm, int? page, int? size) - // { - // var query = new GetUsersByNameQuery - // { - // SearchTerm = searchTerm, - // Page = page, - // Size = size - // }; - // var result = await Mediator.Send(query); - // return Ok(Result>.Succeed(result)); - // } - + /// - /// Disable a user + /// Enable a user /// /// Id of the user to be enabled /// A UserDto of the enabled user @@ -100,9 +92,9 @@ public async Task>> AddUser([FromBody] AddUserComma [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> EnableUser([FromRoute] Guid userId) + public async Task>> Enable([FromRoute] Guid userId) { - var command = new EnableUserCommand() + var command = new EnableUser.Command() { UserId = userId }; @@ -110,14 +102,23 @@ public async Task>> EnableUser([FromRoute] Guid use return Ok(Result.Succeed(result)); } + /// + /// Disable a user + /// + /// Id of the user to be disabled + /// A UserDto of the disabled user [RequiresRole(IdentityData.Roles.Admin)] - [HttpPost("disable")] + [HttpPut("disable/{userId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task>> DisableUser([FromBody] DisableUserCommand command) + public async Task>> Disable([FromRoute] Guid userId) { + var command = new DisableUser.Command() + { + UserId = userId, + }; var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } @@ -135,11 +136,9 @@ public async Task>> DisableUser([FromBody] DisableU [ProducesResponseType(StatusCodes.Status409Conflict)] public async Task>> Update([FromRoute] Guid userId, [FromBody] UpdateUserRequest request) { - var command = new UpdateUserCommand() + var command = new UpdateUser.Command() { UserId = userId, - Username = request.Username, - Email = request.Email, FirstName = request.FirstName, LastName = request.LastName, Role = request.Role, diff --git a/src/Api/Extensions/WebApplicationExtensions.cs b/src/Api/Extensions/WebApplicationExtensions.cs index e0a90371..63e834ee 100644 --- a/src/Api/Extensions/WebApplicationExtensions.cs +++ b/src/Api/Extensions/WebApplicationExtensions.cs @@ -1,10 +1,12 @@ using Api.Middlewares; +using Infrastructure.Persistence; +using Serilog; namespace Api.Extensions; public static class WebApplicationExtensions { - public static void UseInfrastructure(this WebApplication app) + public static void UseInfrastructure(this WebApplication app, IConfiguration configuration) { // Configure the HTTP request pipeline. @@ -15,10 +17,20 @@ public static void UseInfrastructure(this WebApplication app) { app.UseSwagger(); app.UseSwaggerUI(); + app.UseCors("AllowAllOrigins"); + + app.MigrateDatabase((context, _) => + { + ApplicationDbContextSeed.Seed(context, configuration, Log.Logger).Wait(); + }); } - else + + if (app.Environment.IsEnvironment("Testing")) { + app.MigrateDatabase((_, _) => + { + }); } app.UseAuthentication(); diff --git a/src/Api/Program.cs b/src/Api/Program.cs index 42a37f8f..829f0873 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -21,13 +21,9 @@ var app = builder.Build(); - app.UseInfrastructure(); + app.UseInfrastructure(builder.Configuration); - app.MigrateDatabase((context, _) => - { - ApplicationDbContextSeed.Seed(context, builder.Configuration, Log.Logger).Wait(); - }) - .Run(); + app.Run(); } catch (Exception ex) { diff --git a/src/Api/Services/CurrentUserService.cs b/src/Api/Services/CurrentUserService.cs index 34a4b5be..d8b6c481 100644 --- a/src/Api/Services/CurrentUserService.cs +++ b/src/Api/Services/CurrentUserService.cs @@ -1,6 +1,5 @@ using System.IdentityModel.Tokens.Jwt; using Application.Common.Interfaces; -using Application.Identity; namespace Api.Services; diff --git a/src/Api/appsettings.Testing.json b/src/Api/appsettings.Testing.json new file mode 100644 index 00000000..5dbdc362 --- /dev/null +++ b/src/Api/appsettings.Testing.json @@ -0,0 +1,20 @@ +{ + "JweSettings": { + "SigningKeyId": "4bd28be8eac5414fb01c5cbe343b50144bd2", + "EncryptionKeyId": "4bd28be8eac5414fb01c5cbe343b5014", + "TokenLifetime": "00:20:00", + "RefreshTokenLifetimeInDays": 3 + }, + "Seed": true, + "Serilog" : { + "MinimumLevel" : { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.AspNetCore.Authentication": "Debug", + "System": "Warning" + } + } + } +} diff --git a/src/Application/Common/Models/Dtos/DepartmentDto.cs b/src/Application/Common/Models/Dtos/DepartmentDto.cs index 286a4b08..68d00db4 100644 --- a/src/Application/Common/Models/Dtos/DepartmentDto.cs +++ b/src/Application/Common/Models/Dtos/DepartmentDto.cs @@ -1,10 +1,12 @@ using Application.Common.Mappings; +using Application.Common.Models.Dtos.Physical; using Domain.Entities; -namespace Application.Users.Queries; +namespace Application.Common.Models.Dtos; public class DepartmentDto : IMapFrom { public Guid Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = null!; + public RoomDto? Room { get; set; } } \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetAllDocumentsPaginated/DocumentItemDto.cs b/src/Application/Common/Models/Dtos/Physical/DocumentItemDto.cs similarity index 76% rename from src/Application/Documents/Queries/GetAllDocumentsPaginated/DocumentItemDto.cs rename to src/Application/Common/Models/Dtos/Physical/DocumentItemDto.cs index 86cc8533..7420ad90 100644 --- a/src/Application/Documents/Queries/GetAllDocumentsPaginated/DocumentItemDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/DocumentItemDto.cs @@ -1,12 +1,9 @@ using Application.Common.Mappings; -using Application.Common.Models.Dtos.Physical; using Application.Users.Queries; -using AutoMapper; using Domain.Entities.Physical; -namespace Application.Documents.Queries.GetAllDocumentsPaginated; +namespace Application.Common.Models.Dtos.Physical; -[Obsolete] public class DocumentItemDto : IMapFrom { public Guid Id { get; set; } diff --git a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyFolderDto.cs b/src/Application/Common/Models/Dtos/Physical/EmptyFolderDto.cs similarity index 89% rename from src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyFolderDto.cs rename to src/Application/Common/Models/Dtos/Physical/EmptyFolderDto.cs index 0398e867..10e77646 100644 --- a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyFolderDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/EmptyFolderDto.cs @@ -2,7 +2,7 @@ using AutoMapper; using Domain.Entities.Physical; -namespace Application.Rooms.Queries.GetEmptyContainersPaginated; +namespace Application.Common.Models.Dtos.Physical; public class EmptyFolderDto : IMapFrom { diff --git a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyLockerDto.cs b/src/Application/Common/Models/Dtos/Physical/EmptyLockerDto.cs similarity index 86% rename from src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyLockerDto.cs rename to src/Application/Common/Models/Dtos/Physical/EmptyLockerDto.cs index f9f80d7b..6d7537c7 100644 --- a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/EmptyLockerDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/EmptyLockerDto.cs @@ -1,9 +1,8 @@ using Application.Common.Mappings; -using Application.Common.Models.Dtos.Physical; using AutoMapper; using Domain.Entities.Physical; -namespace Application.Rooms.Queries.GetEmptyContainersPaginated; +namespace Application.Common.Models.Dtos.Physical; public class EmptyLockerDto : IMapFrom { diff --git a/src/Application/Common/Models/Dtos/Physical/RoomDto.cs b/src/Application/Common/Models/Dtos/Physical/RoomDto.cs index 5426161f..a2ed7b95 100644 --- a/src/Application/Common/Models/Dtos/Physical/RoomDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/RoomDto.cs @@ -1,5 +1,5 @@ using Application.Common.Mappings; -using Application.Users.Queries.Physical; +using Application.Users.Queries; using AutoMapper; using Domain.Entities.Physical; @@ -8,9 +8,10 @@ namespace Application.Common.Models.Dtos.Physical; public class RoomDto : IMapFrom { public Guid Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public StaffDto Staff { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public StaffDto? Staff { get; set; } + public DepartmentDto? Department { get; set; } public int Capacity { get; set; } public int NumberOfLockers { get; set; } public bool IsAvailable { get; set; } diff --git a/src/Application/Common/Models/Dtos/Physical/StaffDto.cs b/src/Application/Common/Models/Dtos/Physical/StaffDto.cs index b4ff5a80..84563c1b 100644 --- a/src/Application/Common/Models/Dtos/Physical/StaffDto.cs +++ b/src/Application/Common/Models/Dtos/Physical/StaffDto.cs @@ -1,11 +1,11 @@ using Application.Common.Mappings; -using Application.Common.Models.Dtos.Physical; +using Application.Users.Queries; using Domain.Entities.Physical; -namespace Application.Users.Queries.Physical; +namespace Application.Common.Models.Dtos.Physical; public class StaffDto : IMapFrom { - public UserDto User { get; set; } - public RoomDto Room { get; set; } + public UserDto User { get; set; } = null!; + public RoomDto? Room { get; set; } } \ No newline at end of file diff --git a/src/Application/Common/Models/Dtos/UserDto.cs b/src/Application/Common/Models/Dtos/UserDto.cs index ccb4ba64..64cf73dc 100644 --- a/src/Application/Common/Models/Dtos/UserDto.cs +++ b/src/Application/Common/Models/Dtos/UserDto.cs @@ -1,4 +1,5 @@ using Application.Common.Mappings; +using Application.Common.Models.Dtos; using AutoMapper; using Domain.Entities; diff --git a/src/Application/Departments/Commands/AddDepartment.cs b/src/Application/Departments/Commands/AddDepartment.cs new file mode 100644 index 00000000..525d83c4 --- /dev/null +++ b/src/Application/Departments/Commands/AddDepartment.cs @@ -0,0 +1,50 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos; +using Application.Users.Queries; +using AutoMapper; +using Domain.Entities; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Departments.Commands; + +public class AddDepartment +{ + public record Command : IRequest + { + public string Name { get; init; } = null!; + } + + public class AddDepartmentCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public AddDepartmentCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var department = await _context.Departments.FirstOrDefaultAsync(x + => x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()), cancellationToken); + + if (department is not null) + { + throw new ConflictException("Department name already exists."); + } + + var entity = new Department + { + Name = request.Name + }; + + var result = await _context.Departments.AddAsync(entity, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Departments/Commands/AddDepartment/AddDepartmentCommand.cs b/src/Application/Departments/Commands/AddDepartment/AddDepartmentCommand.cs deleted file mode 100644 index 00f42e5c..00000000 --- a/src/Application/Departments/Commands/AddDepartment/AddDepartmentCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Users.Queries; -using AutoMapper; -using Domain.Entities; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Departments.Commands.AddDepartment; - -public record AddDepartmentCommand : IRequest -{ - public string Name { get; init; } = null!; -} - -public class AddDepartmentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public AddDepartmentCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(AddDepartmentCommand request, CancellationToken cancellationToken) - { - var department = await _context.Departments.FirstOrDefaultAsync(x => x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()), cancellationToken); - - if (department is not null) - { - throw new ConflictException("Department name already exists."); - } - var entity = new Department - { - Name = request.Name - }; - - var result = await _context.Departments.AddAsync(entity, cancellationToken); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Departments/Commands/DeleteDepartment.cs b/src/Application/Departments/Commands/DeleteDepartment.cs new file mode 100644 index 00000000..b744630b --- /dev/null +++ b/src/Application/Departments/Commands/DeleteDepartment.cs @@ -0,0 +1,41 @@ +using Application.Common.Interfaces; +using Application.Common.Models.Dtos; +using Application.Users.Queries; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Departments.Commands; + +public class DeleteDepartment +{ + public record Command : IRequest + { + public Guid DepartmentId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var department = await _context.Departments.FirstOrDefaultAsync(x => x.Id == request.DepartmentId, cancellationToken); + + if (department is null) + { + throw new KeyNotFoundException("Department does not exist."); + } + + var result = _context.Departments.Remove(department); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Departments/Commands/DeleteDepartment/DeleteDepartmentCommand.cs b/src/Application/Departments/Commands/DeleteDepartment/DeleteDepartmentCommand.cs deleted file mode 100644 index 93ea7708..00000000 --- a/src/Application/Departments/Commands/DeleteDepartment/DeleteDepartmentCommand.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Users.Queries; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Departments.Commands.DeleteDepartment; - -public record DeleteDepartmentCommand : IRequest -{ - public Guid DepartmentId { get; init; } -} - -public class DeleteDepartmentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public DeleteDepartmentCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(DeleteDepartmentCommand request, CancellationToken cancellationToken) - { - var department = await _context.Departments.FirstOrDefaultAsync(x => x.Id == request.DepartmentId, cancellationToken); - - if (department is null) - { - throw new KeyNotFoundException("Department does not exist"); - } - - var result = _context.Departments.Remove(department); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Departments/Commands/UpdateDepartment.cs b/src/Application/Departments/Commands/UpdateDepartment.cs new file mode 100644 index 00000000..a080e22d --- /dev/null +++ b/src/Application/Departments/Commands/UpdateDepartment.cs @@ -0,0 +1,14 @@ +using Application.Common.Models.Dtos; +using Application.Users.Queries; +using MediatR; + +namespace Application.Departments.Commands; + +public class UpdateDepartment +{ + public record Command : IRequest + { + public Guid DepartmentId { get; set; } + public string Name { get; init; } = null!; + } +} \ No newline at end of file diff --git a/src/Application/Departments/Commands/UpdateDepartment/UpdateDepartmentCommand.cs b/src/Application/Departments/Commands/UpdateDepartment/UpdateDepartmentCommand.cs deleted file mode 100644 index 941879af..00000000 --- a/src/Application/Departments/Commands/UpdateDepartment/UpdateDepartmentCommand.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Application.Users.Queries; -using MediatR; - -namespace Application.Departments.Commands.UpdateDepartment; - -public record UpdateDepartmentCommand : IRequest -{ - public Guid DepartmentId { get; set; } - public string Name { get; init; } = null!; -} \ No newline at end of file diff --git a/src/Application/Departments/Queries/GetAllDepartments.cs b/src/Application/Departments/Queries/GetAllDepartments.cs new file mode 100644 index 00000000..a4e5d711 --- /dev/null +++ b/src/Application/Departments/Queries/GetAllDepartments.cs @@ -0,0 +1,32 @@ +using System.Collections.ObjectModel; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Departments.Queries; + +public class GetAllDepartments +{ + public record Query : IRequest>; + + public class QueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public QueryHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> Handle(Query request, CancellationToken cancellationToken) + { + var departments = await _context.Departments.ToListAsync(cancellationToken); + var result = new ReadOnlyCollection(_mapper.Map>(departments)); + return result; + } + } +} \ No newline at end of file diff --git a/src/Application/Departments/Queries/GetAllDepartments/GetAllDepartmentsQuery.cs b/src/Application/Departments/Queries/GetAllDepartments/GetAllDepartmentsQuery.cs deleted file mode 100644 index a0af15e1..00000000 --- a/src/Application/Departments/Queries/GetAllDepartments/GetAllDepartmentsQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.ObjectModel; -using Application.Common.Interfaces; -using Application.Users.Queries; -using AutoMapper; -using Domain.Entities; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Departments.Queries.GetAllDepartments; - -public record GetAllDepartmentsQuery : IRequest>; - -public class GetAllDepartmentsQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public GetAllDepartmentsQueryHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task> Handle(GetAllDepartmentsQuery request, CancellationToken cancellationToken) - { - var departments = await _context.Departments.ToListAsync(cancellationToken); - var result = new ReadOnlyCollection(_mapper.Map>(departments)); - return result; - } -} \ No newline at end of file diff --git a/src/Application/Departments/Queries/GetDepartmentById.cs b/src/Application/Departments/Queries/GetDepartmentById.cs new file mode 100644 index 00000000..e4120c64 --- /dev/null +++ b/src/Application/Departments/Queries/GetDepartmentById.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos; +using MediatR; + +namespace Application.Departments.Queries; + +public class GetDepartmentById +{ + public record Query : IRequest + { + public Guid DepartmentId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Departments/Queries/GetDepartmentById/GetDepartmentByIdQuery.cs b/src/Application/Departments/Queries/GetDepartmentById/GetDepartmentByIdQuery.cs deleted file mode 100644 index fe0ec11a..00000000 --- a/src/Application/Departments/Queries/GetDepartmentById/GetDepartmentByIdQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Users.Queries; -using MediatR; - -namespace Application.Departments.Queries.GetDepartmentById; - -public record GetDepartmentByIdQuery : IRequest -{ - public Guid DepartmentId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Documents/Commands/DeleteDocument.cs b/src/Application/Documents/Commands/DeleteDocument.cs new file mode 100644 index 00000000..7b22f634 --- /dev/null +++ b/src/Application/Documents/Commands/DeleteDocument.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Documents.Commands; + +public class DeleteDocument +{ + public record Command : IRequest + { + public Guid DocumentId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Documents/Commands/DeleteDocument/DeleteDocumentCommand.cs b/src/Application/Documents/Commands/DeleteDocument/DeleteDocumentCommand.cs deleted file mode 100644 index eefecf87..00000000 --- a/src/Application/Documents/Commands/DeleteDocument/DeleteDocumentCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Documents.Commands.DeleteDocument; - -public record DeleteDocumentCommand : IRequest -{ - public Guid DocumentId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Documents/Commands/ImportDocument.cs b/src/Application/Documents/Commands/ImportDocument.cs new file mode 100644 index 00000000..2f5e8c02 --- /dev/null +++ b/src/Application/Documents/Commands/ImportDocument.cs @@ -0,0 +1,76 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Documents.Commands; + +public class ImportDocument +{ + public record Command : IRequest + { + public string Title { get; init; } = null!; + public string? Description { get; init; } + public string DocumentType { get; init; } = null!; + public Guid ImporterId { get; init; } + public Guid FolderId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var importer = await _context.Users + .Include(x => x.Department) + .FirstOrDefaultAsync(x => x.Id == request.ImporterId, cancellationToken); + if (importer is null) + { + throw new KeyNotFoundException("User does not exist."); + } + + var document = _context.Documents.FirstOrDefault(x => + x.Title.Trim().ToLower().Equals(request.Title.Trim().ToLower()) + && x.Importer != null + && x.Importer.Id == request.ImporterId); + if (document is not null) + { + throw new ConflictException($"Document title already exists for user {importer.LastName}."); + } + + var folder = await _context.Folders + .FirstOrDefaultAsync(x => x.Id == request.FolderId, cancellationToken); + if (folder is null) + { + throw new KeyNotFoundException("Folder does not exist."); + } + + var entity = new Document() + { + Title = request.Title.Trim(), + Description = request.Description?.Trim(), + DocumentType = request.DocumentType.Trim(), + Importer = importer, + Department = importer.Department, + Folder = folder + }; + + var result = await _context.Documents.AddAsync(entity, cancellationToken); + folder.NumberOfDocuments += 1; + _context.Folders.Update(folder); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Documents/Commands/ImportDocument/ImportDocumentCommand.cs b/src/Application/Documents/Commands/ImportDocument/ImportDocumentCommand.cs deleted file mode 100644 index 7989bf5a..00000000 --- a/src/Application/Documents/Commands/ImportDocument/ImportDocumentCommand.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using Domain.Entities.Physical; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Documents.Commands.ImportDocument; - -public record ImportDocumentCommand : IRequest -{ - public string Title { get; init; } = null!; - public string? Description { get; init; } - public string DocumentType { get; init; } = null!; - public Guid ImporterId { get; init; } - public Guid FolderId { get; init; } -} - -public class ImportDocumentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public ImportDocumentCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(ImportDocumentCommand request, CancellationToken cancellationToken) - { - var importer = await _context.Users - .Include(x => x.Department) - .FirstOrDefaultAsync(x => x.Id == request.ImporterId, cancellationToken); - if (importer is null) - { - throw new KeyNotFoundException("User does not exist."); - } - - var document = _context.Documents.FirstOrDefault(x => - x.Title.Trim().ToLower().Equals(request.Title.Trim().ToLower()) - && x.Importer != null - && x.Importer.Id == request.ImporterId); - if (document is not null) - { - throw new ConflictException($"Document title already exists for user {importer.LastName}."); - } - - var folder = await _context.Folders - .FirstOrDefaultAsync(x => x.Id == request.FolderId, cancellationToken); - if (folder is null) - { - throw new KeyNotFoundException("Folder does not exist."); - } - - var entity = new Document() - { - Title = request.Title.Trim(), - Description = request.Description?.Trim(), - DocumentType = request.DocumentType.Trim(), - Importer = importer, - Department = importer.Department, - Folder = folder - }; - - var result = await _context.Documents.AddAsync(entity, cancellationToken); - folder.NumberOfDocuments += 1; - _context.Folders.Update(folder); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Documents/Commands/UpdateDocument.cs b/src/Application/Documents/Commands/UpdateDocument.cs new file mode 100644 index 00000000..b0cac569 --- /dev/null +++ b/src/Application/Documents/Commands/UpdateDocument.cs @@ -0,0 +1,15 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Documents.Commands; + +public class UpdateDocument +{ + public record Command : IRequest + { + public Guid DocumentId { get; init; } + public string Title { get; init; } = null!; + public string? Description { get; init; } + public string DocumentType { get; init; } = null!; + } +} \ No newline at end of file diff --git a/src/Application/Documents/Commands/UpdateDocument/UpdateDocumentCommand.cs b/src/Application/Documents/Commands/UpdateDocument/UpdateDocumentCommand.cs deleted file mode 100644 index d7b06736..00000000 --- a/src/Application/Documents/Commands/UpdateDocument/UpdateDocumentCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Documents.Commands.UpdateDocument; - -public record UpdateDocumentCommand : IRequest -{ - public Guid DocumentId { get; init; } - public string Title { get; init; } = null!; - public string? Description { get; init; } - public string DocumentType { get; init; } = null!; -} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetAllDocumentTypes.cs b/src/Application/Documents/Queries/GetAllDocumentTypes.cs new file mode 100644 index 00000000..41e53d1b --- /dev/null +++ b/src/Application/Documents/Queries/GetAllDocumentTypes.cs @@ -0,0 +1,26 @@ +using System.Collections.ObjectModel; +using Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Documents.Queries; + +public class GetAllDocumentTypes +{ + public record Query : IRequest>; + + public class QueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public QueryHandler(IApplicationDbContext context) + { + _context = context; + } + public async Task> Handle(Query request, CancellationToken cancellationToken) + { + return new ReadOnlyCollection(await _context.Documents.Select(x => x.DocumentType).Distinct() + .ToListAsync(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetAllDocumentsPaginated.cs b/src/Application/Documents/Queries/GetAllDocumentsPaginated.cs new file mode 100644 index 00000000..c6c34a92 --- /dev/null +++ b/src/Application/Documents/Queries/GetAllDocumentsPaginated.cs @@ -0,0 +1,138 @@ +using Application.Common.Exceptions; +using Application.Common.Extensions; +using Application.Common.Interfaces; +using Application.Common.Mappings; +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Documents.Queries; + +public class GetAllDocumentsPaginated +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.RoomId) + .Must((query, roomId) => + { + if (roomId is null) + { + return query.LockerId is null && query.FolderId is null; + } + + if (query.LockerId is null) + { + return query.FolderId is null; + } + + return true; + }).WithMessage("Container orientation is not consistent"); + } + } + + public record Query : IRequest> + { + public Guid? RoomId { get; init; } + public Guid? LockerId { get; init; } + public Guid? FolderId { get; init; } + public string? SearchTerm { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } + + public class QueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public QueryHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> Handle(Query request, + CancellationToken cancellationToken) + { + var documents = _context.Documents.AsQueryable(); + var roomExists = request.RoomId is not null; + var lockerExists = request.LockerId is not null; + var folderExists = request.FolderId is not null; + + documents = documents.Include(x => x.Department); + + if (folderExists) + { + var folder = await _context.Folders + .Include(x => x.Locker) + .ThenInclude(y => y.Room) + .FirstOrDefaultAsync(x => x.Id == request.FolderId + && x.IsAvailable, cancellationToken); + if (folder is null) + { + throw new KeyNotFoundException("Folder does not exist."); + } + + if (folder.Locker.Id != request.LockerId + || folder.Locker.Room.Id != request.RoomId) + { + throw new ConflictException("Either locker or room does not match folder."); + } + + documents = documents + .Where(x => x.Folder!.Id == request.FolderId); + } + else if (lockerExists) + { + var locker = await _context.Lockers + .Include(x => x.Room) + .FirstOrDefaultAsync(x => x.Id == request.LockerId + && x.IsAvailable, cancellationToken); + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + if (locker.Room.Id != request.RoomId) + { + throw new ConflictException("Room does not match locker."); + } + + documents = documents.Where(x => x.Folder!.Locker.Id == request.LockerId); + } + else if (roomExists) + { + var room = await _context.Rooms + .FirstOrDefaultAsync(x => x.Id == request.RoomId + && x.IsAvailable, cancellationToken); + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + documents = documents.Where(x => x.Folder!.Locker.Room.Id == request.RoomId); + } + + var sortBy = request.SortBy ?? nameof(DocumentDto.Id); + var sortOrder = request.SortOrder ?? "asc"; + var pageNumber = request.Page ?? 1; + var sizeNumber = request.Size ?? 5; + var result = await documents + .ProjectTo(_mapper.ConfigurationProvider) + .OrderByCustom(sortBy, sortOrder) + .PaginatedListAsync(pageNumber, sizeNumber); + + return result; + } + } +} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQuery.cs b/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQuery.cs deleted file mode 100644 index ff2de9ad..00000000 --- a/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQuery.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Extensions; -using Application.Common.Interfaces; -using Application.Common.Mappings; -using Application.Common.Models; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using AutoMapper.QueryableExtensions; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Documents.Queries.GetAllDocumentsPaginated; - -public record GetAllDocumentsPaginatedQuery : IRequest> -{ - public Guid? RoomId { get; init; } - public Guid? LockerId { get; init; } - public Guid? FolderId { get; init; } - public int? Page { get; init; } - public int? Size { get; init; } - public string? SortBy { get; init; } - public string? SortOrder { get; init; } -} - -public class GetAllDocumentsPaginatedQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public GetAllDocumentsPaginatedQueryHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task> Handle(GetAllDocumentsPaginatedQuery request, - CancellationToken cancellationToken) - { - var documents = _context.Documents.AsQueryable(); - var roomExists = request.RoomId is not null; - var lockerExists = request.LockerId is not null; - var folderExists = request.FolderId is not null; - - documents = documents.Include(x => x.Department); - - if (folderExists) - { - var folder = await _context.Folders - .Include(x => x.Locker) - .ThenInclude(y => y.Room) - .FirstOrDefaultAsync(x => x.Id == request.FolderId - && x.IsAvailable, cancellationToken); - if (folder is null) - { - throw new KeyNotFoundException("Folder does not exist."); - } - - if (folder.Locker.Id != request.LockerId - || folder.Locker.Room.Id != request.RoomId) - { - throw new ConflictException("Either locker or room does not match folder."); - } - - documents = documents - .Where(x => x.Folder!.Id == request.FolderId); - } - else if (lockerExists) - { - var locker = await _context.Lockers - .Include(x => x.Room) - .FirstOrDefaultAsync(x => x.Id == request.LockerId - && x.IsAvailable, cancellationToken); - if (locker is null) - { - throw new KeyNotFoundException("Locker does not exist."); - } - - if (locker.Room.Id != request.RoomId) - { - throw new ConflictException("Room does not match locker."); - } - - documents = documents.Where(x => x.Folder!.Locker.Id == request.LockerId); - } - else if (roomExists) - { - var room = await _context.Rooms - .FirstOrDefaultAsync(x => x.Id == request.RoomId - && x.IsAvailable, cancellationToken); - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - documents = documents.Where(x => x.Folder!.Locker.Room.Id == request.RoomId); - } - - var sortBy = request.SortBy ?? nameof(DocumentDto.Id); - var sortOrder = request.SortOrder ?? "asc"; - var pageNumber = request.Page ?? 1; - var sizeNumber = request.Size ?? 5; - var result = await documents - .ProjectTo(_mapper.ConfigurationProvider) - .OrderByCustom(sortBy, sortOrder) - .PaginatedListAsync(pageNumber, sizeNumber); - - return result; - } -} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQueryValidator.cs b/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQueryValidator.cs deleted file mode 100644 index d95c7e3d..00000000 --- a/src/Application/Documents/Queries/GetAllDocumentsPaginated/GetAllDocumentsPaginatedQueryValidator.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentValidation; - -namespace Application.Documents.Queries.GetAllDocumentsPaginated; - -public class GetAllDocumentsPaginatedQueryValidator : AbstractValidator -{ - public GetAllDocumentsPaginatedQueryValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(x => x.RoomId) - .Must((query, roomId) => - { - if (roomId is null) - { - return query.LockerId is null && query.FolderId is null; - } - - if (query.LockerId is null) - { - return query.FolderId is null; - } - - return true; - }).WithMessage("Container orientation is not consistent"); - } -} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetDocumentById.cs b/src/Application/Documents/Queries/GetDocumentById.cs new file mode 100644 index 00000000..bf7c29d7 --- /dev/null +++ b/src/Application/Documents/Queries/GetDocumentById.cs @@ -0,0 +1,44 @@ +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Documents.Queries; + +public class GetDocumentById +{ + public record Query : IRequest + { + public Guid DocumentId { get; init; } + } + + public class QueryHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public QueryHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + public async Task Handle(Query request, CancellationToken cancellationToken) + { + var document = await _context.Documents + .Include(x => x.Department) + .Include(x => x.Importer) + .Include(x => x.Folder) + .ThenInclude(y => y.Locker) + .ThenInclude(z => z.Room) + .FirstOrDefaultAsync(x => x.Id == request.DocumentId, cancellationToken); + + if (document is null) + { + throw new KeyNotFoundException("Document does not exist."); + } + + return _mapper.Map(document); + } + } +} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetDocumentById/GetDocumentByIdQuery.cs b/src/Application/Documents/Queries/GetDocumentById/GetDocumentByIdQuery.cs deleted file mode 100644 index c1f62b70..00000000 --- a/src/Application/Documents/Queries/GetDocumentById/GetDocumentByIdQuery.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Documents.Queries.GetDocumentById; - -public record GetDocumentByIdQuery : IRequest -{ - public Guid Id { get; init; } -} - -public class GetDocumentByIdQueryHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public GetDocumentByIdQueryHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - public async Task Handle(GetDocumentByIdQuery request, CancellationToken cancellationToken) - { - var document = await _context.Documents - .Include(x => x.Department) - .Include(x => x.Importer) - .Include(x => x.Folder) - .ThenInclude(y => y.Locker) - .ThenInclude(z => z.Room) - .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); - - if (document is null) - { - throw new KeyNotFoundException("Document does not exist."); - } - - return _mapper.Map(document); - } -} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetDocumentTypes/GetAllDocumentTypesQuery.cs b/src/Application/Documents/Queries/GetDocumentTypes/GetAllDocumentTypesQuery.cs deleted file mode 100644 index 841aa23e..00000000 --- a/src/Application/Documents/Queries/GetDocumentTypes/GetAllDocumentTypesQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.ObjectModel; -using Application.Common.Interfaces; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Documents.Queries.GetDocumentTypes; - -public record GetAllDocumentTypesQuery : IRequest>; - -public class GetAllDocumentTypesQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - - public GetAllDocumentTypesQueryHandler(IApplicationDbContext context) - { - _context = context; - } - public async Task> Handle(GetAllDocumentTypesQuery request, CancellationToken cancellationToken) - { - return new ReadOnlyCollection(await _context.Documents.Select(x => x.DocumentType).Distinct() - .ToListAsync(cancellationToken)); - } -} \ No newline at end of file diff --git a/src/Application/Documents/Queries/GetDocumentTypes/GetDocumentTypesQuery.cs b/src/Application/Documents/Queries/GetDocumentTypes/GetDocumentTypesQuery.cs deleted file mode 100644 index b6f65030..00000000 --- a/src/Application/Documents/Queries/GetDocumentTypes/GetDocumentTypesQuery.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.ObjectModel; -using Application.Common.Interfaces; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Documents.Queries.GetDocumentTypes; - -public record GetDocumentTypesQuery : IRequest>; - -public class GetDocumentTypesQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - - public GetDocumentTypesQueryHandler(IApplicationDbContext context) - { - _context = context; - } - public async Task> Handle(GetDocumentTypesQuery request, CancellationToken cancellationToken) - { - return new ReadOnlyCollection(await _context.Documents.Select(x => x.DocumentType) - .ToListAsync(cancellationToken)); - } -} \ No newline at end of file diff --git a/src/Application/Folders/Commands/AddFolder.cs b/src/Application/Folders/Commands/AddFolder.cs new file mode 100644 index 00000000..4b7bd9b9 --- /dev/null +++ b/src/Application/Folders/Commands/AddFolder.cs @@ -0,0 +1,95 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using Domain.Exceptions; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Folders.Commands; + +public class AddFolder +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(f => f.Name) + .NotEmpty().WithMessage("Name is required.") + .MaximumLength(64).WithMessage("Name cannot exceed 64 characters."); + + RuleFor(f => f.Description) + .MaximumLength(256).WithMessage("Description cannot exceed 256 characters."); + + RuleFor(f => f.Capacity) + .NotEmpty().WithMessage("Folder capacity is required.") + .GreaterThanOrEqualTo(1).WithMessage("Folder's capacity cannot be less than 1."); + + RuleFor(f => f.LockerId) + .NotEmpty().WithMessage("LockerId is required."); + } + } + + public record Command : IRequest + { + public string Name { get; init; } = null!; + public string? Description { get; init; } + public int Capacity { get; init; } + public Guid LockerId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var locker = await _context.Lockers.FirstOrDefaultAsync(l => l.Id == request.LockerId, cancellationToken); + + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + if (locker.NumberOfFolders >= locker.Capacity) + { + throw new LimitExceededException("This locker cannot accept more folders."); + } + + var folder = await _context.Folders.FirstOrDefaultAsync(x => + x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()) + && x.Locker.Id.Equals(request.LockerId), cancellationToken); + + if (folder is not null) + { + throw new ConflictException("Folder name already exists."); + } + + var entity = new Folder + { + Name = request.Name.Trim(), + Description = request.Description, + NumberOfDocuments = 0, + Capacity = request.Capacity, + Locker = locker, + IsAvailable = true + }; + var result = await _context.Folders.AddAsync(entity, cancellationToken); + locker.NumberOfFolders += 1; + _context.Lockers.Update(locker); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Commands/AddFolder/AddFolderCommand.cs b/src/Application/Folders/Commands/AddFolder/AddFolderCommand.cs deleted file mode 100644 index bf974013..00000000 --- a/src/Application/Folders/Commands/AddFolder/AddFolderCommand.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using Domain.Entities.Physical; -using Domain.Exceptions; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Folders.Commands.AddFolder; - -public record AddFolderCommand : IRequest -{ - public string Name { get; init; } = null!; - public string? Description { get; init; } - public int Capacity { get; init; } - public Guid LockerId { get; init; } -} - -public class AddFolderCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public AddFolderCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(AddFolderCommand request, CancellationToken cancellationToken) - { - var locker = await _context.Lockers.FirstOrDefaultAsync(l => l.Id == request.LockerId, cancellationToken); - - if (locker is null) - { - throw new KeyNotFoundException("Locker does not exist."); - } - - if (locker.NumberOfFolders >= locker.Capacity) - { - throw new LimitExceededException("This locker cannot accept more folders."); - } - - var folder = await _context.Folders.FirstOrDefaultAsync(x => x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()) - && x.Locker.Id.Equals(request.LockerId), cancellationToken); - - if (folder is not null) - { - throw new ConflictException("Folder's name already exists."); - } - - var entity = new Folder - { - Name = request.Name.Trim(), - Description = request.Description, - NumberOfDocuments = 0, - Capacity = request.Capacity, - Locker = locker, - IsAvailable = true - }; - var result = await _context.Folders.AddAsync(entity, cancellationToken); - locker.NumberOfFolders += 1; - _context.Lockers.Update(locker); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} diff --git a/src/Application/Folders/Commands/AddFolder/AddFolderCommandValidator.cs b/src/Application/Folders/Commands/AddFolder/AddFolderCommandValidator.cs deleted file mode 100644 index f7717f54..00000000 --- a/src/Application/Folders/Commands/AddFolder/AddFolderCommandValidator.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentValidation; - -namespace Application.Folders.Commands.AddFolder; - -public class AddFolderCommandValidator : AbstractValidator -{ - public AddFolderCommandValidator() - { - - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(f => f.Name) - .NotEmpty().WithMessage("Name is required.") - .MaximumLength(64).WithMessage("Name cannot exceed 64 characters."); - - RuleFor(f => f.Description) - .MaximumLength(256).WithMessage("Description cannot exceed 256 characters."); - - RuleFor(f => f.Capacity) - .NotEmpty().WithMessage("Folder capacity is required.") - .GreaterThanOrEqualTo(1).WithMessage("Folder's capacity cannot be less than 1."); - - RuleFor(f => f.LockerId) - .NotEmpty().WithMessage("LockerId is required."); - } - -} \ No newline at end of file diff --git a/src/Application/Folders/Commands/DisableFolder.cs b/src/Application/Folders/Commands/DisableFolder.cs new file mode 100644 index 00000000..020bc1fe --- /dev/null +++ b/src/Application/Folders/Commands/DisableFolder.cs @@ -0,0 +1,66 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Folders.Commands; + +public class DisableFolder +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(f => f.FolderId) + .NotEmpty().WithMessage("FolderId is required."); + } + } + + public record Command : IRequest + { + public Guid FolderId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var folder = await _context.Folders + .FirstOrDefaultAsync(f => f.Id.Equals(request.FolderId), cancellationToken); + + if (folder is null) + { + throw new KeyNotFoundException("Folder does not exist."); + } + + if (!folder.IsAvailable) + { + throw new ConflictException("Folder has already been disabled."); + } + + if (folder.NumberOfDocuments > 0) + { + throw new InvalidOperationException("Folder cannot be disabled because it contains documents."); + } + + folder.IsAvailable = false; + _context.Folders.Update(folder); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(folder); + } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Commands/DisableFolder/DisableFolderCommand.cs b/src/Application/Folders/Commands/DisableFolder/DisableFolderCommand.cs deleted file mode 100644 index adf845df..00000000 --- a/src/Application/Folders/Commands/DisableFolder/DisableFolderCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Folders.Commands.DisableFolder; - -public record DisableFolderCommand : IRequest -{ - public Guid FolderId { get; init; } -} - -public class DisableFolderCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public DisableFolderCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(DisableFolderCommand request, CancellationToken cancellationToken) - { - var folder = await _context.Folders - .FirstOrDefaultAsync(f => f.Id.Equals(request.FolderId), cancellationToken); - - if (folder is null) - { - throw new KeyNotFoundException("Folder does not exist."); - } - - if (!folder.IsAvailable) - { - throw new ConflictException("Folder has already been disabled."); - } - - if (folder.NumberOfDocuments > 0) - { - throw new InvalidOperationException("Folder cannot be disabled because it contains documents."); - } - - folder.IsAvailable = false; - _context.Folders.Update(folder); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(folder); - } -} \ No newline at end of file diff --git a/src/Application/Folders/Commands/DisableFolder/DisableFolderCommandValidator.cs b/src/Application/Folders/Commands/DisableFolder/DisableFolderCommandValidator.cs deleted file mode 100644 index 04621166..00000000 --- a/src/Application/Folders/Commands/DisableFolder/DisableFolderCommandValidator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FluentValidation; - -namespace Application.Folders.Commands.DisableFolder; - -public class DisableFolderCommandValidator : AbstractValidator -{ - public DisableFolderCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(f => f.FolderId) - .NotEmpty().WithMessage("FolderId is required."); - } -} \ No newline at end of file diff --git a/src/Application/Folders/Commands/EnableFolder.cs b/src/Application/Folders/Commands/EnableFolder.cs new file mode 100644 index 00000000..33e3cb23 --- /dev/null +++ b/src/Application/Folders/Commands/EnableFolder.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Folders.Commands; + +public class EnableFolder +{ + public record Command : IRequest + { + public Guid FolderId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Commands/EnableFolder/EnableFolderCommand.cs b/src/Application/Folders/Commands/EnableFolder/EnableFolderCommand.cs deleted file mode 100644 index eb7f49f9..00000000 --- a/src/Application/Folders/Commands/EnableFolder/EnableFolderCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Folders.Commands.EnableFolder; - -public record EnableFolderCommand : IRequest -{ - public Guid FolderId { get; init; } -} diff --git a/src/Application/Folders/Commands/RemoveFolder.cs b/src/Application/Folders/Commands/RemoveFolder.cs new file mode 100644 index 00000000..528184ce --- /dev/null +++ b/src/Application/Folders/Commands/RemoveFolder.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Folders.Commands; + +public class RemoveFolder +{ + public record Command : IRequest + { + public Guid FolderId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Commands/RemoveFolder/RemoveFolderCommand.cs b/src/Application/Folders/Commands/RemoveFolder/RemoveFolderCommand.cs deleted file mode 100644 index 91db8b5e..00000000 --- a/src/Application/Folders/Commands/RemoveFolder/RemoveFolderCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Folders.Commands.RemoveFolder; - -public record RemoveFolderCommand : IRequest -{ - public Guid FolderId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Folders/Commands/UpdateFolder.cs b/src/Application/Folders/Commands/UpdateFolder.cs new file mode 100644 index 00000000..a467f7e3 --- /dev/null +++ b/src/Application/Folders/Commands/UpdateFolder.cs @@ -0,0 +1,15 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Folders.Commands; + +public class UpdateFolder +{ + public record Command : IRequest + { + public Guid FolderId { get; init; } + public string Name { get; init; } = null!; + public string? Description { get; init; } + public int Capacity { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Commands/UpdateFolder/UpdateFolderCommand.cs b/src/Application/Folders/Commands/UpdateFolder/UpdateFolderCommand.cs deleted file mode 100644 index 30ebea09..00000000 --- a/src/Application/Folders/Commands/UpdateFolder/UpdateFolderCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Folders.Commands.UpdateFolder; - -public record UpdateFolderCommand : IRequest -{ - public Guid FolderId { get; init; } - public string Name { get; init; } = null!; - public string? Description { get; init; } - public int Capacity { get; init; } -} \ No newline at end of file diff --git a/src/Application/Folders/Queries/GetAllFoldersPaginated.cs b/src/Application/Folders/Queries/GetAllFoldersPaginated.cs new file mode 100644 index 00000000..280a4c5c --- /dev/null +++ b/src/Application/Folders/Queries/GetAllFoldersPaginated.cs @@ -0,0 +1,18 @@ +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Folders.Queries; + +public class GetAllFoldersPaginated +{ + public record Query : IRequest> + { + public Guid? RoomId { get; init; } + public Guid? LockerId { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Queries/GetAllFoldersPaginated/GetAllFoldersPaginatedQuery.cs b/src/Application/Folders/Queries/GetAllFoldersPaginated/GetAllFoldersPaginatedQuery.cs deleted file mode 100644 index ecd9fbb2..00000000 --- a/src/Application/Folders/Queries/GetAllFoldersPaginated/GetAllFoldersPaginatedQuery.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Application.Common.Models; -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Folders.Queries.GetAllFoldersPaginated; - -public record GetAllFoldersPaginatedQuery : IRequest> -{ - public Guid? RoomId { get; init; } - public Guid? LockerId { get; init; } - public int? Page { get; init; } - public int? Size { get; init; } - public string? SortBy { get; init; } - public string? SortOrder { get; init; } -} \ No newline at end of file diff --git a/src/Application/Folders/Queries/GetFolderById.cs b/src/Application/Folders/Queries/GetFolderById.cs new file mode 100644 index 00000000..fd0d0b57 --- /dev/null +++ b/src/Application/Folders/Queries/GetFolderById.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Folders.Queries; + +public class GetFolderById +{ + public record Query : IRequest + { + public Guid FolderId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Folders/Queries/GetFolderById/GetFolderByIdQuery.cs b/src/Application/Folders/Queries/GetFolderById/GetFolderByIdQuery.cs deleted file mode 100644 index bf422997..00000000 --- a/src/Application/Folders/Queries/GetFolderById/GetFolderByIdQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Folders.Queries.GetFolderById; - -public record GetFolderByIdQuery : IRequest -{ - public Guid FolderId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Identity/IdentityData.cs b/src/Application/Identity/IdentityData.cs index a9742b72..6e69dbb7 100644 --- a/src/Application/Identity/IdentityData.cs +++ b/src/Application/Identity/IdentityData.cs @@ -2,15 +2,10 @@ namespace Application.Identity; public static class IdentityData { - public static class Claims - { - public const string Role = "role"; - public const string Department = "department"; - } - public static class Roles { public const string Admin = "Admin"; public const string Staff = "Staff"; + public const string Employee = "Employee"; } } \ No newline at end of file diff --git a/src/Application/Lockers/Commands/AddLocker.cs b/src/Application/Lockers/Commands/AddLocker.cs new file mode 100644 index 00000000..78cbb371 --- /dev/null +++ b/src/Application/Lockers/Commands/AddLocker.cs @@ -0,0 +1,96 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using Domain.Exceptions; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Lockers.Commands; + +public class AddLocker +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Capacity) + .GreaterThan(0).WithMessage("Locker's capacity cannot be less than 1"); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Locker's name is required.") + .MaximumLength(64).WithMessage("Locker's name cannot exceed 64 characters."); + + RuleFor(x => x.Description) + .MaximumLength(256).WithMessage("Locker's description cannot exceed 256 characters."); + + RuleFor(x => x.RoomId) + .NotEmpty().WithMessage("RoomId is required."); + } + } + + public record Command : IRequest + { + public string Name { get; init; } = null!; + public string? Description { get; init; } + public Guid RoomId { get; init; } + public int Capacity { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); + + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + if (room.NumberOfLockers >= room.Capacity) + { + throw new LimitExceededException( + "This room cannot accept more lockers." + ); + } + + var locker = await _context.Lockers.FirstOrDefaultAsync( + x => x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()) && x.Room.Id.Equals(request.RoomId), + cancellationToken); + if (locker is not null) + { + throw new ConflictException("Locker name already exists."); + } + + var entity = new Locker + { + Name = request.Name.Trim(), + Description = request.Description?.Trim(), + NumberOfFolders = 0, + Capacity = request.Capacity, + Room = room, + IsAvailable = true + }; + + var result = await _context.Lockers.AddAsync(entity, cancellationToken); + room.NumberOfLockers += 1; + _context.Rooms.Update(room); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/AddLocker/AddLockerCommand.cs b/src/Application/Lockers/Commands/AddLocker/AddLockerCommand.cs deleted file mode 100644 index 7a47bd87..00000000 --- a/src/Application/Lockers/Commands/AddLocker/AddLockerCommand.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using Domain.Entities.Physical; -using Domain.Exceptions; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Lockers.Commands.AddLocker; - -public record AddLockerCommand : IRequest -{ - public string Name { get; init; } = null!; - public string? Description { get; init; } - public Guid RoomId { get; init; } - public int Capacity { get; init; } -} - -public class AddLockerCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public AddLockerCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(AddLockerCommand request, CancellationToken cancellationToken) - { - var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); - - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - if (room.NumberOfLockers >= room.Capacity) - { - throw new LimitExceededException( - "This room cannot accept more lockers." - ); - } - - var locker = await _context.Lockers.FirstOrDefaultAsync(x => x.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()) && x.Room.Id.Equals(request.RoomId) ,cancellationToken); - if (locker is not null) - { - throw new ConflictException("Locker name already exists."); - } - - var entity = new Locker - { - Name = request.Name.Trim(), - Description = request.Description?.Trim(), - NumberOfFolders = 0, - Capacity = request.Capacity, - Room = room, - IsAvailable = true - }; - - var result = await _context.Lockers.AddAsync(entity, cancellationToken); - room.NumberOfLockers += 1; - _context.Rooms.Update(room); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/AddLocker/AddLockerCommandValidator.cs b/src/Application/Lockers/Commands/AddLocker/AddLockerCommandValidator.cs deleted file mode 100644 index 4a919d32..00000000 --- a/src/Application/Lockers/Commands/AddLocker/AddLockerCommandValidator.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Application.Common.Interfaces; -using FluentValidation; - -namespace Application.Lockers.Commands.AddLocker; - -public class AddLockerCommandValidator : AbstractValidator -{ - - public AddLockerCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - RuleFor(x => x.Capacity) - .GreaterThan(0).WithMessage("Locker's capacity cannot be less than 1"); - - RuleFor(x => x.Name) - .NotEmpty().WithMessage("Locker's name is required.") - .MaximumLength(64).WithMessage("Locker's name cannot exceed 64 characters."); - - RuleFor(x => x.Description) - .MaximumLength(256).WithMessage("Locker's description cannot exceed 256 characters."); - - RuleFor(x => x.RoomId) - .NotEmpty().WithMessage("RoomId is required."); - } -} diff --git a/src/Application/Lockers/Commands/DisableLocker.cs b/src/Application/Lockers/Commands/DisableLocker.cs new file mode 100644 index 00000000..eaa4f40b --- /dev/null +++ b/src/Application/Lockers/Commands/DisableLocker.cs @@ -0,0 +1,78 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Lockers.Commands; + +public class DisableLocker +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.LockerId) + .NotEmpty().WithMessage("LockerId is required."); + } + } + + public record Command : IRequest + { + public Guid LockerId { get; init; } + } + + public class RemoveLockerCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var locker = await _context.Lockers + .FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken); + + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + if (!locker.IsAvailable) + { + throw new ConflictException("Locker has already been disabled."); + } + + var canNotDisable = await _context.Documents + .CountAsync(x => x.Folder!.Locker.Id.Equals(request.LockerId), cancellationToken) + > 0; + + if (canNotDisable) + { + throw new InvalidOperationException("Locker cannot be disabled because it contains documents."); + } + + var folders = _context.Folders.Where(x => x.Locker.Room.Id.Equals(locker.Id)); + + foreach (var folder in folders) + { + folder.IsAvailable = false; + } + _context.Folders.UpdateRange(folders); + + locker.IsAvailable = false; + var result = _context.Lockers.Update(locker); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommand.cs b/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommand.cs deleted file mode 100644 index e3785e25..00000000 --- a/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommand.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Lockers.Commands.DisableLocker; - -public record DisableLockerCommand : IRequest -{ - public Guid LockerId { get; init; } -} - -public class RemoveLockerCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(DisableLockerCommand request, CancellationToken cancellationToken) - { - var locker = await _context.Lockers - .FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken); - - if (locker is null) - { - throw new KeyNotFoundException("Locker does not exist."); - } - - if (!locker.IsAvailable) - { - throw new ConflictException("Locker has already been disabled."); - } - - var canNotDisable = await _context.Documents - .CountAsync(x => x.Folder!.Locker.Id.Equals(request.LockerId), cancellationToken) - > 0; - - if (canNotDisable) - { - throw new InvalidOperationException("Locker cannot be disabled because it contains documents."); - } - - var folders = _context.Folders.Where(x => x.Locker.Room.Id.Equals(locker.Id)); - - foreach (var folder in folders) - { - folder.IsAvailable = false; - } - _context.Folders.UpdateRange(folders); - - locker.IsAvailable = false; - var result = _context.Lockers.Update(locker); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommandValidator.cs b/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommandValidator.cs deleted file mode 100644 index e6192be6..00000000 --- a/src/Application/Lockers/Commands/DisableLocker/DisableLockerCommandValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace Application.Lockers.Commands.DisableLocker; - -public class DisableLockerCommandValidator : AbstractValidator -{ - public DisableLockerCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - RuleFor(x => x.LockerId) - .NotEmpty().WithMessage("Locker Id is required."); - } -} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/EnableLocker.cs b/src/Application/Lockers/Commands/EnableLocker.cs new file mode 100644 index 00000000..32dee1aa --- /dev/null +++ b/src/Application/Lockers/Commands/EnableLocker.cs @@ -0,0 +1,60 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Lockers.Commands; + +public class EnableLocker +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.LockerId) + .NotEmpty().WithMessage("LockerId is required."); + } + } + + public record Command : IRequest + { + public Guid LockerId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var locker = await _context.Lockers + .FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken); + if (locker is null) + { + throw new KeyNotFoundException("Locker does not exist."); + } + + if (locker.IsAvailable) + { + throw new ConflictException("Locker has already been enabled."); + } + + locker.IsAvailable = true; + var result = _context.Lockers.Update(locker); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommand.cs b/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommand.cs deleted file mode 100644 index 4c1cb485..00000000 --- a/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommand.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using Domain.Exceptions; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Lockers.Commands.EnableLocker; - -public record EnableLockerCommand : IRequest -{ - public Guid LockerId { get; init; } -} - -public class EnableLockerCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public EnableLockerCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(EnableLockerCommand request, CancellationToken cancellationToken) - { - var locker = await _context.Lockers - .FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken); - if (locker is null) - { - throw new KeyNotFoundException("Locker does not exist."); - } - - if (locker.IsAvailable) - { - throw new ConflictException("Locker has already been enabled."); - } - - locker.IsAvailable = true; - var result = _context.Lockers.Update(locker); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} diff --git a/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommandValidator.cs b/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommandValidator.cs deleted file mode 100644 index 2fcbbec9..00000000 --- a/src/Application/Lockers/Commands/EnableLocker/EnableLockerCommandValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace Application.Lockers.Commands.EnableLocker; - -public class EnableLockerCommandValidator : AbstractValidator -{ - public EnableLockerCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - RuleFor(x => x.LockerId) - .NotEmpty().WithMessage("Locker Id is required."); - } -} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/RemoveLocker.cs b/src/Application/Lockers/Commands/RemoveLocker.cs new file mode 100644 index 00000000..11c6472b --- /dev/null +++ b/src/Application/Lockers/Commands/RemoveLocker.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Lockers.Commands; + +public class RemoveLocker +{ + public record Command : IRequest + { + public Guid LockerId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/RemoveLocker/RemoveLockerCommand.cs b/src/Application/Lockers/Commands/RemoveLocker/RemoveLockerCommand.cs deleted file mode 100644 index 412df4b7..00000000 --- a/src/Application/Lockers/Commands/RemoveLocker/RemoveLockerCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Lockers.Commands.RemoveLocker; - -public record RemoveLockerCommand : IRequest -{ - public Guid LockerId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/UpdateLocker.cs b/src/Application/Lockers/Commands/UpdateLocker.cs new file mode 100644 index 00000000..580c1e6f --- /dev/null +++ b/src/Application/Lockers/Commands/UpdateLocker.cs @@ -0,0 +1,15 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Lockers.Commands; + +public class UpdateLocker +{ + public record Command : IRequest + { + public Guid LockerId { get; init; } + public string Name { get; init; } = null!; + public string? Description { get; init; } + public int Capacity { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Commands/UpdateLocker/UpdateLockerCommand.cs b/src/Application/Lockers/Commands/UpdateLocker/UpdateLockerCommand.cs deleted file mode 100644 index 540ceeef..00000000 --- a/src/Application/Lockers/Commands/UpdateLocker/UpdateLockerCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Lockers.Commands.UpdateLocker; - -public record UpdateLockerCommand : IRequest -{ - public Guid LockerId { get; set; } - public string Name { get; set; } = null!; - public string? Description { get; set; } - public int Capacity { get; init; } -} \ No newline at end of file diff --git a/src/Application/Lockers/Queries/GetAllLockersPaginated.cs b/src/Application/Lockers/Queries/GetAllLockersPaginated.cs new file mode 100644 index 00000000..f7935335 --- /dev/null +++ b/src/Application/Lockers/Queries/GetAllLockersPaginated.cs @@ -0,0 +1,17 @@ +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Lockers.Queries; + +public class GetAllLockersPaginated +{ + public record Query : IRequest> + { + public Guid? RoomId { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Queries/GetAllLockersPaginated/GetAllLockersPaginatedQuery.cs b/src/Application/Lockers/Queries/GetAllLockersPaginated/GetAllLockersPaginatedQuery.cs deleted file mode 100644 index 4e0e8eb7..00000000 --- a/src/Application/Lockers/Queries/GetAllLockersPaginated/GetAllLockersPaginatedQuery.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Application.Common.Models; -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Lockers.Queries.GetAllLockersPaginated; - -public record GetAllLockersPaginatedQuery : IRequest> -{ - public Guid? RoomId { get; init; } - public int? Page { get; init; } - public int? Size { get; init; } - public string? SortBy { get; init; } - public string? SortOrder { get; init; } -} \ No newline at end of file diff --git a/src/Application/Lockers/Queries/GetLockerById.cs b/src/Application/Lockers/Queries/GetLockerById.cs new file mode 100644 index 00000000..65c31416 --- /dev/null +++ b/src/Application/Lockers/Queries/GetLockerById.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Lockers.Queries; + +public class GetLockerById +{ + public record Query : IRequest + { + public Guid LockerId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Lockers/Queries/GetLockerById/GetLockerByIdQuery.cs b/src/Application/Lockers/Queries/GetLockerById/GetLockerByIdQuery.cs deleted file mode 100644 index 1a592104..00000000 --- a/src/Application/Lockers/Queries/GetLockerById/GetLockerByIdQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Lockers.Queries.GetLockerById; - -public record GetLockerByIdQuery : IRequest -{ - public Guid LockerId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/AddRoom.cs b/src/Application/Rooms/Commands/AddRoom.cs new file mode 100644 index 00000000..a317d7cf --- /dev/null +++ b/src/Application/Rooms/Commands/AddRoom.cs @@ -0,0 +1,91 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Rooms.Commands; + +public class AddRoom +{ + public class Validator : AbstractValidator + { + private readonly IApplicationDbContext _context; + public Validator(IApplicationDbContext context) + { + _context = context; + + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Capacity) + .GreaterThanOrEqualTo(1).WithMessage("Room's capacity cannot be less than 1"); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .MaximumLength(64).WithMessage("Name cannot exceed 64 characters.") + .Must(BeUnique).WithMessage("Room name already exists."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Description is required.") + .MaximumLength(256).WithMessage("Description cannot exceed 256 characters."); + } + + private bool BeUnique(string name) + { + return _context.Rooms.FirstOrDefault(x => x.Name.Equals(name)) is null; + } + } + + public record Command : IRequest + { + public string Name { get; init; } = null!; + public string? Description { get; init; } + public int Capacity { get; init; } + public Guid DepartmentId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var department = + await _context.Departments.FirstOrDefaultAsync(x => x.Id == request.DepartmentId, cancellationToken); + + if (department is null) + { + throw new KeyNotFoundException("Department does not exists."); + } + + var room = await _context.Rooms.FirstOrDefaultAsync(r => + r.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()), cancellationToken); + + if (room is not null) + { + throw new ConflictException("Room name already exists."); + } + + var entity = new Room + { + Name = request.Name.Trim(), + Description = request.Description?.Trim(), + NumberOfLockers = 0, + Capacity = request.Capacity, + Department = department, + }; + var result = await _context.Rooms.AddAsync(entity, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/AddRoom/AddRoomCommand.cs b/src/Application/Rooms/Commands/AddRoom/AddRoomCommand.cs deleted file mode 100644 index f255b988..00000000 --- a/src/Application/Rooms/Commands/AddRoom/AddRoomCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using Domain.Entities.Physical; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Rooms.Commands.AddRoom; - -public record AddRoomCommand : IRequest -{ - public string Name { get; init; } = null!; - public string? Description { get; init; } - public int Capacity { get; init; } - -} - -public class AddRoomCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public AddRoomCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(AddRoomCommand request, CancellationToken cancellationToken) - { - - var room = await _context.Rooms.FirstOrDefaultAsync(r => - r.Name.Trim().ToLower().Equals(request.Name.Trim().ToLower()), cancellationToken); - - if (room is not null) - { - throw new ConflictException("Room name already exists."); - } - - var entity = new Room - { - Name = request.Name.Trim(), - Description = request.Description?.Trim(), - NumberOfLockers = 0, - Capacity = request.Capacity - }; - var result = await _context.Rooms.AddAsync(entity, cancellationToken); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/AddRoom/AddRoomCommandValidator.cs b/src/Application/Rooms/Commands/AddRoom/AddRoomCommandValidator.cs deleted file mode 100644 index bfa65d1a..00000000 --- a/src/Application/Rooms/Commands/AddRoom/AddRoomCommandValidator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Application.Common.Interfaces; -using FluentValidation; - -namespace Application.Rooms.Commands.AddRoom; - -public class AddRoomCommandValidator : AbstractValidator -{ - private readonly IApplicationDbContext _context; - public AddRoomCommandValidator(IApplicationDbContext context) - { - _context = context; - - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(x => x.Capacity) - .GreaterThanOrEqualTo(1).WithMessage("Room's capacity cannot be less than 1"); - - RuleFor(x => x.Name) - .NotEmpty().WithMessage("Name is required.") - .MaximumLength(64).WithMessage("Name cannot exceed 64 characters.") - .Must(BeUnique).WithMessage("Room name already exists."); - - RuleFor(x => x.Description) - .NotEmpty().WithMessage("Description is required.") - .MaximumLength(256).WithMessage("Description cannot exceed 256 characters."); - } - - private bool BeUnique(string name) - { - return _context.Rooms.FirstOrDefault(x => x.Name.Equals(name)) is null; - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/DisableRoom.cs b/src/Application/Rooms/Commands/DisableRoom.cs new file mode 100644 index 00000000..580b62c6 --- /dev/null +++ b/src/Application/Rooms/Commands/DisableRoom.cs @@ -0,0 +1,84 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Rooms.Commands; + +public class DisableRoom +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.RoomId) + .NotEmpty().WithMessage("RoomId is required."); + } + } + + public record Command : IRequest + { + public Guid RoomId { get; init; } + } + + public class DisableRoomCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public DisableRoomCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var room = await _context.Rooms + .Include(x => x.Lockers) + .ThenInclude(y => y.Folders) + .FirstOrDefaultAsync(x => x.Id.Equals(request.RoomId), cancellationToken: cancellationToken); + + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + if (!room.IsAvailable) + { + throw new ConflictException("Room have already been disabled."); + } + + var canNotDisable = await _context.Documents + .CountAsync(x => x.Folder!.Locker.Room.Id.Equals(request.RoomId), cancellationToken) + > 0; + + if (canNotDisable) + { + throw new InvalidOperationException("Room cannot be disabled because it contains documents."); + } + + var lockers = _context.Lockers.Include(x=> x.Folders) + .Where(x => x.Room.Id.Equals(room.Id)); + + foreach (var locker in lockers) + { + foreach (var folder in locker.Folders) + { + folder.IsAvailable = false; + } + locker.IsAvailable = false; + } + _context.Lockers.UpdateRange(lockers); + room.IsAvailable = false; + var result = _context.Rooms.Update(room); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommand.cs b/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommand.cs deleted file mode 100644 index 91ca9bdb..00000000 --- a/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommand.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Rooms.Commands.DisableRoom; - -public record DisableRoomCommand : IRequest -{ - public Guid RoomId { get; init; } -} - -public class DisableRoomCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public DisableRoomCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(DisableRoomCommand request, CancellationToken cancellationToken) - { - var room = await _context.Rooms - .Include(x => x.Lockers) - .ThenInclude(y => y.Folders) - .FirstOrDefaultAsync(x => x.Id.Equals(request.RoomId), cancellationToken: cancellationToken); - - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - if (!room.IsAvailable) - { - throw new ConflictException("Room have already been disabled."); - } - - var canNotDisable = await _context.Documents - .CountAsync(x => x.Folder!.Locker.Room.Id.Equals(request.RoomId), cancellationToken) - > 0; - - if (canNotDisable) - { - throw new InvalidOperationException("Room cannot be disabled because it contains documents."); - } - - var lockers = _context.Lockers.Include(x=> x.Folders) - .Where(x => x.Room.Id.Equals(room.Id)); - - foreach (var locker in lockers) - { - foreach (var folder in locker.Folders) - { - folder.IsAvailable = false; - } - locker.IsAvailable = false; - } - _context.Lockers.UpdateRange(lockers); - room.IsAvailable = false; - var result = _context.Rooms.Update(room); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommandValidator.cs b/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommandValidator.cs deleted file mode 100644 index de317f88..00000000 --- a/src/Application/Rooms/Commands/DisableRoom/DisableRoomCommandValidator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FluentValidation; - -namespace Application.Rooms.Commands.DisableRoom; - -public class DisableRoomCommandValidator : AbstractValidator -{ - public DisableRoomCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(x => x.RoomId) - .NotEmpty().WithMessage("RoomId is required."); - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/EnableRoom.cs b/src/Application/Rooms/Commands/EnableRoom.cs new file mode 100644 index 00000000..0e0cf048 --- /dev/null +++ b/src/Application/Rooms/Commands/EnableRoom.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Rooms.Commands; + +public class EnableRoom +{ + public record Command : IRequest + { + public Guid RoomId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/EnableRoom/EnableRoomCommand.cs b/src/Application/Rooms/Commands/EnableRoom/EnableRoomCommand.cs deleted file mode 100644 index d89c52a5..00000000 --- a/src/Application/Rooms/Commands/EnableRoom/EnableRoomCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Rooms.Commands.EnableRoom; - -public record EnableRoomCommand : IRequest -{ - public Guid RoomId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/RemoveRoom.cs b/src/Application/Rooms/Commands/RemoveRoom.cs new file mode 100644 index 00000000..ef079f47 --- /dev/null +++ b/src/Application/Rooms/Commands/RemoveRoom.cs @@ -0,0 +1,63 @@ +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Rooms.Commands; + +public class RemoveRoom +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.RoomId) + .NotEmpty().WithMessage("RoomId is required."); + } + } + + public record Command : IRequest + { + public Guid RoomId { get; init; } + } + + public class RemoveRoomCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public RemoveRoomCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var room = await _context.Rooms + .FirstOrDefaultAsync(x => x.Id.Equals(request.RoomId), cancellationToken: cancellationToken); + + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + var canNotRemove = await _context.Documents + .CountAsync(x => x.Folder!.Locker.Room.Id.Equals(request.RoomId), cancellationToken: cancellationToken) + > 0; + + if (canNotRemove) + { + throw new InvalidOperationException("Room cannot be removed because it contains documents."); + } + + var result = _context.Rooms.Remove(room); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommand.cs b/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommand.cs deleted file mode 100644 index 43e7f575..00000000 --- a/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommand.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Application.Common.Interfaces; -using Application.Common.Models.Dtos.Physical; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Rooms.Commands.RemoveRoom; - -public record RemoveRoomCommand : IRequest -{ - public Guid RoomId { get; init; } -} - -public class RemoveRoomCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public RemoveRoomCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(RemoveRoomCommand request, CancellationToken cancellationToken) - { - var room = await _context.Rooms - .FirstOrDefaultAsync(x => x.Id.Equals(request.RoomId), cancellationToken: cancellationToken); - - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - var canNotRemove = await _context.Documents - .CountAsync(x => x.Folder!.Locker.Room.Id.Equals(request.RoomId), cancellationToken: cancellationToken) - > 0; - - if (canNotRemove) - { - throw new InvalidOperationException("Room cannot be removed because it contains documents."); - } - - room.IsAvailable = false; - var result = _context.Rooms.Remove(room); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommandValidator.cs b/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommandValidator.cs deleted file mode 100644 index d138ba88..00000000 --- a/src/Application/Rooms/Commands/RemoveRoom/RemoveRoomCommandValidator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FluentValidation; - -namespace Application.Rooms.Commands.RemoveRoom; - -public class RemoveRoomCommandValidator : AbstractValidator -{ - public RemoveRoomCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(x => x.RoomId) - .NotEmpty().WithMessage("RoomId is required."); - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/UpdateRoom.cs b/src/Application/Rooms/Commands/UpdateRoom.cs new file mode 100644 index 00000000..3dc20417 --- /dev/null +++ b/src/Application/Rooms/Commands/UpdateRoom.cs @@ -0,0 +1,15 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Rooms.Commands; + +public class UpdateRoom +{ + public record Command : IRequest + { + public Guid RoomId { get; init; } + public string Name { get; set; } = null!; + public string? Description { get; init; } + public int Capacity { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Commands/UpdateRoom/UpdateRoomCommand.cs b/src/Application/Rooms/Commands/UpdateRoom/UpdateRoomCommand.cs deleted file mode 100644 index 2e603d9d..00000000 --- a/src/Application/Rooms/Commands/UpdateRoom/UpdateRoomCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Rooms.Commands.UpdateRoom; - -public record UpdateRoomCommand : IRequest -{ - public Guid RoomId { get; init; } - public string Name { get; set; } = null!; - public string? Description { get; init; } - public int Capacity { get; init; } -} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetAllRoomPaginated/GetAllRoomsPaginatedQuery.cs b/src/Application/Rooms/Queries/GetAllRoomPaginated/GetAllRoomsPaginatedQuery.cs deleted file mode 100644 index c691570f..00000000 --- a/src/Application/Rooms/Queries/GetAllRoomPaginated/GetAllRoomsPaginatedQuery.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Application.Common.Models; -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Rooms.Queries.GetAllRoomPaginated; - -public record GetAllRoomsPaginatedQuery : IRequest> -{ - public int? Page { get; init; } - public int? Size { get; init; } - public string? SortBy { get; init; } - public string? SortOrder { get; init; } -} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs b/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs new file mode 100644 index 00000000..8094f631 --- /dev/null +++ b/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs @@ -0,0 +1,16 @@ +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Rooms.Queries; + +public class GetAllRoomsPaginated +{ + public record Query : IRequest> + { + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetEmptyContainersPaginated.cs b/src/Application/Rooms/Queries/GetEmptyContainersPaginated.cs new file mode 100644 index 00000000..3534dacb --- /dev/null +++ b/src/Application/Rooms/Queries/GetEmptyContainersPaginated.cs @@ -0,0 +1,54 @@ +using Application.Common.Interfaces; +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Rooms.Queries; + +public class GetEmptyContainersPaginated +{ + public record Query : IRequest> + { + public Guid RoomId { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + } + + public class QueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + public QueryHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> Handle(Query request, CancellationToken cancellationToken) + { + var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); + if (room is null) + { + throw new KeyNotFoundException("Room does not exist."); + } + + var pageNumber = request.Page ?? 1; + var sizeNumber = request.Size ?? 5; + var lockers = _context.Lockers + .Where(x => x.Room.Id == request.RoomId + && x.IsAvailable + && x.Folders.Any(y => y.Capacity > y.NumberOfDocuments && y.IsAvailable)) + .ProjectTo(_mapper.ConfigurationProvider) + .AsEnumerable() + .ToList(); + + lockers.ForEach(x => x.Folders = x.Folders.Where(y => y.Slot > 0)); + + var result = new PaginatedList(lockers.ToList(), lockers.Count, pageNumber, sizeNumber); + return result; + } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/GetEmptyContainersPaginatedQuery.cs b/src/Application/Rooms/Queries/GetEmptyContainersPaginated/GetEmptyContainersPaginatedQuery.cs deleted file mode 100644 index 166881aa..00000000 --- a/src/Application/Rooms/Queries/GetEmptyContainersPaginated/GetEmptyContainersPaginatedQuery.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Application.Common.Interfaces; -using Application.Common.Models; -using AutoMapper; -using AutoMapper.QueryableExtensions; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Rooms.Queries.GetEmptyContainersPaginated; - -public record GetEmptyContainersPaginatedQuery : IRequest> -{ - public Guid RoomId { get; init; } - public int Page { get; init; } - public int Size { get; init; } -} - -public class GetEmptyContainersPaginatedQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public GetEmptyContainersPaginatedQueryHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task> Handle(GetEmptyContainersPaginatedQuery request, CancellationToken cancellationToken) - { - var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - var lockers = _context.Lockers - .Where(x => x.Room.Id == request.RoomId - && x.IsAvailable - && x.Folders.Any(y => y.Capacity > y.NumberOfDocuments && y.IsAvailable)) - .ProjectTo(_mapper.ConfigurationProvider) - .AsEnumerable() - .ToList(); - - lockers.ForEach(x => x.Folders = x.Folders.Where(y => y.Slot > 0)); - - var result = new PaginatedList(lockers.ToList(), lockers.Count(), request.Page, request.Size); - return result; - } -} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetRoomById.cs b/src/Application/Rooms/Queries/GetRoomById.cs new file mode 100644 index 00000000..6a3657bd --- /dev/null +++ b/src/Application/Rooms/Queries/GetRoomById.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Rooms.Queries; + +public class GetRoomById +{ + public record Query : IRequest + { + public Guid RoomId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Rooms/Queries/GetRoomById/GetRoomByIdQuery.cs b/src/Application/Rooms/Queries/GetRoomById/GetRoomByIdQuery.cs deleted file mode 100644 index 638e9b51..00000000 --- a/src/Application/Rooms/Queries/GetRoomById/GetRoomByIdQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Common.Models.Dtos.Physical; -using MediatR; - -namespace Application.Rooms.Queries.GetRoomById; - -public record GetRoomByIdQuery : IRequest -{ - public Guid RoomId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Staffs/Commands/AddStaff.cs b/src/Application/Staffs/Commands/AddStaff.cs new file mode 100644 index 00000000..c3415e00 --- /dev/null +++ b/src/Application/Staffs/Commands/AddStaff.cs @@ -0,0 +1,51 @@ +using Application.Common.Interfaces; +using Application.Common.Models.Dtos.Physical; +using AutoMapper; +using Domain.Entities.Physical; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Staffs.Commands; + +public class AddStaff +{ + public record Command : IRequest + { + public Guid UserId { get; init; } + public Guid? RoomId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); + if (user is null) + { + throw new KeyNotFoundException("User does not exist."); + } + + var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); + + var staff = new Staff + { + Id = user.Id, + User = user, + Room = room + }; + + var result = await _context.Staffs.AddAsync(staff, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Staffs/Commands/AddStaff/AddStaffCommand.cs b/src/Application/Staffs/Commands/AddStaff/AddStaffCommand.cs deleted file mode 100644 index 409e8cf9..00000000 --- a/src/Application/Staffs/Commands/AddStaff/AddStaffCommand.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Application.Common.Interfaces; -using Application.Users.Queries.Physical; -using AutoMapper; -using Domain.Entities.Physical; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Staffs.Commands.AddStaff; - -public record AddStaffCommand : IRequest -{ - public Guid UserId { get; init; } - public Guid RoomId { get; init; } -} - -public class AddStaffCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - - public AddStaffCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(AddStaffCommand request, CancellationToken cancellationToken) - { - var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); - if (user is null) - { - throw new KeyNotFoundException("User does not exist."); - } - - var room = await _context.Rooms.FirstOrDefaultAsync(x => x.Id == request.RoomId, cancellationToken); - if (room is null) - { - throw new KeyNotFoundException("Room does not exist."); - } - - var staff = new Staff - { - Id = user.Id, - User = user, - Room = room - }; - - var result = await _context.Staffs.AddAsync(staff, cancellationToken); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Staffs/Commands/RemoveStaffFromRoom.cs b/src/Application/Staffs/Commands/RemoveStaffFromRoom.cs new file mode 100644 index 00000000..607eb057 --- /dev/null +++ b/src/Application/Staffs/Commands/RemoveStaffFromRoom.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Staffs.Commands; + +public class RemoveStaffFromRoom +{ + public record Command : IRequest + { + public Guid StaffId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Staffs/Commands/RemoveStaffFromRoom/RemoveStaffFromRoomCommand.cs b/src/Application/Staffs/Commands/RemoveStaffFromRoom/RemoveStaffFromRoomCommand.cs deleted file mode 100644 index c25f18ca..00000000 --- a/src/Application/Staffs/Commands/RemoveStaffFromRoom/RemoveStaffFromRoomCommand.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Application.Users.Queries.Physical; -using MediatR; - -namespace Application.Staffs.Commands.RemoveStaffFromRoom; - -public record RemoveStaffFromRoomCommand : IRequest -{ - public Guid StaffId { get; init; } - public Guid RoomId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetAllStaffsPaginated.cs b/src/Application/Staffs/Queries/GetAllStaffsPaginated.cs new file mode 100644 index 00000000..270dfe77 --- /dev/null +++ b/src/Application/Staffs/Queries/GetAllStaffsPaginated.cs @@ -0,0 +1,17 @@ +using Application.Common.Models; +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Staffs.Queries; + +public class GetAllStaffsPaginated +{ + public class Query : IRequest> + { + public string? SearchTerm { get; set; } + public int? Page { get; set; } + public int? Size { get; set; } + public string? SortBy { get; set; } + public string? SortOrder { get; set; } + } +} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetAllStaffsPaginated/GetAllStaffsPaginatedQuery.cs b/src/Application/Staffs/Queries/GetAllStaffsPaginated/GetAllStaffsPaginatedQuery.cs deleted file mode 100644 index a640b907..00000000 --- a/src/Application/Staffs/Queries/GetAllStaffsPaginated/GetAllStaffsPaginatedQuery.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Application.Common.Models; -using Application.Users.Queries.Physical; -using MediatR; - -namespace Application.Staffs.Queries.GetAllStaffsPaginated; - -public class GetAllStaffsPaginatedQuery : IRequest> -{ - public string? SearchTerm { get; set; } - public int? Page { get; set; } - public int? Size { get; set; } - public string? SortBy { get; set; } - public string? SortOrder { get; set; } -} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetStaffById.cs b/src/Application/Staffs/Queries/GetStaffById.cs new file mode 100644 index 00000000..7510235e --- /dev/null +++ b/src/Application/Staffs/Queries/GetStaffById.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Staffs.Queries; + +public class GetStaffById +{ + public record Query : IRequest + { + public Guid StaffId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetStaffById/GetStaffByIdQuery.cs b/src/Application/Staffs/Queries/GetStaffById/GetStaffByIdQuery.cs deleted file mode 100644 index 7efed7f2..00000000 --- a/src/Application/Staffs/Queries/GetStaffById/GetStaffByIdQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Users.Queries.Physical; -using MediatR; - -namespace Application.Staffs.Queries.GetStaffById; - -public record GetStaffByIdQuery : IRequest -{ - public Guid StaffId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetStaffByRoom.cs b/src/Application/Staffs/Queries/GetStaffByRoom.cs new file mode 100644 index 00000000..9f1ac07b --- /dev/null +++ b/src/Application/Staffs/Queries/GetStaffByRoom.cs @@ -0,0 +1,12 @@ +using Application.Common.Models.Dtos.Physical; +using MediatR; + +namespace Application.Staffs.Queries; + +public class GetStaffByRoom +{ + public record Query : IRequest + { + public Guid RoomId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Staffs/Queries/GetStaffByRoom/GetStaffByRoomQuery.cs b/src/Application/Staffs/Queries/GetStaffByRoom/GetStaffByRoomQuery.cs deleted file mode 100644 index 0d3d8a1f..00000000 --- a/src/Application/Staffs/Queries/GetStaffByRoom/GetStaffByRoomQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Users.Queries.Physical; -using MediatR; - -namespace Application.Staffs.Queries.GetStaffByRoom; - -public record GetStaffByRoomQuery : IRequest -{ - public Guid RoomId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Users/Commands/AddUser.cs b/src/Application/Users/Commands/AddUser.cs new file mode 100644 index 00000000..448b8c08 --- /dev/null +++ b/src/Application/Users/Commands/AddUser.cs @@ -0,0 +1,118 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Helpers; +using Application.Identity; +using Application.Users.Queries; +using AutoMapper; +using Domain.Entities; +using Domain.Events; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; +using NodaTime; + +namespace Application.Users.Commands; + +public class AddUser +{ + public class Validator : AbstractValidator + { + public Validator() + { + RuleLevelCascadeMode = CascadeMode.Stop; + + RuleFor(x => x.Username) + .NotEmpty().WithMessage("Username is required.") + .MaximumLength(50).WithMessage("Username cannot exceed 64 characters."); + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Require valid email.") + .MaximumLength(320).WithMessage("Email length too long."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required."); + + RuleFor(x => x.Role) + .NotEmpty().WithMessage("Role is required.") + .MaximumLength(64).WithMessage("Role cannot exceed 64 characters.") + .Must(BeNotAdmin).WithMessage("Cannot add a user as Administrator."); + + RuleFor(x => x.FirstName) + .MaximumLength(50).WithMessage("First name cannot exceed 50 characters."); + + RuleFor(x => x.LastName) + .MaximumLength(50).WithMessage("Last name cannot exceed 50 characters."); + + RuleFor(x => x.Position) + .MaximumLength(64).WithMessage("Position cannot exceed 64 characters."); + } + + private static bool BeNotAdmin(string role) + { + return !role.Equals(IdentityData.Roles.Admin); + } + } + + public record Command : IRequest + { + public string Username { get; init; } = null!; + public string Email { get; init; } = null!; + public string Password { get; init; } = null!; + public string? FirstName { get; init; } + public string? LastName { get; init; } + public Guid DepartmentId { get; init; } + public string Role { get; init; } = null!; + public string? Position { get; init; } + } + + public class AddUserCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public AddUserCommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var user = await _context.Users.FirstOrDefaultAsync( + x => x.Username.Equals(request.Username) || x.Email.Equals(request.Email), cancellationToken); + + if (user is not null) + { + throw new ConflictException("Username or Email has been taken."); + } + + var department = await _context.Departments + .FirstOrDefaultAsync(x => x.Id == request.DepartmentId, cancellationToken); + + if (department is null) + { + throw new KeyNotFoundException("Department does not exist."); + } + + var entity = new User + { + Username = request.Username, + PasswordHash = SecurityUtil.Hash(request.Password), + Email = request.Email, + FirstName = request.FirstName?.Trim(), + LastName = request.LastName?.Trim(), + Department = department, + Role = request.Role, + Position = request.Position, + IsActive = true, + IsActivated = false, + Created = LocalDateTime.FromDateTime(DateTime.UtcNow) + }; + entity.AddDomainEvent(new UserCreatedEvent(entity)); + var result = await _context.Users.AddAsync(entity, cancellationToken); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Users/Commands/AddUser/AddUserCommand.cs b/src/Application/Users/Commands/AddUser/AddUserCommand.cs deleted file mode 100644 index 5f5f684c..00000000 --- a/src/Application/Users/Commands/AddUser/AddUserCommand.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Helpers; -using Application.Users.Queries; -using AutoMapper; -using Domain.Entities; -using Domain.Events; -using MediatR; -using Microsoft.EntityFrameworkCore; -using NodaTime; - -namespace Application.Users.Commands.AddUser; - -public record AddUserCommand : IRequest -{ - public string Username { get; init; } = null!; - public string Email { get; init; } = null!; - public string Password { get; init; } = null!; - public string? FirstName { get; init; } - public string? LastName { get; init; } - public Guid DepartmentId { get; init; } - public string Role { get; init; } = null!; - public string? Position { get; init; } -} - -public class AddUserCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public AddUserCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - public async Task Handle(AddUserCommand request, CancellationToken cancellationToken) - { - var user = await _context.Users.FirstOrDefaultAsync( - x => x.Username.Equals(request.Username) || x.Email.Equals(request.Email), cancellationToken); - - if (user is not null) - { - throw new ConflictException("Username or Email has been taken."); - } - - var department = await _context.Departments - .FirstOrDefaultAsync(x => x.Id == request.DepartmentId, cancellationToken); - - if (department is null) - { - throw new KeyNotFoundException("Department does not exist."); - } - - var entity = new User - { - Username = request.Username, - PasswordHash = SecurityUtil.Hash(request.Password), - Email = request.Email, - FirstName = request.FirstName?.Trim(), - LastName = request.LastName?.Trim(), - Department = department, - Role = request.Role, - Position = request.Position, - IsActive = true, - IsActivated = false, - Created = LocalDateTime.FromDateTime(DateTime.UtcNow) - }; - entity.AddDomainEvent(new UserCreatedEvent(entity)); - var result = await _context.Users.AddAsync(entity, cancellationToken); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} - diff --git a/src/Application/Users/Commands/AddUser/AddUserCommandValidator.cs b/src/Application/Users/Commands/AddUser/AddUserCommandValidator.cs deleted file mode 100644 index 16d07c05..00000000 --- a/src/Application/Users/Commands/AddUser/AddUserCommandValidator.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Application.Identity; -using FluentValidation; - -namespace Application.Users.Commands.AddUser; - -public class AddUserCommandValidator : AbstractValidator -{ - public AddUserCommandValidator() - { - RuleLevelCascadeMode = CascadeMode.Stop; - - RuleFor(x => x.Username) - .NotEmpty().WithMessage("Username is required.") - .MaximumLength(50).WithMessage("Username cannot exceed 64 characters."); - - RuleFor(x => x.Email) - .NotEmpty().WithMessage("Email is required.") - .EmailAddress().WithMessage("Require valid email.") - .MaximumLength(320).WithMessage("Email length too long."); - - RuleFor(x => x.Password) - .NotEmpty().WithMessage("Password is required."); - - RuleFor(x => x.Role) - .NotEmpty().WithMessage("Role is required.") - .MaximumLength(64).WithMessage("Role cannot exceed 64 characters.") - .Must(BeNotAdmin).WithMessage("Cannot add a user as Administrator."); - - RuleFor(x => x.FirstName) - .MaximumLength(50).WithMessage("First name cannot exceed 50 characters."); - - RuleFor(x => x.LastName) - .MaximumLength(50).WithMessage("Last name cannot exceed 50 characters."); - - RuleFor(x => x.Position) - .MaximumLength(64).WithMessage("Position cannot exceed 64 characters."); - } - - private static bool BeNotAdmin(string role) - { - return !role.Equals(IdentityData.Roles.Admin); - } -} \ No newline at end of file diff --git a/src/Application/Users/Commands/DisableUser.cs b/src/Application/Users/Commands/DisableUser.cs new file mode 100644 index 00000000..dafb4b31 --- /dev/null +++ b/src/Application/Users/Commands/DisableUser.cs @@ -0,0 +1,47 @@ +using Application.Common.Exceptions; +using Application.Common.Interfaces; +using Application.Users.Queries; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Users.Commands; + +public class DisableUser +{ + public record Command : IRequest + { + public Guid UserId { get; init; } + } + + public class CommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + public CommandHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task Handle(Command request, CancellationToken cancellationToken) + { + var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); + if (user is null) + { + throw new KeyNotFoundException("User does not exist."); + } + + if (!user.IsActive) + { + throw new ConflictException("User has already been disabled."); + } + + user.IsActive = false; + + var result = _context.Users.Update(user); + await _context.SaveChangesAsync(cancellationToken); + return _mapper.Map(result.Entity); + } + } +} \ No newline at end of file diff --git a/src/Application/Users/Commands/DisableUser/DisableUserCommand.cs b/src/Application/Users/Commands/DisableUser/DisableUserCommand.cs deleted file mode 100644 index 43abac74..00000000 --- a/src/Application/Users/Commands/DisableUser/DisableUserCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Application.Common.Exceptions; -using Application.Common.Interfaces; -using Application.Users.Queries; -using AutoMapper; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace Application.Users.Commands.DisableUser; - -public record DisableUserCommand : IRequest -{ - public Guid UserId { get; init; } -} - -public class DisableUserCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public DisableUserCommandHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task Handle(DisableUserCommand request, CancellationToken cancellationToken) - { - var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); - if (user is null) - { - throw new KeyNotFoundException("User does not exist."); - } - - if (!user.IsActive) - { - throw new ConflictException("User has already been disabled."); - } - - user.IsActive = false; - - var result = _context.Users.Update(user); - await _context.SaveChangesAsync(cancellationToken); - return _mapper.Map(result.Entity); - } -} \ No newline at end of file diff --git a/src/Application/Users/Commands/EnableUser.cs b/src/Application/Users/Commands/EnableUser.cs new file mode 100644 index 00000000..7da8ab48 --- /dev/null +++ b/src/Application/Users/Commands/EnableUser.cs @@ -0,0 +1,12 @@ +using Application.Users.Queries; +using MediatR; + +namespace Application.Users.Commands; + +public class EnableUser +{ + public record Command : IRequest + { + public Guid UserId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Users/Commands/EnableUser/EnableUserCommand.cs b/src/Application/Users/Commands/EnableUser/EnableUserCommand.cs deleted file mode 100644 index 047f13d5..00000000 --- a/src/Application/Users/Commands/EnableUser/EnableUserCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Application.Users.Queries; -using MediatR; - -namespace Application.Users.Commands.EnableUser; - -public record EnableUserCommand : IRequest -{ - public Guid UserId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Users/Commands/UpdateUser.cs b/src/Application/Users/Commands/UpdateUser.cs new file mode 100644 index 00000000..4d894cc0 --- /dev/null +++ b/src/Application/Users/Commands/UpdateUser.cs @@ -0,0 +1,16 @@ +using Application.Users.Queries; +using MediatR; + +namespace Application.Users.Commands; + +public class UpdateUser +{ + public record Command : IRequest + { + public Guid UserId { get; init; } + public string? FirstName { get; init; } + public string? LastName { get; init; } + public string Role { get; init; } = null!; + public string? Position { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Users/Commands/UpdateUser/UpdateUserCommand.cs b/src/Application/Users/Commands/UpdateUser/UpdateUserCommand.cs deleted file mode 100644 index 0f3736ea..00000000 --- a/src/Application/Users/Commands/UpdateUser/UpdateUserCommand.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Application.Users.Queries; -using MediatR; - -namespace Application.Users.Commands.UpdateUser; - -public record UpdateUserCommand : IRequest -{ - public Guid UserId { get; init; } - public string Username { get; init; } = null!; - public string Email { get; init; } = null!; - public string? FirstName { get; init; } - public string? LastName { get; init; } - public string Role { get; init; } = null!; - public string? Position { get; init; } -} \ No newline at end of file diff --git a/src/Application/Users/Queries/GetAllUsersPaginated.cs b/src/Application/Users/Queries/GetAllUsersPaginated.cs new file mode 100644 index 00000000..738ed5ea --- /dev/null +++ b/src/Application/Users/Queries/GetAllUsersPaginated.cs @@ -0,0 +1,17 @@ +using Application.Common.Models; +using MediatR; + +namespace Application.Users.Queries; + +public class GetAllUsersPaginated +{ + public record Query : IRequest> + { + public Guid? DepartmentId { get; init; } + public string? SearchTerm { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Users/Queries/GetAllUsersPaginated/GetAllUsersPaginatedQuery.cs b/src/Application/Users/Queries/GetAllUsersPaginated/GetAllUsersPaginatedQuery.cs deleted file mode 100644 index 0c3146c7..00000000 --- a/src/Application/Users/Queries/GetAllUsersPaginated/GetAllUsersPaginatedQuery.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Application.Common.Models; -using MediatR; - -namespace Application.Users.Queries.GetAllUsersPaginated; - -public record GetAllUsersPaginatedQuery : IRequest> -{ - public Guid? DepartmentId { get; init; } - public string? SearchTerm { get; init; } - public int? Page { get; init; } - public int? Size { get; init; } - public string? SortBy { get; init; } - public string? SortOrder { get; init; } -} \ No newline at end of file diff --git a/src/Application/Users/Queries/GetUserById.cs b/src/Application/Users/Queries/GetUserById.cs new file mode 100644 index 00000000..e34b58d2 --- /dev/null +++ b/src/Application/Users/Queries/GetUserById.cs @@ -0,0 +1,11 @@ +using MediatR; + +namespace Application.Users.Queries; + +public class GetUserById +{ + public record Query : IRequest + { + public Guid UserId { get; init; } + } +} \ No newline at end of file diff --git a/src/Application/Users/Queries/GetUserById/GetUserByIdQuery.cs b/src/Application/Users/Queries/GetUserById/GetUserByIdQuery.cs deleted file mode 100644 index 9f2e60d7..00000000 --- a/src/Application/Users/Queries/GetUserById/GetUserByIdQuery.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MediatR; - -namespace Application.Users.Queries.GetUserById; - -public record GetUserByIdQuery : IRequest -{ - public Guid UserId { get; init; } -} \ No newline at end of file diff --git a/src/Application/Users/Queries/GetUsersByName/GetUsersByNameQuery.cs b/src/Application/Users/Queries/GetUsersByName/GetUsersByNameQuery.cs deleted file mode 100644 index 76604efb..00000000 --- a/src/Application/Users/Queries/GetUsersByName/GetUsersByNameQuery.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Application.Common.Interfaces; -using Application.Common.Mappings; -using Application.Common.Models; -using AutoMapper; -using AutoMapper.QueryableExtensions; -using MediatR; - -namespace Application.Users.Queries.GetUsersByName; - -public record GetUsersByNameQuery : IRequest> -{ - public string? SearchTerm { get; init; } - public int? Page { get; init; } - public int? Size { get; init; } -} - -public class GetUsersByNameQueryHandler : IRequestHandler> -{ - private readonly IApplicationDbContext _context; - private readonly IMapper _mapper; - public GetUsersByNameQueryHandler(IApplicationDbContext context, IMapper mapper) - { - _context = context; - _mapper = mapper; - } - - public async Task> Handle(GetUsersByNameQuery request, CancellationToken cancellationToken) - { - var pageNumber = request.Page ?? 1; - var sizeNumber = request.Size ?? 5; - var users = await _context.Users - .Where(x => string.IsNullOrEmpty(request.SearchTerm) - || x.FirstName.ToLower().Contains(request.SearchTerm.ToLower()) - || x.LastName.ToLower().Contains(request.SearchTerm.ToLower())) - .ProjectTo(_mapper.ConfigurationProvider) - .OrderBy(x => x.Username) - .PaginatedListAsync(pageNumber, sizeNumber); - return users; - } -} \ No newline at end of file diff --git a/src/Domain/Entities/Department.cs b/src/Domain/Entities/Department.cs index 6513aa25..b43bbfe7 100644 --- a/src/Domain/Entities/Department.cs +++ b/src/Domain/Entities/Department.cs @@ -1,8 +1,10 @@ using Domain.Common; +using Domain.Entities.Physical; namespace Domain.Entities; public class Department : BaseEntity { public string Name { get; set; } = null!; + public Room? Room { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entities/Physical/Room.cs b/src/Domain/Entities/Physical/Room.cs index 517ab146..3d2318ac 100644 --- a/src/Domain/Entities/Physical/Room.cs +++ b/src/Domain/Entities/Physical/Room.cs @@ -7,10 +7,12 @@ public class Room : BaseEntity public string Name { get; set; } = null!; public string? Description { get; set; } public Staff? Staff { get; set; } + public Guid DepartmentId { get; set; } public int Capacity { get; set; } public int NumberOfLockers { get; set; } public bool IsAvailable { get; set; } // Navigation property + public Department Department { get; set; } = null!; public ICollection Lockers { get; set; } = new List(); } \ No newline at end of file diff --git a/src/Domain/Entities/Physical/Staff.cs b/src/Domain/Entities/Physical/Staff.cs index 1184a040..bb6f2a28 100644 --- a/src/Domain/Entities/Physical/Staff.cs +++ b/src/Domain/Entities/Physical/Staff.cs @@ -5,5 +5,5 @@ namespace Domain.Entities.Physical; public class Staff : BaseEntity { public User User { get; set; } = null!; - public Room Room { get; set; } = null!; + public Room? Room { get; set; } } \ No newline at end of file diff --git a/src/Domain/Entities/User.cs b/src/Domain/Entities/User.cs index 210a75a5..cc8c2345 100644 --- a/src/Domain/Entities/User.cs +++ b/src/Domain/Entities/User.cs @@ -6,7 +6,7 @@ namespace Domain.Entities; public class User : BaseAuditableEntity { public string Username { get; set; } = null!; - public string? Email { get; set; } + public string Email { get; set; } = null!; public string PasswordHash { get; set; } = null!; public string? FirstName { get; set; } public string? LastName { get; set; } diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 911a81f6..8537748f 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -21,8 +21,4 @@ - - - - diff --git a/src/Infrastructure/Persistence/Configurations/DepartmentConfiguration.cs b/src/Infrastructure/Persistence/Configurations/DepartmentConfiguration.cs index 8af33bea..1c7f00ca 100644 --- a/src/Infrastructure/Persistence/Configurations/DepartmentConfiguration.cs +++ b/src/Infrastructure/Persistence/Configurations/DepartmentConfiguration.cs @@ -1,4 +1,5 @@ using Domain.Entities; +using Domain.Entities.Physical; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.ValueGeneration; diff --git a/src/Infrastructure/Persistence/Configurations/RoomConfiguration.cs b/src/Infrastructure/Persistence/Configurations/RoomConfiguration.cs index 636a330b..2ad65961 100644 --- a/src/Infrastructure/Persistence/Configurations/RoomConfiguration.cs +++ b/src/Infrastructure/Persistence/Configurations/RoomConfiguration.cs @@ -1,3 +1,4 @@ +using Domain.Entities; using Domain.Entities.Physical; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; diff --git a/src/Infrastructure/Persistence/Configurations/StaffConfiguration.cs b/src/Infrastructure/Persistence/Configurations/StaffConfiguration.cs index 316bf414..b2881567 100644 --- a/src/Infrastructure/Persistence/Configurations/StaffConfiguration.cs +++ b/src/Infrastructure/Persistence/Configurations/StaffConfiguration.cs @@ -22,6 +22,6 @@ public void Configure(EntityTypeBuilder builder) builder.HasOne(x => x.Room) .WithOne(x => x.Staff) .HasForeignKey("RoomId") - .IsRequired(); + .IsRequired(false); } } \ No newline at end of file diff --git a/src/Infrastructure/Persistence/Configurations/UserConfiguration.cs b/src/Infrastructure/Persistence/Configurations/UserConfiguration.cs index a12b422f..b3e55685 100644 --- a/src/Infrastructure/Persistence/Configurations/UserConfiguration.cs +++ b/src/Infrastructure/Persistence/Configurations/UserConfiguration.cs @@ -19,7 +19,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Email) .HasMaxLength(320) - .IsRequired(false); + .IsRequired(); builder.Property(x => x.PasswordHash) .HasMaxLength(64) diff --git a/src/Infrastructure/Persistence/Migrations/20230524080300_Add_Refresh_Token.Designer.cs b/src/Infrastructure/Persistence/Migrations/00000000000007_Add_Refresh_Token.Designer.cs similarity index 100% rename from src/Infrastructure/Persistence/Migrations/20230524080300_Add_Refresh_Token.Designer.cs rename to src/Infrastructure/Persistence/Migrations/00000000000007_Add_Refresh_Token.Designer.cs diff --git a/src/Infrastructure/Persistence/Migrations/20230524080300_Add_Refresh_Token.cs b/src/Infrastructure/Persistence/Migrations/00000000000007_Add_Refresh_Token.cs similarity index 100% rename from src/Infrastructure/Persistence/Migrations/20230524080300_Add_Refresh_Token.cs rename to src/Infrastructure/Persistence/Migrations/00000000000007_Add_Refresh_Token.cs diff --git a/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.Designer.cs b/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.Designer.cs new file mode 100644 index 00000000..1ff0d731 --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.Designer.cs @@ -0,0 +1,475 @@ +// +using System; +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230527234317_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship")] + partial class UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Department", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasAlternateKey("Name"); + + b.HasIndex("RoomId") + .IsUnique(); + + b.ToTable("Departments"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Borrow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BorrowTime") + .HasColumnType("timestamp without time zone"); + + b.Property("BorrowerId") + .HasColumnType("uuid"); + + b.Property("DocumentId") + .HasColumnType("uuid"); + + b.Property("DueTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("BorrowerId"); + + b.HasIndex("DocumentId"); + + b.ToTable("Borrows"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("FolderId") + .HasColumnType("uuid"); + + b.Property("ImporterId") + .HasColumnType("uuid"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("FolderId"); + + b.HasIndex("ImporterId"); + + b.ToTable("Documents"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("LockerId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfDocuments") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("LockerId"); + + b.ToTable("Folders"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfFolders") + .HasColumnType("integer"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId"); + + b.ToTable("Lockers"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfLockers") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasAlternateKey("Name"); + + b.ToTable("Rooms"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId") + .IsUnique(); + + b.ToTable("Staffs"); + }); + + modelBuilder.Entity("Domain.Entities.RefreshToken", b => + { + b.Property("Token") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExpiryDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("IsInvalidated") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUsed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JwtId") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Token"); + + b.HasIndex("UserId"); + + b.ToTable("RefreshTokens"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("FirstName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActivated") + .HasColumnType("boolean"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp without time zone"); + + b.Property("LastModifiedBy") + .HasColumnType("uuid"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Position") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Domain.Entities.Department", b => + { + b.HasOne("Domain.Entities.Physical.Room", "Room") + .WithOne("Department") + .HasForeignKey("Domain.Entities.Department", "RoomId"); + + b.Navigation("Room"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Borrow", b => + { + b.HasOne("Domain.Entities.User", "Borrower") + .WithMany() + .HasForeignKey("BorrowerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Physical.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Borrower"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Document", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId"); + + b.HasOne("Domain.Entities.Physical.Folder", "Folder") + .WithMany("Documents") + .HasForeignKey("FolderId"); + + b.HasOne("Domain.Entities.User", "Importer") + .WithMany() + .HasForeignKey("ImporterId"); + + b.Navigation("Department"); + + b.Navigation("Folder"); + + b.Navigation("Importer"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.HasOne("Domain.Entities.Physical.Locker", "Locker") + .WithMany("Folders") + .HasForeignKey("LockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Locker"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.HasOne("Domain.Entities.Physical.Room", "Room") + .WithMany("Lockers") + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Room"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Staff", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("Domain.Entities.Physical.Staff", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Physical.Room", "Room") + .WithOne("Staff") + .HasForeignKey("Domain.Entities.Physical.Staff", "RoomId"); + + b.Navigation("Room"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.RefreshToken", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId"); + + b.Navigation("Department"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.Navigation("Folders"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.Navigation("Department"); + + b.Navigation("Lockers"); + + b.Navigation("Staff"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.cs b/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.cs new file mode 100644 index 00000000..6bd9968b --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/00000000000008_UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship.cs @@ -0,0 +1,123 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + /// + public partial class UpdateRoomAndStaffRelationshipAndRoomAndDepartmentRelationship : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Staffs_Rooms_RoomId", + table: "Staffs"); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Users", + type: "character varying(320)", + maxLength: 320, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "character varying(320)", + oldMaxLength: 320, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RoomId", + table: "Staffs", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "DepartmentId", + table: "Rooms", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "RoomId", + table: "Departments", + type: "uuid", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Departments_RoomId", + table: "Departments", + column: "RoomId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Departments_Rooms_RoomId", + table: "Departments", + column: "RoomId", + principalTable: "Rooms", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Staffs_Rooms_RoomId", + table: "Staffs", + column: "RoomId", + principalTable: "Rooms", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Departments_Rooms_RoomId", + table: "Departments"); + + migrationBuilder.DropForeignKey( + name: "FK_Staffs_Rooms_RoomId", + table: "Staffs"); + + migrationBuilder.DropIndex( + name: "IX_Departments_RoomId", + table: "Departments"); + + migrationBuilder.DropColumn( + name: "DepartmentId", + table: "Rooms"); + + migrationBuilder.DropColumn( + name: "RoomId", + table: "Departments"); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Users", + type: "character varying(320)", + maxLength: 320, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(320)", + oldMaxLength: 320); + + migrationBuilder.AlterColumn( + name: "RoomId", + table: "Staffs", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Staffs_Rooms_RoomId", + table: "Staffs", + column: "RoomId", + principalTable: "Rooms", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.Designer.cs b/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.Designer.cs new file mode 100644 index 00000000..a20e242e --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.Designer.cs @@ -0,0 +1,477 @@ +// +using System; +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230528182741_RoomMustHaveADepartment")] + partial class RoomMustHaveADepartment + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entities.Department", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasAlternateKey("Name"); + + b.ToTable("Departments"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Borrow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BorrowTime") + .HasColumnType("timestamp without time zone"); + + b.Property("BorrowerId") + .HasColumnType("uuid"); + + b.Property("DocumentId") + .HasColumnType("uuid"); + + b.Property("DueTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("BorrowerId"); + + b.HasIndex("DocumentId"); + + b.ToTable("Borrows"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("FolderId") + .HasColumnType("uuid"); + + b.Property("ImporterId") + .HasColumnType("uuid"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.HasIndex("FolderId"); + + b.HasIndex("ImporterId"); + + b.ToTable("Documents"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("LockerId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfDocuments") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("LockerId"); + + b.ToTable("Folders"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfFolders") + .HasColumnType("integer"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId"); + + b.ToTable("Lockers"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NumberOfLockers") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasAlternateKey("Name"); + + b.HasIndex("DepartmentId") + .IsUnique(); + + b.ToTable("Rooms"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Staff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("RoomId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoomId") + .IsUnique(); + + b.ToTable("Staffs"); + }); + + modelBuilder.Entity("Domain.Entities.RefreshToken", b => + { + b.Property("Token") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExpiryDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("IsInvalidated") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsUsed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JwtId") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Token"); + + b.HasIndex("UserId"); + + b.ToTable("RefreshTokens"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DepartmentId") + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("FirstName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsActivated") + .HasColumnType("boolean"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp without time zone"); + + b.Property("LastModifiedBy") + .HasColumnType("uuid"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Position") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Borrow", b => + { + b.HasOne("Domain.Entities.User", "Borrower") + .WithMany() + .HasForeignKey("BorrowerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Physical.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Borrower"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Document", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId"); + + b.HasOne("Domain.Entities.Physical.Folder", "Folder") + .WithMany("Documents") + .HasForeignKey("FolderId"); + + b.HasOne("Domain.Entities.User", "Importer") + .WithMany() + .HasForeignKey("ImporterId"); + + b.Navigation("Department"); + + b.Navigation("Folder"); + + b.Navigation("Importer"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.HasOne("Domain.Entities.Physical.Locker", "Locker") + .WithMany("Folders") + .HasForeignKey("LockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Locker"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.HasOne("Domain.Entities.Physical.Room", "Room") + .WithMany("Lockers") + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Room"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithOne("Room") + .HasForeignKey("Domain.Entities.Physical.Room", "DepartmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Department"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Staff", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithOne() + .HasForeignKey("Domain.Entities.Physical.Staff", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Physical.Room", "Room") + .WithOne("Staff") + .HasForeignKey("Domain.Entities.Physical.Staff", "RoomId"); + + b.Navigation("Room"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.RefreshToken", b => + { + b.HasOne("Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithMany() + .HasForeignKey("DepartmentId"); + + b.Navigation("Department"); + }); + + modelBuilder.Entity("Domain.Entities.Department", b => + { + b.Navigation("Room"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Locker", b => + { + b.Navigation("Folders"); + }); + + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.Navigation("Lockers"); + + b.Navigation("Staff"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.cs b/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.cs new file mode 100644 index 00000000..a9fa4a85 --- /dev/null +++ b/src/Infrastructure/Persistence/Migrations/00000000000009_RoomMustHaveADepartment.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Persistence.Migrations +{ + /// + public partial class RoomMustHaveADepartment : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs index a69d09d2..49740741 100644 --- a/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -189,6 +189,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Capacity") .HasColumnType("integer"); + b.Property("DepartmentId") + .HasColumnType("uuid"); + b.Property("Description") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -208,6 +211,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasAlternateKey("Name"); + b.HasIndex("DepartmentId") + .IsUnique(); + b.ToTable("Rooms"); }); @@ -218,7 +224,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("UserId"); - b.Property("RoomId") + b.Property("RoomId") .HasColumnType("uuid"); b.HasKey("Id"); @@ -281,6 +287,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("Email") + .IsRequired() .HasMaxLength(320) .HasColumnType("character varying(320)"); @@ -392,6 +399,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Room"); }); + modelBuilder.Entity("Domain.Entities.Physical.Room", b => + { + b.HasOne("Domain.Entities.Department", "Department") + .WithOne("Room") + .HasForeignKey("Domain.Entities.Physical.Room", "DepartmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Department"); + }); + modelBuilder.Entity("Domain.Entities.Physical.Staff", b => { b.HasOne("Domain.Entities.User", "User") @@ -402,9 +420,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Domain.Entities.Physical.Room", "Room") .WithOne("Staff") - .HasForeignKey("Domain.Entities.Physical.Staff", "RoomId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("Domain.Entities.Physical.Staff", "RoomId"); b.Navigation("Room"); @@ -431,6 +447,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Department"); }); + modelBuilder.Entity("Domain.Entities.Department", b => + { + b.Navigation("Room"); + }); + modelBuilder.Entity("Domain.Entities.Physical.Folder", b => { b.Navigation("Documents"); diff --git a/tests/Application.Tests.Integration/BaseClassFixture.cs b/tests/Application.Tests.Integration/BaseClassFixture.cs index 7e0d22c0..370fbc40 100644 --- a/tests/Application.Tests.Integration/BaseClassFixture.cs +++ b/tests/Application.Tests.Integration/BaseClassFixture.cs @@ -1,13 +1,13 @@ -using Application.Departments.Commands.AddDepartment; +using Application.Helpers; using Bogus; using Domain.Common; using Domain.Entities; using Domain.Entities.Physical; -using FluentAssertions; using Infrastructure.Persistence; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using NodaTime; using Xunit; namespace Application.Tests.Integration; @@ -15,19 +15,16 @@ namespace Application.Tests.Integration; [Collection(nameof(BaseCollectionFixture))] public class BaseClassFixture { - protected readonly Faker _departmentGenerator = new Faker() - .RuleFor(x => x.Name, faker => faker.Commerce.Department()); - - protected static IServiceScopeFactory _scopeFactory = null!; + protected static IServiceScopeFactory ScopeFactory = null!; protected BaseClassFixture(CustomApiFactory apiFactory) { - _scopeFactory = apiFactory.Services.GetRequiredService(); + ScopeFactory = apiFactory.Services.GetRequiredService(); } protected static async Task SendAsync(IRequest request) { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var mediator = scope.ServiceProvider.GetRequiredService(); @@ -36,7 +33,7 @@ protected static async Task SendAsync(IRequest protected void Remove(TEntity entity) where TEntity : BaseEntity? { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); @@ -47,7 +44,7 @@ protected void Remove(TEntity entity) where TEntity : BaseEntity? protected static async Task FindAsync(params object[] keyValues) where TEntity : class { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); @@ -57,7 +54,7 @@ protected void Remove(TEntity entity) where TEntity : BaseEntity? protected static async Task AddAsync(TEntity entity) where TEntity : class { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); @@ -69,7 +66,7 @@ protected static async Task AddAsync(TEntity entity) protected static async Task Add(TEntity entity) where TEntity : class { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); @@ -80,7 +77,7 @@ protected static async Task Add(TEntity entity) protected static async Task CountAsync() where TEntity : class { - using var scope = _scopeFactory.CreateScope(); + using var scope = ScopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); @@ -142,7 +139,7 @@ protected Locker CreateLocker(params Folder[] folders) return locker; } - protected Room CreateRoom(params Locker[] lockers) + protected Room CreateRoom(Department department, params Locker[] lockers) { var room = new Room() { @@ -150,7 +147,9 @@ protected Room CreateRoom(params Locker[] lockers) Name = new Faker().Commerce.ProductName(), Capacity = 3, NumberOfLockers = lockers.Length, - IsAvailable = true + IsAvailable = true, + Department = department, + DepartmentId = department.Id, }; foreach (var locker in lockers) @@ -160,4 +159,41 @@ protected Room CreateRoom(params Locker[] lockers) return room; } + + protected static Department CreateDepartment() + { + return new Department() + { + Id = Guid.NewGuid(), + Name = new Faker().Random.Word() + }; + } + + protected static User CreateUser(string role, string password) + { + return new User() + { + Id = Guid.NewGuid(), + Username = new Faker().Person.UserName, + Email = new Faker().Person.Email, + FirstName = new Faker().Person.FirstName, + LastName = new Faker().Person.LastName, + Role = role, + Position = new Faker().Random.Word(), + IsActivated = true, + IsActive = true, + Created = LocalDateTime.FromDateTime(DateTime.Now), + PasswordHash = SecurityUtil.Hash(password) + }; + } + + protected static Staff CreateStaff(User user, Room? room) + { + return new Staff() + { + Id = user.Id, + User = user, + Room = room, + }; + } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/CustomApiFactory.cs b/tests/Application.Tests.Integration/CustomApiFactory.cs index 9d30c073..f76d4ac3 100644 --- a/tests/Application.Tests.Integration/CustomApiFactory.cs +++ b/tests/Application.Tests.Integration/CustomApiFactory.cs @@ -28,7 +28,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) var databaseSettings = GetConfiguration().GetSection(nameof(DatabaseSettings)).Get(); services.AddDbContext(options => { - options.UseNpgsql(databaseSettings?.ConnectionString, optionsBuilder => optionsBuilder.UseNodaTime()); + options.UseNpgsql(databaseSettings!.ConnectionString, optionsBuilder => optionsBuilder.UseNodaTime()); }); }); } diff --git a/tests/Application.Tests.Integration/Departments/Commands/AddDepartmentTests.cs b/tests/Application.Tests.Integration/Departments/Commands/AddDepartmentTests.cs index 8823a9fb..dc3d31e1 100644 --- a/tests/Application.Tests.Integration/Departments/Commands/AddDepartmentTests.cs +++ b/tests/Application.Tests.Integration/Departments/Commands/AddDepartmentTests.cs @@ -1,5 +1,5 @@ using Application.Common.Exceptions; -using Application.Departments.Commands.DeleteDepartment; +using Application.Departments.Commands; using Domain.Entities; using FluentAssertions; using Xunit; @@ -12,11 +12,14 @@ public AddDepartmentTests(CustomApiFactory apiFactory) : base(apiFactory) { } - [Fact(Timeout = 200)] + [Fact] public async Task ShouldCreateDepartment_WhenDepartmentNameIsValid() { // Arrange - var createDepartmentCommand = _departmentGenerator.Generate(); + var createDepartmentCommand = new AddDepartment.Command() + { + Name = "something", + }; // Act var department = await SendAsync(createDepartmentCommand); @@ -33,11 +36,16 @@ public async Task ShouldCreateDepartment_WhenDepartmentNameIsValid() public async Task ShouldReturnConflict_WhenDepartmentNameHasExisted() { // Arrange - var createDepartmentCommand = _departmentGenerator.Generate(); - var department = await SendAsync(createDepartmentCommand); + var department = CreateDepartment(); + await AddAsync(department); + + var command = new AddDepartment.Command() + { + Name = department.Name, + }; // Act - var action = async () => await SendAsync(createDepartmentCommand); + var action = async () => await SendAsync(command); // Assert await action.Should().ThrowAsync().WithMessage("Department name already exists."); diff --git a/tests/Application.Tests.Integration/Departments/Queries/GetAllDepartmentsTests.cs b/tests/Application.Tests.Integration/Departments/Queries/GetAllDepartmentsTests.cs index 6bd82481..f126e367 100644 --- a/tests/Application.Tests.Integration/Departments/Queries/GetAllDepartmentsTests.cs +++ b/tests/Application.Tests.Integration/Departments/Queries/GetAllDepartmentsTests.cs @@ -1,6 +1,6 @@ using Application.Common.Mappings; -using Application.Departments.Queries.GetAllDepartments; -using Application.Identity; +using Application.Common.Models.Dtos; +using Application.Departments.Queries; using Application.Users.Queries; using AutoMapper; using Bogus; @@ -23,14 +23,13 @@ public GetAllDepartmentsTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldReturnDepartments_WhenDepartmentsExist() { // Arrange - var department = new Department() { Id = Guid.NewGuid(), Name = new Faker().Commerce.Department() }; await AddAsync(department); - var query = new GetAllDepartmentsQuery(); + var query = new GetAllDepartments.Query(); // Act var result = await SendAsync(query); @@ -46,13 +45,12 @@ public async Task ShouldReturnDepartments_WhenDepartmentsExist() public async Task ShouldReturnEmptyList_WhenNoDepartmentsExist() { // Arrange - var query = new GetAllDepartmentsQuery(); + var query = new GetAllDepartments.Query(); // Act var result = await SendAsync(query); // Assert - result.Count().Should().Be(1); - result.First().Name.Should().Be(IdentityData.Roles.Admin); + result.Count().Should().Be(0); } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentTypesTests.cs b/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentTypesTests.cs index bf83e5f5..672aec9a 100644 --- a/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentTypesTests.cs +++ b/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentTypesTests.cs @@ -1,6 +1,4 @@ -using Application.Documents.Queries.GetDocumentTypes; -using Bogus; -using Domain.Entities.Physical; +using Application.Documents.Queries; using FluentAssertions; using Xunit; @@ -16,14 +14,9 @@ public GetAllDocumentTypesTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldReturnDocumentTypes_WhenDocumentTypesExist() { // Arrange - var document = new Document() - { - Id = Guid.NewGuid(), - Title = new Faker().Name.JobTitle(), - DocumentType = new Faker().Commerce.ProductName(), - }; + var document = CreateNDocuments(1).First(); await AddAsync(document); - var query = new GetAllDocumentTypesQuery(); + var query = new GetAllDocumentTypes.Query(); // Act var result = await SendAsync(query); @@ -39,7 +32,7 @@ public async Task ShouldReturnDocumentTypes_WhenDocumentTypesExist() public async Task ShouldReturnEmptyList_WhenNoDocumentTypesExist() { // Arrange - var query = new GetAllDocumentTypesQuery(); + var query = new GetAllDocumentTypes.Query(); // Act var result = await SendAsync(query); diff --git a/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentsPaginatedTests.cs b/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentsPaginatedTests.cs index b051e093..42d3b5d1 100644 --- a/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentsPaginatedTests.cs +++ b/tests/Application.Tests.Integration/Documents/Queries/GetAllDocumentsPaginatedTests.cs @@ -1,422 +1,438 @@ - using Application.Common.Exceptions; - using Application.Common.Extensions; - using Application.Common.Mappings; - using Application.Common.Models.Dtos.Physical; - using Application.Documents.Queries.GetAllDocumentsPaginated; - using AutoMapper; - using Bogus; - using Domain.Entities.Physical; - using FluentAssertions; - using Xunit; - - namespace Application.Tests.Integration.Documents.Queries; - - public class GetAllDocumentsPaginatedTests : BaseClassFixture +using Application.Common.Exceptions; +using Application.Common.Extensions; +using Application.Common.Mappings; +using Application.Common.Models.Dtos.Physical; +using Application.Documents.Queries; +using AutoMapper; +using Domain.Entities; +using FluentAssertions; +using Xunit; + +namespace Application.Tests.Integration.Documents.Queries; + +public class GetAllDocumentsPaginatedTests : BaseClassFixture +{ + private readonly IMapper _mapper; + public GetAllDocumentsPaginatedTests(CustomApiFactory apiFactory) : base(apiFactory) { - private readonly IMapper _mapper; - public GetAllDocumentsPaginatedTests(CustomApiFactory apiFactory) : base(apiFactory) - { - var configuration = new MapperConfiguration(config => config.AddProfile()); + var configuration = new MapperConfiguration(config => config.AddProfile()); - _mapper = configuration.CreateMapper(); - } + _mapper = configuration.CreateMapper(); + } - [Fact] - public async Task ShouldReturnAllDocuments_WhenNoContainersAreDefined() - { - // Arrange - var documents = CreateNDocuments(1); - var folder = CreateFolder(documents); - var locker = CreateLocker(folder); - var room = CreateRoom(locker); - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery(); - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.Should().ContainEquivalentOf(_mapper.Map(documents.First())); - - // Cleanup - Remove(documents.First()); - Remove(folder); - Remove(locker); - Remove(room); - } - - [Fact] - public async Task ShouldReturnEmptyPaginatedList_WhenNoDocumentsExist() - { - // Arrange - var room = CreateRoom(); + [Fact] + public async Task ShouldReturnAllDocuments_WhenNoContainersAreDefined() + { + // Arrange + var department = CreateDepartment(); + var documents = CreateNDocuments(1); + documents.First().Department = department; + var folder = CreateFolder(documents); + var locker = CreateLocker(folder); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query(); + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.First().Title.Should().Be(documents.First().Title); + + // Cleanup + Remove(documents.First()); + Remove(folder); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldReturnEmptyPaginatedList_WhenNoDocumentsExist() + { + // Arrange + var department = CreateDepartment(); + var room = CreateRoom(department); - await AddAsync(room); + await AddAsync(room); - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id - }; + var query = new GetAllDocumentsPaginated.Query() + { + RoomId = room.Id + }; - // Act - var result = await SendAsync(query); + // Act + var result = await SendAsync(query); - // Assert - result.Items.Should().BeEmpty(); - - // Cleanup - Remove(room); - } + // Assert + result.Items.Should().BeEmpty(); + + // Cleanup + Remove(room); + Remove(await FindAsync(department.Id)); + } - [Fact] - public async Task ShouldReturnDocumentsOfRoom_WhenOnlyRoomIdIsPresent() + [Fact] + public async Task ShouldReturnDocumentsOfRoom_WhenOnlyRoomIdIsPresent() + { + // Arrange + var department1 = CreateDepartment(); + var department2 = CreateDepartment(); + var documents1 = CreateNDocuments(2); + var folder1 = CreateFolder(documents1); + var locker1 = CreateLocker(folder1); + var room1 = CreateRoom(department1, locker1); + var documents2 = CreateNDocuments(2); + var folder2 = CreateFolder(documents2); + var locker2 = CreateLocker(folder2); + var room2 = CreateRoom(department2, locker2); + await AddAsync(room1); + await AddAsync(room2); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var documents1 = CreateNDocuments(2); - - var folder1 = CreateFolder(documents1); - - var locker1 = CreateLocker(folder1); - - var room1 = CreateRoom(locker1); - - var documents2 = CreateNDocuments(2); - - var folder2 = CreateFolder(documents2); - - var locker2 = CreateLocker(folder2); - - var room2 = CreateRoom(locker2); - - await AddAsync(room1); - await AddAsync(room2); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room1.Id - }; - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.Should().ContainEquivalentOf(_mapper.Map(documents1[0])); - result.Items.Should().ContainEquivalentOf(_mapper.Map(documents1[1])); - result.Items.Should().NotContainEquivalentOf(_mapper.Map(documents2[0])); - result.Items.Should().NotContainEquivalentOf(_mapper.Map(documents2[1])); - - // Cleanup - Remove(documents1[0]); - Remove(documents1[1]); - Remove(documents2[0]); - Remove(documents2[1]); - Remove(folder1); - Remove(folder2); - Remove(locker1); - Remove(locker2); - Remove(room1); - Remove(room2); - } - - [Fact] - public async Task ShouldThrowKeyNotFoundException_WhenOnlyRoomIdIsPresentButDoesNotExist() + RoomId = room1.Id + }; + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.Should() + .BeEquivalentTo(_mapper.Map(documents1), x => x.IgnoringCyclicReferences()); + result.Items.Should() + .NotBeEquivalentTo(_mapper.Map(documents2), x => x.IgnoringCyclicReferences()); + + // Cleanup + Remove(documents1[0]); + Remove(documents1[1]); + Remove(documents2[0]); + Remove(documents2[1]); + Remove(folder1); + Remove(folder2); + Remove(locker1); + Remove(locker2); + Remove(room1); + Remove(room2); + Remove(await FindAsync(department1.Id)); + Remove(await FindAsync(department2.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenOnlyRoomIdIsPresentButDoesNotExist() + { + // Arrange + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = Guid.NewGuid() - }; - - // Act - var action = async () => await SendAsync(query); - - // Assert - await action.Should().ThrowAsync("Room does not exist."); - } - - [Fact] - public async Task ShouldReturnDocumentsOfLocker_WhenOnlyRoomIdAndLockerIdArePresentAndLockerIsInRoom() + RoomId = Guid.NewGuid(), + }; + + // Act + var action = async () => await SendAsync(query); + + // Assert + await action.Should().ThrowAsync("Room does not exist."); + } + + [Fact] + public async Task ShouldReturnDocumentsOfLocker_WhenOnlyRoomIdAndLockerIdArePresentAndLockerIsInRoom() + { + // Arrange + var department = CreateDepartment(); + var documents1 = CreateNDocuments(2); + var folder1 = CreateFolder(documents1); + var locker1 = CreateLocker(folder1); + var documents2 = CreateNDocuments(2); + var folder2 = CreateFolder(documents2); + var locker2 = CreateLocker(folder2); + var room = CreateRoom(department, locker1, locker2); + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var documents1 = CreateNDocuments(2); - - var folder1 = CreateFolder(documents1); - - var locker1 = CreateLocker(folder1); - - var documents2 = CreateNDocuments(2); - - var folder2 = CreateFolder(documents2); - - var locker2 = CreateLocker(folder2); - - var room = CreateRoom(locker1, locker2); - - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id, - LockerId = locker1.Id - }; - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.Should().BeEquivalentTo(_mapper.Map>(documents1)); - result.Items.Should().NotContainEquivalentOf(_mapper.Map>(documents2)); - - // Cleanup - Remove(documents1[0]); - Remove(documents1[1]); - Remove(documents2[0]); - Remove(documents2[1]); - Remove(folder1); - Remove(folder2); - Remove(locker1); - Remove(locker2); - Remove(room); - } - - [Fact] - public async Task ShouldThrowKeyNotFoundException_WhenOnlyRoomIdAndLockerIdArePresentAndLockerDoesNotExist() + RoomId = room.Id, + LockerId = locker1.Id + }; + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.Should() + .BeEquivalentTo(_mapper.Map(documents1), x => x.IgnoringCyclicReferences()); + result.Items.Should() + .NotBeEquivalentTo(_mapper.Map(documents2), x => x.IgnoringCyclicReferences()); + + // Cleanup + Remove(documents1[0]); + Remove(documents1[1]); + Remove(documents2[0]); + Remove(documents2[1]); + Remove(folder1); + Remove(folder2); + Remove(locker1); + Remove(locker2); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenOnlyRoomIdAndLockerIdArePresentAndLockerDoesNotExist() + { + // Arrange + var department = CreateDepartment(); + var room = CreateRoom(department); + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var room = CreateRoom(); - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id, - LockerId = Guid.NewGuid() - }; - - // Act - var action = async () => await SendAsync(query); - - // Assert - await action.Should().ThrowAsync("Locker does not exist."); - - // Cleanup - Remove(room); - } + RoomId = room.Id, + LockerId = Guid.NewGuid() + }; + + // Act + var action = async () => await SendAsync(query); + + // Assert + await action.Should().ThrowAsync("Locker does not exist."); - [Fact] - public async Task ShouldThrowConflictException_WhenOnlyRoomIdAndLockerIdArePresentAndLockerIsNotInRoom() + // Cleanup + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenOnlyRoomIdAndLockerIdArePresentAndLockerIsNotInRoom() + { + // Arrange + var department1 = CreateDepartment(); + var department2 = CreateDepartment(); + var locker = CreateLocker(); + var room1 = CreateRoom(department1); + var room2 = CreateRoom(department2, locker); + await AddAsync(room1); + await AddAsync(room2); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var locker = CreateLocker(); - - var room1 = CreateRoom(); - - var room2 = CreateRoom(locker); - - await AddAsync(room1); - await AddAsync(room2); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room1.Id, - LockerId = locker.Id - }; - - // Act - var action = async () => await SendAsync(query); - - // Assert - await action.Should().ThrowAsync("Room does not match locker."); - - // Cleanup - Remove(locker); - Remove(room1); - Remove(room2); - } + RoomId = room1.Id, + LockerId = locker.Id + }; + + // Act + var action = async () => await SendAsync(query); + + // Assert + await action.Should().ThrowAsync("Room does not match locker."); - [Fact] - public async Task ShouldReturnDocumentsOfFolder_WhenAllIdsArePresentAndFolderIsInBothLockerAndRoom() + // Cleanup + Remove(locker); + Remove(room1); + Remove(room2); + Remove(await FindAsync(department1.Id)); + Remove(await FindAsync(department2.Id)); + } + + [Fact] + public async Task ShouldReturnDocumentsOfFolder_WhenAllIdsArePresentAndFolderIsInBothLockerAndRoom() + { + // Arrange + var department = CreateDepartment(); + var documents1 = CreateNDocuments(2); + documents1[0].Department = department; + documents1[1].Department = department; + var folder1 = CreateFolder(documents1); + var documents2 = CreateNDocuments(2); + documents2[0].Department = department; + documents2[1].Department = department; + var folder2 = CreateFolder(documents2); + var locker = CreateLocker(folder1, folder2); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() + { + RoomId = room.Id, + LockerId = locker.Id, + FolderId = folder1.Id + }; + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.Should() + .BeEquivalentTo(_mapper.Map(documents1), x => x.IgnoringCyclicReferences()); + result.Items.Should() + .NotBeEquivalentTo(_mapper.Map(documents2), x => x.IgnoringCyclicReferences()); + + // Cleanup + Remove(documents1[0]); + Remove(documents1[1]); + Remove(documents2[0]); + Remove(documents2[1]); + Remove(folder1); + Remove(folder2); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowKeyNotFoundException_WhenAllIdsArePresentAndValidAndFolderDoesNotExist() + { + // Arrange + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); + + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var documents1 = CreateNDocuments(2); - var folder1 = CreateFolder(documents1); - - var documents2 = CreateNDocuments(2); - var folder2 = CreateFolder(documents2); - - var locker = CreateLocker(folder1, folder2); - var room = CreateRoom(locker); - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id, - LockerId = locker.Id, - FolderId = folder1.Id - }; - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.Should().BeEquivalentTo(_mapper.Map(documents1)); - result.Items.Should().NotBeEquivalentTo(_mapper.Map(documents2)); - - // Cleanup - Remove(documents1[0]); - Remove(documents1[1]); - Remove(documents2[0]); - Remove(documents2[1]); - Remove(folder1); - Remove(folder2); - Remove(locker); - Remove(room); - } + RoomId = room.Id, + LockerId = locker.Id, + FolderId = Guid.NewGuid() + }; + + // Act + var action = async () => await SendAsync(query); + + // Assert + await action.Should().ThrowAsync("Folder does not exist."); - [Fact] - public async Task ShouldThrowKeyNotFoundException_WhenAllIdsArePresentAndValidAndFolderDoesNotExist() + // Cleanup + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Fact] + public async Task ShouldThrowConflictException_WhenAllIdsArePresentAndFolderIsNotInLockerOrInRoom() + { + // Arrange + var department1 = CreateDepartment(); + var department2 = CreateDepartment(); + var folder1 = CreateFolder(); + var locker1 = CreateLocker(folder1); + var room1 = CreateRoom(department1, locker1); + var folder2 = CreateFolder(); + var locker2 = CreateLocker(folder2); + var room2 = CreateRoom(department2, locker2); + await AddAsync(room1); + await AddAsync(room2); + + var query1 = new GetAllDocumentsPaginated.Query() { - // Arrange - var locker = CreateLocker(); - var room = CreateRoom(locker); - - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id, - LockerId = locker.Id, - FolderId = Guid.NewGuid() - }; - - // Act - var action = async () => await SendAsync(query); - - // Assert - await action.Should().ThrowAsync("Folder does not exist."); - - // Cleanup - Remove(locker); - Remove(room); - } + RoomId = room1.Id, + LockerId = locker2.Id, + FolderId = folder1.Id + }; - [Fact] - public async Task ShouldThrowConflictException_WhenAllIdsArePresentAndFolderIsNotInLockerOrInRoom() + var query2 = new GetAllDocumentsPaginated.Query() { - // Arrange - var folder1 = CreateFolder(); - var locker1 = CreateLocker(folder1); - var room1 = CreateRoom(locker1); - - var folder2 = CreateFolder(); - var locker2 = CreateLocker(folder2); - var room2 = CreateRoom(locker2); - - await AddAsync(room1); - await AddAsync(room2); - - var query1 = new GetAllDocumentsPaginatedQuery() - { - RoomId = room1.Id, - LockerId = locker2.Id, - FolderId = folder1.Id - }; - - var query2 = new GetAllDocumentsPaginatedQuery() - { - RoomId = room1.Id, - LockerId = locker2.Id, - FolderId = folder2.Id - }; - - // Act - var action1 = async () => await SendAsync(query1); - var action2 = async () => await SendAsync(query2); - - // Assert - await action1.Should().ThrowAsync("Either locker or room does not match folder."); - await action1.Should().ThrowAsync("Either locker or room does not match folder."); - - // Cleanup - Remove(folder1); - Remove(folder2); - Remove(locker1); - Remove(locker2); - Remove(room1); - Remove(room2); - } + RoomId = room1.Id, + LockerId = locker2.Id, + FolderId = folder2.Id + }; + + // Act + var action1 = async () => await SendAsync(query1); + var action2 = async () => await SendAsync(query2); + + // Assert + await action1.Should().ThrowAsync("Either locker or room does not match folder."); + await action2.Should().ThrowAsync("Either locker or room does not match folder."); - [Fact] - public async Task ShouldReturnSortedByIdPaginatedList_WhenSortByIsNotPresent() + // Cleanup + Remove(folder1); + Remove(folder2); + Remove(locker1); + Remove(locker2); + Remove(room1); + Remove(room2); + Remove(await FindAsync(department1.Id)); + Remove(await FindAsync(department2.Id)); + } + + [Fact] + public async Task ShouldReturnSortedByIdPaginatedList_WhenSortByIsNotPresent() + { + // Arrange + var department = CreateDepartment(); + var documents = CreateNDocuments(2); + var folder = CreateFolder(documents); + var locker = CreateLocker(folder); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var documents = CreateNDocuments(2); - var folder = CreateFolder(documents); - var locker = CreateLocker(folder); - var room = CreateRoom(locker); - - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id - }; - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.First().Should().BeEquivalentTo(_mapper.Map(documents[0].Id.CompareTo(documents[1].Id) <= 0 ? documents[0] : documents[1])); - - // Cleanup - Remove(documents[0]); - Remove(documents[1]); - Remove(folder); - Remove(locker); - Remove(room); - } - - [Theory] - [InlineData(nameof(DocumentDto.Id), "asc")] - [InlineData(nameof(DocumentDto.Title), "asc")] - [InlineData(nameof(DocumentDto.DocumentType), "asc")] - [InlineData(nameof(DocumentDto.Description), "asc")] - [InlineData(nameof(DocumentDto.Id), "desc")] - [InlineData(nameof(DocumentDto.Title), "desc")] - [InlineData(nameof(DocumentDto.DocumentType), "desc")] - [InlineData(nameof(DocumentDto.Description), "desc")] - public async Task ShouldReturnSortedByPropertyPaginatedList_WhenSortByIsPresent(string sortBy, string sortOrder) + RoomId = room.Id, + }; + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.First().Should() + .BeEquivalentTo( + _mapper.Map(documents[0].Id.CompareTo(documents[1].Id) <= 0 ? documents[0] : documents[1]), + x => x.IgnoringCyclicReferences()); + + // Cleanup + Remove(documents[0]); + Remove(documents[1]); + Remove(folder); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } + + [Theory] + [InlineData(nameof(DocumentDto.Id), "asc")] + [InlineData(nameof(DocumentDto.Title), "asc")] + [InlineData(nameof(DocumentDto.DocumentType), "asc")] + [InlineData(nameof(DocumentDto.Description), "asc")] + [InlineData(nameof(DocumentDto.Id), "desc")] + [InlineData(nameof(DocumentDto.Title), "desc")] + [InlineData(nameof(DocumentDto.DocumentType), "desc")] + [InlineData(nameof(DocumentDto.Description), "desc")] + public async Task ShouldReturnSortedByPropertyPaginatedList_WhenSortByIsPresent(string sortBy, string sortOrder) + { + // Arrange + var department = CreateDepartment(); + var documents = CreateNDocuments(2); + var folder = CreateFolder(documents); + var locker = CreateLocker(folder); + var room = CreateRoom(department, locker); + + await AddAsync(room); + + var query = new GetAllDocumentsPaginated.Query() { - // Arrange - var documents = CreateNDocuments(2); - var folder = CreateFolder(documents); - var locker = CreateLocker(folder); - var room = CreateRoom(locker); - - await AddAsync(room); - - var query = new GetAllDocumentsPaginatedQuery() - { - RoomId = room.Id, - SortBy = sortBy, - SortOrder = sortOrder - }; - - var list = new EnumerableQuery(_mapper.Map>(documents)); - var list2 = list.OrderByCustom(sortBy, sortOrder); - var expected = list2.ToList(); - - // Act - var result = await SendAsync(query); - - // Assert - result.Items.Should().BeEquivalentTo(expected); - - // Cleanup - Remove(documents[0]); - Remove(documents[1]); - Remove(folder); - Remove(locker); - Remove(room); - } - } \ No newline at end of file + RoomId = room.Id, + SortBy = sortBy, + SortOrder = sortOrder + }; + + var list = new EnumerableQuery(_mapper.Map>(documents)); + var list2 = list.OrderByCustom(sortBy, sortOrder); + var expected = list2.ToList(); + + // Act + var result = await SendAsync(query); + + // Assert + result.Items.Should().BeEquivalentTo(expected, x => x.IgnoringCyclicReferences()); + + // Cleanup + Remove(documents[0]); + Remove(documents[1]); + Remove(folder); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); + } +} \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Folders/Commands/AddFolderTests.cs b/tests/Application.Tests.Integration/Folders/Commands/AddFolderTests.cs index 8832e0c3..42601c21 100644 --- a/tests/Application.Tests.Integration/Folders/Commands/AddFolderTests.cs +++ b/tests/Application.Tests.Integration/Folders/Commands/AddFolderTests.cs @@ -1,9 +1,6 @@ using Application.Common.Exceptions; -using Application.Common.Models.Dtos.Physical; -using Application.Folders.Commands.AddFolder; -using Application.Lockers.Commands.AddLocker; -using Application.Rooms.Commands.AddRoom; -using Bogus; +using Application.Folders.Commands; +using Domain.Entities; using Domain.Entities.Physical; using Domain.Exceptions; using FluentAssertions; @@ -13,20 +10,6 @@ namespace Application.Tests.Integration.Folders.Commands; public class AddFolderTests : BaseClassFixture { - private readonly Faker _folderGenerator = new Faker() - .RuleFor(f => f.Name, faker => faker.Commerce.ProductName()) - .RuleFor(f => f.Description, faker => faker.Commerce.ProductDescription()) - .RuleFor(f => f.Capacity, faker => faker.Random.Int(1,9999)); - - private readonly Faker _roomGenerator = new Faker() - .RuleFor(r => r.Name, faker => faker.Commerce.ProductName()) - .RuleFor(r => r.Description, faker => faker.Commerce.ProductDescription()) - .RuleFor(r => r.Capacity, faker => faker.Random.Int(1,9999)); - - private readonly Faker _lockerGenerator = new Faker() - .RuleFor(l => l.Name, faker => faker.Commerce.ProductName()) - .RuleFor(l => l.Description, faker => faker.Commerce.ProductDescription()) - .RuleFor(l => l.Capacity, faker => faker.Random.Int(1,9999)); public AddFolderTests(CustomApiFactory apiFactory) : base(apiFactory) { } @@ -35,30 +18,25 @@ public AddFolderTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldAddFolder_WhenAddDetailsAreValid() { // Arrange - var addRoomCommand = _roomGenerator.Generate(); - var room = await SendAsync(addRoomCommand); - - var addLockerCommand = _lockerGenerator.Generate(); - addLockerCommand = addLockerCommand with - { - RoomId = room.Id, - Capacity = 1 - }; - var locker = await SendAsync(addLockerCommand); + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); + await AddAsync(room); - var addFolderCommand = _folderGenerator.Generate(); - addFolderCommand = addFolderCommand with + var command = new AddFolder.Command() { LockerId = locker.Id, - Capacity = 1 + Capacity = 1, + Name = "something" }; // Act - var folder = await SendAsync(addFolderCommand); + var folder = await SendAsync(command); + // Assert - folder.Name.Should().Be(addFolderCommand.Name); - folder.Description.Should().Be(addFolderCommand.Description); - folder.Capacity.Should().Be(addFolderCommand.Capacity); + folder.Name.Should().Be(command.Name); + folder.Description.Should().Be(command.Description); + folder.Capacity.Should().Be(command.Capacity); folder.Locker.Id.Should().Be(locker.Id); folder.Locker.NumberOfFolders.Should().Be(locker.NumberOfFolders + 1); folder.NumberOfDocuments.Should().Be(0); @@ -66,182 +44,128 @@ public async Task ShouldAddFolder_WhenAddDetailsAreValid() // Clean up var folderEntity = await FindAsync(folder.Id); - var lockerEntity = await FindAsync(locker.Id); - var roomEntity = await FindAsync(room.Id); Remove(folderEntity); - Remove(lockerEntity); - Remove(roomEntity); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldAddFolder_WhenFoldersHasSameNameButInDifferentLockers() { // Arrange - var sameFolderName = new Faker().Commerce.ProductName(); - var addRoomCommand = _roomGenerator.Generate(); - addRoomCommand = addRoomCommand with - { - Capacity = 2 - }; - - var room = await SendAsync(addRoomCommand); - - var addLockerCommand = _lockerGenerator.Generate(); - - var addLockerACommand = addLockerCommand with - { - RoomId = room.Id, - Capacity = 1 - }; - var addLockerBCommand = addLockerCommand with - { - RoomId = room.Id, - Name = new Faker().Commerce.ProductName(), - Capacity = 1 - }; - - var lockerA = await SendAsync(addLockerACommand); - var lockerB = await SendAsync(addLockerBCommand); - - var addFolderCommand = _folderGenerator.Generate(); - var addFolderCommandForLockerA = addFolderCommand with - { - Name = sameFolderName, - LockerId = lockerA.Id - }; - var addFolderCommandForLockerB = addFolderCommand with + var department = CreateDepartment(); + var folder1 = CreateFolder(); + var locker1 = CreateLocker(folder1); + var locker2 = CreateLocker(); + var room = CreateRoom(department, locker1, locker2); + await AddAsync(room); + + var command = new AddFolder.Command() { - Name = sameFolderName, - LockerId = lockerB.Id + LockerId = locker2.Id, + Name = folder1.Name, + Capacity = 3, }; - var folderA = await SendAsync(addFolderCommandForLockerA); // Act - var folderB = await SendAsync(addFolderCommandForLockerB); + var folder2 = await SendAsync(command); // Assert - folderA.Locker.Id.Should().NotBe(folderB.Locker.Id); - folderA.Name.Should().Be(folderB.Name); + folder1.Name.Should().Be(folder2.Name); // Cleanup - var folderAEntity = await FindAsync(folderA.Id); - var folderBEntity = await FindAsync(folderB.Id); - var lockerAEntity = await FindAsync(lockerA.Id); - var lockerBEntity = await FindAsync(lockerB.Id); - var roomEntity = await FindAsync(room.Id); - Remove(folderAEntity); - Remove(folderBEntity); - Remove(lockerAEntity); - Remove(lockerBEntity); - Remove(roomEntity); + Remove(folder1); + Remove(await FindAsync(folder2.Id)); + Remove(locker1); + Remove(locker2); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowConflictException_WhenFolderAlreadyExistsInTheSameLocker() { // Arrange - var sameFolderName = new Faker().Commerce.ProductName(); - var addRoomCommand = _roomGenerator.Generate(); - var room = await SendAsync(addRoomCommand); - - var addLockerCommand = _lockerGenerator.Generate(); - addLockerCommand = addLockerCommand with - { - RoomId = room.Id, - Capacity = 2 - }; - var locker = await SendAsync(addLockerCommand); - - var addFolderCommand = _folderGenerator.Generate(); - var addFolderCommandForLockerA = addFolderCommand with - { - Name = sameFolderName, - LockerId = locker.Id - }; - var addFolderCommandForLockerB = addFolderCommand with + var department = CreateDepartment(); + var folder = CreateFolder(); + var locker = CreateLocker(folder); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var command = new AddFolder.Command() { - Name = sameFolderName, - LockerId = locker.Id + Name = folder.Name, + LockerId = locker.Id, + Capacity = 3 }; - var folder = await SendAsync(addFolderCommandForLockerA); // Act - var action = async () => await SendAsync(addFolderCommandForLockerB); + var action = async () => await SendAsync(command); // Assert - await action.Should().ThrowAsync().WithMessage("Folder's name already exists."); + await action.Should().ThrowAsync() + .WithMessage("Folder name already exists."); // Cleanup - var folderEntity = await FindAsync(folder.Id); - var lockerEntity = await FindAsync(locker.Id); - var roomEntity = await FindAsync(room.Id); - Remove(folderEntity); - Remove(lockerEntity); - Remove(roomEntity); + Remove(folder); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowLimitExceededException_WhenGoingOverCapacity() { // Arrange - var addRoomCommand = _roomGenerator.Generate(); - var room = await SendAsync(addRoomCommand); - - var addLockerCommand = _lockerGenerator.Generate(); - addLockerCommand = addLockerCommand with + var department = CreateDepartment(); + var folder1 = CreateFolder(); + var folder2 = CreateFolder(); + var folder3 = CreateFolder(); + var locker = CreateLocker(folder1, folder2, folder3); + var room = CreateRoom(department, locker); + await AddAsync(room); + + var command = new AddFolder.Command() { - RoomId = room.Id, - Capacity = new Faker().Random.Int(1,10) + Name = "something", + Capacity = 3, + LockerId = locker.Id, + Description = "something else", }; - var locker = await SendAsync(addLockerCommand); - var list = new List(); // Act - var action = async () => - { - - for (var i = 0; i <= locker.Capacity; i++) - { - var addFolderCommand = _folderGenerator.Generate(); - addFolderCommand = addFolderCommand with - { - LockerId = locker.Id - }; - list.Add(await SendAsync(addFolderCommand)); - } - }; + var action = async () => await SendAsync(command); // Assert await action.Should().ThrowAsync() .WithMessage("This locker cannot accept more folders."); // Cleanup - foreach (var f in list) - { - var folderEntity = await FindAsync(f.Id); - Remove(folderEntity); - } - var lockerEntity = await FindAsync(locker.Id); - var roomEntity = await FindAsync(room.Id); - Remove(lockerEntity); - Remove(roomEntity); + Remove(folder1); + Remove(folder2); + Remove(folder3); + Remove(locker); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowKeyNotFoundException_WhenLockerIdNotExists() { // Arrange - var addFolderCommand = _folderGenerator.Generate(); - addFolderCommand = addFolderCommand with + var command = new AddFolder.Command() { - LockerId = Guid.NewGuid() + LockerId = Guid.NewGuid(), + Name = "something", + Capacity = 3 }; // Act - var folder = async () => await SendAsync(addFolderCommand); + var action = async () => await SendAsync(command); // Assert - await folder.Should().ThrowAsync() + await action.Should().ThrowAsync() .WithMessage("Locker does not exist."); } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Folders/Commands/DisableFolderTests.cs b/tests/Application.Tests.Integration/Folders/Commands/DisableFolderTests.cs index 1dee5f9d..02a9e97b 100644 --- a/tests/Application.Tests.Integration/Folders/Commands/DisableFolderTests.cs +++ b/tests/Application.Tests.Integration/Folders/Commands/DisableFolderTests.cs @@ -1,5 +1,5 @@ using Application.Common.Exceptions; -using Application.Folders.Commands.DisableFolder; +using Application.Folders.Commands; using Domain.Entities.Physical; using FluentAssertions; using Xunit; @@ -46,7 +46,7 @@ public async Task ShouldDisableFolder_WhenFolderHaveNoDocument() }; await AddAsync(folder); - var disableFolderCommand = new DisableFolderCommand() + var disableFolderCommand = new DisableFolder.Command() { FolderId = folder.Id }; @@ -67,7 +67,7 @@ public async Task ShouldDisableFolder_WhenFolderHaveNoDocument() public async Task ShouldThrowKeyNotFoundException_WhenFolderDoesNotExist() { // Arrange - var disableFolderCommand = new DisableFolderCommand() + var disableFolderCommand = new DisableFolder.Command() { FolderId = Guid.NewGuid() }; @@ -113,7 +113,7 @@ public async Task ShouldThrowInvalidOperationException_WhenFolderIsAlreadyDisabl Locker = locker }; await AddAsync(folder); - var disableFolderCommand = new DisableFolderCommand() + var disableFolderCommand = new DisableFolder.Command() { FolderId = folder.Id }; @@ -173,7 +173,7 @@ public async Task ShouldThrowInvalidOperationException_WhenFolderHasDocuments() }; await AddAsync(document); - var disableFolderCommand = new DisableFolderCommand() + var disableFolderCommand = new DisableFolder.Command() { FolderId = folder.Id }; diff --git a/tests/Application.Tests.Integration/Lockers/Commands/AddLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/AddLockerTests.cs index 8f3d30e0..2da1413f 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/AddLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/AddLockerTests.cs @@ -1,11 +1,9 @@ using Application.Common.Exceptions; -using Application.Lockers.Commands.AddLocker; +using Application.Lockers.Commands; using Bogus; using Domain.Entities.Physical; using Domain.Exceptions; using FluentAssertions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Application.Tests.Integration.Lockers.Commands; @@ -14,7 +12,6 @@ public class AddLockerTests : BaseClassFixture { public AddLockerTests(CustomApiFactory apiFactory) : base(apiFactory) { - } [Fact] @@ -33,7 +30,7 @@ public async Task ShouldReturnLocker_WhenCreateDetailsAreValid() await AddAsync(room); - var addLockerCommand = new AddLockerCommand() + var addLockerCommand = new AddLocker.Command() { Name = new Faker().Name.JobTitle(), Description = new Faker().Lorem.Sentence(), @@ -77,7 +74,7 @@ public async Task ShouldThrowConflictException_WhenLockerAlreadyExistsInTheSameR await AddAsync(room); - var addLockerCommand = new AddLockerCommand() + var addLockerCommand = new AddLocker.Command() { Name = new Faker().Name.JobTitle(), Description = new Faker().Lorem.Sentence(), @@ -126,7 +123,7 @@ public async Task ShouldReturnLocker_WhenLockersHasSameNameButInDifferentRooms() await AddAsync(room2); - var addLockerCommand = new AddLockerCommand() + var addLockerCommand = new AddLocker.Command() { Name = new Faker().Name.JobTitle(), Description = new Faker().Lorem.Sentence(), @@ -134,7 +131,7 @@ public async Task ShouldReturnLocker_WhenLockersHasSameNameButInDifferentRooms() RoomId = room1.Id, }; - var addLockerCommand2 = new AddLockerCommand() + var addLockerCommand2 = new AddLocker.Command() { Name = addLockerCommand.Name, Description = new Faker().Lorem.Sentence(), @@ -181,7 +178,7 @@ public async Task ShouldThrowLimitExceededException_WhenGoingOverCapacity() await AddAsync(room); - var addLockerCommand = new AddLockerCommand() + var addLockerCommand = new AddLocker.Command() { Name = new Faker().Name.JobTitle(), Description = new Faker().Lorem.Sentence(), @@ -189,7 +186,7 @@ public async Task ShouldThrowLimitExceededException_WhenGoingOverCapacity() RoomId = room.Id, }; - var addLockerCommand2 = new AddLockerCommand() + var addLockerCommand2 = new AddLocker.Command() { Name = new Faker().Name.JobTitle(), Description = new Faker().Lorem.Sentence(), diff --git a/tests/Application.Tests.Integration/Lockers/Commands/DisableLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/DisableLockerTests.cs index 28538e7b..5cf76d23 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/DisableLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/DisableLockerTests.cs @@ -1,7 +1,6 @@ using Application.Common.Exceptions; -using Application.Lockers.Commands.AddLocker; -using Application.Lockers.Commands.DisableLocker; -using Bogus; +using Application.Lockers.Commands; +using Domain.Entities; using Domain.Entities.Physical; using FluentAssertions; using Xunit; @@ -12,37 +11,18 @@ public class DisableLockerTests : BaseClassFixture { public DisableLockerTests(CustomApiFactory apiFactory) : base(apiFactory) { - } [Fact] public async Task ShouldDisableLocker_WhenLockerExistsAndIsAvailable() { // Arrange - var room = new Room() - { - Id = Guid.NewGuid(), - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 1, - IsAvailable = true, - NumberOfLockers = 0, - }; - + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); await AddAsync(room); - var createLockerCommand = new AddLockerCommand() - { - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 2, - RoomId = room.Id, - }; - - var locker = await SendAsync(createLockerCommand); - room.NumberOfLockers += 1; - - var disableLockerCommand = new DisableLockerCommand() + var disableLockerCommand = new DisableLocker.Command() { LockerId = locker.Id, }; @@ -58,15 +38,15 @@ public async Task ShouldDisableLocker_WhenLockerExistsAndIsAvailable() result.NumberOfFolders.Should().Be(locker.NumberOfFolders); // Cleanup - var roomEntity = await FindAsync(room.Id); - Remove(roomEntity); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowKeyNotFoundException_WhenLockerDoesNotExist() { // Arrange - var disableLockerCommand = new DisableLockerCommand() + var disableLockerCommand = new DisableLocker.Command() { LockerId = Guid.NewGuid(), }; @@ -84,28 +64,12 @@ await action.Should() public async Task ShouldThrowConflictException_WhenLockerIsAlreadyDisabled() { // Arrange - var room = new Room() - { - Id = Guid.NewGuid(), - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 1, - IsAvailable = true, - NumberOfLockers = 0, - }; - + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); await AddAsync(room); - - var createLockerCommand = new AddLockerCommand() - { - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 2, - RoomId = room.Id, - }; - var locker = await SendAsync(createLockerCommand); - var disableLockerCommand = new DisableLockerCommand() + var disableLockerCommand = new DisableLocker.Command() { LockerId = locker.Id, }; @@ -118,7 +82,7 @@ public async Task ShouldThrowConflictException_WhenLockerIsAlreadyDisabled() await action.Should().ThrowAsync().WithMessage("Locker has already been disabled."); // Cleanup - var roomEntity = await FindAsync(room.Id); - Remove(roomEntity); + Remove(room); + Remove(await FindAsync(department.Id)); } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Lockers/Commands/EnableLockerTests.cs b/tests/Application.Tests.Integration/Lockers/Commands/EnableLockerTests.cs index 7f3a9dbb..b4ec3df7 100644 --- a/tests/Application.Tests.Integration/Lockers/Commands/EnableLockerTests.cs +++ b/tests/Application.Tests.Integration/Lockers/Commands/EnableLockerTests.cs @@ -1,10 +1,6 @@ using Application.Common.Exceptions; -using Application.Lockers.Commands.AddLocker; -using Application.Lockers.Commands.DisableLocker; -using Application.Lockers.Commands.EnableLocker; -using Bogus; -using Domain.Entities.Physical; -using Domain.Exceptions; +using Application.Lockers.Commands; +using Domain.Entities; using FluentAssertions; using Xunit; @@ -21,63 +17,39 @@ public EnableLockerTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldEnableLocker_WhenLockerExistsAndIsNotAvailable() { // Arrange - var room = new Room() - { - Id = Guid.NewGuid(), - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 1, - IsAvailable = true, - NumberOfLockers = 0, - }; - + var department = CreateDepartment(); + var locker = CreateLocker(); + locker.IsAvailable = false; + var room = CreateRoom(department, locker); await AddAsync(room); - var createLockerCommand = new AddLockerCommand() - { - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 2, - RoomId = room.Id, - }; - - var locker = await SendAsync(createLockerCommand); - - var disableLockerCommand = new DisableLockerCommand() - { - LockerId = locker.Id, - }; - - await SendAsync(disableLockerCommand); - // Act - - var enableLockerCommand = new EnableLockerCommand() + var command = new EnableLocker.Command() { LockerId = locker.Id, }; - var result = await SendAsync(enableLockerCommand); + var result = await SendAsync(command); // Assert result.IsAvailable.Should().BeTrue(); // Cleanup - var roomEntity = await FindAsync(room.Id); - Remove(roomEntity); + Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowKeyNotFoundException_WhenLockerDoesNotExist() { // Arrange - var enableLockerCommand = new EnableLockerCommand() + var command = new EnableLocker.Command() { LockerId = Guid.NewGuid(), }; // Act - var action = async () => await SendAsync(enableLockerCommand); + var action = async () => await SendAsync(command); // Assert await action.Should() @@ -89,28 +61,12 @@ await action.Should() public async Task ShouldThrowConflictException_WhenLockerIsAlreadyEnabled() { // Arrange - var room = new Room() - { - Id = Guid.NewGuid(), - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 1, - IsAvailable = true, - NumberOfLockers = 0, - }; - + var department = CreateDepartment(); + var locker = CreateLocker(); + var room = CreateRoom(department, locker); await AddAsync(room); - - var createLockerCommand = new AddLockerCommand() - { - Name = new Faker().Commerce.ProductName(), - Description = new Faker().Lorem.Sentence(), - Capacity = 2, - RoomId = room.Id, - }; - var locker = await SendAsync(createLockerCommand); - var enableLockerCommand = new EnableLockerCommand() + var enableLockerCommand = new EnableLocker.Command() { LockerId = locker.Id, }; @@ -124,7 +80,7 @@ await action.Should() .WithMessage("Locker has already been enabled."); // Cleanup - var roomEntity = await FindAsync(room.Id); - Remove(roomEntity); + Remove(room); + Remove(await FindAsync(department.Id)); } } diff --git a/tests/Application.Tests.Integration/Rooms/Commands/DisableRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/DisableRoomTests.cs index 9fefabc4..39badcb8 100644 --- a/tests/Application.Tests.Integration/Rooms/Commands/DisableRoomTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Commands/DisableRoomTests.cs @@ -1,12 +1,8 @@ using Application.Common.Exceptions; -using Application.Helpers; -using Application.Lockers.Commands.AddLocker; -using Application.Rooms.Commands.DisableRoom; -using Bogus; +using Application.Rooms.Commands; using Domain.Entities; using Domain.Entities.Physical; using FluentAssertions; -using NodaTime; using Xunit; namespace Application.Tests.Integration.Rooms.Commands; @@ -21,12 +17,13 @@ public DisableRoomTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldDisableRoom_WhenRoomHaveNoDocument() { // Arrange + var department = CreateDepartment(); var folder = CreateFolder(); var locker = CreateLocker(folder); - var room = CreateRoom(locker); + var room = CreateRoom(department, locker); await AddAsync(room); - var disableRoomCommand = new DisableRoomCommand() + var disableRoomCommand = new DisableRoom.Command() { RoomId = room.Id }; @@ -46,13 +43,14 @@ public async Task ShouldDisableRoom_WhenRoomHaveNoDocument() Remove(folder); Remove(locker); Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowKeyNotFoundException_WhenRoomDoesNotExist() { // Arrange - var disableRoomCommand = new DisableRoomCommand() + var disableRoomCommand = new DisableRoom.Command() { RoomId = Guid.NewGuid() }; @@ -69,14 +67,15 @@ await action.Should().ThrowAsync() public async Task ShouldThrowInvalidOperationException_WhenRoomIsNotEmptyOfDocuments() { // Arrange + var department = CreateDepartment(); var documents = CreateNDocuments(1); var folder = CreateFolder(documents); var locker = CreateLocker(folder); - var room = CreateRoom(locker); + var room = CreateRoom(department, locker); await AddAsync(room); - var disableRoomCommand = new DisableRoomCommand() + var disableRoomCommand = new DisableRoom.Command() { RoomId = room.Id }; @@ -93,17 +92,19 @@ await action.Should().ThrowAsync() Remove(folder); Remove(locker); Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowInvalidOperationException_WhenRoomIsNotAvailable() { // Arrange - var room = CreateRoom(); + var department = CreateDepartment(); + var room = CreateRoom(department); room.IsAvailable = false; await AddAsync(room); - var command = new DisableRoomCommand() + var command = new DisableRoom.Command() { RoomId = room.Id }; @@ -117,5 +118,6 @@ await action.Should().ThrowAsync() // Cleanup Remove(room); + Remove(await FindAsync(department.Id)); } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Rooms/Commands/RemoveRoomTests.cs b/tests/Application.Tests.Integration/Rooms/Commands/RemoveRoomTests.cs index 70954ce7..69fff2e8 100644 --- a/tests/Application.Tests.Integration/Rooms/Commands/RemoveRoomTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Commands/RemoveRoomTests.cs @@ -1,5 +1,5 @@ -using Application.Rooms.Commands.RemoveRoom; -using Bogus; +using Application.Rooms.Commands; +using Domain.Entities; using Domain.Entities.Physical; using FluentAssertions; using Xunit; @@ -16,33 +16,38 @@ public RemoveRoomTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldRemoveRoom_WhenRoomHasNoDocuments() { // Arrange - var room = CreateRoom(); + var department = CreateDepartment(); + var room = CreateRoom(department); await Add(room); - var command = new RemoveRoomCommand() + var command = new RemoveRoom.Command() { RoomId = room.Id }; + // Act - var result = await SendAsync(command); + await SendAsync(command); // Assert var deletedRoom = await FindAsync(room.Id); - result.IsAvailable.Should().BeFalse(); deletedRoom.Should().BeNull(); + + // Cleanup + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowInvalidOperationException_WhenRoomHaveDocuments() { // Arrange + var department = CreateDepartment(); var documents = CreateNDocuments(1); var folder = CreateFolder(documents); var locker = CreateLocker(folder); - var room = CreateRoom(locker); + var room = CreateRoom(department, locker); await AddAsync(room); - var command = new RemoveRoomCommand() + var command = new RemoveRoom.Command() { RoomId = room.Id }; @@ -59,13 +64,14 @@ await action.Should().ThrowAsync() Remove(folder); Remove(locker); Remove(room); + Remove(await FindAsync(department.Id)); } [Fact] public async Task ShouldThrowKeyNotFoundException_WhenRoomDoesNotExist() { // Arrange - var command = new RemoveRoomCommand() + var command = new RemoveRoom.Command() { RoomId = Guid.NewGuid() }; diff --git a/tests/Application.Tests.Integration/Rooms/Queries/GetEmptyContainersPaginatedTests.cs b/tests/Application.Tests.Integration/Rooms/Queries/GetEmptyContainersPaginatedTests.cs index c0c001b1..05ca1bf0 100644 --- a/tests/Application.Tests.Integration/Rooms/Queries/GetEmptyContainersPaginatedTests.cs +++ b/tests/Application.Tests.Integration/Rooms/Queries/GetEmptyContainersPaginatedTests.cs @@ -1,11 +1,9 @@ -using Application.Lockers.Commands.AddLocker; -using Application.Rooms.Queries.GetEmptyContainersPaginated; +using Application.Rooms.Queries; using Bogus; using Domain.Entities.Physical; using FluentAssertions; using Infrastructure.Persistence; using Microsoft.Extensions.DependencyInjection; -using Microsoft.VisualBasic; using Xunit; namespace Application.Tests.Integration.Rooms.Queries; @@ -20,8 +18,19 @@ public GetEmptyContainersPaginatedTests(CustomApiFactory apiFactory) : base(apiF public async Task ShouldReturnLockersWithEmptyFolders() { // Arrange - var room = await SetupTestEntities(); - var query = new GetEmptyContainersPaginatedQuery() + var department = CreateDepartment(); + var folder1 = CreateFolder(); + var folder2 = CreateFolder(); + var folder3 = CreateFolder(); + folder3.IsAvailable = false; + var locker1 = CreateLocker(folder1, folder2); + locker1.Capacity = 2; + var locker2 = CreateLocker(folder3); + locker2.Capacity = 2; + var room = CreateRoom(department, locker1, locker2); + await AddAsync(room); + + var query = new GetEmptyContainersPaginated.Query() { Page = 1, Size = 2, @@ -36,23 +45,24 @@ public async Task ShouldReturnLockersWithEmptyFolders() result.Items.First().Id.Should().Be(room.Lockers.ElementAt(0).Id); result.Items.First().Name.Should().Be(room.Lockers.ElementAt(0).Name); result.Items.First().Description.Should().Be(room.Lockers.ElementAt(0).Description); - result.Items.First().NumberOfFreeFolders.Should().Be(1); - result.Items.First().Capacity.Should().Be(4); - result.Items.First().NumberOfFolders.Should().Be(2); - result.Items.First().Folders.First().Id.Should().Be(room.Lockers.ElementAt(0).Folders.First().Id); - result.Items.First().Folders.First().Name.Should().Be(room.Lockers.ElementAt(0).Folders.First().Name); - result.Items.First().Folders.First().Description.Should().Be(room.Lockers.ElementAt(0).Folders.First().Description); - result.Items.First().Folders.First().Slot.Should().Be(3); + result.Items.First().NumberOfFreeFolders.Should().Be(2); + result.Items.First().Capacity.Should().Be(2); // Cleanup - await CleanupTestEntities(room); + Remove(folder1); + Remove(folder2); + Remove(folder3); + Remove(locker1); + Remove(locker2); + Remove(room); + Remove(department); } [Fact] public async Task ShouldThrowNotFound_WhenRoomDoesNotExist() { // Arrange - var query = new GetEmptyContainersPaginatedQuery() + var query = new GetEmptyContainersPaginated.Query() { Page = 1, Size = 2, @@ -65,92 +75,4 @@ public async Task ShouldThrowNotFound_WhenRoomDoesNotExist() // Assert await action.Should().ThrowAsync("Room does not exist"); } - - private async Task SetupTestEntities() - { - var room = new Room() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.FirstName, - Capacity = 3, - IsAvailable = true, - NumberOfLockers = 2, - }; - - var locker1 = new Locker() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.LastName, - Room = room, - Capacity = 4, - IsAvailable = true, - NumberOfFolders = 2 - }; - - var locker2 = new Locker() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.UserName, - Room = room, - Capacity = 4, - IsAvailable = false, - NumberOfFolders = 1 - }; - - var folder1 = new Folder() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.FirstName, - Locker = locker1, - IsAvailable = true, - Capacity = 3, - NumberOfDocuments = 0, - }; - - var folder2 = new Folder() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.LastName, - Locker = locker1, - IsAvailable = true, - Capacity = 2, - NumberOfDocuments = 3 - }; - - var folder3 = new Folder() - { - Id = Guid.NewGuid(), - Name = new Faker().Person.UserName, - Locker = locker2, - IsAvailable = false, - Capacity = 3, - NumberOfDocuments = 1 - }; - - using var scope = _scopeFactory.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - - await context.Rooms.AddAsync(room); - await context.Lockers.AddAsync(locker1); - await context.Lockers.AddAsync(locker2); - await context.Folders.AddAsync(folder1); - await context.Folders.AddAsync(folder2); - await context.Folders.AddAsync(folder3); - - await context.SaveChangesAsync(); - - return room; - } - - private async Task CleanupTestEntities(Room room) - { - using var scope = _scopeFactory.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - - context.RemoveRange(room.Lockers.SelectMany(l => l.Folders)); - context.RemoveRange(room.Lockers); - context.Remove(room); - - await context.SaveChangesAsync(); - } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/Users/Commands/AddUserTests.cs b/tests/Application.Tests.Integration/Users/Commands/AddUserTests.cs index 52a431c9..89842090 100644 --- a/tests/Application.Tests.Integration/Users/Commands/AddUserTests.cs +++ b/tests/Application.Tests.Integration/Users/Commands/AddUserTests.cs @@ -1,4 +1,4 @@ -using Application.Users.Commands.AddUser; +using Application.Users.Commands; using Bogus; using Domain.Entities; using FluentAssertions; @@ -8,14 +8,6 @@ namespace Application.Tests.Integration.Users.Commands; public class AddUserTests : BaseClassFixture { - private readonly Faker _userGenerator = new Faker() - .RuleFor(x => x.Username, faker => faker.Person.UserName) - .RuleFor(x => x.Email, faker => faker.Person.Email) - .RuleFor(x => x.FirstName, faker => faker.Person.FirstName) - .RuleFor(x => x.LastName, faker => faker.Person.LastName) - .RuleFor(x => x.Password, faker => faker.Random.String()) - .RuleFor(x => x.Role, faker => faker.Random.Word()) - .RuleFor(x => x.Position, faker => faker.Random.Word()); public AddUserTests(CustomApiFactory apiFactory) : base(apiFactory) { } @@ -24,29 +16,32 @@ public AddUserTests(CustomApiFactory apiFactory) : base(apiFactory) public async Task ShouldCreateUser_WhenCreateDetailsAreValid() { // Arrange - var department = new Department() - { - Id = Guid.NewGuid(), - Name = new Faker().Commerce.Department() - }; + var department = CreateDepartment(); await AddAsync(department); - var createUserCommand = _userGenerator.Generate(); - createUserCommand = createUserCommand with + + var command = new AddUser.Command() { - DepartmentId = department.Id + Username = new Faker().Person.UserName, + Email = new Faker().Person.Email, + FirstName = new Faker().Person.FirstName, + LastName = new Faker().Person.LastName, + Password = new Faker().Random.Word(), + Role = new Faker().Random.Word(), + DepartmentId = department.Id, + Position = new Faker().Random.Word(), }; // Act - var user = await SendAsync(createUserCommand); + var user = await SendAsync(command); // Assert - user.Username.Should().Be(createUserCommand.Username); - user.FirstName.Should().Be(createUserCommand.FirstName); - user.LastName.Should().Be(createUserCommand.LastName); + user.Username.Should().Be(command.Username); + user.FirstName.Should().Be(command.FirstName); + user.LastName.Should().Be(command.LastName); user.Department.Should().BeEquivalentTo(new { department.Id, department.Name }); - user.Email.Should().Be(createUserCommand.Email); - user.Role.Should().Be(createUserCommand.Role); - user.Position.Should().Be(createUserCommand.Position); + user.Email.Should().Be(command.Email); + user.Role.Should().Be(command.Role); + user.Position.Should().Be(command.Position); user.IsActive.Should().Be(true); user.IsActivated.Should().Be(false); diff --git a/tests/Application.Tests.Unit/Common/Mappings/MappingTests.cs b/tests/Application.Tests.Unit/Common/Mappings/MappingTests.cs index 36b57359..28df4253 100644 --- a/tests/Application.Tests.Unit/Common/Mappings/MappingTests.cs +++ b/tests/Application.Tests.Unit/Common/Mappings/MappingTests.cs @@ -2,8 +2,6 @@ using Application.Common.Mappings; using Application.Common.Models.Dtos; using Application.Common.Models.Dtos.Physical; -using Application.Documents.Queries.GetAllDocumentsPaginated; -using Application.Rooms.Queries.GetEmptyContainersPaginated; using Application.Users.Queries; using AutoMapper; using Domain.Entities;