diff --git a/Retroactiune.Core/Entities/Token.cs b/Retroactiune.Core/Entities/Token.cs index 702d182..314be63 100644 --- a/Retroactiune.Core/Entities/Token.cs +++ b/Retroactiune.Core/Entities/Token.cs @@ -33,6 +33,16 @@ namespace Retroactiune.Core.Entities [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) { if (!(obj is Token convertedObj)) @@ -50,13 +60,18 @@ namespace Retroactiune.Core.Entities 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) { Guard.Against.Null(feedbackReceiver, nameof(feedbackReceiver)); - var hasExpired = ExpiryTime != null && ExpiryTime <= DateTime.UtcNow; var differentFeedbackReceiver = !FeedbackReceiverId.Equals(feedbackReceiver.Id); - var isUsed = TimeUsed != null; - return !(hasExpired || differentFeedbackReceiver || isUsed); + return !differentFeedbackReceiver && IsValid(); } } } \ No newline at end of file diff --git a/Retroactiune.Core/Services/FeedbacksListFilters.cs b/Retroactiune.Core/Services/FeedbacksListFilters.cs index a0c4cde..3a46a9d 100644 --- a/Retroactiune.Core/Services/FeedbacksListFilters.cs +++ b/Retroactiune.Core/Services/FeedbacksListFilters.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Retroactiune.Core.Services { @@ -7,12 +8,11 @@ namespace Retroactiune.Core.Services /// public class FeedbacksListFilters { - /// /// FeedbackReceiverId the ID of the FeedbackReceiver. /// public string FeedbackReceiverId { get; set; } - + /// /// CreatedAfter filters items that have been created after the given date. /// @@ -27,5 +27,23 @@ namespace Retroactiune.Core.Services /// Rating filters for the rating. /// 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); + } } } \ No newline at end of file diff --git a/Retroactiune.UnitTests/Retroactiune.WebAPI/Controllers/TestFeedbackReceiverController.cs b/Retroactiune.UnitTests/Retroactiune.WebAPI/Controllers/TestFeedbackReceiverController.cs index 0dc176f..e3fc00c 100644 --- a/Retroactiune.UnitTests/Retroactiune.WebAPI/Controllers/TestFeedbackReceiverController.cs +++ b/Retroactiune.UnitTests/Retroactiune.WebAPI/Controllers/TestFeedbackReceiverController.cs @@ -202,9 +202,6 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers mockService.Verify(s => s.FindAsync(filterArr, offset, limit), Times.Once); } - // Invalid token - // happy - [Theory, AutoData] public async Task AddFeedback_No_FeedbackReceiver(FeedbackInDto requestBody) { @@ -272,7 +269,7 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers TimeUsed = DateTime.UtcNow } }); - + // Test var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object, feedbacksService.Object, mapper, null, @@ -282,8 +279,8 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers // Assert Assert.IsType(result); } - - + + [Theory, AutoData] public async Task AddFeedback_Happy(FeedbackInDto requestBody) { @@ -295,11 +292,15 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers var logger = new Mock>(); feedbackReceiversService - .Setup(i => i.FindAsync(It.IsAny>(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new[] {new FeedbackReceiver + .Setup(i => i.FindAsync(It.IsAny>(), + It.IsAny(), It.IsAny())) + .ReturnsAsync(new[] { - Id = "batman" - }}); + new FeedbackReceiver + { + Id = "batman" + } + }); tokensService.Setup(i => i.FindAsync(It.IsAny())) .ReturnsAsync(new[] @@ -310,7 +311,7 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers TimeUsed = null } }); - + // Test var controller = new FeedbackReceiversController(feedbackReceiversService.Object, tokensService.Object, feedbacksService.Object, mapper, null, logger.Object); @@ -318,6 +319,32 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers // Assert Assert.IsType(result); + feedbacksService.Verify(i => i.AddFeedbackAsync(It.IsAny(), + It.IsAny())); + tokensService.Verify(i => i.MarkTokenAsUsedAsync(It.IsAny())); + } + + [Theory, AutoData] + public async Task GetFeedbacks_Happy(string guid, ListFeedbacksFiltersDto filters) + { + // Arrange + var mapper = TestUtils.GetMapper(); + var feedbackReceiversService = new Mock(); + var tokensService = new Mock(); + var feedbacksService = new Mock(); + var logger = new Mock>(); + + // 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(result); + + var listFilters = mapper.Map(filters); + listFilters.FeedbackReceiverId = guid; + feedbacksService.Verify(i => i.GetFeedbacksAsync(listFilters)); } } } \ No newline at end of file diff --git a/Retroactiune.WebAPI/Controllers/FeedbackReceivers.FeedbacksController.cs b/Retroactiune.WebAPI/Controllers/FeedbackReceivers.FeedbacksController.cs index 8281010..e9fd1c7 100644 --- a/Retroactiune.WebAPI/Controllers/FeedbackReceivers.FeedbacksController.cs +++ b/Retroactiune.WebAPI/Controllers/FeedbackReceivers.FeedbacksController.cs @@ -82,7 +82,7 @@ namespace Retroactiune.Controllers [ProducesResponseType(typeof(BasicResponse), StatusCodes.Status400BadRequest)] public async Task GetFeedbacks(string guid, [FromQuery] ListFeedbacksFiltersDto filters) { - // TODO: Unit & Integration test. + // TODO: Integration test. var feedbacksListFilters = _mapper.Map(filters); feedbacksListFilters.FeedbackReceiverId = guid; var response = await _feedbacksService.GetFeedbacksAsync(feedbacksListFilters); diff --git a/Retroactiune.WebAPI/Controllers/TokensController.cs b/Retroactiune.WebAPI/Controllers/TokensController.cs index a50a5e7..23c4c72 100644 --- a/Retroactiune.WebAPI/Controllers/TokensController.cs +++ b/Retroactiune.WebAPI/Controllers/TokensController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; @@ -145,5 +146,41 @@ namespace Retroactiune.Controllers }); } } + + /// + /// Checks if a token is valid or not. + /// + /// The the result of the check. + /// The request is invalid. + [HttpGet("{guid}/check")] + [ProducesResponseType(typeof(CheckTokenDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task 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 + }); + } + } } } \ No newline at end of file diff --git a/Retroactiune.WebAPI/DataTransferObjects/CheckTokenDto.cs b/Retroactiune.WebAPI/DataTransferObjects/CheckTokenDto.cs new file mode 100644 index 0000000..384b826 --- /dev/null +++ b/Retroactiune.WebAPI/DataTransferObjects/CheckTokenDto.cs @@ -0,0 +1,7 @@ +namespace Retroactiune.DataTransferObjects +{ + public class CheckTokenDto + { + public bool IsValid { get; set; } + } +} \ No newline at end of file diff --git a/Retroactiune.WebAPI/Startup.cs b/Retroactiune.WebAPI/Startup.cs index 2a47f32..3adbfdf 100644 --- a/Retroactiune.WebAPI/Startup.cs +++ b/Retroactiune.WebAPI/Startup.cs @@ -21,6 +21,7 @@ namespace Retroactiune public class Startup { // TODO: External auth provider. + // TODO: Improve coverage. // TODO: UI? public Startup(IConfiguration configuration) {