diff --git a/src/APP.Platform/Models/PublicationModel.cs b/src/APP.Platform/Models/PublicationModel.cs new file mode 100644 index 00000000..26066b70 --- /dev/null +++ b/src/APP.Platform/Models/PublicationModel.cs @@ -0,0 +1,6 @@ +namespace Domain.Models.ViewModels; + +public sealed class PublicationModel +{ + public string? Link { get; set; } +} diff --git a/src/APP.Platform/Pages/Canal/Index.cshtml.cs b/src/APP.Platform/Pages/Canal/Index.cshtml.cs index 7e308a6c..99de3ffc 100644 --- a/src/APP.Platform/Pages/Canal/Index.cshtml.cs +++ b/src/APP.Platform/Pages/Canal/Index.cshtml.cs @@ -3,6 +3,7 @@ using System.Text.Json; using Background; using ClassLib.Follow.Models.ViewModels; +using Domain.Contracts; using Domain.Entities; using Domain.Enums; using Domain.Models.ViewModels; @@ -22,6 +23,8 @@ namespace APP.Platform.Pages; public sealed class CanalIndexModel : CustomPageModel { + private readonly IPublicationWebService _publicationWebService; + [BindProperty] public JoinTime? JoinTime { get; set; } public Dictionary? MyEvents { get; set; } @@ -62,7 +65,8 @@ public CanalIndexModel( IMessagePublisher messagePublisher, Settings settings, IPerfilWebService perfilWebService, - ILiveService liveService + ILiveService liveService, + IPublicationWebService publicationWebService ) : base(context, httpClientFactory, httpContextAccessor, settings) { @@ -73,6 +77,7 @@ ILiveService liveService _context = context; _messagePublisher = messagePublisher; _liveService = liveService; + _publicationWebService = publicationWebService; } public async Task OnGetAsync(string usr) @@ -540,4 +545,13 @@ em receber mentoria {timeSelection.TituloTemporario} return Redirect("./?event=" + JoinTime.TimeSelectionId); } + + public async Task OnPostPublication([FromBody] PublicationModel publicationModel) + { + await _publicationWebService.Add( + new CreatePublicationRequest(UserProfile.Id, publicationModel.Link) + ); + + return Page(); + } } diff --git a/src/Core/Application/DependencyInjection.cs b/src/Core/Application/DependencyInjection.cs index 5635070f..9e96dc82 100644 --- a/src/Core/Application/DependencyInjection.cs +++ b/src/Core/Application/DependencyInjection.cs @@ -25,6 +25,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } } diff --git a/src/Core/Application/Logic/Publication/IPublicationBusinessLogic.cs b/src/Core/Application/Logic/Publication/IPublicationBusinessLogic.cs new file mode 100644 index 00000000..57ae4889 --- /dev/null +++ b/src/Core/Application/Logic/Publication/IPublicationBusinessLogic.cs @@ -0,0 +1,10 @@ +using Domain.Contracts; + +//using Domain.Entities; + +namespace Application.Logic; + +public interface IPublicationBusinessLogic +{ + public Task AddPublication(CreatePublicationRequest createPublicationRequest); +} diff --git a/src/Core/Application/Logic/Publication/PublicationBusinessLogic.cs b/src/Core/Application/Logic/Publication/PublicationBusinessLogic.cs new file mode 100644 index 00000000..b37cabcf --- /dev/null +++ b/src/Core/Application/Logic/Publication/PublicationBusinessLogic.cs @@ -0,0 +1,18 @@ +using Domain.Contracts; +using Domain.Entities; +using Domain.Repositories; + +namespace Application.Logic; + +public sealed class PublicationBusinessLogic(IPublicationRepository _repository) + : IPublicationBusinessLogic +{ + public async Task AddPublication(CreatePublicationRequest createPublicationRequest) + { + var publication = + Publication.Create(createPublicationRequest) + ?? throw new UriFormatException("A URL fornecida é inválida!"); + + await _repository.AddAsync(publication); + } +} diff --git a/src/Core/Domain/Contracts/Publication/CreatePublicationRequest.cs b/src/Core/Domain/Contracts/Publication/CreatePublicationRequest.cs new file mode 100644 index 00000000..159988b2 --- /dev/null +++ b/src/Core/Domain/Contracts/Publication/CreatePublicationRequest.cs @@ -0,0 +1,3 @@ +namespace Domain.Contracts; + +public record CreatePublicationRequest(Guid PerfilId, string? Link); diff --git a/src/Core/Domain/Entities/Publication.cs b/src/Core/Domain/Entities/Publication.cs index 9ff86d86..1a1ff8f6 100644 --- a/src/Core/Domain/Entities/Publication.cs +++ b/src/Core/Domain/Entities/Publication.cs @@ -1,3 +1,5 @@ +using System.Text.RegularExpressions; +using Domain.Contracts; using Domain.Primitives; namespace Domain.Entities; @@ -8,9 +10,13 @@ public sealed class Publication(Guid id, Guid perfilId, string link, bool isVali public string Link { get; private set; } = link; public bool IsValid { get; private set; } = isValid; - public static Publication Create(Guid perfilId, string link) + public static Publication? Create(CreatePublicationRequest createPublicationRequest) { - return new Publication(Guid.NewGuid(), perfilId, link, true); + string? url = createPublicationRequest.Link; + if (url == null || !UrlIsValid(url)) + return null; + + return new Publication(Guid.NewGuid(), createPublicationRequest.PerfilId, url, true); } public void NotValid() @@ -22,4 +28,21 @@ public void Valid() { IsValid = true; } + + public static bool UrlIsValid(string url) + { + string[] allowedDomains = + { + @"^https:\/\/(www\.)?linkedin\.com(\/.*)?$", + @"^https:\/\/x\.com(\/.*)?$", + @"^https:\/\/dev\.to(\/.*)?$", + @"^https:\/\/tabnews\.com\.br(\/.*)?$", + @"^https:\/\/medium\.com(\/.*)?$", + }; + + if (!allowedDomains.Any(domain => Regex.IsMatch(url, domain, RegexOptions.IgnoreCase))) + return false; + + return true; + } } diff --git a/src/Core/Domain/Interfaces/Repositories/IPublicationRepository.cs b/src/Core/Domain/Interfaces/Repositories/IPublicationRepository.cs new file mode 100644 index 00000000..8e083cf9 --- /dev/null +++ b/src/Core/Domain/Interfaces/Repositories/IPublicationRepository.cs @@ -0,0 +1,9 @@ +using Domain.Entities; + +namespace Domain.Repositories +{ + public interface IPublicationRepository + { + public Task AddAsync(Publication publication); + } +} diff --git a/src/Core/Domain/Interfaces/WebServices/IPublicationWebService.cs b/src/Core/Domain/Interfaces/WebServices/IPublicationWebService.cs new file mode 100644 index 00000000..c27e289d --- /dev/null +++ b/src/Core/Domain/Interfaces/WebServices/IPublicationWebService.cs @@ -0,0 +1,10 @@ +using Domain.Contracts; +using Domain.Entities; +using Microsoft.AspNetCore.Http; + +namespace Domain.WebServices; + +public interface IPublicationWebService +{ + public Task Add(CreatePublicationRequest request); +} diff --git a/src/Core/Infrastructure/DependencyInjection.cs b/src/Core/Infrastructure/DependencyInjection.cs index df214505..bf40323e 100644 --- a/src/Core/Infrastructure/DependencyInjection.cs +++ b/src/Core/Infrastructure/DependencyInjection.cs @@ -217,6 +217,7 @@ IConfiguration configuration services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } @@ -274,6 +275,7 @@ public static IServiceCollection AddRepositories(this IServiceCollection service services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } } diff --git a/src/Core/Infrastructure/Repositories/PublicationRepository.cs b/src/Core/Infrastructure/Repositories/PublicationRepository.cs new file mode 100644 index 00000000..3fd060bc --- /dev/null +++ b/src/Core/Infrastructure/Repositories/PublicationRepository.cs @@ -0,0 +1,18 @@ +using System; +using Domain.Entities; +using Domain.Repositories; +using Infrastructure.Contexts; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Repositories; + +public sealed class PublicationRepository(ApplicationDbContext context) + : GenericRepository(context), + IPublicationRepository +{ + public async Task AddAsync(Publication publication) + { + await DbContext.Publications.AddAsync(publication); + await DbContext.SaveChangesAsync(); + } +} diff --git a/src/Core/Infrastructure/WebServices/Core/PublicationWebService.cs b/src/Core/Infrastructure/WebServices/Core/PublicationWebService.cs new file mode 100644 index 00000000..c4d8008a --- /dev/null +++ b/src/Core/Infrastructure/WebServices/Core/PublicationWebService.cs @@ -0,0 +1,25 @@ +using Domain.Contracts; +using Domain.WebServices; + +namespace Infrastructure.WebServices; + +public sealed class PublicationWebService(CoreClient client) : IPublicationWebService +{ + const string baseRoute = "api/publication"; + + public async Task Add(CreatePublicationRequest request) + { + var route = Path.Combine(baseRoute, string.Empty); + + try + { + await client.PostAsync(route, request); + } + catch + { + throw new Exception( + "Erro! Não foi possível adicionar sua publicação. Verifique a URL ou se o formato do arquivo JSON está correto." + ); + } + } +} diff --git a/src/Core/Presentation/DependencyInjection.cs b/src/Core/Presentation/DependencyInjection.cs index 4c777110..75ba9369 100644 --- a/src/Core/Presentation/DependencyInjection.cs +++ b/src/Core/Presentation/DependencyInjection.cs @@ -316,6 +316,7 @@ public static IEndpointRouteBuilder AddEndPoints(this IEndpointRouteBuilder app) app.AddFollowEndPoints(); app.AddLikeEndPoints(); app.AddHelpResponseEndPoints(); + app.AddPublicationEndPoints(); return app; } diff --git a/src/Core/Presentation/EndPoints/PublicationEndPoints.cs b/src/Core/Presentation/EndPoints/PublicationEndPoints.cs new file mode 100644 index 00000000..8fd8460a --- /dev/null +++ b/src/Core/Presentation/EndPoints/PublicationEndPoints.cs @@ -0,0 +1,36 @@ +using Application.Logic; +using Domain.Contracts; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +namespace Presentation.EndPoints; + +public static class PublicationEndPoints +{ + public static void AddPublicationEndPoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("api/publication"); + + group.WithOpenApi(); + + group.MapPost(string.Empty, Add); + } + + public static async Task Add( + [FromServices] IPublicationBusinessLogic _logic, + [FromBody] CreatePublicationRequest _request + ) + { + try + { + await _logic.AddPublication(_request); + return Results.Ok(); + } + catch (Exception ex) + { + return Results.BadRequest(ex.Message); + } + } +}