diff --git a/src/Tests/SystemTests/Weather.API.SystemTests/WeatherSystemTests.cs b/src/Tests/SystemTests/Weather.API.SystemTests/WeatherSystemTests.cs index c122239..1b27a06 100644 --- a/src/Tests/SystemTests/Weather.API.SystemTests/WeatherSystemTests.cs +++ b/src/Tests/SystemTests/Weather.API.SystemTests/WeatherSystemTests.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Mvc.Testing; using Newtonsoft.Json; +using SmallApiToolkit.Core.Response; using System.Text; using Weather.Domain.Commands; using Weather.Domain.Dtos; -using Weather.Domain.Http; namespace Weather.API.SystemTests { diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/AddFavoriteHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/AddFavoriteHandlerTests.cs index f609c4a..5f42bc2 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/AddFavoriteHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/AddFavoriteHandlerTests.cs @@ -1,4 +1,6 @@ -using Weather.Core.Abstractions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Validation; +using Weather.Core.Abstractions; using Weather.Core.Commands; using Weather.Core.Resources; using Weather.Domain.Commands; @@ -10,15 +12,15 @@ namespace Weather.Core.UnitTests.Commands public class AddFavoriteHandlerTests { private readonly Mock _weatherCommandsRepositoryMock; - private readonly Mock> _addFavoriteCommandValidatorMock; - private readonly Mock> _loggerMock; + private readonly Mock> _addFavoriteCommandValidatorMock; + private readonly Mock> _loggerMock; - private readonly IAddFavoriteHandler _uut; + private readonly IHttpRequestHandler _uut; public AddFavoriteHandlerTests() { - _weatherCommandsRepositoryMock = new Mock(); - _addFavoriteCommandValidatorMock = new Mock>(); - _loggerMock = new Mock>(); + _weatherCommandsRepositoryMock = new(); + _addFavoriteCommandValidatorMock = new(); + _loggerMock = new(); _uut = new AddFavoriteHandler(_weatherCommandsRepositoryMock.Object, _addFavoriteCommandValidatorMock.Object, _loggerMock.Object); } @@ -30,7 +32,7 @@ public async Task InvalidLocation() //Arrange var addFavoriteCommand = new AddFavoriteCommand { Location = new Domain.Dtos.LocationDto { Latitude = 1, Longitude = 1 } }; - _addFavoriteCommandValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _addFavoriteCommandValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false}); //Act var result = await _uut.HandleAsync(addFavoriteCommand, CancellationToken.None); @@ -38,7 +40,7 @@ public async Task InvalidLocation() //Assert Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); Assert.Single(result.Errors); - _addFavoriteCommandValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); + _addFavoriteCommandValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); } [Fact] @@ -48,7 +50,7 @@ public async Task AddFavoriteLocation_Failed() var addFavoriteCommand = new AddFavoriteCommand { Location = new Domain.Dtos.LocationDto { Latitude = 1, Longitude = 1 } }; var errorMessage = "errorMessage"; - _addFavoriteCommandValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _addFavoriteCommandValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherCommandsRepositoryMock.Setup(x => x.AddFavoriteLocation(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Fail(errorMessage)); //Act @@ -58,7 +60,7 @@ public async Task AddFavoriteLocation_Failed() Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); Assert.Single(result.Errors); Assert.Equal(ErrorMessages.CantStoreLocation, result.Errors.Single()); - _addFavoriteCommandValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); + _addFavoriteCommandValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); _weatherCommandsRepositoryMock.Verify(x => x.AddFavoriteLocation(It.Is(y=>y.Equals(addFavoriteCommand)), It.IsAny()), Times.Once); _loggerMock.VerifyLog(LogLevel.Error, LogEvents.FavoriteWeathersStoreToDatabase, errorMessage, Times.Once()); } @@ -70,7 +72,7 @@ public async Task Success() var addFavoriteCommand = new AddFavoriteCommand { Location = new Domain.Dtos.LocationDto { Latitude = 1, Longitude = 1 } }; var locationId = 1; - _addFavoriteCommandValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _addFavoriteCommandValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherCommandsRepositoryMock.Setup(x => x.AddFavoriteLocation(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(locationId)); //Act @@ -80,7 +82,7 @@ public async Task Success() Assert.Equal(HttpStatusCode.OK, result.StatusCode); Assert.Empty(result.Errors); Assert.Equal(locationId, result.Data); - _addFavoriteCommandValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); + _addFavoriteCommandValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(addFavoriteCommand))), Times.Once); _weatherCommandsRepositoryMock.Verify(x => x.AddFavoriteLocation(It.Is(y => y.Equals(addFavoriteCommand)), It.IsAny()), Times.Once); } } diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/DeleteFavoriteHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/DeleteFavoriteHandlerTests.cs index cb2f878..6e1bf15 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/DeleteFavoriteHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Commands/DeleteFavoriteHandlerTests.cs @@ -1,4 +1,6 @@ -using Weather.Core.Abstractions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Validation; +using Weather.Core.Abstractions; using Weather.Core.Commands; using Weather.Domain.Commands; @@ -7,9 +9,9 @@ namespace Weather.Core.UnitTests.Commands public class DeleteFavoriteHandlerTests { private readonly Mock _weatherCommandsRepositoryMock; - private readonly Mock> _validatorMock; + private readonly Mock> _validatorMock; - private readonly IDeleteFavoriteHandler _uut; + private readonly IHttpRequestHandler _uut; public DeleteFavoriteHandlerTests() { _weatherCommandsRepositoryMock = new(); @@ -24,7 +26,7 @@ public async Task InvalidRequest() //Arrange var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 5 }; - _validatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _validatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false}); //Act var result = await _uut.HandleAsync(deleteFavoriteCommand, CancellationToken.None); @@ -32,7 +34,7 @@ public async Task InvalidRequest() //Assert Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); Assert.Single(result.Errors); - _validatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(deleteFavoriteCommand))), Times.Once); + _validatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(deleteFavoriteCommand))), Times.Once); } [Fact] @@ -40,8 +42,8 @@ public async Task DeleteFavoriteLocationSafeAsync_Failed() { //Arrange var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 5 }; - - _validatorMock.Setup(x => x.IsValid(deleteFavoriteCommand)).Returns(true); + + _validatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherCommandsRepositoryMock.Setup(x => x.DeleteFavoriteLocationSafeAsync(deleteFavoriteCommand, CancellationToken.None)) .ReturnsAsync(Result.Fail(string.Empty)); @@ -51,7 +53,7 @@ public async Task DeleteFavoriteLocationSafeAsync_Failed() //Assert Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); Assert.Single(result.Errors); - _validatorMock.Verify(x => x.IsValid(deleteFavoriteCommand), Times.Once); + _validatorMock.Verify(x => x.Validate(deleteFavoriteCommand), Times.Once); _weatherCommandsRepositoryMock.Verify(x => x.DeleteFavoriteLocationSafeAsync(deleteFavoriteCommand, CancellationToken.None), Times.Once); } @@ -61,7 +63,7 @@ public async Task DeleteFavoriteLocationSafeAsync_Success() //Arrange var deleteFavoriteCommand = new DeleteFavoriteCommand { Id = 5 }; - _validatorMock.Setup(x => x.IsValid(deleteFavoriteCommand)).Returns(true); + _validatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherCommandsRepositoryMock.Setup(x => x.DeleteFavoriteLocationSafeAsync(deleteFavoriteCommand, CancellationToken.None)) .ReturnsAsync(Result.Ok()); @@ -71,7 +73,7 @@ public async Task DeleteFavoriteLocationSafeAsync_Success() //Assert Assert.Equal(HttpStatusCode.OK, result.StatusCode); Assert.Empty(result.Errors); - _validatorMock.Verify(x => x.IsValid(deleteFavoriteCommand), Times.Once); + _validatorMock.Verify(x => x.Validate(deleteFavoriteCommand), Times.Once); _weatherCommandsRepositoryMock.Verify(x => x.DeleteFavoriteLocationSafeAsync(deleteFavoriteCommand, CancellationToken.None), Times.Once); } } diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetCurrentWeatherHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetCurrentWeatherHandlerTests.cs index 46d217b..279a541 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetCurrentWeatherHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetCurrentWeatherHandlerTests.cs @@ -1,4 +1,6 @@ -using Validot.Results; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Validation; +using Validot.Results; using Weather.Core.Abstractions; using Weather.Core.Queries; using Weather.Core.Resources; @@ -11,18 +13,18 @@ namespace Weather.Core.UnitTests.Queries { public class GetCurrentWeatherHandlerTests { - private readonly Mock> _getCurrentWeatherQueryValidatorMock; - private readonly Mock> _currentWeatherValidatorMock; + private readonly Mock> _getCurrentWeatherQueryValidatorMock; + private readonly Mock> _currentWeatherValidatorMock; private readonly Mock _weatherServiceMock; - private readonly Mock> _loggerMock; + private readonly Mock> _loggerMock; - private readonly IGetCurrentWeatherHandler _uut; + private readonly IHttpRequestHandler _uut; public GetCurrentWeatherHandlerTests() { - _getCurrentWeatherQueryValidatorMock = new Mock>(); - _currentWeatherValidatorMock = new Mock>(); - _weatherServiceMock = new Mock(); - _loggerMock = new Mock>(); + _getCurrentWeatherQueryValidatorMock = new(); + _currentWeatherValidatorMock = new(); + _weatherServiceMock = new(); + _loggerMock = new(); _uut = new GetCurrentWeatherHandler(_getCurrentWeatherQueryValidatorMock.Object, _currentWeatherValidatorMock.Object, _weatherServiceMock.Object, _loggerMock.Object); } @@ -33,7 +35,7 @@ public async Task InvalidLocation() //Arrange var getCurrentWeatherQuery = new GetCurrentWeatherQuery(1,1); - _getCurrentWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _getCurrentWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false}); //Act var result = await _uut.HandleAsync(getCurrentWeatherQuery, CancellationToken.None); @@ -42,7 +44,7 @@ public async Task InvalidLocation() Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); Assert.Single(result.Errors); Assert.Null(result.Data); - _getCurrentWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); + _getCurrentWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); } [Fact] @@ -52,7 +54,7 @@ public async Task GetCurrentWeather_Failed() var errorMessage = "error"; var getCurrentWeatherQuery = new GetCurrentWeatherQuery(1, 1); - _getCurrentWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getCurrentWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x=>x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Fail(errorMessage)); //Act var result = await _uut.HandleAsync(getCurrentWeatherQuery, CancellationToken.None); @@ -62,7 +64,7 @@ public async Task GetCurrentWeather_Failed() Assert.Single(result.Errors); Assert.Equal(ErrorMessages.ExternalApiError, result.Errors.Single()); Assert.Null(result.Data); - _getCurrentWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); + _getCurrentWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y => y.Equals(getCurrentWeatherQuery.Location)), It.IsAny()), Times.Once); _loggerMock.VerifyLog(LogLevel.Error, LogEvents.CurrentWeathersGet, errorMessage, Times.Once()); } @@ -74,11 +76,9 @@ public async Task CurrentWeather_ValidationFailed() var getCurrentWeatherQuery = new GetCurrentWeatherQuery(1, 1); var currentWeather = new CurrentWeatherDto(); - var validationResutlMock = new Mock(); - validationResutlMock.SetupGet(x=>x.AnyErrors).Returns(true); - _getCurrentWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getCurrentWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(currentWeather)); - _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(validationResutlMock.Object); + _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false}); //Act var result = await _uut.HandleAsync(getCurrentWeatherQuery, CancellationToken.None); @@ -87,10 +87,9 @@ public async Task CurrentWeather_ValidationFailed() Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); Assert.Single(result.Errors); Assert.Null(result.Data); - _getCurrentWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); + _getCurrentWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y => y.Equals(getCurrentWeatherQuery.Location)), It.IsAny()), Times.Once); - _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y=>y.Equals(currentWeather)), It.Is(y=>!y)), Times.Once); - validationResutlMock.VerifyGet(x=>x.AnyErrors, Times.Once); + _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y=>y.Equals(currentWeather))), Times.Once); _loggerMock.VerifyLog(LogLevel.Error, LogEvents.CurrentWeathersValidation, Times.Once()); } @@ -101,11 +100,9 @@ public async Task Success() var getCurrentWeatherQuery = new GetCurrentWeatherQuery(1, 1); var currentWeather = new CurrentWeatherDto(); - var validationResutlMock = new Mock(); - validationResutlMock.SetupGet(x => x.AnyErrors).Returns(false); - _getCurrentWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getCurrentWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(currentWeather)); - _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(validationResutlMock.Object); + _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true}); //Act var result = await _uut.HandleAsync(getCurrentWeatherQuery, CancellationToken.None); @@ -115,10 +112,9 @@ public async Task Success() Assert.Empty(result.Errors); Assert.NotNull(result.Data); Assert.Equal(currentWeather, result.Data); - _getCurrentWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); + _getCurrentWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getCurrentWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y => y.Equals(getCurrentWeatherQuery.Location)), It.IsAny()), Times.Once); - _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(currentWeather)), It.Is(y => !y)), Times.Once); - validationResutlMock.VerifyGet(x => x.AnyErrors, Times.Once); + _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(currentWeather))), Times.Once); } } } diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs index e4200eb..bdacd64 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetFavoritesHandlerTests.cs @@ -1,9 +1,10 @@ -using Weather.Core.Abstractions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; +using Weather.Core.Abstractions; using Weather.Core.Queries; -using Weather.Core.Resources; using Weather.Domain.BusinessEntities; using Weather.Domain.Dtos; -using Weather.Domain.Http; using Weather.Domain.Logging; using Weather.UnitTests.Common.Extensions; @@ -13,18 +14,18 @@ public class GetFavoritesHandlerTests { private readonly Mock _weatherRepositoryMock; private readonly Mock _weatherServiceMock; - private readonly Mock> _loggerMock; - private readonly Mock> _locationValidatorMock; - private readonly Mock> _currentWeatherValidatorMock; + private readonly Mock> _loggerMock; + private readonly Mock> _locationValidatorMock; + private readonly Mock> _currentWeatherValidatorMock; - private readonly IGetFavoritesHandler _uut; + private readonly IHttpRequestHandler _uut; public GetFavoritesHandlerTests() { - _weatherRepositoryMock = new Mock(); - _weatherServiceMock = new Mock(); - _loggerMock = new Mock>(); - _locationValidatorMock = new Mock>(); - _currentWeatherValidatorMock = new Mock>(); + _weatherRepositoryMock = new(); + _weatherServiceMock = new(); + _loggerMock = new(); + _locationValidatorMock = new(); + _currentWeatherValidatorMock = new(); _uut = new GetFavoritesHandler(_weatherRepositoryMock.Object, _weatherServiceMock.Object, @@ -61,7 +62,7 @@ public async Task InvalidLocation() locationDto, }); - _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _locationValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false}); //Act var result = await _uut.HandleAsync(EmptyRequest.Instance, CancellationToken.None); @@ -72,7 +73,7 @@ public async Task InvalidLocation() Assert.Null(result.Data); _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y => y.Equals(locationDto)), It.IsAny()), Times.Never); - _locationValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(locationDto))), Times.Once); + _locationValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(locationDto))), Times.Once); } [Fact] @@ -88,7 +89,7 @@ public async Task EmptyResult_GetCurrentWeather_Fail() locationDto, }); - _locationValidatorMock.Setup(x=>x.IsValid(It.IsAny())).Returns(true); + _locationValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Fail(failMessage)); //Act @@ -100,7 +101,7 @@ public async Task EmptyResult_GetCurrentWeather_Fail() _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y=>y.Equals(locationDto)), It.IsAny()), Times.Once); _loggerMock.VerifyLog(LogLevel.Warning, LogEvents.FavoriteWeathersGeneral, failMessage, Times.Once()); - _locationValidatorMock.Verify(x => x.IsValid(It.Is(y=>y.Equals(locationDto))), Times.Once); + _locationValidatorMock.Verify(x => x.Validate(It.Is(y=>y.Equals(locationDto))), Times.Once); } [Fact] @@ -117,11 +118,11 @@ public async Task One_Of_GetCurrentWeather_Failed() new FavoriteLocation(), }); - _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _locationValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); var currentWeather = new CurrentWeatherDto(); - _currentWeatherValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.Is(y=> y.Equals(locationDto)), It.IsAny())).ReturnsAsync(Result.Fail(failMessage)); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.Is(y => !y.Equals(locationDto)), It.IsAny())).ReturnsAsync(Result.Ok(currentWeather)); @@ -137,8 +138,8 @@ public async Task One_Of_GetCurrentWeather_Failed() _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.IsAny(), It.IsAny()), Times.Exactly(2)); _loggerMock.VerifyLog(LogLevel.Warning, LogEvents.FavoriteWeathersGeneral, failMessage, Times.Once()); - _locationValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(locationDto))), Times.Once); - _currentWeatherValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(currentWeather))), Times.Once); + _locationValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(locationDto))), Times.Once); + _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(currentWeather))), Times.Once); } [Fact] @@ -153,8 +154,8 @@ public async Task GetCurrentWeather_Validation_Fail() locationDto, }); - _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); - _currentWeatherValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _locationValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); + _currentWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false }); var currentWeather = new CurrentWeatherDto(); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(currentWeather)); @@ -167,8 +168,8 @@ public async Task GetCurrentWeather_Validation_Fail() Assert.Null(result.Data); _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y => y.Equals(locationDto)), It.IsAny()), Times.Once); - _locationValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(locationDto))), Times.Once); - _currentWeatherValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(currentWeather))), Times.Once); + _locationValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(locationDto))), Times.Once); + _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(currentWeather))), Times.Once); } [Fact] @@ -183,8 +184,8 @@ public async Task Success() locationDto, }); - _locationValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); - _currentWeatherValidatorMock.Setup(x=>x.IsValid(It.IsAny())).Returns(true); + _locationValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); + _currentWeatherValidatorMock.Setup(x=>x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); var currentWeather = new CurrentWeatherDto(); _weatherServiceMock.Setup(x => x.GetCurrentWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(currentWeather)); @@ -199,8 +200,8 @@ public async Task Success() Assert.Equal(currentWeather.CityName, result.Data.FavoriteWeathers.Single().CityName); _weatherRepositoryMock.Verify(x => x.GetFavorites(It.IsAny()), Times.Once); _weatherServiceMock.Verify(x => x.GetCurrentWeather(It.Is(y=>y.Equals(locationDto)), It.IsAny()), Times.Once); - _locationValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(locationDto))), Times.Once); - _currentWeatherValidatorMock.Verify(x => x.IsValid(It.Is(y=>y.Equals(currentWeather))), Times.Once); + _locationValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(locationDto))), Times.Once); + _currentWeatherValidatorMock.Verify(x => x.Validate(It.Is(y=>y.Equals(currentWeather))), Times.Once); } } } diff --git a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs index f535227..83092ea 100644 --- a/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs +++ b/src/Tests/UnitTests/Weather.Core.UnitTests/Queries/GetForecastWeatherHandlerTests.cs @@ -1,4 +1,6 @@ -using Validot.Results; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Validation; +using Validot.Results; using Weather.Core.Abstractions; using Weather.Core.Queries; using Weather.Core.Resources; @@ -11,20 +13,24 @@ namespace Weather.Core.UnitTests.Queries { public class GetForecastWeatherHandlerTests { - private readonly Mock> _getForecastWeatherQueryValidatorMock; - private readonly Mock> _forecastWeatherValidatorMock; + private readonly Mock> _getForecastWeatherQueryValidatorMock; + private readonly Mock> _forecastWeatherValidatorMock; private readonly Mock _weatherServiceMock; - private readonly Mock> _loggerMock; + private readonly Mock> _loggerMock; - private readonly IGetForecastWeatherHandler _uut; + private readonly IHttpRequestHandler _uut; public GetForecastWeatherHandlerTests() { - _getForecastWeatherQueryValidatorMock = new Mock>(); - _forecastWeatherValidatorMock = new Mock>(); - _weatherServiceMock = new Mock(); - _loggerMock = new Mock>(); - - _uut = new GetForecastWeatherHandler(_getForecastWeatherQueryValidatorMock.Object, _weatherServiceMock.Object, _forecastWeatherValidatorMock.Object, _loggerMock.Object); + _getForecastWeatherQueryValidatorMock = new(); + _forecastWeatherValidatorMock = new(); + _weatherServiceMock = new(); + _loggerMock = new(); + + _uut = new GetForecastWeatherHandler( + _getForecastWeatherQueryValidatorMock.Object, + _weatherServiceMock.Object, + _forecastWeatherValidatorMock.Object, + _loggerMock.Object); } [Fact] @@ -33,7 +39,7 @@ public async Task InvalidLocation() //Arrange var getForecastWeatherQuery = new GetForecastWeatherQuery(1, 1); - _getForecastWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(false); + _getForecastWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false }); //Act var result = await _uut.HandleAsync(getForecastWeatherQuery, CancellationToken.None); @@ -42,7 +48,7 @@ public async Task InvalidLocation() Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); Assert.Single(result.Errors); Assert.Null(result.Data); - _getForecastWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); + _getForecastWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); } [Fact] @@ -52,7 +58,7 @@ public async Task GetForecastWeather_Failed() var errorMessage = "error"; var getForecastWeatherQuery = new GetForecastWeatherQuery(1, 1); - _getForecastWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getForecastWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetForecastWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Fail(errorMessage)); //Act var result = await _uut.HandleAsync(getForecastWeatherQuery, CancellationToken.None); @@ -62,7 +68,7 @@ public async Task GetForecastWeather_Failed() Assert.Single(result.Errors); Assert.Equal(ErrorMessages.ExternalApiError, result.Errors.Single()); Assert.Null(result.Data); - _getForecastWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); + _getForecastWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetForecastWeather(It.Is(y => y.Equals(getForecastWeatherQuery.Location)), It.IsAny()), Times.Once); _loggerMock.VerifyLog(LogLevel.Error, LogEvents.ForecastWeathersGet, errorMessage, Times.Once()); } @@ -74,11 +80,9 @@ public async Task GetForecastWeather_ValidationFailed() var getForecastWeatherQuery = new GetForecastWeatherQuery(1, 1); var forecastWeather = new ForecastWeatherDto(); - var validationResutlMock = new Mock(); - validationResutlMock.SetupGet(x => x.AnyErrors).Returns(true); - _getForecastWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getForecastWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetForecastWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(forecastWeather)); - _forecastWeatherValidatorMock.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(validationResutlMock.Object); + _forecastWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = false }); //Act var result = await _uut.HandleAsync(getForecastWeatherQuery, CancellationToken.None); @@ -87,10 +91,9 @@ public async Task GetForecastWeather_ValidationFailed() Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode); Assert.Single(result.Errors); Assert.Null(result.Data); - _getForecastWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); + _getForecastWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetForecastWeather(It.Is(y => y.Equals(getForecastWeatherQuery.Location)), It.IsAny()), Times.Once); - _forecastWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(forecastWeather)), It.Is(y => !y)), Times.Once); - validationResutlMock.VerifyGet(x => x.AnyErrors, Times.Once); + _forecastWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(forecastWeather))), Times.Once); _loggerMock.VerifyLog(LogLevel.Error, LogEvents.ForecastWeathersValidation, Times.Once()); } @@ -101,11 +104,9 @@ public async Task Success() var getForecastWeatherQuery = new GetForecastWeatherQuery(1, 1); var forecastWeather = new ForecastWeatherDto(); - var validationResutlMock = new Mock(); - validationResutlMock.SetupGet(x => x.AnyErrors).Returns(false); - _getForecastWeatherQueryValidatorMock.Setup(x => x.IsValid(It.IsAny())).Returns(true); + _getForecastWeatherQueryValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); _weatherServiceMock.Setup(x => x.GetForecastWeather(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Ok(forecastWeather)); - _forecastWeatherValidatorMock.Setup(x => x.Validate(It.IsAny(), It.IsAny())).Returns(validationResutlMock.Object); + _forecastWeatherValidatorMock.Setup(x => x.Validate(It.IsAny())).Returns(new RequestValidationResult { IsValid = true }); //Act var result = await _uut.HandleAsync(getForecastWeatherQuery, CancellationToken.None); @@ -115,10 +116,9 @@ public async Task Success() Assert.Empty(result.Errors); Assert.NotNull(result.Data); Assert.Equal(forecastWeather, result.Data); - _getForecastWeatherQueryValidatorMock.Verify(x => x.IsValid(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); + _getForecastWeatherQueryValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(getForecastWeatherQuery))), Times.Once); _weatherServiceMock.Verify(x => x.GetForecastWeather(It.Is(y => y.Equals(getForecastWeatherQuery.Location)), It.IsAny()), Times.Once); - _forecastWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(forecastWeather)), It.Is(y => !y)), Times.Once); - validationResutlMock.VerifyGet(x => x.AnyErrors, Times.Once); + _forecastWeatherValidatorMock.Verify(x => x.Validate(It.Is(y => y.Equals(forecastWeather))), Times.Once); } } } diff --git a/src/Weather.API/EndpointBuilders/WeatherBuilder.cs b/src/Weather.API/EndpointBuilders/WeatherBuilder.cs index 0c4222a..8d795b2 100644 --- a/src/Weather.API/EndpointBuilders/WeatherBuilder.cs +++ b/src/Weather.API/EndpointBuilders/WeatherBuilder.cs @@ -1,9 +1,10 @@ using Microsoft.AspNetCore.Mvc; -using Weather.API.Extensions; -using Weather.Core.Abstractions; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Extensions; using Weather.Domain.Commands; using Weather.Domain.Dtos; -using Weather.Domain.Http; using Weather.Domain.Queries; namespace Weather.API.EndpointBuilders @@ -15,6 +16,7 @@ public static IEndpointRouteBuilder BuildWeatherEndpoints(this IEndpointRouteBui endpointRouteBuilder .MapGroup("weather") + .MapVersionGroup(1) .BuildActualWeatherEndpoints() .BuildForecastWeatherEndpoints() .BuildFavoriteWeatherEndpoints(); @@ -24,10 +26,10 @@ public static IEndpointRouteBuilder BuildWeatherEndpoints(this IEndpointRouteBui private static IEndpointRouteBuilder BuildActualWeatherEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) { - endpointRouteBuilder.MapGet("v1/current", - async (double latitude, double longitude, [FromServices] IGetCurrentWeatherHandler handler, CancellationToken cancellationToken) => + endpointRouteBuilder.MapGet("current", + async (double latitude, double longitude, [FromServices] IHttpRequestHandler handler, CancellationToken cancellationToken) => await handler.SendAsync(new GetCurrentWeatherQuery(latitude, longitude), cancellationToken)) - .Produces>() + .ProducesDataResponse() .WithName("GetCurrentWeather") .WithTags("Getters"); return endpointRouteBuilder; @@ -35,10 +37,10 @@ await handler.SendAsync(new GetCurrentWeatherQuery(latitude, longitude), cancell private static IEndpointRouteBuilder BuildForecastWeatherEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) { - endpointRouteBuilder.MapGet("v1/forecast", - async (double latitude, double longitude, [FromServices] IGetForecastWeatherHandler handler, CancellationToken cancellationToken) => + endpointRouteBuilder.MapGet("forecast", + async (double latitude, double longitude, [FromServices] IHttpRequestHandler handler, CancellationToken cancellationToken) => await handler.SendAsync(new GetForecastWeatherQuery(latitude, longitude), cancellationToken)) - .Produces>() + .ProducesDataResponse() .WithName("GetForecastWeather") .WithTags("Getters"); @@ -47,24 +49,24 @@ await handler.SendAsync(new GetForecastWeatherQuery(latitude, longitude), cancel private static IEndpointRouteBuilder BuildFavoriteWeatherEndpoints(this IEndpointRouteBuilder endpointRouteBuilder) { - endpointRouteBuilder.MapGet("v1/favorites", - async ([FromServices] IGetFavoritesHandler handler, CancellationToken cancellationToken) => + endpointRouteBuilder.MapGet("favorites", + async ([FromServices] IHttpRequestHandler handler, CancellationToken cancellationToken) => await handler.SendAsync(EmptyRequest.Instance, cancellationToken)) - .Produces>() + .ProducesDataResponse() .WithName("GetFavorites") .WithTags("Getters"); - endpointRouteBuilder.MapPost("v1/favorite", - async ([FromBody] AddFavoriteCommand addFavoriteCommand, [FromServices] IAddFavoriteHandler handler, CancellationToken cancellationToken) => + endpointRouteBuilder.MapPost("favorite", + async ([FromBody] AddFavoriteCommand addFavoriteCommand, [FromServices] IHttpRequestHandler handler, CancellationToken cancellationToken) => await handler.SendAsync(addFavoriteCommand, cancellationToken)) - .Produces>() + .ProducesDataResponse() .WithName("AddFavorite") .WithTags("Setters"); - endpointRouteBuilder.MapDelete("v1/favorite/{id}", - async (int id, [FromServices] IDeleteFavoriteHandler handler, CancellationToken cancellationToken) => + endpointRouteBuilder.MapDelete("favorite/{id}", + async (int id, [FromServices] IHttpRequestHandler handler, CancellationToken cancellationToken) => await handler.SendAsync(new DeleteFavoriteCommand { Id = id }, cancellationToken)) - .Produces>() + .ProducesDataResponse() .WithName("DeleteFavorite") .WithTags("Delete"); diff --git a/src/Weather.API/Extensions/IHandlerExtension.cs b/src/Weather.API/Extensions/IHandlerExtension.cs deleted file mode 100644 index 309d9a4..0000000 --- a/src/Weather.API/Extensions/IHandlerExtension.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Weather.Core.Abstractions; -using Weather.Domain.Http; - -namespace Weather.API.Extensions -{ - internal static class IHandlerExtension - { - internal static async Task SendAsync(this IRequestHandler requestHandler, TRequest request, CancellationToken cancellationToken) - { - var response = await requestHandler.HandleAsync(request, cancellationToken); - return Results.Json(new DataResponse { Data = response.Data, Errors = response.Errors }, statusCode: (int)response.StatusCode); - } - } -} diff --git a/src/Weather.API/Middlewares/ExceptionMiddleware.cs b/src/Weather.API/Middlewares/ExceptionMiddleware.cs deleted file mode 100644 index c120184..0000000 --- a/src/Weather.API/Middlewares/ExceptionMiddleware.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Ardalis.GuardClauses; -using Newtonsoft.Json; -using System.Net; -using Weather.Domain.Http; -using Weather.Domain.Logging; - -namespace Weather.API.Middlewares -{ - public sealed class ExceptionMiddleware - { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - - public ExceptionMiddleware(RequestDelegate next, ILogger logger) - { - _next = Guard.Against.Null(next); - _logger = Guard.Against.Null(logger); - } - - public async Task InvokeAsync(HttpContext context) - { - try - { - await _next(context); - } - catch (Exception generalEx) - { - _logger.LogError(LogEvents.GeneralError, generalEx, "Unexpected error occured."); - await WriteResponseAsync(generalEx, context); - } - } - - private async Task WriteResponseAsync(Exception generalEx, HttpContext context) - { - var (responseCode, responseMessage) = ExtractFromException(generalEx); - context.Response.ContentType = "application/json"; - context.Response.StatusCode = (int)responseCode; - var jsonResult = CreateResponseJson(responseMessage); - await context.Response.WriteAsync(jsonResult); - } - - private string CreateResponseJson(string errorMessage) - { - var response = new DataResponse(); - response.Errors.Add(errorMessage); - return JsonConvert.SerializeObject(response); - } - - private (HttpStatusCode responseCode, string responseMessage) ExtractFromException(Exception generalEx) - => generalEx switch - { - TaskCanceledException taskCanceledException =>(HttpStatusCode.NoContent, taskCanceledException.Message), - _ => (HttpStatusCode.InternalServerError, "Generic error occured on server. Check logs for more info.") - }; - } -} diff --git a/src/Weather.API/Program.cs b/src/Weather.API/Program.cs index d5e6929..b981d75 100644 --- a/src/Weather.API/Program.cs +++ b/src/Weather.API/Program.cs @@ -1,6 +1,6 @@ +using SmallApiToolkit.Middleware; using Weather.API.Configuration; using Weather.API.EndpointBuilders; -using Weather.API.Middlewares; using Weather.Core.Configuration; using Weather.Infrastructure.Configuration; @@ -24,6 +24,7 @@ app.UseHttpsRedirection(); app.UseMiddleware(); +app.UseMiddleware(); app.BuildWeatherEndpoints(); diff --git a/src/Weather.API/Weather.API.csproj b/src/Weather.API/Weather.API.csproj index 7b5c203..a3a45ba 100644 --- a/src/Weather.API/Weather.API.csproj +++ b/src/Weather.API/Weather.API.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Weather.API/appsettings.json b/src/Weather.API/appsettings.json index 9397026..9850bb2 100644 --- a/src/Weather.API/appsettings.json +++ b/src/Weather.API/appsettings.json @@ -8,7 +8,7 @@ "AllowedHosts": "*", "Weatherbit": { "BaseUrl": "https://weatherbit-v1-mashape.p.rapidapi.com", - "XRapidAPIKey": "deacce6499mshd341d6b6a2df09bp198bc6jsn760c92c08824", + "XRapidAPIKey": "", "XRapidAPIHost": "weatherbit-v1-mashape.p.rapidapi.com" } } diff --git a/src/Weather.Core/Abstractions/IAddFavoriteHandler.cs b/src/Weather.Core/Abstractions/IAddFavoriteHandler.cs deleted file mode 100644 index 34a5af3..0000000 --- a/src/Weather.Core/Abstractions/IAddFavoriteHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Weather.Domain.Commands; - -namespace Weather.Core.Abstractions -{ - public interface IAddFavoriteHandler : IRequestHandler - { - - } -} diff --git a/src/Weather.Core/Abstractions/IDeleteFavoriteHandler.cs b/src/Weather.Core/Abstractions/IDeleteFavoriteHandler.cs deleted file mode 100644 index d302dae..0000000 --- a/src/Weather.Core/Abstractions/IDeleteFavoriteHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Weather.Domain.Commands; - -namespace Weather.Core.Abstractions -{ - public interface IDeleteFavoriteHandler : IRequestHandler - { - } -} diff --git a/src/Weather.Core/Abstractions/IGetCurrentWeatherHandler.cs b/src/Weather.Core/Abstractions/IGetCurrentWeatherHandler.cs deleted file mode 100644 index ece47f4..0000000 --- a/src/Weather.Core/Abstractions/IGetCurrentWeatherHandler.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Weather.Core.Queries; -using Weather.Domain.Dtos; -using Weather.Domain.Queries; - -namespace Weather.Core.Abstractions -{ - public interface IGetCurrentWeatherHandler : IRequestHandler - { - } -} diff --git a/src/Weather.Core/Abstractions/IGetFavoritesHandler.cs b/src/Weather.Core/Abstractions/IGetFavoritesHandler.cs deleted file mode 100644 index d68d0ed..0000000 --- a/src/Weather.Core/Abstractions/IGetFavoritesHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Weather.Domain.Dtos; -using Weather.Domain.Http; - -namespace Weather.Core.Abstractions -{ - public interface IGetFavoritesHandler : IRequestHandler - { - } -} diff --git a/src/Weather.Core/Abstractions/IGetForecastWeatherHandler.cs b/src/Weather.Core/Abstractions/IGetForecastWeatherHandler.cs deleted file mode 100644 index 9049b75..0000000 --- a/src/Weather.Core/Abstractions/IGetForecastWeatherHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Weather.Domain.Dtos; -using Weather.Domain.Queries; - -namespace Weather.Core.Abstractions -{ - public interface IGetForecastWeatherHandler : IRequestHandler - { - } -} diff --git a/src/Weather.Core/Abstractions/IRequestHandler.cs b/src/Weather.Core/Abstractions/IRequestHandler.cs deleted file mode 100644 index cc0f9d2..0000000 --- a/src/Weather.Core/Abstractions/IRequestHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Weather.Domain.Http; - -namespace Weather.Core.Abstractions -{ - public interface IRequestHandler - { - Task> HandleAsync(TRequest request, CancellationToken cancellationToken); - } -} diff --git a/src/Weather.Core/Commands/AddFavoriteHandler.cs b/src/Weather.Core/Commands/AddFavoriteHandler.cs index be1f806..2e5acc3 100644 --- a/src/Weather.Core/Commands/AddFavoriteHandler.cs +++ b/src/Weather.Core/Commands/AddFavoriteHandler.cs @@ -1,34 +1,33 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; -using Validot; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Abstractions; using Weather.Core.Resources; using Weather.Domain.Commands; using Weather.Domain.Extensions; -using Weather.Domain.Http; using Weather.Domain.Logging; namespace Weather.Core.Commands { - internal sealed class AddFavoriteHandler : IAddFavoriteHandler + internal sealed class AddFavoriteHandler : ValidationHttpRequestHandler { - private readonly IValidator _addFavoriteCommandValidator; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IWeatherCommandsRepository _weatherCommandsRepository; - public AddFavoriteHandler(IWeatherCommandsRepository weatherCommandsRepository, IValidator addFavoriteCommandValidator, ILogger logger) + public AddFavoriteHandler( + IWeatherCommandsRepository weatherCommandsRepository, + IRequestValidator addFavoriteCommandValidator, + ILogger logger) + :base(addFavoriteCommandValidator) { _weatherCommandsRepository = Guard.Against.Null(weatherCommandsRepository); - _addFavoriteCommandValidator = Guard.Against.Null(addFavoriteCommandValidator); _logger = Guard.Against.Null(logger); } - public async Task> HandleAsync(AddFavoriteCommand request, CancellationToken cancellationToken) + protected override async Task> HandleValidRequestAsync(AddFavoriteCommand request, CancellationToken cancellationToken) { - if (!_addFavoriteCommandValidator.IsValid(request)) - { - return HttpDataResponses.AsBadRequest(string.Format(ErrorMessages.RequestValidationError, request)); - } - var addResult = await _weatherCommandsRepository.AddFavoriteLocation(request, cancellationToken); if(addResult.IsFailed) { diff --git a/src/Weather.Core/Commands/DeleteFavoriteHandler.cs b/src/Weather.Core/Commands/DeleteFavoriteHandler.cs index 3b1450e..8acfff0 100644 --- a/src/Weather.Core/Commands/DeleteFavoriteHandler.cs +++ b/src/Weather.Core/Commands/DeleteFavoriteHandler.cs @@ -1,33 +1,27 @@ using Ardalis.GuardClauses; -using Validot; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Abstractions; -using Weather.Core.Resources; using Weather.Domain.Commands; -using Weather.Domain.Extensions; -using Weather.Domain.Http; namespace Weather.Core.Commands { - internal sealed class DeleteFavoriteHandler : IDeleteFavoriteHandler + internal sealed class DeleteFavoriteHandler : ValidationHttpRequestHandler { private readonly IWeatherCommandsRepository _weatherCommandsRepository; - private readonly IValidator _validator; public DeleteFavoriteHandler( - IWeatherCommandsRepository weatherCommandsRepository, - IValidator validator) + IWeatherCommandsRepository weatherCommandsRepository, + IRequestValidator validator) + : base(validator) { _weatherCommandsRepository = Guard.Against.Null(weatherCommandsRepository); - _validator = Guard.Against.Null(validator); } - public async Task> HandleAsync(DeleteFavoriteCommand request, CancellationToken cancellationToken) + protected override async Task> HandleValidRequestAsync(DeleteFavoriteCommand request, CancellationToken cancellationToken) { - if (!_validator.IsValid(request)) - { - return HttpDataResponses.AsBadRequest(string.Format(ErrorMessages.RequestValidationError, request)); - } - var addResult = await _weatherCommandsRepository.DeleteFavoriteLocationSafeAsync(request, cancellationToken); if (addResult.IsFailed) { diff --git a/src/Weather.Core/Configuration/ContainerConfigurationExtension.cs b/src/Weather.Core/Configuration/ContainerConfigurationExtension.cs index c9d28b3..5133697 100644 --- a/src/Weather.Core/Configuration/ContainerConfigurationExtension.cs +++ b/src/Weather.Core/Configuration/ContainerConfigurationExtension.cs @@ -1,14 +1,13 @@ using Microsoft.Extensions.DependencyInjection; -using Validot; -using Weather.Core.Abstractions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Commands; -using Weather.Core.Extensions; using Weather.Core.Queries; using Weather.Core.Validation; using Weather.Domain.Commands; using Weather.Domain.Dtos; using Weather.Domain.Queries; -using Wheaterbit.Client.Validation; namespace Weather.Core.Configuration { @@ -21,20 +20,20 @@ public static IServiceCollection AddCore(this IServiceCollection serviceCollecti private static IServiceCollection AddHandlers(this IServiceCollection serviceCollection) => serviceCollection - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); + .AddScoped, GetCurrentWeatherHandler>() + .AddScoped, GetFavoritesHandler>() + .AddScoped, GetForecastWeatherHandler>() + .AddScoped, AddFavoriteHandler>() + .AddScoped, DeleteFavoriteHandler>(); private static IServiceCollection AddValidation(this IServiceCollection serviceCollection) => serviceCollection - .AddValidotSingleton, CurrentWeatherDtoSpecificationHolder, CurrentWeatherDto>() - .AddValidotSingleton, ForecastWeatherDtoSpecificationHolder, ForecastWeatherDto>() - .AddValidotSingleton, LocationDtoSpecificationHolder, LocationDto>() - .AddValidotSingleton, AddFavoriteCommandSpecificationHolder, AddFavoriteCommand>() - .AddValidotSingleton, GetCurrentWeatherQuerySpecificationHolder, GetCurrentWeatherQuery>() - .AddValidotSingleton, GetForecastWeatherSpecificationHolder, GetForecastWeatherQuery>() - .AddValidotSingleton, DeleteFavoriteCommandSpecificationHolder, DeleteFavoriteCommand>(); + .AddSingleton, CurrentWeatherDtoValidator>() + .AddSingleton, ForecastWeatherDtoValidator>() + .AddSingleton, LocationDtoValidator>() + .AddSingleton, AddFavoriteCommandValidator>() + .AddSingleton, GetCurrentWeatherQueryValidator>() + .AddSingleton, GetForecastWeatherValidator>() + .AddSingleton, DeleteFavoriteCommandValidator>(); } } diff --git a/src/Weather.Core/Extensions/ValidotDependencyInjectionExtensions.cs b/src/Weather.Core/Extensions/ValidotDependencyInjectionExtensions.cs deleted file mode 100644 index c98fc52..0000000 --- a/src/Weather.Core/Extensions/ValidotDependencyInjectionExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Validot; - -namespace Weather.Core.Extensions -{ - public static class ValidotDependencyInjectionExtensions - { - public static IServiceCollection AddValidotSingleton(this IServiceCollection serviceCollection) - where TValidator : IValidator - where THolder : ISpecificationHolder, new() - { - return serviceCollection.AddSingleton(typeof(TValidator), Validator.Factory.Create(new THolder())); - } - } -} diff --git a/src/Weather.Core/Queries/GetCurrentWeatherHandler.cs b/src/Weather.Core/Queries/GetCurrentWeatherHandler.cs index 23ccdee..980f03c 100644 --- a/src/Weather.Core/Queries/GetCurrentWeatherHandler.cs +++ b/src/Weather.Core/Queries/GetCurrentWeatherHandler.cs @@ -1,49 +1,52 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; -using Validot; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Abstractions; using Weather.Core.Resources; using Weather.Domain.Dtos; using Weather.Domain.Extensions; -using Weather.Domain.Http; using Weather.Domain.Logging; using Weather.Domain.Queries; using Weather.Domain.Resources; namespace Weather.Core.Queries { - internal sealed class GetCurrentWeatherHandler : IGetCurrentWeatherHandler + internal sealed class GetCurrentWeatherHandler : ValidationHttpRequestHandler { - private readonly IValidator _getCurrentWeatcherQueryValidator; - private readonly IValidator _currentWeatherValidator; + private readonly IRequestValidator _currentWeatherValidator; private readonly IWeatherService _weatherService; - private readonly ILogger _logger; - public GetCurrentWeatherHandler(IValidator getCurrentWeatcherQueryValidator, - IValidator currentWeatherValidator, + private readonly ILogger _logger; + public GetCurrentWeatherHandler(IRequestValidator getCurrentWeatherQueryValidator, + IRequestValidator currentWeatherValidator, IWeatherService weatherService, - ILogger logger) + ILogger logger) + : base(getCurrentWeatherQueryValidator) { - _getCurrentWeatcherQueryValidator = Guard.Against.Null(getCurrentWeatcherQueryValidator); _weatherService = Guard.Against.Null(weatherService); _currentWeatherValidator = Guard.Against.Null(currentWeatherValidator); _logger = Guard.Against.Null(logger); } - public async Task> HandleAsync(GetCurrentWeatherQuery request, CancellationToken cancellationToken) + + protected override HttpDataResponse CreateInvalidResponse(GetCurrentWeatherQuery request, RequestValidationResult validationResult) { - if(!_getCurrentWeatcherQueryValidator.IsValid(request)) - { - return HttpDataResponses.AsBadRequest(string.Format(ErrorMessages.RequestValidationError, request)); - } + _logger.LogError(LogEvents.CurrentWeathersValidation, validationResult.ToString()); + return HttpDataResponses.AsBadRequest(string.Format(ErrorMessages.RequestValidationError, request)); + } + protected override async Task> HandleValidRequestAsync(GetCurrentWeatherQuery request, CancellationToken cancellationToken) + { var getCurrentWeatherResult = await _weatherService.GetCurrentWeather(request.Location, cancellationToken); - if(getCurrentWeatherResult.IsFailed) + if (getCurrentWeatherResult.IsFailed) { _logger.LogError(LogEvents.CurrentWeathersGet, getCurrentWeatherResult.Errors.JoinToMessage()); return HttpDataResponses.AsInternalServerError(ErrorMessages.ExternalApiError); } var validationResult = _currentWeatherValidator.Validate(getCurrentWeatherResult.Value); - if(validationResult.AnyErrors) + if (!validationResult.IsValid) { _logger.LogError(LogEvents.CurrentWeathersValidation, ErrorLogMessages.ValidationErrorLog, validationResult.ToString()); return HttpDataResponses.AsInternalServerError(ErrorMessages.ExternalApiError); diff --git a/src/Weather.Core/Queries/GetFavoritesHandler.cs b/src/Weather.Core/Queries/GetFavoritesHandler.cs index ff8fbb8..22f3d14 100644 --- a/src/Weather.Core/Queries/GetFavoritesHandler.cs +++ b/src/Weather.Core/Queries/GetFavoritesHandler.cs @@ -1,31 +1,33 @@ using Ardalis.GuardClauses; using FluentResults; using Microsoft.Extensions.Logging; -using Validot; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Abstractions; using Weather.Core.Resources; using Weather.Domain.BusinessEntities; using Weather.Domain.Dtos; using Weather.Domain.Extensions; -using Weather.Domain.Http; using Weather.Domain.Logging; using Weather.Domain.Resources; namespace Weather.Core.Queries { - internal sealed class GetFavoritesHandler : IGetFavoritesHandler + internal sealed class GetFavoritesHandler : IHttpRequestHandler { - private readonly IValidator _locationValidator; - private readonly IValidator _currentWeatherValidator; + private readonly IRequestValidator _locationValidator; + private readonly IRequestValidator _currentWeatherValidator; private readonly IWeatherQueriesRepository _weatherQueriesRepository; private readonly IWeatherService _weatherService; - private readonly ILogger _logger; + private readonly ILogger _logger; public GetFavoritesHandler(IWeatherQueriesRepository weatherQueriesRepository, IWeatherService weatherService, - IValidator locationValidator, - IValidator currentWeatherValidator, - ILogger logger) + IRequestValidator locationValidator, + IRequestValidator currentWeatherValidator, + ILogger logger) { _locationValidator = Guard.Against.Null(locationValidator); _currentWeatherValidator = Guard.Against.Null(currentWeatherValidator); @@ -80,7 +82,7 @@ await favoriteLocationsResult.ForEachAsync(async (location) => private async Task> GetWeatherAsync(LocationDto location, CancellationToken cancellationToken) { - if (!_locationValidator.IsValid(location)) + if (!_locationValidator.Validate(location).IsValid) { _logger.LogWarning(LogEvents.FavoriteWeathersGeneral, ErrorLogMessages.InvalidLocation, location); return Result.Fail(string.Format(ErrorMessages.InvalidStoredLocation, location)); @@ -93,7 +95,7 @@ private async Task> GetWeatherAsync(LocationDto locati return Result.Fail(ErrorMessages.ExternalApiError); } - if (!_currentWeatherValidator.IsValid(favoriteWeather.Value)) + if (!_currentWeatherValidator.Validate(favoriteWeather.Value).IsValid) { _logger.LogWarning(LogEvents.FavoriteWeathersGeneral, ErrorLogMessages.InvalidWeather, location); return Result.Fail(ErrorMessages.ExternalApiError); diff --git a/src/Weather.Core/Queries/GetForecastWeatherHandler.cs b/src/Weather.Core/Queries/GetForecastWeatherHandler.cs index 5738688..7f7bd79 100644 --- a/src/Weather.Core/Queries/GetForecastWeatherHandler.cs +++ b/src/Weather.Core/Queries/GetForecastWeatherHandler.cs @@ -1,41 +1,37 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; -using Validot; +using SmallApiToolkit.Core.Extensions; +using SmallApiToolkit.Core.RequestHandlers; +using SmallApiToolkit.Core.Response; +using SmallApiToolkit.Core.Validation; using Weather.Core.Abstractions; using Weather.Core.Resources; using Weather.Domain.Dtos; using Weather.Domain.Extensions; -using Weather.Domain.Http; using Weather.Domain.Logging; using Weather.Domain.Queries; using Weather.Domain.Resources; namespace Weather.Core.Queries { - internal sealed class GetForecastWeatherHandler : IGetForecastWeatherHandler + internal sealed class GetForecastWeatherHandler : ValidationHttpRequestHandler { - private readonly IValidator _getForecastWeatherQueryValidator; - private readonly IValidator _forecastWeatherValidator; + private readonly IRequestValidator _forecastWeatherValidator; private readonly IWeatherService _weatherService; - private readonly ILogger _logger; + private readonly ILogger _logger; public GetForecastWeatherHandler( - IValidator getForecastWeatherQueryValidator, - IWeatherService weatherService, - IValidator forecastWeatherValidator, - ILogger logger) + IRequestValidator getForecastWeatherQueryValidator, + IWeatherService weatherService, + IRequestValidator forecastWeatherValidator, + ILogger logger) + : base(getForecastWeatherQueryValidator) { - _getForecastWeatherQueryValidator = Guard.Against.Null(getForecastWeatherQueryValidator); _weatherService = Guard.Against.Null(weatherService); _forecastWeatherValidator = Guard.Against.Null(forecastWeatherValidator); _logger = Guard.Against.Null(logger); } - public async Task> HandleAsync(GetForecastWeatherQuery request, CancellationToken cancellationToken) + protected override async Task> HandleValidRequestAsync(GetForecastWeatherQuery request, CancellationToken cancellationToken) { - if(!_getForecastWeatherQueryValidator.IsValid(request)) - { - return HttpDataResponses.AsBadRequest(string.Format(ErrorMessages.RequestValidationError, request)); - } - var forecastResult = await _weatherService.GetForecastWeather(request.Location, cancellationToken); if(forecastResult.IsFailed) @@ -45,7 +41,7 @@ public async Task> HandleAsync(GetForecastW } var validationResult = _forecastWeatherValidator.Validate(forecastResult.Value); - if (validationResult.AnyErrors) + if (!validationResult.IsValid) { _logger.LogError(LogEvents.ForecastWeathersValidation, ErrorLogMessages.ValidationErrorLog, validationResult.ToString()); return HttpDataResponses.AsInternalServerError(ErrorMessages.ExternalApiError); diff --git a/src/Weather.Core/Validation/AddFavoriteCommandSpecificationHolder.cs b/src/Weather.Core/Validation/AddFavoriteCommandSpecificationHolder.cs deleted file mode 100644 index ad19f81..0000000 --- a/src/Weather.Core/Validation/AddFavoriteCommandSpecificationHolder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Validot; -using Weather.Domain.Commands; - -namespace Weather.Core.Validation -{ - internal sealed class AddFavoriteCommandSpecificationHolder : ISpecificationHolder - { - public Specification Specification { get; } - - public AddFavoriteCommandSpecificationHolder() - { - Specification addFavoriteCommandSpecification = s => s - .Member(m => m.Location, GeneralPredicates.isValidLocation); - - Specification = addFavoriteCommandSpecification; - } - } -} diff --git a/src/Weather.Core/Validation/AddFavoriteCommandValidator.cs b/src/Weather.Core/Validation/AddFavoriteCommandValidator.cs new file mode 100644 index 0000000..57e8530 --- /dev/null +++ b/src/Weather.Core/Validation/AddFavoriteCommandValidator.cs @@ -0,0 +1,23 @@ +using SmallApiToolkit.Core.Validation; +using Validot; +using Weather.Domain.Commands; + +namespace Weather.Core.Validation +{ + internal sealed class AddFavoriteCommandValidator : IRequestValidator + { + private readonly IValidator _validator; + + public AddFavoriteCommandValidator() + { + Specification addFavoriteCommandSpecification = s => s + .Member(m => m.Location, GeneralPredicates.isValidLocation); + + _validator = Validot.Validator.Factory.Create(addFavoriteCommandSpecification); + } + + public RequestValidationResult Validate(AddFavoriteCommand request) + => new() { IsValid = _validator.IsValid(request) }; + + } +} diff --git a/src/Weather.Core/Validation/CurrentWeatherDtoSpecificationHolder.cs b/src/Weather.Core/Validation/CurrentWeatherDtoValidator.cs similarity index 50% rename from src/Weather.Core/Validation/CurrentWeatherDtoSpecificationHolder.cs rename to src/Weather.Core/Validation/CurrentWeatherDtoValidator.cs index a27d00f..c8eaeb5 100644 --- a/src/Weather.Core/Validation/CurrentWeatherDtoSpecificationHolder.cs +++ b/src/Weather.Core/Validation/CurrentWeatherDtoValidator.cs @@ -1,14 +1,14 @@ -using Validot; -using Weather.Core.Validation; +using SmallApiToolkit.Core.Validation; +using Validot; using Weather.Domain.Dtos; -namespace Wheaterbit.Client.Validation +namespace Weather.Core.Validation { - internal sealed class CurrentWeatherDtoSpecificationHolder : ISpecificationHolder + internal sealed class CurrentWeatherDtoValidator : IRequestValidator { - public Specification Specification { get; } + private readonly IValidator _validator; - public CurrentWeatherDtoSpecificationHolder() + public CurrentWeatherDtoValidator() { Specification timeStringSpecification = s => s .NotEmpty() @@ -24,7 +24,17 @@ public CurrentWeatherDtoSpecificationHolder() .Member(m => m.Temperature, tempSpecification) .Member(m => m.CityName, m => m.NotEmpty().NotWhiteSpace()); - Specification = currentWeatherDtoSpecification; + _validator = Validot.Validator.Factory.Create(currentWeatherDtoSpecification); + } + + public RequestValidationResult Validate(CurrentWeatherDto request) + { + var result = _validator.Validate(request); + return new RequestValidationResult + { + IsValid = !result.AnyErrors, + ErrorMessages = [.. result.Codes] + }; } } } diff --git a/src/Weather.Core/Validation/DeleteFavoriteCommandSpecificationHolder.cs b/src/Weather.Core/Validation/DeleteFavoriteCommandSpecificationHolder.cs deleted file mode 100644 index b34c8c9..0000000 --- a/src/Weather.Core/Validation/DeleteFavoriteCommandSpecificationHolder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Validot; -using Weather.Domain.Commands; - -namespace Weather.Core.Validation -{ - internal sealed class DeleteFavoriteCommandSpecificationHolder : ISpecificationHolder - { - public Specification Specification { get; } - - public DeleteFavoriteCommandSpecificationHolder() - { - Specification deleteFavoriteCommandSpecification = s => s - .Member(m => m.Id, r => r.NonNegative()); - - Specification = deleteFavoriteCommandSpecification; - } - } -} diff --git a/src/Weather.Core/Validation/DeleteFavoriteCommandValidator.cs b/src/Weather.Core/Validation/DeleteFavoriteCommandValidator.cs new file mode 100644 index 0000000..7a2ad15 --- /dev/null +++ b/src/Weather.Core/Validation/DeleteFavoriteCommandValidator.cs @@ -0,0 +1,22 @@ +using SmallApiToolkit.Core.Validation; +using Validot; +using Weather.Domain.Commands; + +namespace Weather.Core.Validation +{ + internal sealed class DeleteFavoriteCommandValidator : IRequestValidator + { + private readonly IValidator _validator; + + public DeleteFavoriteCommandValidator() + { + Specification deleteFavoriteCommandSpecification = s => s + .Member(m => m.Id, r => r.NonNegative()); + + _validator = Validot.Validator.Factory.Create(deleteFavoriteCommandSpecification); + } + + public RequestValidationResult Validate(DeleteFavoriteCommand request) + => new() { IsValid = _validator.IsValid(request) }; + } +} diff --git a/src/Weather.Core/Validation/ForecastWeatherDtoSpecificationHolder.cs b/src/Weather.Core/Validation/ForecastWeatherDtoValidator.cs similarity index 62% rename from src/Weather.Core/Validation/ForecastWeatherDtoSpecificationHolder.cs rename to src/Weather.Core/Validation/ForecastWeatherDtoValidator.cs index dc6782d..44571f9 100644 --- a/src/Weather.Core/Validation/ForecastWeatherDtoSpecificationHolder.cs +++ b/src/Weather.Core/Validation/ForecastWeatherDtoValidator.cs @@ -1,12 +1,13 @@ -using Validot; +using SmallApiToolkit.Core.Validation; +using Validot; using Weather.Domain.Dtos; namespace Weather.Core.Validation { - internal sealed class ForecastWeatherDtoSpecificationHolder : ISpecificationHolder + internal sealed class ForecastWeatherDtoValidator : IRequestValidator { - public Specification Specification { get; } - public ForecastWeatherDtoSpecificationHolder() + private readonly IValidator _validator; + public ForecastWeatherDtoValidator() { Specification tempSpecification = s => s .Rule(GeneralPredicates.isValidTemperature); @@ -22,7 +23,10 @@ public ForecastWeatherDtoSpecificationHolder() .Member(m => m.ForecastTemperatures, m => m.AsCollection(forecastTemperatureSpecification)) .Member(m => m.CityName, m=>m.NotEmpty().NotWhiteSpace()); - Specification = forecastSpecification; + _validator = Validot.Validator.Factory.Create(forecastSpecification); } + + public RequestValidationResult Validate(ForecastWeatherDto request) + => new() { IsValid = _validator.IsValid(request) }; } } diff --git a/src/Weather.Core/Validation/GetCurrentWeatherQuerySpecificationHolder.cs b/src/Weather.Core/Validation/GetCurrentWeatherQuerySpecificationHolder.cs deleted file mode 100644 index f6f16d6..0000000 --- a/src/Weather.Core/Validation/GetCurrentWeatherQuerySpecificationHolder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Validot; -using Weather.Domain.Queries; - -namespace Weather.Core.Validation -{ - internal sealed class GetCurrentWeatherQuerySpecificationHolder : ISpecificationHolder - { - public Specification Specification { get; } - - public GetCurrentWeatherQuerySpecificationHolder() - { - Specification getCurrentWeatherQuerySpecification = s => s - .Member(m => m.Location, GeneralPredicates.isValidLocation); - - Specification = getCurrentWeatherQuerySpecification; - } - } -} diff --git a/src/Weather.Core/Validation/GetCurrentWeatherQueryValidator.cs b/src/Weather.Core/Validation/GetCurrentWeatherQueryValidator.cs new file mode 100644 index 0000000..253cc64 --- /dev/null +++ b/src/Weather.Core/Validation/GetCurrentWeatherQueryValidator.cs @@ -0,0 +1,29 @@ +using SmallApiToolkit.Core.Validation; +using Validot; +using Weather.Domain.Queries; + +namespace Weather.Core.Validation +{ + internal sealed class GetCurrentWeatherQueryValidator : IRequestValidator + { + private readonly IValidator _validator; + + public GetCurrentWeatherQueryValidator() + { + Specification getCurrentWeatherQuerySpecification = s => s + .Member(m => m.Location, GeneralPredicates.isValidLocation); + + _validator = Validot.Validator.Factory.Create(getCurrentWeatherQuerySpecification); + } + + public RequestValidationResult Validate(GetCurrentWeatherQuery request) + { + var result = _validator.Validate(request); + return new RequestValidationResult + { + IsValid = !result.AnyErrors, + ErrorMessages = [.. result.Codes] + }; + } + } +} diff --git a/src/Weather.Core/Validation/GetForecastWeatherSpecificationHolder.cs b/src/Weather.Core/Validation/GetForecastWeatherSpecificationHolder.cs deleted file mode 100644 index 986bc1f..0000000 --- a/src/Weather.Core/Validation/GetForecastWeatherSpecificationHolder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Validot; -using Weather.Domain.Queries; - -namespace Weather.Core.Validation -{ - internal sealed class GetForecastWeatherSpecificationHolder : ISpecificationHolder - { - public Specification Specification { get; } - - public GetForecastWeatherSpecificationHolder() - { - Specification getForecastWeatherQuerySpecification = s => s - .Member(m => m.Location, GeneralPredicates.isValidLocation); - - Specification = getForecastWeatherQuerySpecification; - } - } -} diff --git a/src/Weather.Core/Validation/GetForecastWeatherValidator.cs b/src/Weather.Core/Validation/GetForecastWeatherValidator.cs new file mode 100644 index 0000000..19eac5c --- /dev/null +++ b/src/Weather.Core/Validation/GetForecastWeatherValidator.cs @@ -0,0 +1,22 @@ +using SmallApiToolkit.Core.Validation; +using Validot; +using Weather.Domain.Queries; + +namespace Weather.Core.Validation +{ + internal sealed class GetForecastWeatherValidator : IRequestValidator + { + private readonly IValidator _validator; + + public GetForecastWeatherValidator() + { + Specification getForecastWeatherQuerySpecification = s => s + .Member(m => m.Location, GeneralPredicates.isValidLocation); + + _validator = Validot.Validator.Factory.Create(getForecastWeatherQuerySpecification); + } + + public RequestValidationResult Validate(GetForecastWeatherQuery request) + => new() { IsValid = _validator.IsValid(request) }; + } +} diff --git a/src/Weather.Core/Validation/LocationDtoSpecificationHolder.cs b/src/Weather.Core/Validation/LocationDtoSpecificationHolder.cs deleted file mode 100644 index c3f854e..0000000 --- a/src/Weather.Core/Validation/LocationDtoSpecificationHolder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Validot; -using Weather.Domain.Dtos; - -namespace Weather.Core.Validation -{ - internal sealed class LocationDtoSpecificationHolder : ISpecificationHolder - { - public Specification Specification { get; } - - public LocationDtoSpecificationHolder() - { - Specification = GeneralPredicates.isValidLocation; - } - } -} diff --git a/src/Weather.Core/Validation/LocationDtoValidator.cs b/src/Weather.Core/Validation/LocationDtoValidator.cs new file mode 100644 index 0000000..2450fb9 --- /dev/null +++ b/src/Weather.Core/Validation/LocationDtoValidator.cs @@ -0,0 +1,19 @@ +using SmallApiToolkit.Core.Validation; +using Validot; +using Weather.Domain.Dtos; + +namespace Weather.Core.Validation +{ + internal sealed class LocationDtoValidator : IRequestValidator + { + private readonly IValidator _validator; + + public LocationDtoValidator() + { + _validator = Validot.Validator.Factory.Create(GeneralPredicates.isValidLocation); + } + + public RequestValidationResult Validate(LocationDto request) + => new() { IsValid = _validator.IsValid(request) }; + } +} diff --git a/src/Weather.Core/Weather.Core.csproj b/src/Weather.Core/Weather.Core.csproj index be170ad..14011f2 100644 --- a/src/Weather.Core/Weather.Core.csproj +++ b/src/Weather.Core/Weather.Core.csproj @@ -14,6 +14,7 @@ + @@ -38,4 +39,10 @@ + + + <_Parameter1>DynamicProxyGenAssembly2 + + + diff --git a/src/Weather.Domain/Extensions/EnumerableExtensions.cs b/src/Weather.Domain/Extensions/EnumerableExtensions.cs index 3deab9c..b73db0b 100644 --- a/src/Weather.Domain/Extensions/EnumerableExtensions.cs +++ b/src/Weather.Domain/Extensions/EnumerableExtensions.cs @@ -19,8 +19,12 @@ public static async Task ForEachAsync(this IEnumerable values, Func(this IEnumerable values) - { - return values?.Any() ?? false; - } + => values?.Any() ?? false; + + public static string JoinToMessage(this IEnumerable values) + => values.JoinToMessage(','); + + public static string JoinToMessage(this IEnumerable values, char separator) + => string.Join(separator, values); } } diff --git a/src/Weather.Domain/Extensions/HttpDataResponses.cs b/src/Weather.Domain/Extensions/HttpDataResponses.cs deleted file mode 100644 index d9fa136..0000000 --- a/src/Weather.Domain/Extensions/HttpDataResponses.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Weather.Domain.Http; - -namespace Weather.Domain.Extensions -{ - public static class HttpDataResponses - { - public static HttpDataResponse AsBadRequest(IEnumerable errorMessages) - { - return new HttpDataResponse - { - StatusCode = System.Net.HttpStatusCode.BadRequest, - Errors = errorMessages.ToList() - }; - } - - public static HttpDataResponse AsBadRequest(string errorMessages) - { - return new HttpDataResponse - { - StatusCode = System.Net.HttpStatusCode.BadRequest, - Errors = new List { errorMessages } - }; - } - - public static HttpDataResponse AsInternalServerError(IEnumerable errorMessages) - { - return new HttpDataResponse - { - StatusCode = System.Net.HttpStatusCode.InternalServerError, - Errors = errorMessages.ToList() - }; - } - - public static HttpDataResponse AsInternalServerError(string errorMessage) - { - return new HttpDataResponse - { - StatusCode = System.Net.HttpStatusCode.InternalServerError, - Errors = new List { errorMessage } - }; - } - - public static HttpDataResponse AsOK(T data) - { - return new HttpDataResponse - { - Data = data, - StatusCode = System.Net.HttpStatusCode.OK, - }; - } - - public static HttpDataResponse AsOK(T data, IEnumerable errorMessages) - { - return new HttpDataResponse - { - Data = data, - StatusCode = System.Net.HttpStatusCode.OK, - Errors = errorMessages.ToList() - }; - } - - public static HttpDataResponse AsNoContent() - { - return new HttpDataResponse - { - StatusCode = System.Net.HttpStatusCode.NoContent, - }; - } - } -} diff --git a/src/Weather.Domain/Http/DataResponse.cs b/src/Weather.Domain/Http/DataResponse.cs deleted file mode 100644 index e3345dc..0000000 --- a/src/Weather.Domain/Http/DataResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Weather.Domain.Http -{ - public class DataResponse - { - public T? Data { get; init; } - - public IList Errors { get; init; } = new List(); - } -} diff --git a/src/Weather.Domain/Http/EmptyRequest.cs b/src/Weather.Domain/Http/EmptyRequest.cs deleted file mode 100644 index 15b19d9..0000000 --- a/src/Weather.Domain/Http/EmptyRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Weather.Domain.Http -{ - public class EmptyRequest - { - private static EmptyRequest? _instance; - public static EmptyRequest Instance - { - get - { - _instance ??= new EmptyRequest(); - return _instance; - } - } - - private EmptyRequest() - { - - } - } -} diff --git a/src/Weather.Domain/Http/HttpDataResponse.cs b/src/Weather.Domain/Http/HttpDataResponse.cs deleted file mode 100644 index 28cc1ae..0000000 --- a/src/Weather.Domain/Http/HttpDataResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Net; - -namespace Weather.Domain.Http -{ - public class HttpDataResponse : DataResponse - { - public HttpStatusCode StatusCode { get; init; } - - } -} diff --git a/src/global.json b/src/global.json index e2138e0..a679dd1 100644 --- a/src/global.json +++ b/src/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "8.0.200" + "version": "8.0.401" } } \ No newline at end of file