Implement CheckToken API operation.

This commit is contained in:
Denis-Cosmin Nutiu 2021-07-24 18:39:08 +03:00
parent 127652002b
commit bf431cb3e4
7 changed files with 123 additions and 18 deletions

View file

@ -33,6 +33,16 @@ namespace Retroactiune.Core.Entities
[JsonPropertyName("expiry_time")] public DateTime? ExpiryTime { get; set; } [JsonPropertyName("expiry_time")] public DateTime? ExpiryTime { get; set; }
public static bool operator ==(Token left, Token right)
{
return Equals(left, right);
}
public static bool operator !=(Token left, Token right)
{
return !Equals(left, right);
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (!(obj is Token convertedObj)) if (!(obj is Token convertedObj))
@ -50,13 +60,18 @@ namespace Retroactiune.Core.Entities
return RuntimeHelpers.GetHashCode(this); return RuntimeHelpers.GetHashCode(this);
} }
public bool IsValid()
{
var hasExpired = ExpiryTime != null && ExpiryTime <= DateTime.UtcNow;
var isUsed = TimeUsed != null;
return !(hasExpired || isUsed);
}
public bool IsValid(FeedbackReceiver feedbackReceiver) public bool IsValid(FeedbackReceiver feedbackReceiver)
{ {
Guard.Against.Null(feedbackReceiver, nameof(feedbackReceiver)); Guard.Against.Null(feedbackReceiver, nameof(feedbackReceiver));
var hasExpired = ExpiryTime != null && ExpiryTime <= DateTime.UtcNow;
var differentFeedbackReceiver = !FeedbackReceiverId.Equals(feedbackReceiver.Id); var differentFeedbackReceiver = !FeedbackReceiverId.Equals(feedbackReceiver.Id);
var isUsed = TimeUsed != null; return !differentFeedbackReceiver && IsValid();
return !(hasExpired || differentFeedbackReceiver || isUsed);
} }
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
namespace Retroactiune.Core.Services namespace Retroactiune.Core.Services
{ {
@ -7,12 +8,11 @@ namespace Retroactiune.Core.Services
/// </summary> /// </summary>
public class FeedbacksListFilters public class FeedbacksListFilters
{ {
/// <summary> /// <summary>
/// FeedbackReceiverId the ID of the FeedbackReceiver. /// FeedbackReceiverId the ID of the FeedbackReceiver.
/// </summary> /// </summary>
public string FeedbackReceiverId { get; set; } public string FeedbackReceiverId { get; set; }
/// <summary> /// <summary>
/// CreatedAfter filters items that have been created after the given date. /// CreatedAfter filters items that have been created after the given date.
/// </summary> /// </summary>
@ -27,5 +27,23 @@ namespace Retroactiune.Core.Services
/// Rating filters for the rating. /// Rating filters for the rating.
/// </summary> /// </summary>
public uint Rating { get; set; } public uint Rating { get; set; }
public override bool Equals(object obj)
{
return obj != null && Equals((FeedbacksListFilters) obj);
}
private bool Equals(FeedbacksListFilters other)
{
return FeedbackReceiverId == other.FeedbackReceiverId &&
Nullable.Equals(CreatedAfter, other.CreatedAfter) &&
Nullable.Equals(CreatedBefore, other.CreatedBefore) && Rating == other.Rating;
}
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode()
{
return HashCode.Combine(FeedbackReceiverId, CreatedAfter, CreatedBefore, Rating);
}
} }
} }

View file

@ -202,9 +202,6 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
mockService.Verify(s => s.FindAsync(filterArr, offset, limit), Times.Once); mockService.Verify(s => s.FindAsync(filterArr, offset, limit), Times.Once);
} }
// Invalid token
// happy
[Theory, AutoData] [Theory, AutoData]
public async Task AddFeedback_No_FeedbackReceiver(FeedbackInDto requestBody) public async Task AddFeedback_No_FeedbackReceiver(FeedbackInDto requestBody)
{ {
@ -272,7 +269,7 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
TimeUsed = DateTime.UtcNow TimeUsed = DateTime.UtcNow
} }
}); });
// Test // Test
var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object, var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object,
feedbacksService.Object, mapper, null, feedbacksService.Object, mapper, null,
@ -282,8 +279,8 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Assert // Assert
Assert.IsType<BadRequestObjectResult>(result); Assert.IsType<BadRequestObjectResult>(result);
} }
[Theory, AutoData] [Theory, AutoData]
public async Task AddFeedback_Happy(FeedbackInDto requestBody) public async Task AddFeedback_Happy(FeedbackInDto requestBody)
{ {
@ -295,11 +292,15 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
var logger = new Mock<ILogger<FeedbackReceiversController>>(); var logger = new Mock<ILogger<FeedbackReceiversController>>();
feedbackReceiversService feedbackReceiversService
.Setup(i => i.FindAsync(It.IsAny<IEnumerable<string>>(), It.IsAny<int?>(), It.IsAny<int?>())) .Setup(i => i.FindAsync(It.IsAny<IEnumerable<string>>(),
.ReturnsAsync(new[] {new FeedbackReceiver It.IsAny<int?>(), It.IsAny<int?>()))
.ReturnsAsync(new[]
{ {
Id = "batman" new FeedbackReceiver
}}); {
Id = "batman"
}
});
tokensService.Setup(i => i.FindAsync(It.IsAny<TokenListFilters>())) tokensService.Setup(i => i.FindAsync(It.IsAny<TokenListFilters>()))
.ReturnsAsync(new[] .ReturnsAsync(new[]
@ -310,7 +311,7 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
TimeUsed = null TimeUsed = null
} }
}); });
// Test // Test
var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object, var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object,
feedbacksService.Object, mapper, null, logger.Object); feedbacksService.Object, mapper, null, logger.Object);
@ -318,6 +319,32 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
// Assert // Assert
Assert.IsType<OkResult>(result); Assert.IsType<OkResult>(result);
feedbacksService.Verify(i => i.AddFeedbackAsync(It.IsAny<Feedback>(),
It.IsAny<FeedbackReceiver>()));
tokensService.Verify(i => i.MarkTokenAsUsedAsync(It.IsAny<Token>()));
}
[Theory, AutoData]
public async Task GetFeedbacks_Happy(string guid, ListFeedbacksFiltersDto filters)
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackReceiversService = new Mock<IFeedbackReceiversService>();
var tokensService = new Mock<ITokensService>();
var feedbacksService = new Mock<IFeedbacksService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
// Test
var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object,
feedbacksService.Object, mapper, null, logger.Object);
var result = await controller.GetFeedbacks(guid, filters);
// Assert
Assert.IsType<OkObjectResult>(result);
var listFilters = mapper.Map<FeedbacksListFilters>(filters);
listFilters.FeedbackReceiverId = guid;
feedbacksService.Verify(i => i.GetFeedbacksAsync(listFilters));
} }
} }
} }

View file

@ -82,7 +82,7 @@ namespace Retroactiune.Controllers
[ProducesResponseType(typeof(BasicResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(BasicResponse), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetFeedbacks(string guid, [FromQuery] ListFeedbacksFiltersDto filters) public async Task<IActionResult> GetFeedbacks(string guid, [FromQuery] ListFeedbacksFiltersDto filters)
{ {
// TODO: Unit & Integration test. // TODO: Integration test.
var feedbacksListFilters = _mapper.Map<FeedbacksListFilters>(filters); var feedbacksListFilters = _mapper.Map<FeedbacksListFilters>(filters);
feedbacksListFilters.FeedbackReceiverId = guid; feedbacksListFilters.FeedbackReceiverId = guid;
var response = await _feedbacksService.GetFeedbacksAsync(feedbacksListFilters); var response = await _feedbacksService.GetFeedbacksAsync(feedbacksListFilters);

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -145,5 +146,41 @@ namespace Retroactiune.Controllers
}); });
} }
} }
/// <summary>
/// Checks if a token is valid or not.
/// </summary>
/// <response code="200">The the result of the check.</response>
/// <response code="400">The request is invalid.</response>
[HttpGet("{guid}/check")]
[ProducesResponseType(typeof(CheckTokenDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CheckToken(
[StringLength(24, ErrorMessage = "invalid guid, must be 24 characters", MinimumLength = 24)]
string guid
)
{
// TODO: Unit test.
var response = await _tokensService.FindAsync(new TokenListFilters
{
Ids = new[] {guid}
});
try
{
var token = response.ElementAt(0);
return Ok(new CheckTokenDto
{
IsValid = token.IsValid()
});
}
catch (ArgumentOutOfRangeException)
{
_logger.LogWarning("Invalid token {Guid}", guid);
return Ok(new CheckTokenDto
{
IsValid = true
});
}
}
} }
} }

View file

@ -0,0 +1,7 @@
namespace Retroactiune.DataTransferObjects
{
public class CheckTokenDto
{
public bool IsValid { get; set; }
}
}

View file

@ -21,6 +21,7 @@ namespace Retroactiune
public class Startup public class Startup
{ {
// TODO: External auth provider. // TODO: External auth provider.
// TODO: Improve coverage.
// TODO: UI? // TODO: UI?
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
{ {