-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implementation of delete endpoint * add and fix unit tests * fix codacy issues
- Loading branch information
Showing
15 changed files
with
260 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
src/Tests/Weather.API.UnitTests/Features/DeleteFavorites/DeleteFavoriteHandlerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.Logging; | ||
using Moq; | ||
using System.Net; | ||
using Validot; | ||
using Weather.API.Domain.Abstractions; | ||
using Weather.API.Domain.Database.Entities; | ||
using Weather.API.Features.DeleteFavorites; | ||
using Weather.API.UnitTests.Domain.Database; | ||
|
||
|
||
namespace Weather.API.UnitTests.Features.DeleteFavorites | ||
{ | ||
public class DeleteFavoriteHandlerTests | ||
{ | ||
private readonly Mock<IValidator<DeleteFavoriteCommand>> _deleteFavoriteCommandValidatorMock; | ||
private readonly Mock<TestWeatherContext> _weatherContextMock; | ||
private readonly Mock<DbSet<FavoriteLocationEntity>> _favoriteLocationEntityDbSetMock; | ||
|
||
private readonly IRequestHandler<bool, DeleteFavoriteCommand> _uut; | ||
public DeleteFavoriteHandlerTests() | ||
{ | ||
_deleteFavoriteCommandValidatorMock = new(); | ||
var loggerMock = new Mock<ILogger<DeleteFavoriteHandler>>(); | ||
_weatherContextMock = new(); | ||
_favoriteLocationEntityDbSetMock = new(); | ||
|
||
_uut = new DeleteFavoriteHandler(_deleteFavoriteCommandValidatorMock.Object, loggerMock.Object, _weatherContextMock.Object); | ||
} | ||
|
||
[Fact] | ||
public async Task InvalidRequest() | ||
{ | ||
//Arrange | ||
var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 1 }; | ||
|
||
_deleteFavoriteCommandValidatorMock.Setup(x => x.IsValid(deleteFavoriteCommand)).Returns(false); | ||
|
||
//Act | ||
var result = await _uut.HandleAsync(deleteFavoriteCommand, CancellationToken.None); | ||
|
||
//Assert | ||
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); | ||
Assert.Single(result.Errors); | ||
Assert.False(result.Data); | ||
} | ||
|
||
[Fact] | ||
public async Task DeleteFavoriteLocationSafeAsync_Failed() | ||
{ | ||
//Arrange | ||
var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 1 }; | ||
|
||
_deleteFavoriteCommandValidatorMock.Setup(x => x.IsValid(deleteFavoriteCommand)).Returns(true); | ||
|
||
_favoriteLocationEntityDbSetMock.Setup(x => x.FindAsync(It.IsAny<int>(), CancellationToken.None)).ThrowsAsync(new DbUpdateException()); | ||
_weatherContextMock.Setup(x => x.FavoriteLocations).Returns(_favoriteLocationEntityDbSetMock.Object); | ||
|
||
//Act | ||
var result = await _uut.HandleAsync(deleteFavoriteCommand, CancellationToken.None); | ||
|
||
//Assert | ||
Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); | ||
Assert.Single(result.Errors); | ||
Assert.False(result.Data); | ||
} | ||
|
||
[Fact] | ||
public async Task Success() | ||
{ | ||
//Arrange | ||
var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 1 }; | ||
|
||
_deleteFavoriteCommandValidatorMock.Setup(x => x.IsValid(deleteFavoriteCommand)).Returns(true); | ||
|
||
var favoriteLocation = new FavoriteLocationEntity(); | ||
_favoriteLocationEntityDbSetMock.Setup(x => x.FindAsync(It.IsAny<int>(), CancellationToken.None)).ReturnsAsync(favoriteLocation); | ||
_favoriteLocationEntityDbSetMock.Setup(x => x.Remove(favoriteLocation)); | ||
_weatherContextMock.Setup(x => x.FavoriteLocations).Returns(_favoriteLocationEntityDbSetMock.Object); | ||
|
||
//Act | ||
var result = await _uut.HandleAsync(deleteFavoriteCommand, CancellationToken.None); | ||
|
||
//Assert | ||
Assert.Equal(HttpStatusCode.OK, result.StatusCode); | ||
Assert.Empty(result.Errors); | ||
Assert.True(result.Data); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/Weather.API/Features/DeleteFavorites/ContainerConfigurationExtension.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
using Validot; | ||
using Weather.API.Domain.Abstractions; | ||
using Weather.API.Domain.Extensions; | ||
using WeatherApi.Domain.Http; | ||
|
||
namespace Weather.API.Features.DeleteFavorites | ||
{ | ||
public static class ContainerConfigurationExtension | ||
{ | ||
public static IEndpointRouteBuilder BuildDeleteFavoriteWeatherEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) | ||
{ | ||
endpointRouteBuilder.MapDelete("v1/favorite/{id}", | ||
async (int id, [FromServices] IRequestHandler<bool, DeleteFavoriteCommand> handler, CancellationToken cancellationToken) => | ||
await handler.SendAsync(new DeleteFavoriteCommand { Id = id }, cancellationToken)) | ||
.Produces<DataResponse<bool>>() | ||
.WithName("DeleteFavorite") | ||
.WithTags("Delete"); | ||
|
||
return endpointRouteBuilder; | ||
} | ||
|
||
public static IServiceCollection AddDeleteFavorites(this IServiceCollection serviceCollection) | ||
=> serviceCollection | ||
.AddScoped<IRequestHandler<bool, DeleteFavoriteCommand>, DeleteFavoriteHandler>() | ||
.AddValidotSingleton<IValidator<DeleteFavoriteCommand>, DeleteFavoriteCommandSpecificationHolder, DeleteFavoriteCommand>(); | ||
|
||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/Weather.API/Features/DeleteFavorites/DeleteFavoriteCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Weather.API.Features.DeleteFavorites | ||
{ | ||
internal sealed class DeleteFavoriteCommand | ||
{ | ||
public int Id { get; init; } | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/Weather.API/Features/DeleteFavorites/DeleteFavoriteCommandSpecificationHolder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Validot; | ||
|
||
namespace Weather.API.Features.DeleteFavorites | ||
{ | ||
internal sealed class DeleteFavoriteCommandSpecificationHolder : ISpecificationHolder<DeleteFavoriteCommand> | ||
{ | ||
public Specification<DeleteFavoriteCommand> Specification { get; } | ||
|
||
public DeleteFavoriteCommandSpecificationHolder() | ||
{ | ||
Specification<DeleteFavoriteCommand> addFavoriteCommandSpecification = s => s | ||
.Member(m => m.Id, r => r.NonNegative()); | ||
|
||
Specification = addFavoriteCommandSpecification; | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
src/Weather.API/Features/DeleteFavorites/DeleteFavoriteHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using Ardalis.GuardClauses; | ||
using FluentResults; | ||
using Microsoft.EntityFrameworkCore; | ||
using Validot; | ||
using Weather.API.Domain.Abstractions; | ||
using Weather.API.Domain.Database.EFContext; | ||
using Weather.API.Domain.Extensions; | ||
using Weather.API.Domain.Logging; | ||
using Weather.API.Domain.Resources; | ||
using WeatherApi.Domain.Http; | ||
|
||
namespace Weather.API.Features.DeleteFavorites | ||
{ | ||
internal sealed class DeleteFavoriteHandler : IRequestHandler<bool, DeleteFavoriteCommand> | ||
{ | ||
private readonly WeatherContext _weatherContext; | ||
private readonly IValidator<DeleteFavoriteCommand> _validator; | ||
private readonly ILogger<DeleteFavoriteHandler> _logger; | ||
public DeleteFavoriteHandler( | ||
IValidator<DeleteFavoriteCommand> validator, | ||
ILogger<DeleteFavoriteHandler> logger, | ||
WeatherContext weatherContext) | ||
{ | ||
_validator = Guard.Against.Null(validator); | ||
_logger = Guard.Against.Null(logger); | ||
_weatherContext = Guard.Against.Null(weatherContext); | ||
} | ||
|
||
public async Task<HttpDataResponse<bool>> HandleAsync(DeleteFavoriteCommand request, CancellationToken cancellationToken) | ||
{ | ||
if (!_validator.IsValid(request)) | ||
{ | ||
return HttpDataResponses.AsBadRequest<bool>(string.Format(ErrorMessages.RequestValidationError, request)); | ||
} | ||
|
||
var addResult = await DeleteFavoriteLocationSafeAsync(request, cancellationToken); | ||
if (addResult.IsFailed) | ||
{ | ||
return HttpDataResponses.AsInternalServerError<bool>("Location was not deleted from database."); | ||
} | ||
|
||
return HttpDataResponses.AsOK(true); | ||
} | ||
|
||
public async Task<Result> DeleteFavoriteLocationSafeAsync(DeleteFavoriteCommand command, CancellationToken cancellationToken) | ||
{ | ||
try | ||
{ | ||
var location = await _weatherContext.FavoriteLocations.FindAsync(command.Id, cancellationToken); | ||
_weatherContext.Remove(location!); | ||
await _weatherContext.SaveChangesAsync(cancellationToken); | ||
return Result.Ok(); | ||
} | ||
catch (DbUpdateException ex) | ||
{ | ||
_logger.LogError(LogEvents.FavoriteWeathersStoreToDatabase, ex, "Can't delete location."); | ||
return Result.Fail(ex.Message); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.