Skip to content
Merged
193 changes: 191 additions & 2 deletions src/Api/Controllers/DocumentsController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using Api.Controllers.Payload.Requests.Documents;
using Application.Common.Interfaces;
using Application.Common.Models;
using Application.Common.Models.Dtos;
using Application.Common.Models.Dtos.ImportDocument;
using Application.Common.Models.Dtos.Physical;
using Application.Documents.Commands;
using Application.Documents.Queries;
using Application.Identity;
using Domain.Enums;
using Infrastructure.Identity.Authorization;
using Microsoft.AspNetCore.Mvc;

Expand Down Expand Up @@ -43,12 +46,45 @@ public async Task<ActionResult<Result<DocumentDto>>> GetById([FromRoute] Guid do
/// </summary>
/// <param name="queryParameters">Get all documents query parameters</param>
/// <returns>A paginated list of DocumentDto</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpGet("issued")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<IssuedDocumentDto>>>> GetAllIssuedPaginated(
[FromQuery] GetAllIssuedPaginatedQueryParameters queryParameters)
{
var departmentId = _currentUserService.GetCurrentDepartmentForStaff();
if (departmentId is null)
{
return Result<PaginatedList<IssuedDocumentDto>>.Fail(new Exception("Staff does not have a room"));
}
var query = new GetAllIssuedDocumentsPaginated.Query()
{
DepartmentId = departmentId.Value,
SearchTerm = queryParameters.SearchTerm,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
SortOrder = queryParameters.SortOrder,
};
var result = await Mediator.Send(query);
return Ok(Result<PaginatedList<IssuedDocumentDto>>.Succeed(result));
}

/// <summary>
/// Get all documents paginated
/// </summary>
/// <param name="queryParameters">Get all documents query parameters</param>
/// <returns>A paginated list of DocumentDto</returns>
[RequiresRole(IdentityData.Roles.Admin)]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<DocumentDto>>>> GetAllPaginated(
public async Task<ActionResult<Result<PaginatedList<DocumentDto>>>> GetAllForAdminPaginated(
[FromQuery] GetAllDocumentsPaginatedQueryParameters queryParameters)
{
var query = new GetAllDocumentsPaginated.Query()
Expand All @@ -66,6 +102,36 @@ public async Task<ActionResult<Result<PaginatedList<DocumentDto>>>> GetAllPagina
return Ok(Result<PaginatedList<DocumentDto>>.Succeed(result));
}

/// <summary>
/// Get all documents for staff paginated
/// </summary>
/// <param name="queryParameters">Get all documents for staff query parameters</param>
/// <returns>A paginated list of DocumentDto</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpGet("staff")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<DocumentDto>>>> GetAllForStaffPaginated(
[FromQuery] GetAllDocumentsForStaffPaginatedQueryParameters queryParameters)
{
var roomId = _currentUserService.GetCurrentRoomForStaff();
var query = new GetAllDocumentsPaginated.Query()
{
RoomId = 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<PaginatedList<DocumentDto>>.Succeed(result));
}

/// <summary>
/// Get all document types
/// </summary>
Expand Down Expand Up @@ -120,17 +186,43 @@ public async Task<ActionResult<Result<DocumentDto>>> Import([FromBody] ImportDoc
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<DocumentDto>>> RequestImport([FromBody] RequestImportDocumentRequest request)
public async Task<ActionResult<Result<IssuedDocumentDto>>> RequestImport([FromBody] RequestImportDocumentRequest request)
{
var performingUserId = _currentUserService.GetId();
var command = new RequestImportDocument.Command()
{
Title = request.Title,
Description = request.Description,
DocumentType = request.DocumentType,
IsPrivate = request.IsPrivate,
IssuerId = performingUserId,
};
var result = await Mediator.Send(command);
return Ok(Result<IssuedDocumentDto>.Succeed(result));
}

/// <summary>
/// Checkin a document
/// </summary>
/// <param name="documentId"></param>
/// <returns>A DocumentDto of the imported document</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("checkin{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<DocumentDto>>> Checkin(
[FromRoute] Guid documentId)
{
var performingUserId = _currentUserService.GetId();
var command = new CheckinDocument.Command()
{
PerformingUserId = performingUserId,
DocumentId = documentId,
};
var result = await Mediator.Send(command);
return Ok(Result<DocumentDto>.Succeed(result));
}

Expand Down Expand Up @@ -177,4 +269,101 @@ public async Task<ActionResult<Result<DocumentDto>>> Delete([FromRoute] Guid doc
var result = await Mediator.Send(query);
return Ok(Result<DocumentDto>.Succeed(result));
}

/// <summary>
/// Approve a document request
/// </summary>
/// <param name="documentId">Id of the document to be approved</param>
/// <param name="request"></param>
/// <returns>A DocumentDto of the approved document</returns>
[HttpPost("approve/{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Result<DocumentDto>>> Approve(
[FromRoute] Guid documentId,
[FromBody] ApproveImportRequest request)
{
var performingUserId = _currentUserService.GetId();
var query = new ApproveDocument.Command()
{
PerformingUserId = performingUserId,
DocumentId = documentId,
Reason = request.Reason,
};
var result = await Mediator.Send(query);
return Ok(Result<DocumentDto>.Succeed(result));
}

/// <summary>
/// Reject a document request
/// </summary>
/// <param name="documentId">Id of the document to be rejected</param>
/// <param name="request"></param>
/// <returns>A DocumentDto of the rejected document</returns>
[HttpPost("reject/{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Result<DocumentDto>>> Reject(
[FromRoute] Guid documentId,
[FromBody] RejectImportRequest request)
{
var performingUserId = _currentUserService.GetId();
var query = new RejectDocument.Command()
{
PerformingUserId = performingUserId,
DocumentId = documentId,
Reason = request.Reason,
};
var result = await Mediator.Send(query);
return Ok(Result<DocumentDto>.Succeed(result));
}

/// <summary>
/// Get a document request reason
/// </summary>
/// <param name="documentId">Id of the document to be rejected</param>
/// <returns>A DocumentDto of the rejected document</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("reason/{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Result<ReasonDto>>> Reason(
[FromRoute] Guid documentId)
{
var query = new GetDocumentReason.Query()
{
DocumentId = documentId,
Type = RequestType.Import,
};
var result = await Mediator.Send(query);
return Ok(Result<ReasonDto>.Succeed(result));
}

/// <summary>
/// Assign a document to
/// </summary>
/// <param name="documentId">Id of the document to be rejected</param>
/// <param name="request"></param>
/// <returns>A DocumentDto of the rejected document</returns>
[HttpPost("{documentId:guid}/assign")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Result<DocumentDto>>> Assign(
[FromRoute] Guid documentId,
[FromBody] AssignDocumentToFolderRequest request)
{
var performingUserId = _currentUserService.GetId();
var query = new AssignDocument.Command()
{
PerformingUserId = performingUserId,
DocumentId = documentId,
FolderId = request.FolderId,
};
var result = await Mediator.Send(query);
return Ok(Result<DocumentDto>.Succeed(result));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Api.Controllers.Payload.Requests.Documents;

public class ApproveImportRequest
{
public string Reason { get; set; } = null!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Api.Controllers.Payload.Requests.Documents;

public class AssignDocumentToFolderRequest
{
public Guid FolderId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Api.Controllers.Payload.Requests.Documents;

public class GetAllDocumentsForStaffPaginatedQueryParameters : PaginatedQueryParameters
{
/// <summary>
/// Id of the room to find documents in
/// </summary>
public Guid? RoomId { get; set; }
/// <summary>
/// Id of the locker to find documents in
/// </summary>
public Guid? LockerId { get; set; }
/// <summary>
/// Id of the folder to find documents in
/// </summary>
public Guid? FolderId { get; set; }
/// <summary>
/// Search term
/// </summary>
public string? SearchTerm { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Api.Controllers.Payload.Requests.Documents;

public class GetAllIssuedPaginatedQueryParameters : PaginatedQueryParameters
{
public string? SearchTerm { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Api.Controllers.Payload.Requests.Documents;

public class RejectImportRequest
{
public string Reason { get; set; } = null!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ public class RequestImportDocumentRequest
/// Document type of the document to be imported
/// </summary>
public string DocumentType { get; set; } = null!;
public bool IsPrivate { get; set; }
}
4 changes: 4 additions & 0 deletions src/Application/Common/Messages/DocumentLogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ public static class Import
{
public const string NewImport = "Imported new document";
public const string NewImportRequest = "Created new import request";
public const string Checkin = "Checked in document";
public const string Approve = "Approved import request";
public const string Reject = "Rejected import request";
public const string Assign = "Assigned to a folder";
}
}
1 change: 1 addition & 0 deletions src/Application/Common/Messages/FolderLogMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public static class FolderLogMessage
{
public const string Add = "Added folder";
public const string Update = "Updated folder";
public const string AssignDocument = "Assigned document to folder";
}
7 changes: 7 additions & 0 deletions src/Application/Common/Messages/RequestLogMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Application.Common.Messages;

public static class RequestLogMessages
{
public const string ApproveImport = "Approved import request";
public const string RejectImport = "Rejected import request";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Application.Common.Mappings;
using AutoMapper;
using Domain.Entities.Physical;

namespace Application.Common.Models.Dtos.ImportDocument;

public class IssuedDocumentDto : IMapFrom<Document>
{
public Guid Id { get; set; }
public string Title { get; set; } = null!;
public string? Description { get; set; }
public string DocumentType { get; set; } = null!;
public IssuerDto? Issuer { get; set; }
public string Status { get; set; } = null!;
public bool IsPrivate { get; set; }

public void Mapping(Profile profile)
{
profile.CreateMap<Document, IssuedDocumentDto>()
.ForMember(dest => dest.Status,
opt => opt.MapFrom(src => src.Status.ToString()))
.ForMember(dest => dest.Issuer,
opt => opt.MapFrom(x => x.Importer));
}
}
17 changes: 17 additions & 0 deletions src/Application/Common/Models/Dtos/ImportDocument/IssuerDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Application.Common.Mappings;
using Domain.Entities;

namespace Application.Common.Models.Dtos.ImportDocument;

public class IssuerDto : IMapFrom<User>
{
public Guid Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Role { get; set; }
public string Position { get; set; }
public bool IsActive { get; set; }
public bool IsActivated { get; set; }
}
11 changes: 11 additions & 0 deletions src/Application/Common/Models/Dtos/ReasonDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Application.Common.Mappings;
using Domain.Entities.Logging;
using Domain.Enums;

namespace Application.Common.Models.Dtos;

public class ReasonDto : IMapFrom<RequestLog>
{
public RequestType Type { get; set; }
public string Reason { get; set; } = null!;
}
Loading