diff --git a/.gitignore b/.gitignore index 9491a2f..e8143ec 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,8 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +*.db +*.db-shm +*.db-wal +/Tests/ConsoleAppConsole diff --git a/Core/News.Application/Common/Behaviors/ValidationBehavior.cs b/Core/News.Application/Common/Behaviors/ValidationBehavior.cs new file mode 100644 index 0000000..a4f6df2 --- /dev/null +++ b/Core/News.Application/Common/Behaviors/ValidationBehavior.cs @@ -0,0 +1,34 @@ +using FluentValidation; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace News.Application.Common.Behaviors +{ + public class ValidationBehavior + : IPipelineBehavior where TRequest : IRequest + { + private readonly IEnumerable> _validators; + + public ValidationBehavior(IEnumerable> validators) => _validators = validators; + + public Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + var context = new ValidationContext(request); + var fails = _validators + .Select(v => v.Validate(context)) + .SelectMany(res => res.Errors) + .Where(fail => fail is not null) + .ToList(); + + if(fails.Any()) + throw new ValidationException(fails); + + return next(); + } + } +} diff --git a/Core/News.Application/Common/Mappings/AssemblyMappingProfiler.cs b/Core/News.Application/Common/Mappings/AssemblyMappingProfiler.cs index 37fc599..fb6a9ff 100644 --- a/Core/News.Application/Common/Mappings/AssemblyMappingProfiler.cs +++ b/Core/News.Application/Common/Mappings/AssemblyMappingProfiler.cs @@ -1,7 +1,6 @@ -using System; +using AutoMapper; using System.Linq; using System.Reflection; -using AutoMapper; namespace News.Application.Common.Mappings { @@ -11,17 +10,14 @@ public class AssemblyMappingProfiler : Profile private void ApplyMappingFromAssembly(Assembly assembly) { - var assemblyTypes = assembly.GetTypes() - .Where(t => - t.GetInterfaces(). - Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapWith<>)) - ); + var assemblyTypes = assembly.GetExportedTypes().Where(t => t.GetCustomAttributes().Any()).ToList(); foreach (var assemblyType in assemblyTypes) { - - var obj = Activator.CreateInstance(assemblyType); - assemblyType.GetMethod("CreateMapping")?.Invoke(obj, new[] { this }); + foreach (var attr in assemblyType.GetCustomAttributes()) + { + this.CreateMap(attr.MapSourceType, assemblyType); + } } } } diff --git a/Core/News.Application/Common/Mappings/IMapWith.cs b/Core/News.Application/Common/Mappings/IMapWith.cs index 901b91d..ff49603 100644 --- a/Core/News.Application/Common/Mappings/IMapWith.cs +++ b/Core/News.Application/Common/Mappings/IMapWith.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; namespace News.Application.Common.Mappings { + [System.Obsolete("This method is obsolete. Use MapToAttribute", false)] public interface IMapWith { - void CreateMapping(Profile profile) => profile.CreateMap(typeof(T), GetType());//TODO: check for error. + public void CreateMapping(Profile profile) + => profile.CreateMap(typeof(T), GetType()); } } diff --git a/Core/News.Application/Common/Mappings/MapToAttribute.cs b/Core/News.Application/Common/Mappings/MapToAttribute.cs new file mode 100644 index 0000000..fe59cf1 --- /dev/null +++ b/Core/News.Application/Common/Mappings/MapToAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace News.Application.Common.Mappings +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class MapWithAttribute : Attribute + { + public Type MapSourceType { get; set; } + + public MapWithAttribute() { } + public MapWithAttribute(Type mapWithType) => MapSourceType = mapWithType; + } +} diff --git a/Core/News.Application/DIConfigurator.cs b/Core/News.Application/DIConfigurator.cs index 9be9f08..be43ebe 100644 --- a/Core/News.Application/DIConfigurator.cs +++ b/Core/News.Application/DIConfigurator.cs @@ -1,6 +1,8 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using System.Reflection; +using FluentValidation; +using News.Application.Common.Behaviors; namespace News.Application { @@ -9,6 +11,8 @@ public static class DIConfigurator public static IServiceCollection AddApplication(this IServiceCollection services) { services.AddMediatR(Assembly.GetExecutingAssembly()); + services.AddValidatorsFromAssemblies(new[] {Assembly.GetExecutingAssembly()}); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); return services; } } diff --git a/Core/News.Application/Interfaces/INewsDbContext.cs b/Core/News.Application/Interfaces/INewsDbContext.cs index bd2a121..bb27496 100644 --- a/Core/News.Application/Interfaces/INewsDbContext.cs +++ b/Core/News.Application/Interfaces/INewsDbContext.cs @@ -9,5 +9,7 @@ public interface INewsDbContext DbSet News { get; set; } Task SaveChangesAsync(CancellationToken cancellationToken); + + bool Initialize(); } } diff --git a/Core/News.Application/News.Application.csproj b/Core/News.Application/News.Application.csproj index 5608ce2..0eab8c1 100644 --- a/Core/News.Application/News.Application.csproj +++ b/Core/News.Application/News.Application.csproj @@ -6,6 +6,8 @@ + + diff --git a/Core/News.Application/News/Commands/Create/CreateNewsCommandHandler.cs b/Core/News.Application/News/Commands/Create/CreateNewsCommandHandler.cs index 3be748a..f77ef1e 100644 --- a/Core/News.Application/News/Commands/Create/CreateNewsCommandHandler.cs +++ b/Core/News.Application/News/Commands/Create/CreateNewsCommandHandler.cs @@ -1,21 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using News.Application.Interfaces; +using System; using System.Threading; using System.Threading.Tasks; -using MediatR; -using News.Application.Interfaces; namespace News.Application.News.Commands.Create { - public class CreateNewsCommandHandler : MediatR.IRequestHandler + public class CreateNewsCommandHandler : MediatR.IRequestHandler { private readonly INewsDbContext newsDb; public CreateNewsCommandHandler(INewsDbContext newsDb) => this.newsDb = newsDb; - public async Task Handle(CreateNewsCommandReques request, CancellationToken cancellationToken) + public async Task Handle(CreateNewsCommandRequest request, CancellationToken cancellationToken) { Domain.News news = new() { diff --git a/Core/News.Application/News/Commands/Create/CreateNewsCommandReques.cs b/Core/News.Application/News/Commands/Create/CreateNewsCommandRequest.cs similarity index 79% rename from Core/News.Application/News/Commands/Create/CreateNewsCommandReques.cs rename to Core/News.Application/News/Commands/Create/CreateNewsCommandRequest.cs index 446aaf3..efdb343 100644 --- a/Core/News.Application/News/Commands/Create/CreateNewsCommandReques.cs +++ b/Core/News.Application/News/Commands/Create/CreateNewsCommandRequest.cs @@ -3,7 +3,7 @@ namespace News.Application.News.Commands.Create { - public class CreateNewsCommandReques : IRequest + public class CreateNewsCommandRequest : IRequest { public Guid UserId { get; set; } diff --git a/Core/News.Application/News/Commands/Create/CreateNewsCommandValidator.cs b/Core/News.Application/News/Commands/Create/CreateNewsCommandValidator.cs new file mode 100644 index 0000000..21bc507 --- /dev/null +++ b/Core/News.Application/News/Commands/Create/CreateNewsCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using System; + +namespace News.Application.News.Commands.Create +{ + public class CreateNewsCommandValidator : AbstractValidator + { + public CreateNewsCommandValidator() + { + RuleFor(req => req.UserId).NotEqual(Guid.Empty); + RuleFor(req => req.Title).NotEmpty().MaximumLength(150); + } + } +} diff --git a/Core/News.Application/News/Commands/Delete/DeleteNewsCommandValidator.cs b/Core/News.Application/News/Commands/Delete/DeleteNewsCommandValidator.cs new file mode 100644 index 0000000..9e51d93 --- /dev/null +++ b/Core/News.Application/News/Commands/Delete/DeleteNewsCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using System; + +namespace News.Application.News.Commands.Delete +{ + public class DeleteNewsCommandValidator : AbstractValidator + { + public DeleteNewsCommandValidator() + { + RuleFor(req => req.UserId).NotEqual(Guid.Empty); + RuleFor(req => req.Id).NotEqual(Guid.Empty); + } + } +} diff --git a/Core/News.Application/News/Commands/Update/UpdateNewsCommandValidator.cs b/Core/News.Application/News/Commands/Update/UpdateNewsCommandValidator.cs new file mode 100644 index 0000000..16b6215 --- /dev/null +++ b/Core/News.Application/News/Commands/Update/UpdateNewsCommandValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; +using System; + +namespace News.Application.News.Commands.Update +{ + public class UpdateNewsCommandValidator : AbstractValidator + { + public UpdateNewsCommandValidator() + { + RuleFor(req => req.UserId).NotEqual(Guid.Empty); + RuleFor(req => req.Id).NotEqual(Guid.Empty); + RuleFor(req => req.Title).MinimumLength(150).NotEmpty(); + } + } +} diff --git a/Core/News.Application/News/Queries/GetDetails/GetNewsDetailsValidator.cs b/Core/News.Application/News/Queries/GetDetails/GetNewsDetailsValidator.cs new file mode 100644 index 0000000..f373555 --- /dev/null +++ b/Core/News.Application/News/Queries/GetDetails/GetNewsDetailsValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using System; + +namespace News.Application.News.Queries.GetDetails +{ + public class GetNewsDetailsValidator : AbstractValidator + { + public GetNewsDetailsValidator() + { + RuleFor(req => req.UserId).NotEqual(Guid.Empty); + RuleFor(req => req.Id).NotEqual(Guid.Empty); + } + } +} diff --git a/Core/News.Application/News/Queries/GetDetails/NewsDetailsVm.cs b/Core/News.Application/News/Queries/GetDetails/NewsDetailsVm.cs index b2e28b4..90012c7 100644 --- a/Core/News.Application/News/Queries/GetDetails/NewsDetailsVm.cs +++ b/Core/News.Application/News/Queries/GetDetails/NewsDetailsVm.cs @@ -3,7 +3,8 @@ namespace News.Application.News.Queries.GetDetails { - public class NewsDetailsVm : IMapWith + [MapWith(MapSourceType = typeof(Domain.News))] + public class NewsDetailsVm { public Guid Id { get; set; } public string Title { get; set; } diff --git a/Core/News.Application/News/Queries/GetNewsTitlesList/GetNewsTitilesListValidator.cs b/Core/News.Application/News/Queries/GetNewsTitlesList/GetNewsTitilesListValidator.cs new file mode 100644 index 0000000..d9b004b --- /dev/null +++ b/Core/News.Application/News/Queries/GetNewsTitlesList/GetNewsTitilesListValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; +using System; + +namespace News.Application.News.Queries.GetNewsTitlesList +{ + public class GetNewsTitilesListValidator : AbstractValidator + { + public GetNewsTitilesListValidator() + { + RuleFor(req => req.UserId).NotEqual(Guid.Empty); + } + } +} diff --git a/Core/News.Application/News/Queries/GetNewsTitlesList/NewsLookupDto.cs b/Core/News.Application/News/Queries/GetNewsTitlesList/NewsLookupDto.cs index 2930d3a..3cd632e 100644 --- a/Core/News.Application/News/Queries/GetNewsTitlesList/NewsLookupDto.cs +++ b/Core/News.Application/News/Queries/GetNewsTitlesList/NewsLookupDto.cs @@ -3,7 +3,8 @@ namespace News.Application.News.Queries.GetNewsTitlesList { - public class NewsLookupDto : IMapWith + [MapWith(MapSourceType = typeof(Domain.News))] + public class NewsLookupDto { public Guid Id { get; set; } public string Title { get; set; } diff --git a/Infrastructure/News.Persistance/NewsDbContext.cs b/Infrastructure/News.Persistance/NewsDbContext.cs index a29f0eb..af96558 100644 --- a/Infrastructure/News.Persistance/NewsDbContext.cs +++ b/Infrastructure/News.Persistance/NewsDbContext.cs @@ -1,6 +1,8 @@ using Microsoft.EntityFrameworkCore; using News.Application.Interfaces; using News.Persistence.EntityTypeConfigurations; +using System.Threading; +using System.Threading.Tasks; namespace News.Persistence { @@ -15,5 +17,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.ApplyConfiguration(new NewsConfiguration()); base.OnModelCreating(modelBuilder); } + + public bool Initialize() => Database.EnsureCreated(); } } diff --git a/NewsApp.sln b/NewsApp.sln index 203cb56..c3228dd 100644 --- a/NewsApp.sln +++ b/NewsApp.sln @@ -11,13 +11,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentatio EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1742E329-98BB-4C11-B913-4958AFA14B25}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Domain", "Core\News.Domain\News.Domain.csproj", "{8901FDE3-7660-4A3D-989B-D12483DE150D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Domain", "Core\News.Domain\News.Domain.csproj", "{8901FDE3-7660-4A3D-989B-D12483DE150D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Application", "Core\News.Application\News.Application.csproj", "{32AED4DC-CA9D-4F55-9214-9478DA1A68C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Application", "Core\News.Application\News.Application.csproj", "{32AED4DC-CA9D-4F55-9214-9478DA1A68C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Persistence", "Infrastructure\News.Persistance\News.Persistence.csproj", "{46965A37-776A-45A1-9A75-2AF1FC07E5BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Persistence", "Infrastructure\News.Persistance\News.Persistence.csproj", "{46965A37-776A-45A1-9A75-2AF1FC07E5BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.WebAPI", "Presentation\News.WebAPI\News.WebAPI.csproj", "{2C5DCA55-7078-4251-B8FB-FE0584495B44}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.WebAPI", "Presentation\News.WebAPI\News.WebAPI.csproj", "{2C5DCA55-7078-4251-B8FB-FE0584495B44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppConsole", "Tests\ConsoleAppConsole\ConsoleAppConsole.csproj", "{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -41,6 +43,10 @@ Global {2C5DCA55-7078-4251-B8FB-FE0584495B44}.Debug|Any CPU.Build.0 = Debug|Any CPU {2C5DCA55-7078-4251-B8FB-FE0584495B44}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C5DCA55-7078-4251-B8FB-FE0584495B44}.Release|Any CPU.Build.0 = Release|Any CPU + {3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +56,7 @@ Global {32AED4DC-CA9D-4F55-9214-9478DA1A68C5} = {5CDDE7D7-CDA8-4E19-82FB-9460BB9D6AEE} {46965A37-776A-45A1-9A75-2AF1FC07E5BF} = {77BBFD40-0A20-40EA-B05A-1381B15C5037} {2C5DCA55-7078-4251-B8FB-FE0584495B44} = {D64A7A3D-AA4F-42BD-B216-1403C7C34C42} + {3DA46089-4245-48EF-8DC3-8FF8B7A6FA00} = {1742E329-98BB-4C11-B913-4958AFA14B25} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4389CF9-FA13-4412-9317-33B6E7848912} diff --git a/Presentation/News.WebAPI/Controllers/BaseApiController.cs b/Presentation/News.WebAPI/Controllers/BaseApiController.cs new file mode 100644 index 0000000..8bf3e3f --- /dev/null +++ b/Presentation/News.WebAPI/Controllers/BaseApiController.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Security.Claims; + +namespace News.WebAPI.Controllers +{ + [ApiController] + [Route("api/[controller]/[action]")] + public class BaseApiController : ControllerBase + { + private MediatR.IMediator _mediator; + + protected MediatR.IMediator Mediator => + _mediator ??= HttpContext.RequestServices.GetService(); + + internal Guid UserId => + !User.Identity.IsAuthenticated ? + Guid.Empty : + Guid.Parse( + HttpContext.RequestServices.GetService>().GetUserId(User) + ); + } +} diff --git a/Presentation/News.WebAPI/Controllers/NewsController.cs b/Presentation/News.WebAPI/Controllers/NewsController.cs new file mode 100644 index 0000000..242fac9 --- /dev/null +++ b/Presentation/News.WebAPI/Controllers/NewsController.cs @@ -0,0 +1,82 @@ +using AutoMapper; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using News.Application.News.Commands.Create; +using News.Application.News.Commands.Delete; +using News.Application.News.Commands.Update; +using News.Application.News.Queries.GetDetails; +using News.Application.News.Queries.GetNewsTitlesList; +using System; +using System.Threading.Tasks; + +namespace News.WebAPI.Controllers +{ + [Route("api/[controller]")] + public class NewsController : BaseApiController + { + private readonly IMediator _mediator; + + private readonly IMapper _mapper; + public NewsController(IMediator mediator, IMapper mapper) => + (_mediator, _mapper) = (mediator, mapper); + + [HttpGet] + public async Task> GetAll() + { + GetNewsTitilesListRequest request = new() + { + UserId = UserId + }; + var vm = await _mediator.Send(request); + return Ok(vm); + } + + [HttpGet("{id}")] + public async Task> Get(Guid id) + { + GetNewsDetailsRequest request = new() + { + Id = id, + UserId = UserId + }; + var vm = await _mediator.Send(request); + return Ok(vm); + } + + [HttpPost] + public async Task> Create([FromBody] Models.CreateNewsDto newsDto) + { + var request = _mapper.Map(newsDto); + request.UserId = UserId; + + Guid id = await _mediator.Send(request); + + return Ok(id); + } + + [HttpPut] + public async Task Update([FromBody] Models.UpdateNewsDto newsDto) + { + var request = _mapper.Map(newsDto); + request.UserId = UserId; + + await _mediator.Send(request); + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task> Delete(Guid id) + { + DeleteNewsCommandRequest request = new() + { + UserId = UserId, + Id = id + }; + + var vm = await _mediator.Send(request); + + return Ok(vm); + } + } +} diff --git a/Presentation/News.WebAPI/MiddleWare/CustomExceptionsApiHandler.cs b/Presentation/News.WebAPI/MiddleWare/CustomExceptionsApiHandler.cs new file mode 100644 index 0000000..c9e3cd2 --- /dev/null +++ b/Presentation/News.WebAPI/MiddleWare/CustomExceptionsApiHandler.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Http; +using News.Application.Common.Exceptions; +using System; +using System.Net; +using System.Text.Json; +using System.Threading.Tasks; + +namespace News.WebAPI.MiddleWare +{ + public class CustomExceptionsApiHandler + { + private readonly RequestDelegate _next; + + public CustomExceptionsApiHandler(RequestDelegate next) => _next = next; + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + + await HandleExceptionAsync(context, ex); + } + } + + private Task HandleExceptionAsync(HttpContext context, Exception ex) + { + var errCode = HttpStatusCode.InternalServerError; + var res = string.Empty; + + switch (ex) + { + case NotFoundException: + errCode = HttpStatusCode.NotFound; + break; + + case FluentValidation.ValidationException validationEx: + errCode = HttpStatusCode.BadRequest; + res = JsonSerializer.Serialize(validationEx.Errors); + break; + + default: + break; + } + + context.Response.ContentType = "application/json"; + context.Response.StatusCode = (int)errCode; + + if (string.IsNullOrEmpty(res)) + res = JsonSerializer.Serialize(new { errinfo = ex.Message }); + + return context.Response.WriteAsync(res); + } + } +} diff --git a/Presentation/News.WebAPI/MiddleWare/CustomExceptionsHandlerMiddlewareExtension.cs b/Presentation/News.WebAPI/MiddleWare/CustomExceptionsHandlerMiddlewareExtension.cs new file mode 100644 index 0000000..ca1b1ff --- /dev/null +++ b/Presentation/News.WebAPI/MiddleWare/CustomExceptionsHandlerMiddlewareExtension.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Builder; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace News.WebAPI.MiddleWare +{ + public static class CustomExceptionsHandlerMiddlewareExtension + { + public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder builder) + => builder.UseMiddleware(); + } +} diff --git a/Presentation/News.WebAPI/Models/CreateNewsDto.cs b/Presentation/News.WebAPI/Models/CreateNewsDto.cs new file mode 100644 index 0000000..9c39996 --- /dev/null +++ b/Presentation/News.WebAPI/Models/CreateNewsDto.cs @@ -0,0 +1,12 @@ +using News.Application.Common.Mappings; +using News.Application.News.Commands.Create; + +namespace News.WebAPI.Models +{ + [MapWith(MapSourceType = typeof(CreateNewsCommandRequest))] + public class CreateNewsDto + { + public string Title { get; set; } + public string Content { get; set; } + } +} diff --git a/Presentation/News.WebAPI/Models/UpdateNewsDto.cs b/Presentation/News.WebAPI/Models/UpdateNewsDto.cs new file mode 100644 index 0000000..dc6fa73 --- /dev/null +++ b/Presentation/News.WebAPI/Models/UpdateNewsDto.cs @@ -0,0 +1,18 @@ +using News.Application.Common.Mappings; +using News.Application.News.Commands.Update; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace News.WebAPI.Models +{ + [MapWith(MapSourceType = typeof(UpdateNewsCommandRequest))] + public class UpdateNewsDto + { + public Guid Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + } +} diff --git a/Presentation/News.WebAPI/News.WebAPI.csproj b/Presentation/News.WebAPI/News.WebAPI.csproj index 842a770..1d8d365 100644 --- a/Presentation/News.WebAPI/News.WebAPI.csproj +++ b/Presentation/News.WebAPI/News.WebAPI.csproj @@ -4,4 +4,13 @@ net5.0 + + + + + + + + + diff --git a/Presentation/News.WebAPI/Program.cs b/Presentation/News.WebAPI/Program.cs index 43187de..b3b9f5b 100644 --- a/Presentation/News.WebAPI/Program.cs +++ b/Presentation/News.WebAPI/Program.cs @@ -1,7 +1,10 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using News.Application.Interfaces; +using News.Persistence; using System; using System.Collections.Generic; using System.Linq; @@ -13,7 +16,16 @@ public class Program { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var host = CreateHostBuilder(args).Build(); + + using(var scope = host.Services.CreateScope()) + { + var services = scope.ServiceProvider; + var dbContext = services.GetService(); + dbContext.Initialize(); + } + + host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => diff --git a/Presentation/News.WebAPI/Properties/launchSettings.json b/Presentation/News.WebAPI/Properties/launchSettings.json index 1dda6e5..aa142d7 100644 --- a/Presentation/News.WebAPI/Properties/launchSettings.json +++ b/Presentation/News.WebAPI/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:3373", + "applicationUrl": "http://localhost:3373/api/news", "sslPort": 0 } }, @@ -18,7 +18,7 @@ "News.WebAPI": { "commandName": "Project", "dotnetRunMessages": "true", - "launchBrowser": true, + "launchBrowser": false, "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/Presentation/News.WebAPI/Startup.cs b/Presentation/News.WebAPI/Startup.cs index b295e96..738a77e 100644 --- a/Presentation/News.WebAPI/Startup.cs +++ b/Presentation/News.WebAPI/Startup.cs @@ -1,39 +1,55 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using News.Application; +using News.Application.Common.Mappings; +using News.Application.Interfaces; +using News.Persistence; +using News.WebAPI.MiddleWare; using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; +using System.Reflection; namespace News.WebAPI { public class Startup { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public IConfiguration Configuration { get; private set; } + + public Startup(IConfiguration configuration) => Configuration = configuration; + public void ConfigureServices(IServiceCollection services) { + services + .AddPersistence(Configuration) + .AddApplication() + .AddAutoMapper(cfg => + { + cfg.AddProfile(new AssemblyMappingProfiler(assembly: Assembly.GetExecutingAssembly())); + cfg.AddProfile(new AssemblyMappingProfiler( + assembly: typeof(INewsDbContext).Assembly + //assembly: AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asbly => asbly.GetName().Name == "News.Application") + )); + }) + .AddControllers(); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } + app.UseCustomExceptionHandler(); app.UseRouting(); + app.UseEndpoints(endpoints => { - endpoints.MapGet("/", async context => - { - await context.Response.WriteAsync("Hello World!"); - }); + endpoints.MapControllers(); }); } } diff --git a/Presentation/News.WebAPI/appsettings.json b/Presentation/News.WebAPI/appsettings.json index 1ba0b1b..9d29328 100644 --- a/Presentation/News.WebAPI/appsettings.json +++ b/Presentation/News.WebAPI/appsettings.json @@ -8,6 +8,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "SQLite": "Dada Source=NewsDBlite-GV" + "SQLite": "Data Source=NewsDBlite-GV.db" } }