Implement ListTokens controller action.

This commit is contained in:
Denis-Cosmin Nutiu 2021-06-27 15:14:34 +03:00
parent cd72710d24
commit b03979b8b2
9 changed files with 167 additions and 28 deletions

View file

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Retroactiune.Core.Entities;
using Retroactiune.Core.Services;
namespace Retroactiune.Core.Interfaces
{
@ -22,5 +24,12 @@ namespace Retroactiune.Core.Interfaces
/// <param name="tokenIds">A list of tokens to delete.</param>
/// <returns>The result of the delete operation.</returns>
public Task DeleteTokens(IEnumerable<string> tokenIds);
/// <summary>
/// List and filters tokens.
/// </summary>
/// <param name="filters">Filters object for filtering results.</param>
/// <returns>A list of tokens matching the filters.</returns>
public Task<IEnumerable<Token>> ListTokens(TokenListFilters filters);
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace Retroactiune.Core.Services
{
/// <summary>
/// TokenListFilters is a data class representing the filters used to list tokens.
/// </summary>
public class TokenListFilters
{
/// <summary>
/// Id filters tokens by their ids.
/// </summary>
public IEnumerable<string> Id { get; set; }
/// <summary>
/// FeedbackReceiverId filters tokens by their assigned FeedbackReceiverId.
/// </summary>
public string FeedbackReceiverId { get; set; }
/// <summary>
/// CreatedAfter filters token that have been created after the given date.
/// </summary>
public DateTime? CreatedAfter { get; set; }
/// <summary>
/// CreatedBefore filters token that have been created before the given date.
/// </summary>
public DateTime? CreatedBefore { get; set; }
/// <summary>
/// UsedAfter filters token that have been used after the given date.
/// </summary>
public DateTime? UsedAfter { get; set; }
/// <summary>
/// UsedBefore filters token that have been used before the given date.
/// </summary>
public DateTime? UsedBefore { get; set; }
}
}

View file

@ -52,5 +52,12 @@ namespace Retroactiune.Core.Services
throw new GenericServiceException($"Operation failed: {e.Message} {e.StackTrace}");
}
}
public async Task<IEnumerable<Token>> ListTokens(TokenListFilters filters)
{
// TODO Write unit tests.
// TODO: Implement
throw new NotImplementedException();
}
}
}

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using AutoFixture.Xunit2;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using Retroactiune.Controllers;
using Retroactiune.Core.Entities;
@ -20,9 +21,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.Post(new List<FeedbackReceiverInDto>());
// Assert, null because we don't have the ApiBehaviourOptions set, which would generate the IActionResult for the invalid input.
@ -36,9 +38,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.Post(items);
// Assert
@ -52,9 +55,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.Delete("bad_guid_but_unit_test_works_cause_validation_doesnt");
// Assert
@ -69,9 +73,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var items = new[] {"bad_guid_but_unit_test_works_cause_validation_doesnt", "2", "3"};
var result = await controller.DeleteMany(items);
@ -87,11 +92,12 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
mockService.Setup(i => i.FindAsync(It.IsAny<IEnumerable<string>>(), null, null))
.ReturnsAsync(new[] {new FeedbackReceiver()});
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.Get("bad_guid_but_unit_test_works_cause_validation_doesnt");
// Assert
@ -107,9 +113,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.Get("bad_guid_but_unit_test_works_cause_validation_doesnt");
// Assert
@ -125,10 +132,11 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
var filterArr = filter as string[] ?? filter.ToArray();
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null);
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var result = await controller.List(filterArr, offset, limit);
Assert.IsType<OkObjectResult>(result);

View file

@ -2,6 +2,7 @@
using System.Threading.Tasks;
using AutoFixture.Xunit2;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using Retroactiune.Controllers;
using Retroactiune.Core.Entities;
@ -19,10 +20,12 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
{
// Arrange
var feedbackService = new Mock<IFeedbackReceiverService>();
var mapper = TestUtils.GetMapper();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.GenerateTokens(new GenerateTokensDto());
// Assert
@ -33,8 +36,10 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
public async Task Test_GenerateTokens_Success(FeedbackReceiver randFedFeedbackReceiver)
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
feedbackService.Setup(i => i.FindAsync(It.IsAny<IEnumerable<string>>(),
It.IsAny<int?>(),
@ -42,7 +47,7 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
.ReturnsAsync(new[] {randFedFeedbackReceiver});
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.GenerateTokens(new GenerateTokensDto
{
NumberOfTokens = 2,
@ -58,11 +63,13 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
public async Task Test_Delete_Ok()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.DeleteToken("my_guid");
// Assert
@ -74,13 +81,15 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
public async Task Test_Delete_BadRequest()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
tokens.Setup(i => i.DeleteTokens(It.IsAny<IEnumerable<string>>()))
.Throws(new GenericServiceException("op fail"));
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.DeleteToken("my_guid");
// Assert
@ -92,11 +101,13 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
public async Task Test_DeleteMany_Ok()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.DeleteTokens(new[] {"my_guid", "b"});
// Assert
@ -108,13 +119,15 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
public async Task Test_DeleteMany_BadRequest()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
tokens.Setup(i => i.DeleteTokens(It.IsAny<IEnumerable<string>>()))
.Throws(new GenericServiceException("op fail"));
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object);
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.DeleteTokens(new[] {"my_guid", "b"});
// Assert

View file

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Retroactiune.Core.Entities;
using Retroactiune.Core.Interfaces;
@ -21,13 +22,15 @@ namespace Retroactiune.Controllers
private readonly IOptions<ApiBehaviorOptions> _apiBehaviorOptions;
private readonly IFeedbackReceiverService _service;
private readonly IMapper _mapper;
private readonly ILogger _logger;
public FeedbackReceiversController(IFeedbackReceiverService service, IMapper mapper,
IOptions<ApiBehaviorOptions> apiBehaviorOptions)
IOptions<ApiBehaviorOptions> apiBehaviorOptions, ILogger<FeedbackReceiversController> logger)
{
_service = service;
_mapper = mapper;
_apiBehaviorOptions = apiBehaviorOptions;
_logger = logger;
}
@ -80,7 +83,7 @@ namespace Retroactiune.Controllers
[StringLength(24, ErrorMessage = "invalid guid, must be 24 characters", MinimumLength = 24)]
string guid)
{
await _service.DeleteManyAsync(new [] {guid});
await _service.DeleteManyAsync(new[] {guid});
return NoContent();
}
@ -143,7 +146,7 @@ namespace Retroactiune.Controllers
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(BasicResponse),StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(BasicResponse), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteMany([Required] IEnumerable<string> ids)
{
try
@ -153,6 +156,7 @@ namespace Retroactiune.Controllers
}
catch (GenericServiceException e)
{
_logger.LogError("{Message}", e.Message);
return BadRequest(new BasicResponse
{
Message = e.Message

View file

@ -2,8 +2,11 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Retroactiune.Core.Entities;
using Retroactiune.Core.Interfaces;
using Retroactiune.Core.Services;
using Retroactiune.DataTransferObjects;
@ -14,18 +17,46 @@ namespace Retroactiune.Controllers
[Route("api/v1/[controller]")]
public class TokensController : ControllerBase
{
// TODO: Implement ListTokens.
// Filters for: FeedbackReceiver IDS
// for: start < CreatedTime < end
// for start < TimeUsed end
private readonly IFeedbackReceiverService _feedbackReceiverService;
private readonly ITokensService _tokensService;
private readonly IMapper _mapper;
private readonly ILogger _logger;
public TokensController(IFeedbackReceiverService feedbackReceiverService, ITokensService tokensService)
public TokensController(IFeedbackReceiverService feedbackReceiverService, ITokensService tokensService,
ILogger<TokensController> logger, IMapper mapper)
{
_feedbackReceiverService = feedbackReceiverService;
_tokensService = tokensService;
_mapper = mapper;
_logger = logger;
}
/// <summary>
/// The list tokens controller retrieves a list of tokens.
/// </summary>
/// <param name="filtersDto">Object that holds filters for listing tokens.</param>
/// <response code="200">A list of tokens.</response>
/// <response code="400">The request is invalid.</response>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Token>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ListTokens([FromQuery] ListTokensFiltersDto filtersDto)
{
// TODO: Write unit & integration tests.
try
{
var tokenFilters = _mapper.Map<TokenListFilters>(filtersDto);
var response = await _tokensService.ListTokens(tokenFilters);
return Ok(response);
}
catch (GenericServiceException e)
{
_logger.LogError("{Message}", e.Message);
return BadRequest(new BasicResponse()
{
Message = e.Message
});
}
}
/// <summary>
@ -69,7 +100,7 @@ namespace Retroactiune.Controllers
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(BasicResponse),StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(BasicResponse), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteTokens([Required] IEnumerable<string> tokenIds)
{
try
@ -79,6 +110,7 @@ namespace Retroactiune.Controllers
}
catch (GenericServiceException e)
{
_logger.LogError("{Message}", e.Message);
return BadRequest(new BasicResponse
{
Message = e.Message
@ -97,15 +129,17 @@ namespace Retroactiune.Controllers
[ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteToken(
[StringLength(24, ErrorMessage = "invalid guid, must be 24 characters", MinimumLength = 24)] string guid)
[StringLength(24, ErrorMessage = "invalid guid, must be 24 characters", MinimumLength = 24)]
string guid)
{
try
{
await _tokensService.DeleteTokens(new []{ guid });
await _tokensService.DeleteTokens(new[] {guid});
return NoContent();
}
catch (GenericServiceException e)
{
_logger.LogError("{Message}", e.Message);
return BadRequest(new BasicResponse
{
Message = e.Message

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Retroactiune.Core.Entities;
namespace Retroactiune.DataTransferObjects
{
/// <summary>
/// DTO with filters for listing tokens. <see cref="Token"/>
/// </summary>
public class ListTokensFiltersDto
{
public IEnumerable<string> Id { get; set; }
[StringLength(24, ErrorMessage = "invalid guid, must be 24 characters", MinimumLength = 24)]
public string FeedbackReceiverId { get; set; }
public DateTime? CreatedAfter { get; set; }
public DateTime? CreatedBefore { get; set; }
public DateTime? UsedAfter { get; set; }
public DateTime? UsedBefore { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using AutoMapper;
using Retroactiune.Core.Entities;
using Retroactiune.Core.Services;
using Retroactiune.DataTransferObjects;
namespace Retroactiune
@ -8,6 +9,7 @@ namespace Retroactiune
{
public MappingProfile()
{
CreateMap<ListTokensFiltersDto, TokenListFilters>();
CreateMap<FeedbackReceiver, FeedbackReceiverInDto>().ReverseMap();
CreateMap<FeedbackReceiver, FeedbackReceiverOutDto>();
}