Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
- '8888:80'
environment:
- ASPNETCORE_ENVIRONMENT=Testing
- PROFILE_DatabaseSettings__ConnectionString=Server=database;Port=5432;Database=mytestdb;User ID=profiletester;Password=supasupasecured;
- PROFILE_DatabaseSettings__ConnectionString=Server=database;Port=5432;Database=mytestdb;User ID=profiletester;Password=supasupasecured;Include Error Detail=true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the test error message should be clearer

depends_on:
database:
condition: service_started
Expand Down
3 changes: 3 additions & 0 deletions src/Api/ConfigureServices.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Reflection;
using Api.Middlewares;
using Api.Policies;
using Api.Services;
using Application.Common.Interfaces;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.OpenApi.Models;

Expand Down Expand Up @@ -53,6 +55,7 @@ private static IServiceCollection AddServices(this IServiceCollection services)
{
// In order for ExceptionMiddleware to work
services.AddScoped<ExceptionMiddleware>();
services.AddScoped<ICurrentUserService, CurrentUserService>();

return services;
}
Expand Down
264 changes: 264 additions & 0 deletions src/Api/Controllers/BorrowsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
using Api.Controllers.Payload.Requests.Borrows;
using Application.Borrows.Commands;
using Application.Borrows.Queries;
using Application.Common.Interfaces;
using Application.Common.Models;
using Application.Common.Models.Dtos.Physical;
using Application.Identity;
using Infrastructure.Identity.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Api.Controllers;

public class BorrowsController : ApiControllerBase
{
private readonly ICurrentUserService _currentUserService;
public BorrowsController(ICurrentUserService currentUserService)
{
_currentUserService = currentUserService;
}
/// <summary>
/// Borrow a document request
/// </summary>
/// <param name="request">Borrow document details</param>
/// <returns>A BorrowDto of the requested borrow</returns>
[RequiresRole(IdentityData.Roles.Employee)]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> BorrowDocument([FromBody] BorrowDocumentRequest request)
{
var borrowerId = _currentUserService.GetCurrentUser().Id;
var command = new BorrowDocument.Command()
{
BorrowerId = borrowerId,
DocumentId = request.DocumentId,
BorrowFrom = request.BorrowFrom,
BorrowTo = request.BorrowTo,
Reason = request.Reason,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}

[RequiresRole(IdentityData.Roles.Staff)]
[HttpGet("staffs")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<BorrowDto>>>> GetAllRequestsAsStaffPaginated(
[FromQuery] GetAllBorrowRequestsPaginatedAsStaffQueryParameters queryParameters)
{
var departmentId = _currentUserService.GetCurrentDepartmentForStaff();
var command = new GetAllBorrowRequestsPaginated.Query()
{
DepartmentId = departmentId,
EmployeeId = queryParameters.EmployeeId,
DocumentId = queryParameters.DocumentId,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
SortOrder = queryParameters.SortOrder,
};
var result = await Mediator.Send(command);
return Ok(Result<PaginatedList<BorrowDto>>.Succeed(result));
}

/// <summary>
/// Get all borrow requests as admin paginated
/// </summary>
/// <param name="queryParameters"></param>
/// <returns></returns>
[RequiresRole(IdentityData.Roles.Admin)]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<BorrowDto>>>> GetAllRequestsAsAdminPaginated(
[FromQuery] GetAllBorrowRequestsPaginatedAsAdminQueryParameters queryParameters)
{
var command = new GetAllBorrowRequestsPaginated.Query()
{
DepartmentId = queryParameters.DepartmentId,
EmployeeId = queryParameters.EmployeeId,
DocumentId = queryParameters.DocumentId,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
SortOrder = queryParameters.SortOrder,
};
var result = await Mediator.Send(command);
return Ok(Result<PaginatedList<BorrowDto>>.Succeed(result));
}

/// <summary>
/// Get all borrow requests as employee paginated
/// </summary>
/// <param name="queryParameters"></param>
/// <returns></returns>
[RequiresRole(IdentityData.Roles.Employee)]
[HttpGet("employees")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<BorrowDto>>>> GetAllRequestsAsEmployeePaginated(
[FromQuery] GetAllBorrowRequestsPaginatedAsEmployeeQueryParameters queryParameters)
{
var userId = _currentUserService.GetCurrentUser().Id;
var command = new GetAllBorrowRequestsPaginated.Query()
{
EmployeeId = userId,
DocumentId = queryParameters.DocumentId,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
SortOrder = queryParameters.SortOrder,
};
var result = await Mediator.Send(command);
return Ok(Result<PaginatedList<BorrowDto>>.Succeed(result));
}

/// <summary>
/// Get all borrow requests for a document paginated
/// </summary>
/// <param name="documentId"></param>
/// <param name="queryParameters"></param>
/// <returns></returns>
[RequiresRole(IdentityData.Roles.Employee)]
[HttpGet("documents/{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<PaginatedList<BorrowDto>>>> GetAllRequestsForDocumentPaginated(
[FromRoute] Guid documentId,
[FromQuery] GetAllBorrowRequestsPaginatedForDocumentQueryParameters queryParameters)
{
var command = new GetAllBorrowRequestsPaginated.Query()
{
DocumentId = documentId,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
SortOrder = queryParameters.SortOrder,
};
var result = await Mediator.Send(command);
return Ok(Result<PaginatedList<BorrowDto>>.Succeed(result));
}

/// <summary>
/// Approve a borrow request
/// </summary>
/// <param name="borrowId">Id of the borrow request to be approved</param>
/// <returns>A BorrowDto of the approved borrow request</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("approve/{borrowId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> ApproveRequest([FromRoute] Guid borrowId)
{
var command = new ApproveBorrowRequest.Command()
{
BorrowId = borrowId,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}

/// <summary>
/// Reject a borrow request
/// </summary>
/// <param name="borrowId">Id of the borrow request to be rejected</param>
/// <returns>A BorrowDto of the rejected borrow request</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("reject/{borrowId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> RejectRequest([FromRoute] Guid borrowId)
{
var command = new RejectBorrowRequest.Command()
{
BorrowId = borrowId,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}

/// <summary>
/// Check out a borrow request
/// </summary>
/// <param name="borrowId">Id of the borrow request to be checked out</param>
/// <returns>A BorrowDto of the checked out borrow request</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("checkout/{borrowId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> Checkout([FromRoute] Guid borrowId)
{
var command = new CheckoutDocument.Command()
{
BorrowId = borrowId,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}

/// <summary>
/// Return document
/// </summary>
/// <param name="documentId">Id of the document to be returned</param>
/// <returns>A BorrowDto of the returned document borrow request</returns>
[RequiresRole(IdentityData.Roles.Staff)]
[HttpPost("return/{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> Return([FromRoute] Guid documentId)
{
var command = new ReturnDocument.Command()
{
DocumentId = documentId,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}

/// <summary>
/// Update borrow request
/// </summary>
/// <param name="borrowId">Id of the borrow request to be updated</param>
/// <param name="request">Update borrow details</param>
/// <returns>A BorrowDto of the updated borrow request</returns>
[RequiresRole(IdentityData.Roles.Employee)]
[HttpPut("{borrowId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<Result<BorrowDto>>> Update(
[FromRoute] Guid borrowId,
[FromBody] UpdateBorrowRequest request)
{
var command = new UpdateBorrow.Command()
{
BorrowId = borrowId,
BorrowFrom = request.BorrowFrom,
BorrowTo = request.BorrowTo,
Reason = request.Reason,
};
var result = await Mediator.Send(command);
return Ok(Result<BorrowDto>.Succeed(result));
}
}
4 changes: 2 additions & 2 deletions src/Api/Controllers/DocumentsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public async Task<ActionResult<Result<DocumentDto>>> Import([FromBody] ImportDoc
/// <param name="documentId">Id of the document to be updated</param>
/// <param name="request">Update document details</param>
/// <returns>A DocumentDto of the updated document</returns>
[RequiresRole(IdentityData.Roles.Admin)]
[RequiresRole(IdentityData.Roles.Admin, IdentityData.Roles.Staff)]
[HttpPut("{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
Expand All @@ -128,7 +128,7 @@ public async Task<ActionResult<Result<DocumentDto>>> Update([FromRoute] Guid doc
/// Delete a document
/// </summary>
/// <param name="documentId">Id of the document to be deleted</param>
/// <returns>A DocumentDto of the deleted document</returns>
/// <returns>A DocumentDto of the deleted document</returns>
[HttpDelete("{documentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Api.Controllers.Payload.Requests.Borrows;

/// <summary>
/// Request details to borrow a document
/// </summary>
public class BorrowDocumentRequest
{
/// <summary>
/// Id of the document to be borrowed
/// </summary>
public Guid DocumentId { get; set; }
/// <summary>
/// Borrow from
/// </summary>
public DateTime BorrowFrom { get; set; }
/// <summary>
/// Borrow to
/// </summary>
public DateTime BorrowTo { get; set; }
/// <summary>
/// Reason of borrowing
/// </summary>
public string Reason { get; set; } = null!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Api.Controllers.Payload.Requests.Borrows;

/// <summary>
/// Query parameters for getting all borrow requests with pagination as admin
/// </summary>
public class GetAllBorrowRequestsPaginatedAsAdminQueryParameters : PaginatedQueryParameters
{
/// <summary>
/// Id of the department to get borrow requests in
/// </summary>
public Guid? DepartmentId { get; set; }
public Guid? DocumentId { get; set; }
public Guid? EmployeeId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Api.Controllers.Payload.Requests.Borrows;

/// <summary>
/// Query parameters for getting all borrow requests with pagination as employee
/// </summary>
public class GetAllBorrowRequestsPaginatedAsEmployeeQueryParameters : PaginatedQueryParameters
{
public Guid? DocumentId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Api.Controllers.Payload.Requests.Borrows;

/// <summary>
/// Query parameters for getting all borrow requests with pagination as staff
/// </summary>
public class GetAllBorrowRequestsPaginatedAsStaffQueryParameters : PaginatedQueryParameters
{
public Guid? DocumentId { get; set; }
public Guid? EmployeeId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Api.Controllers.Payload.Requests.Borrows;

public class GetAllBorrowRequestsPaginatedForDocumentQueryParameters : PaginatedQueryParameters
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Api.Controllers.Payload.Requests.Borrows;

public class UpdateBorrowRequest
{
public DateTime BorrowFrom { get; init; }
public DateTime BorrowTo { get; init; }
public string Reason { get; init; } = null!;
}
Loading