diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml index 72c0c564b..4f10cb4bd 100644 --- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml +++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml @@ -163,19 +163,19 @@ modules: method: POST use: downstream downstream: identity-service/access-tokens/revoke - auth: true + auth: false - upstream: /refresh-tokens/use method: POST use: downstream downstream: identity-service/refresh-tokens/use - auth: true + auth: false - upstream: /refresh-tokens/revoke method: POST use: downstream downstream: identity-service/refresh-tokens/revoke - auth: true + auth: false services: identity-service: @@ -772,7 +772,7 @@ modules: auth: true - upstream: /search - method: POST + method: GET use: downstream downstream: posts-service/posts/search auth: true diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs index 54519972e..51e700084 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Convey.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands @@ -8,21 +7,20 @@ public class CreateComment : ICommand { public Guid CommentId { get; set; } public Guid ContextId { get; set; } - public string CommentContext { get; set; } - public Guid StudentId { get; set; } + public string CommentContext { get; set; } + public Guid UserId { get; set; } public Guid ParentId { get; set; } - public string Comment { get; set; } - + public string TextContent { get; set; } - public CreateComment(Guid commentId, Guid contextId, string commentContext, Guid studentId, Guid parentId, - string comment) + public CreateComment(Guid commentId, Guid contextId, string commentContext, Guid userId, Guid parentId, + string textContent) { CommentId = commentId == Guid.Empty ? Guid.NewGuid() : commentId; ContextId = contextId; CommentContext = commentContext; - StudentId = studentId; + UserId = userId; ParentId = parentId; - Comment = comment; + TextContent = textContent; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs index 8ff86829c..9d4e9cb01 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs @@ -6,7 +6,12 @@ namespace MiniSpace.Services.Comments.Application.Commands public class DeleteComment : ICommand { public Guid CommentId { get; } - - public DeleteComment(Guid commentId) => CommentId = commentId; - } + public string CommentContext { get; } + + public DeleteComment(Guid commentId, string commentContext) + { + CommentId = commentId; + CommentContext = commentContext; + } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs index ae067c0cb..4481b83a8 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs @@ -3,9 +3,11 @@ using System.Threading; using System.Threading.Tasks; using Convey.CQRS.Commands; +using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; +using MiniSpace.Services.Comments.Application.Services.Clients; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Exceptions; using MiniSpace.Services.Comments.Core.Repositories; @@ -14,17 +16,30 @@ namespace MiniSpace.Services.Comments.Application.Commands.Handlers { public class CreateCommentHandler : ICommandHandler { - private readonly ICommentRepository _commentRepository; - private readonly IStudentRepository _studentRepository; + private readonly IOrganizationEventsCommentRepository _organizationEventsCommentRepository; + private readonly IOrganizationPostsCommentRepository _organizationPostsCommentRepository; + private readonly IUserEventsCommentRepository _userEventsCommentRepository; + private readonly IUserPostsCommentRepository _userPostsCommentRepository; private readonly IDateTimeProvider _dateTimeProvider; private readonly IMessageBroker _messageBroker; private readonly IAppContext _appContext; + private readonly IStudentsServiceClient _userServiceClient; - public CreateCommentHandler(ICommentRepository commentRepository, IStudentRepository studentRepository, - IDateTimeProvider dateTimeProvider, IMessageBroker messageBroker, IAppContext appContext) + public CreateCommentHandler( + IOrganizationEventsCommentRepository organizationEventsCommentRepository, + IOrganizationPostsCommentRepository organizationPostsCommentRepository, + IUserEventsCommentRepository userEventsCommentRepository, + IUserPostsCommentRepository userPostsCommentRepository, + IStudentsServiceClient userServiceClient, + IDateTimeProvider dateTimeProvider, + IMessageBroker messageBroker, + IAppContext appContext) { - _commentRepository = commentRepository; - _studentRepository = studentRepository; + _organizationEventsCommentRepository = organizationEventsCommentRepository; + _organizationPostsCommentRepository = organizationPostsCommentRepository; + _userEventsCommentRepository = userEventsCommentRepository; + _userPostsCommentRepository = userPostsCommentRepository; + _userServiceClient = userServiceClient; _dateTimeProvider = dateTimeProvider; _messageBroker = messageBroker; _appContext = appContext; @@ -33,42 +48,114 @@ public CreateCommentHandler(ICommentRepository commentRepository, IStudentReposi public async Task HandleAsync(CreateComment command, CancellationToken cancellationToken = default) { var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != command.StudentId) + if (identity.IsAuthenticated && identity.Id != command.UserId) { throw new UnauthorizedCommentAccessException(command.ContextId, identity.Id); } - if (!(await _studentRepository.ExistsAsync(command.StudentId))) + + var user = await _userServiceClient.GetAsync(command.UserId); + if (user == null) { - throw new StudentNotFoundException(command.StudentId); + throw new UserNotFoundException(command.UserId); } - if (!Enum.TryParse(command.CommentContext, true, out var newCommentContext)) + if (!Enum.TryParse(command.CommentContext, true, out var commentContext)) { throw new InvalidCommentContextEnumException(command.CommentContext); } var now = _dateTimeProvider.Now; - var comment = Comment.Create(command.CommentId, command.ContextId, newCommentContext, command.StudentId, - identity.Name, command.ParentId, command.Comment, now); - + Comment comment; + + switch (commentContext) + { + case CommentContext.OrganizationEvent: + comment = Comment.Create(command.CommentId, command.ContextId, commentContext, command.UserId, command.ParentId, command.TextContent, now); + await _organizationEventsCommentRepository.AddAsync(comment); + break; + + case CommentContext.OrganizationPost: + comment = Comment.Create(command.CommentId, command.ContextId, commentContext, command.UserId, command.ParentId, command.TextContent, now); + await _organizationPostsCommentRepository.AddAsync(comment); + break; + + case CommentContext.UserEvent: + comment = Comment.Create(command.CommentId, command.ContextId, commentContext, command.UserId, command.ParentId, command.TextContent, now); + await _userEventsCommentRepository.AddAsync(comment); + break; + + case CommentContext.UserPost: + comment = Comment.Create(command.CommentId, command.ContextId, commentContext, command.UserId, command.ParentId, command.TextContent, now); + await _userPostsCommentRepository.AddAsync(comment); + break; + + default: + throw new InvalidCommentContextEnumException(command.CommentContext); + } + if (command.ParentId != Guid.Empty) { - var parentComment = await _commentRepository.GetAsync(command.ParentId); + Comment parentComment = null; + Guid replyId = Guid.NewGuid(); + + switch (commentContext) + { + case CommentContext.OrganizationEvent: + parentComment = await _organizationEventsCommentRepository.GetAsync(command.ParentId); + break; + case CommentContext.OrganizationPost: + parentComment = await _organizationPostsCommentRepository.GetAsync(command.ParentId); + break; + case CommentContext.UserEvent: + parentComment = await _userEventsCommentRepository.GetAsync(command.ParentId); + break; + case CommentContext.UserPost: + parentComment = await _userPostsCommentRepository.GetAsync(command.ParentId); + break; + } + if (parentComment is null) { throw new ParentCommentNotFoundException(command.ParentId); } + if (parentComment.ParentId != Guid.Empty) { throw new InvalidParentCommentException(command.ParentId); } - parentComment.AddReply(now); - await _commentRepository.UpdateAsync(parentComment); + + parentComment.AddReply(replyId, command.UserId, command.TextContent, now); + + switch (commentContext) + { + case CommentContext.OrganizationEvent: + await _organizationEventsCommentRepository.UpdateAsync(parentComment); + break; + case CommentContext.OrganizationPost: + await _organizationPostsCommentRepository.UpdateAsync(parentComment); + break; + case CommentContext.UserEvent: + await _userEventsCommentRepository.UpdateAsync(parentComment); + break; + case CommentContext.UserPost: + await _userPostsCommentRepository.UpdateAsync(parentComment); + break; + } } - - await _commentRepository.AddAsync(comment); - await _messageBroker.PublishAsync(new CommentCreated(command.CommentId)); - } + + await _messageBroker.PublishAsync(new CommentCreated( + comment.Id, + comment.ContextId, + comment.CommentContext.ToString(), + comment.UserId, + comment.ParentId, + comment.TextContent, + comment.CreatedAt, + comment.LastUpdatedAt, + comment.RepliesCount, + comment.IsDeleted + )); + } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs index 447d23985..c4fc5e79a 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs @@ -1,48 +1,100 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Convey.CQRS.Commands; -using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Application.Events; namespace MiniSpace.Services.Comments.Application.Commands.Handlers { public class DeleteCommentHandler : ICommandHandler { - private readonly ICommentRepository _commentRepository; + private readonly IOrganizationEventsCommentRepository _organizationEventsCommentRepository; + private readonly IOrganizationPostsCommentRepository _organizationPostsCommentRepository; + private readonly IUserEventsCommentRepository _userEventsCommentRepository; + private readonly IUserPostsCommentRepository _userPostsCommentRepository; private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; - - public DeleteCommentHandler(ICommentRepository commentRepository, IAppContext appContext, + + public DeleteCommentHandler( + IOrganizationEventsCommentRepository organizationEventsCommentRepository, + IOrganizationPostsCommentRepository organizationPostsCommentRepository, + IUserEventsCommentRepository userEventsCommentRepository, + IUserPostsCommentRepository userPostsCommentRepository, + IAppContext appContext, IMessageBroker messageBroker) { - _commentRepository = commentRepository; + _organizationEventsCommentRepository = organizationEventsCommentRepository; + _organizationPostsCommentRepository = organizationPostsCommentRepository; + _userEventsCommentRepository = userEventsCommentRepository; + _userPostsCommentRepository = userPostsCommentRepository; _appContext = appContext; _messageBroker = messageBroker; } - + public async Task HandleAsync(DeleteComment command, CancellationToken cancellationToken = default) { - var comment = await _commentRepository.GetAsync(command.CommentId); + var identity = _appContext.Identity; + + Comment comment = null; + + switch (command.CommentContext) + { + case nameof(CommentContext.OrganizationEvent): + comment = await _organizationEventsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.OrganizationPost): + comment = await _organizationPostsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.UserEvent): + comment = await _userEventsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.UserPost): + comment = await _userPostsCommentRepository.GetAsync(command.CommentId); + break; + + default: + throw new InvalidCommentContextEnumException(command.CommentContext); + } + if (comment is null) { throw new CommentNotFoundException(command.CommentId); } - - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != comment.StudentId && !identity.IsAdmin) + + if (identity.IsAuthenticated && identity.Id != comment.UserId) { throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id); } comment.Delete(); - await _commentRepository.UpdateAsync(comment); + + switch (command.CommentContext) + { + case nameof(CommentContext.OrganizationEvent): + await _organizationEventsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.OrganizationPost): + await _organizationPostsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.UserEvent): + await _userEventsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.UserPost): + await _userPostsCommentRepository.UpdateAsync(comment); + break; + } await _messageBroker.PublishAsync(new CommentDeleted(command.CommentId)); } - } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs index 6e64f0e80..75d6bd4df 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs @@ -34,7 +34,7 @@ public async Task HandleAsync(DeleteLike command, CancellationToken cancellation } var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != comment.StudentId) + if (identity.IsAuthenticated && identity.Id != comment.UserId) { throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id); } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs index 879d52563..5f986c966 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Convey.CQRS.Commands; @@ -13,15 +12,27 @@ namespace MiniSpace.Services.Comments.Application.Commands.Handlers { public class UpdateCommentHandler : ICommandHandler { - private readonly ICommentRepository _commentRepository; + private readonly IOrganizationEventsCommentRepository _organizationEventsCommentRepository; + private readonly IOrganizationPostsCommentRepository _organizationPostsCommentRepository; + private readonly IUserEventsCommentRepository _userEventsCommentRepository; + private readonly IUserPostsCommentRepository _userPostsCommentRepository; private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; private readonly IDateTimeProvider _dateTimeProvider; - public UpdateCommentHandler(ICommentRepository commentRepository, IAppContext appContext, - IMessageBroker messageBroker, IDateTimeProvider dateTimeProvider) + public UpdateCommentHandler( + IOrganizationEventsCommentRepository organizationEventsCommentRepository, + IOrganizationPostsCommentRepository organizationPostsCommentRepository, + IUserEventsCommentRepository userEventsCommentRepository, + IUserPostsCommentRepository userPostsCommentRepository, + IAppContext appContext, + IMessageBroker messageBroker, + IDateTimeProvider dateTimeProvider) { - _commentRepository = commentRepository; + _organizationEventsCommentRepository = organizationEventsCommentRepository; + _organizationPostsCommentRepository = organizationPostsCommentRepository; + _userEventsCommentRepository = userEventsCommentRepository; + _userPostsCommentRepository = userPostsCommentRepository; _appContext = appContext; _messageBroker = messageBroker; _dateTimeProvider = dateTimeProvider; @@ -29,22 +40,63 @@ public UpdateCommentHandler(ICommentRepository commentRepository, IAppContext ap public async Task HandleAsync(UpdateComment command, CancellationToken cancellationToken = default) { - var comment = await _commentRepository.GetAsync(command.CommentId); + Comment comment = null; + + switch (command.CommentContext) + { + case nameof(CommentContext.OrganizationEvent): + comment = await _organizationEventsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.OrganizationPost): + comment = await _organizationPostsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.UserEvent): + comment = await _userEventsCommentRepository.GetAsync(command.CommentId); + break; + + case nameof(CommentContext.UserPost): + comment = await _userPostsCommentRepository.GetAsync(command.CommentId); + break; + + default: + throw new InvalidCommentContextEnumException(command.CommentContext); + } + if (comment is null) { throw new CommentNotFoundException(command.CommentId); } - + var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != comment.StudentId && !identity.IsAdmin) + if (identity.IsAuthenticated && identity.Id != comment.UserId) { throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id); } - + comment.Update(command.TextContent, _dateTimeProvider.Now); - await _commentRepository.UpdateAsync(comment); + + switch (command.CommentContext) + { + case nameof(CommentContext.OrganizationEvent): + await _organizationEventsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.OrganizationPost): + await _organizationPostsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.UserEvent): + await _userEventsCommentRepository.UpdateAsync(comment); + break; + + case nameof(CommentContext.UserPost): + await _userPostsCommentRepository.UpdateAsync(comment); + break; + } await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId)); } - } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs index e93a0e67d..e113a499a 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs @@ -7,11 +7,13 @@ public class UpdateComment : ICommand { public Guid CommentId { get; } public string TextContent { get; } + public string CommentContext { get; } - public UpdateComment(Guid commentId, string textContent) + public UpdateComment(Guid commentId, string textContent, string commentContext) { CommentId = commentId; TextContent = textContent; + CommentContext = commentContext; } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/CommentDto.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/CommentDto.cs index abc5fdc53..0ea7c0beb 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/CommentDto.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/CommentDto.cs @@ -12,8 +12,7 @@ public class CommentDto public Guid Id { get; set; } public Guid ContextId { get; set; } public string CommentContext { get; set; } - public Guid StudentId { get; set; } - public string StudentName { get; set; } + public Guid UserId { get; set; } public IEnumerable Likes { get; set; } public Guid ParentId { get; set; } public string TextContent { get; set; } @@ -22,18 +21,18 @@ public class CommentDto public DateTime LastReplyAt { get; set; } public int RepliesCount { get; set; } public bool IsDeleted { get; set; } - + public IEnumerable Replies { get; set; } // Including replies in the DTO + public CommentDto() { } - - public CommentDto (Comment comment) + + public CommentDto(Comment comment) { Id = comment.Id; ContextId = comment.ContextId; CommentContext = comment.CommentContext.ToString().ToLowerInvariant(); - StudentId = comment.StudentId; - StudentName = comment.StudentName; + UserId = comment.UserId; Likes = comment.Likes; ParentId = comment.ParentId; TextContent = comment.TextContent; @@ -42,6 +41,7 @@ public CommentDto (Comment comment) LastReplyAt = comment.LastReplyAt; RepliesCount = comment.RepliesCount; IsDeleted = comment.IsDeleted; + Replies = comment.Replies.Select(reply => new ReplyDto(reply)).ToList(); } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/ReplyDto.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/ReplyDto.cs new file mode 100644 index 000000000..bc3989d67 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/ReplyDto.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using MiniSpace.Services.Comments.Core.Entities; + +namespace MiniSpace.Services.Comments.Application.Dto +{ + [ExcludeFromCodeCoverage] + public class ReplyDto + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid CommentId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } + + public ReplyDto(Reply reply) + { + Id = reply.Id; + UserId = reply.UserId; + CommentId = reply.CommentId; + TextContent = reply.TextContent; + CreatedAt = reply.CreatedAt; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/UserDto.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/UserDto.cs new file mode 100644 index 000000000..89b6b533d --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/UserDto.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Comments.Application.Dto; + +namespace MiniSpace.Services.Comments.Application.Dto +{ + [ExcludeFromCodeCoverage] + public class UserDto + { + public Guid Id { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string ProfileImageUrl { get; set; } + public string Description { get; set; } + public DateTime? DateOfBirth { get; set; } + public bool EmailNotifications { get; set; } + public bool IsBanned { get; set; } + public string State { get; set; } + public DateTime CreatedAt { get; set; } + public string ContactEmail { get; set; } + public string BannerUrl { get; set; } + public string PhoneNumber { get; set; } + public IEnumerable Languages { get; set; } + public IEnumerable Interests { get; set; } + // public IEnumerable Education { get; set; } + // public IEnumerable Work { get; set; } + public bool IsTwoFactorEnabled { get; set; } + public string TwoFactorSecret { get; set; } + public IEnumerable InterestedInEvents { get; set; } + public IEnumerable SignedUpEvents { get; set; } + public string Country { get; set; } + public string City { get; set; } + + // public UserSettingsDto UserSettings { get; set; } + + // public List GalleryOfImageUrls { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs index 2007156a5..5fbef70aa 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs @@ -6,10 +6,30 @@ namespace MiniSpace.Services.Comments.Application.Events public class CommentCreated : IEvent { public Guid CommentId { get; } + public Guid ContextId { get; } + public string CommentContext { get; } + public Guid UserId { get; } + public Guid ParentId { get; } + public string TextContent { get; } + public DateTime CreatedAt { get; } + public DateTime LastUpdatedAt { get; } + public int RepliesCount { get; } + public bool IsDeleted { get; } - public CommentCreated(Guid commentId) + public CommentCreated(Guid commentId, Guid contextId, string commentContext, Guid userId, + Guid parentId, string textContent, DateTime createdAt, + DateTime lastUpdatedAt, int repliesCount, bool isDeleted) { CommentId = commentId; + ContextId = contextId; + CommentContext = commentContext; + UserId = userId; + ParentId = parentId; + TextContent = textContent; + CreatedAt = createdAt; + LastUpdatedAt = lastUpdatedAt; + RepliesCount = repliesCount; + IsDeleted = isDeleted; } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentCreatedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentCreatedHandler.cs deleted file mode 100644 index 19e873bf9..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentCreatedHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using MiniSpace.Services.Comments.Application.Exceptions; -using MiniSpace.Services.Comments.Core.Entities; -using MiniSpace.Services.Comments.Core.Repositories; - -namespace MiniSpace.Services.Comments.Application.Events.External.Handlers -{ - public class StudentCreatedHandler : IEventHandler - { - private readonly IStudentRepository _studentRepository; - - public StudentCreatedHandler(IStudentRepository studentRepository) - { - _studentRepository = studentRepository; - } - - public async Task HandleAsync(StudentCreated @event, CancellationToken cancellationToken = default) - { - if (await _studentRepository.ExistsAsync(@event.StudentId)) - { - throw new StudentAlreadyExistsException(@event.StudentId); - } - - await _studentRepository.AddAsync(new Student(@event.StudentId)); - } - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs deleted file mode 100644 index fe9321fe6..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using MiniSpace.Services.Comments.Application.Exceptions; -using MiniSpace.Services.Comments.Core.Repositories; - -namespace MiniSpace.Services.Comments.Application.Events.External.Handlers -{ - public class StudentDeletedHandler : IEventHandler - { - private readonly IStudentRepository _studentRepository; - - public StudentDeletedHandler(IStudentRepository studentRepository) - { - _studentRepository = studentRepository; - } - - public async Task HandleAsync(StudentDeleted @event, CancellationToken cancellationToken = default) - { - if (!(await _studentRepository.ExistsAsync(@event.StudentId))) - { - throw new StudentNotFoundException(@event.StudentId); - } - - await _studentRepository.DeleteAsync(@event.StudentId); - } - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs deleted file mode 100644 index b85346e58..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using System; - -namespace MiniSpace.Services.Comments.Application.Events.External -{ - [Message("students")] - public class StudentCreated : IEvent - { - public Guid StudentId { get; } - public string FullName { get; } - - public StudentCreated(Guid studentId, string fullName) - { - StudentId = studentId; - FullName = fullName; - } - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs deleted file mode 100644 index 75f0176af..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using System; - -namespace MiniSpace.Services.Comments.Application.Events.External -{ - [Message("students")] - public class StudentDeleted : IEvent - { - public Guid StudentId { get; } - public string FullName { get; } - - public StudentDeleted(Guid studentId, string fullName) - { - StudentId = studentId; - FullName = fullName; - } - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UserNotFoundException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UserNotFoundException.cs new file mode 100644 index 000000000..36f7aa5b5 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UserNotFoundException.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Application.Exceptions +{ + [ExcludeFromCodeCoverage] + public class UserNotFoundException : AppException + { + public override string Code { get; } = "user_not_found"; + public Guid UserId { get; } + + public UserNotFoundException(Guid userId) + : base($"User with ID: {userId} was not found.") + { + UserId = userId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/Clients/IStudentsServiceClient.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/Clients/IStudentsServiceClient.cs new file mode 100644 index 000000000..3dd736a49 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/Clients/IStudentsServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MiniSpace.Services.Comments.Application.Dto; + +namespace MiniSpace.Services.Comments.Application.Services.Clients +{ + public interface IStudentsServiceClient + { + Task GetAsync(Guid id); + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/ICommentService.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/ICommentService.cs index 3bbad565c..0124f30e6 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/ICommentService.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/ICommentService.cs @@ -3,11 +3,12 @@ using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Wrappers; namespace MiniSpace.Services.Comments.Application.Services { public interface ICommentService { - Task>> BrowseCommentsAsync(SearchComments command); + Task> BrowseCommentsAsync(SearchComments command); } } \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs index db27763e1..c6f499693 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs @@ -1,5 +1,5 @@ using Convey.CQRS.Events; -using MiniSpace.Services.Comments.Core; +using MiniSpace.Services.Comments.Core.Events; using System.Collections.Generic; namespace MiniSpace.Services.Comments.Application.Services diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateRoot.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateRoot.cs index d22957966..e0583d4ac 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateRoot.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateRoot.cs @@ -1,4 +1,4 @@ -using MiniSpace.Services.Comments.Core; +using MiniSpace.Services.Comments.Core.Events; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Comment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Comment.cs index cefd9bf3e..328c55b9f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Comment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Comment.cs @@ -1,76 +1,89 @@ using MiniSpace.Services.Comments.Core.Exceptions; +using MiniSpace.Services.Comments.Core.Events; using System; using System.Collections.Generic; using System.Linq; - namespace MiniSpace.Services.Comments.Core.Entities { public class Comment : AggregateRoot { private ISet _likes = new HashSet(); + private ISet _replies = new HashSet(); + public Guid ContextId { get; private set; } public CommentContext CommentContext { get; private set; } - public Guid StudentId { get; private set; } - public string StudentName { get; private set; } + public Guid UserId { get; private set; } public Guid ParentId { get; private set; } public string TextContent { get; private set; } public DateTime CreatedAt { get; private set; } public DateTime LastUpdatedAt { get; private set; } public DateTime LastReplyAt { get; private set; } - public int RepliesCount { get; private set; } + public int RepliesCount => _replies.Count; public bool IsDeleted { get; private set; } - + public IEnumerable Likes { get => _likes; private set => _likes = new HashSet(value); } - public Comment(Guid id, Guid contextId, CommentContext commentContext, Guid studentId, string studentName, + public IEnumerable Replies + { + get => _replies; + private set => _replies = new HashSet(value); + } + + public Comment(Guid id, Guid contextId, CommentContext commentContext, Guid userId, IEnumerable likes, Guid parentId, string textContent, DateTime createdAt, DateTime lastUpdatedAt, - DateTime lastReplyAt, int repliesCount, bool isDeleted) + DateTime lastReplyAt, IEnumerable replies, bool isDeleted) { Id = id; ContextId = contextId; CommentContext = commentContext; - StudentId = studentId; - StudentName = studentName; + UserId = userId; Likes = likes; ParentId = parentId; TextContent = textContent; CreatedAt = createdAt; LastUpdatedAt = lastUpdatedAt; LastReplyAt = lastReplyAt; - RepliesCount = repliesCount; + Replies = replies; IsDeleted = isDeleted; } - public void Like(Guid studentId) + public void Like(Guid userId) { - if (Likes.Any(id => id == studentId)) + if (Likes.Any(id => id == userId)) { - throw new StudentAlreadyLikesCommentException(studentId); + throw new UserAlreadyLikesCommentException(userId); } - _likes.Add(studentId); + + _likes.Add(userId); + AddEvent(new CommentLiked(Id, userId)); } - public void UnLike(Guid studentId) + public void UnLike(Guid userId) { - if (Likes.All(id => id != studentId)) + if (Likes.All(id => id != userId)) { - throw new StudentNotLikeCommentException(studentId, Id); + throw new UserNotLikeCommentException(userId, Id); } - _likes.Remove(studentId); + + _likes.Remove(userId); + AddEvent(new CommentUnliked(Id, userId)); } - public static Comment Create(AggregateId id, Guid contextId, CommentContext commentContext, Guid studentId, - string studentName, Guid parentId, string textContent, DateTime createdAt) + public static Comment Create(AggregateId id, Guid contextId, CommentContext commentContext, Guid userId, + Guid parentId, string textContent, DateTime createdAt) { CheckContent(id, textContent); - return new Comment(id, contextId, commentContext, studentId, studentName, new List(), parentId, textContent, - createdAt, createdAt, createdAt, 0,false); + var comment = new Comment(id, contextId, commentContext, userId, new List(), parentId, textContent, + createdAt, createdAt, createdAt, new List(), false); + + comment.AddEvent(new CommentCreated(comment.Id, userId, contextId, textContent, createdAt)); + return comment; } public void Update(string textContent, DateTime now) @@ -93,12 +106,25 @@ public void Delete() { IsDeleted = true; TextContent = ""; + AddEvent(new CommentDeleted(Id)); } - - public void AddReply(DateTime now) + + public void AddReply(Guid replyId, Guid userId, string textContent, DateTime now) { - RepliesCount++; + var reply = new Reply(replyId, userId, Id, textContent, now); + _replies.Add(reply); LastReplyAt = now; + + AddEvent(new CommentReplyAdded(Id, replyId, userId, textContent, now)); + } + + public void RemoveReply(Guid replyId) + { + var reply = _replies.FirstOrDefault(r => r.Id == replyId); + if (reply != null) + { + _replies.Remove(reply); + } } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/CommentContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/CommentContext.cs index 866c3b3aa..04bd30c99 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/CommentContext.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/CommentContext.cs @@ -8,7 +8,9 @@ namespace MiniSpace.Services.Comments.Core.Entities { public enum CommentContext { - Post, - Event + UserPost, + UserEvent, + OrganizationPost, + OrganizationEvent } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Reply.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Reply.cs new file mode 100644 index 000000000..7d084f016 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Reply.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Core.Entities +{ + public class Reply + { + public Guid Id { get; } + public Guid UserId { get; } + public Guid CommentId { get; } + public string TextContent { get; } + public DateTime CreatedAt { get; } + + public Reply(Guid id, Guid userId, Guid commentId, string textContent, DateTime createdAt) + { + Id = id; + UserId = userId; + CommentId = commentId; + TextContent = textContent; + CreatedAt = createdAt; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Student.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Student.cs deleted file mode 100644 index b9290f47f..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Student.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace MiniSpace.Services.Comments.Core.Entities -{ - public class Student - { - public Guid Id { get; private set; } - - public Student(Guid id) - { - Id = id; - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentCreated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentCreated.cs new file mode 100644 index 000000000..a4cbae8d4 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentCreated.cs @@ -0,0 +1,22 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Events +{ + public class CommentCreated : IDomainEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + public Guid ContextId { get; } + public string TextContent { get; } + public DateTime CreatedAt { get; } + + public CommentCreated(Guid commentId, Guid userId, Guid contextId, string textContent, DateTime createdAt) + { + CommentId = commentId; + UserId = userId; + ContextId = contextId; + TextContent = textContent; + CreatedAt = createdAt; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentDeleted.cs new file mode 100644 index 000000000..0348169ce --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentDeleted.cs @@ -0,0 +1,14 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Events +{ + public class CommentDeleted : IDomainEvent + { + public Guid CommentId { get; } + + public CommentDeleted(Guid commentId) + { + CommentId = commentId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentLiked.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentLiked.cs new file mode 100644 index 000000000..c50325b28 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentLiked.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Events +{ + public class CommentLiked : IDomainEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + + public CommentLiked(Guid commentId, Guid userId) + { + CommentId = commentId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentReplyAdded.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentReplyAdded.cs new file mode 100644 index 000000000..de443f5f4 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentReplyAdded.cs @@ -0,0 +1,22 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Events +{ + public class CommentReplyAdded : IDomainEvent + { + public Guid CommentId { get; } + public Guid ReplyId { get; } + public Guid UserId { get; } + public string TextContent { get; } + public DateTime CreatedAt { get; } + + public CommentReplyAdded(Guid commentId, Guid replyId, Guid userId, string textContent, DateTime createdAt) + { + CommentId = commentId; + ReplyId = replyId; + UserId = userId; + TextContent = textContent; + CreatedAt = createdAt; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentUnliked.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentUnliked.cs new file mode 100644 index 000000000..91b298ed9 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/CommentUnliked.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Events +{ + public class CommentUnliked : IDomainEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + + public CommentUnliked(Guid commentId, Guid userId) + { + CommentId = commentId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/IDomainEvent.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/IDomainEvent.cs new file mode 100644 index 000000000..b2ebf9434 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Events/IDomainEvent.cs @@ -0,0 +1,7 @@ +namespace MiniSpace.Services.Comments.Core.Events +{ + public interface IDomainEvent + { + + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserAlreadyLikesCommentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserAlreadyLikesCommentException.cs new file mode 100644 index 000000000..98a931684 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserAlreadyLikesCommentException.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Exceptions +{ + public class UserAlreadyLikesCommentException : DomainException + { + public override string Code { get; } = "user_already_likes_comment"; + public Guid UserId { get; } + + public UserAlreadyLikesCommentException(Guid userId) + : base($"User with id: {userId} already likes this comment.") + { + UserId = userId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserNotLikeCommentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserNotLikeCommentException.cs new file mode 100644 index 000000000..1f30270d2 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/UserNotLikeCommentException.cs @@ -0,0 +1,18 @@ +using System; + +namespace MiniSpace.Services.Comments.Core.Exceptions +{ + public class UserNotLikeCommentException : DomainException + { + public override string Code { get; } = "user_not_liked_comment"; + public Guid UserId { get; } + public Guid CommentId { get; } + + public UserNotLikeCommentException(Guid userId, Guid commentId) + : base($"User with id: {userId} has not liked the comment with id: {commentId}.") + { + UserId = userId; + CommentId = commentId; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/IDomainEvent.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/IDomainEvent.cs deleted file mode 100644 index ce8577480..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/IDomainEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MiniSpace.Services.Comments.Core -{ - public interface IDomainEvent - { - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/MiniSpace.Services.Comments.Core.sln b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/MiniSpace.Services.Comments.Core.sln new file mode 100644 index 000000000..459b44a0c --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/MiniSpace.Services.Comments.Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Core", "MiniSpace.Services.Comments.Core.csproj", "{D56DA11C-4E1E-4B97-88AD-09139F7EF882}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D56DA11C-4E1E-4B97-88AD-09139F7EF882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D56DA11C-4E1E-4B97-88AD-09139F7EF882}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D56DA11C-4E1E-4B97-88AD-09139F7EF882}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D56DA11C-4E1E-4B97-88AD-09139F7EF882}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {48341AFC-81AE-477E-8FE6-BE9BB4A0AFB8} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/ICommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/ICommentRepository.cs index 20ea99c79..046fff005 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/ICommentRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/ICommentRepository.cs @@ -1,7 +1,9 @@ using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Application.Wrappers; using System; using System.Collections.Generic; using System.Threading.Tasks; +using MiniSpace.Services.Comments.Core.Wrappers; namespace MiniSpace.Services.Comments.Core.Repositories { @@ -13,10 +15,6 @@ public interface ICommentRepository Task DeleteAsync(Guid id); Task> GetByEventIdAsync(Guid eventId); Task> GetByPostIdAsync(Guid postId); - - Task<(IEnumerable comments, int pageNumber, int pageSize, int totalPages, int totalElements)> - BrowseCommentsAsync(int pageNumber, int pageSize, Guid contextId, CommentContext context, Guid parentId, - IEnumerable sortBy, string direction); - - } -} \ No newline at end of file + Task> BrowseCommentsAsync(BrowseCommentsRequest request); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationEventsCommentRepository.cs new file mode 100644 index 000000000..6aed5e452 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationEventsCommentRepository.cs @@ -0,0 +1,19 @@ +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Application.Wrappers; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MiniSpace.Services.Comments.Core.Wrappers; + +namespace MiniSpace.Services.Comments.Core.Repositories +{ + public interface IOrganizationEventsCommentRepository + { + Task GetAsync(Guid id); + Task AddAsync(Comment comment); + Task UpdateAsync(Comment comment); + Task DeleteAsync(Guid id); + Task> GetByEventIdAsync(Guid eventId); + Task> BrowseCommentsAsync(BrowseCommentsRequest request); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationPostsCommentRepository.cs new file mode 100644 index 000000000..0cabe0eb5 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IOrganizationPostsCommentRepository.cs @@ -0,0 +1,19 @@ +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Application.Wrappers; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MiniSpace.Services.Comments.Core.Wrappers; + +namespace MiniSpace.Services.Comments.Core.Repositories +{ + public interface IOrganizationPostsCommentRepository + { + Task GetAsync(Guid id); + Task AddAsync(Comment comment); + Task UpdateAsync(Comment comment); + Task DeleteAsync(Guid id); + Task> GetByPostIdAsync(Guid postId); + Task> BrowseCommentsAsync(BrowseCommentsRequest request); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IStudentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IStudentRepository.cs deleted file mode 100644 index 5043e91dd..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IStudentRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MiniSpace.Services.Comments.Core.Entities; -using System; -using System.Threading.Tasks; - -namespace MiniSpace.Services.Comments.Core.Repositories -{ - public interface IStudentRepository - { - Task GetAsync(Guid id); - Task ExistsAsync(Guid id); - Task AddAsync(Student student); - Task DeleteAsync(Guid id); - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserEventsCommentRepository.cs new file mode 100644 index 000000000..a55e51aa9 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserEventsCommentRepository.cs @@ -0,0 +1,19 @@ +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Application.Wrappers; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MiniSpace.Services.Comments.Core.Wrappers; + +namespace MiniSpace.Services.Comments.Core.Repositories +{ + public interface IUserEventsCommentRepository + { + Task GetAsync(Guid id); + Task AddAsync(Comment comment); + Task UpdateAsync(Comment comment); + Task DeleteAsync(Guid id); + Task> GetByEventIdAsync(Guid eventId); + Task> BrowseCommentsAsync(BrowseCommentsRequest request); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserPostsCommentRepository.cs new file mode 100644 index 000000000..5419803fd --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IUserPostsCommentRepository.cs @@ -0,0 +1,19 @@ +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Application.Wrappers; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MiniSpace.Services.Comments.Core.Wrappers; + +namespace MiniSpace.Services.Comments.Core.Repositories +{ + public interface IUserPostsCommentRepository + { + Task GetAsync(Guid id); + Task AddAsync(Comment comment); + Task UpdateAsync(Comment comment); + Task DeleteAsync(Guid id); + Task> GetByPostIdAsync(Guid postId); + Task> BrowseCommentsAsync(BrowseCommentsRequest request); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/BrowseCommentsRequest.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/BrowseCommentsRequest.cs new file mode 100644 index 000000000..8fcd75a52 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/BrowseCommentsRequest.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Services.Comments.Core.Entities; + +namespace MiniSpace.Services.Comments.Core.Wrappers +{ + public class BrowseCommentsRequest + { + public int PageNumber { get; set; } + public int PageSize { get; set; } + public Guid ContextId { get; set; } + public CommentContext CommentContext { get; set; } + public Guid ParentId { get; set; } + public IEnumerable SortBy { get; set; } + public string SortDirection { get; set; } + public BrowseCommentsRequest(int pageNumber, int pageSize, Guid contextId, + CommentContext context, Guid parentId, IEnumerable sortBy, string sortDirection) + { + PageNumber = pageNumber; + PageSize = pageSize; + ContextId = contextId; + CommentContext = context; + ParentId = parentId; + SortBy = sortBy; + SortDirection = sortDirection; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs index 8dd369a50..71d091dd5 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs @@ -1,31 +1,35 @@ -using System.Diagnostics.CodeAnalysis; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; -namespace MiniSpace.Services.Comments.Application.Wrappers +namespace MiniSpace.Services.Comments.Core.Wrappers { [ExcludeFromCodeCoverage] - public class PagedResponse : Response + public class PagedResponse { + public IEnumerable Items { get; } public int TotalPages { get; } - public int TotalElements { get; } - public int Size { get; } - public int Number { get; } + public int TotalItems { get; } + public int PageSize { get; } + public int Page { get; } public bool First { get; } public bool Last { get; } public bool Empty { get; } + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; - public PagedResponse(T content, int pageNumber, int pageSize, int totalPages, int totalElements) + public PagedResponse(IEnumerable items, int page, int pageSize, int totalItems) { - Content = content; - TotalPages = totalPages; - TotalElements = totalElements; - Size = pageSize; - Number = pageNumber; - First = pageNumber == 0; - Last = pageNumber == totalPages - 1; - Empty = totalElements == 0; - Succeeded = true; - Errors = null; - Message = null; + Items = items; + PageSize = pageSize; + TotalItems = totalItems; + TotalPages = pageSize > 0 ? (int)Math.Ceiling((decimal)totalItems / pageSize) : 0; + Page = page; + First = page == 1; + Last = page == TotalPages; + Empty = !items.Any(); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs index f8d1e2ec3..1d997e6d8 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs @@ -47,8 +47,12 @@ public static class Extensions { public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) { - builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddTransient(); @@ -73,8 +77,11 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) .AddMetrics() .AddJaeger() .AddHandlersLogging() - .AddMongoRepository("students") .AddMongoRepository("comments") + .AddMongoRepository("organization_events_comments") + .AddMongoRepository("organization_posts_comments") + .AddMongoRepository("user_events_comments") + .AddMongoRepository("user_posts_comments") .AddWebApiSwaggerDocs() .AddCertificateAuthentication() .AddSecurity(); @@ -92,9 +99,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .UseRabbitMq() .SubscribeCommand() .SubscribeCommand() - .SubscribeCommand() - .SubscribeEvent() - .SubscribeEvent(); + .SubscribeCommand(); return app; } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs index ca36c3c5e..293a4ffa4 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -30,18 +30,6 @@ private static IReadOnlyDictionary MessageTemplates After = "Deleted the comment with id: {CommentId}." } }, - { - typeof(StudentCreated), new HandlerLogTemplate - { - After = "Created a new student with id: {StudentId}." - } - }, - { - typeof(StudentDeleted), new HandlerLogTemplate - { - After = "Deleted a student with id: {StudentId}." - } - }, { typeof(AddLike), new HandlerLogTemplate { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.sln b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.sln new file mode 100644 index 000000000..ce73f2f23 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Infrastructure", "MiniSpace.Services.Comments.Infrastructure.csproj", "{8CB92C1A-A5B1-44E7-BE57-2F4BF090A7F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CB92C1A-A5B1-44E7-BE57-2F4BF090A7F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CB92C1A-A5B1-44E7-BE57-2F4BF090A7F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CB92C1A-A5B1-44E7-BE57-2F4BF090A7F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CB92C1A-A5B1-44E7-BE57-2F4BF090A7F8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {18D232A8-ED7A-49FA-80BE-3DB893733A06} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs index f203397b6..40f274ea6 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs @@ -1,5 +1,7 @@ using Convey.Types; using MiniSpace.Services.Comments.Core.Entities; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents @@ -7,18 +9,17 @@ namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents [ExcludeFromCodeCoverage] public class CommentDocument : IIdentifiable { - public Guid Id { get; set; } - public Guid ContextId { get; set; } + public Guid Id { get; set; } + public Guid ContextId { get; set; } public CommentContext CommentContext { get; set; } - public Guid StudentId { get; set; } - public string StudentName { get; set; } - public IEnumerable Likes { get; set; } - public Guid ParentId { get; set; } - public string TextContent { get; set; } - public DateTime CreatedAt { get; set; } + public Guid UserId { get; set; } + public IEnumerable Likes { get; set; } = new List(); + public Guid ParentId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } public DateTime LastUpdatedAt { get; set; } - public DateTime LastReplyAt { get; set; } - public int RepliesCount { get; set; } + public DateTime LastReplyAt { get; set; } + public IEnumerable Replies { get; set; } = new List(); public bool IsDeleted { get; set; } - } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/Extensions.cs index 93a578ec3..7f87c8a54 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/Extensions.cs @@ -1,5 +1,9 @@ using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System; +using System.Linq; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents @@ -8,26 +12,36 @@ namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents public static class Extensions { public static Comment AsEntity(this CommentDocument document) - => new Comment(document.Id,document.ContextId,document.CommentContext, document.StudentId, - document.StudentName, document.Likes, document.ParentId, document.TextContent, document.CreatedAt, - document.LastUpdatedAt, document.LastReplyAt, document.RepliesCount, document.IsDeleted); + => new Comment( + document.Id, + document.ContextId, + document.CommentContext, + document.UserId, + document.Likes, + document.ParentId, + document.TextContent, + document.CreatedAt, + document.LastUpdatedAt, + document.LastReplyAt, + document.Replies.Select(r => r.AsEntity()), + document.IsDeleted + ); - public static CommentDocument AsDocument(this Comment entity) + public static CommentDocument ToDocument(this Comment entity) => new CommentDocument() { Id = entity.Id, ContextId = entity.ContextId, CommentContext = entity.CommentContext, - StudentId = entity.StudentId, - StudentName = entity.StudentName, + UserId = entity.UserId, Likes = entity.Likes, ParentId = entity.ParentId, TextContent = entity.TextContent, CreatedAt = entity.CreatedAt, LastUpdatedAt = entity.LastUpdatedAt, LastReplyAt = entity.LastReplyAt, - RepliesCount = entity.RepliesCount, - IsDeleted = entity.IsDeleted, + Replies = entity.Replies.Select(r => r.ToDocument()).ToList(), + IsDeleted = entity.IsDeleted }; public static CommentDto AsDto(this CommentDocument document) @@ -36,25 +50,70 @@ public static CommentDto AsDto(this CommentDocument document) Id = document.Id, ContextId = document.ContextId, CommentContext = document.CommentContext.ToString().ToLowerInvariant(), - StudentId = document.StudentId, - StudentName = document.StudentName, + UserId = document.UserId, Likes = document.Likes, ParentId = document.ParentId, TextContent = document.TextContent, CreatedAt = document.CreatedAt, LastUpdatedAt = document.LastUpdatedAt, LastReplyAt = document.LastReplyAt, - RepliesCount = document.RepliesCount, - IsDeleted= document.IsDeleted, + RepliesCount = document.Replies.Count(), + IsDeleted = document.IsDeleted }; - - public static Student AsEntity(this StudentDocument document) - => new Student(document.Id); - public static StudentDocument AsDocument(this Student entity) - => new StudentDocument + public static Reply AsEntity(this ReplyDocument document) + => new Reply( + document.Id, + document.UserId, + document.CommentId, + document.TextContent, + document.CreatedAt + ); + + public static ReplyDocument ToDocument(this Reply entity) + => new ReplyDocument { Id = entity.Id, + UserId = entity.UserId, + CommentId = entity.CommentId, + TextContent = entity.TextContent, + CreatedAt = entity.CreatedAt + }; + + public static OrganizationEventCommentDocument ToOrganizationEventDocument(this IEnumerable comments, Guid organizationEventId, Guid organizationId) + => new OrganizationEventCommentDocument + { + Id = Guid.NewGuid(), + OrganizationEventId = organizationEventId, + OrganizationId = organizationId, + Comments = comments.Select(c => c.ToDocument()).ToList() + }; + + public static OrganizationPostCommentDocument ToOrganizationPostDocument(this IEnumerable comments, Guid organizationPostId, Guid organizationId) + => new OrganizationPostCommentDocument + { + Id = Guid.NewGuid(), + OrganizationPostId = organizationPostId, + OrganizationId = organizationId, + Comments = comments.Select(c => c.ToDocument()).ToList() + }; + + public static UserEventCommentDocument ToUserEventDocument(this IEnumerable comments, Guid userEventId, Guid userId) + => new UserEventCommentDocument + { + Id = Guid.NewGuid(), + UserEventId = userEventId, + UserId = userId, + Comments = comments.Select(c => c.ToDocument()).ToList() + }; + + public static UserPostCommentDocument ToUserPostDocument(this IEnumerable comments, Guid userPostId, Guid userId) + => new UserPostCommentDocument + { + Id = Guid.NewGuid(), + UserPostId = userPostId, + UserId = userId, + Comments = comments.Select(c => c.ToDocument()).ToList() }; - } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs new file mode 100644 index 000000000..6b2a280f0 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs @@ -0,0 +1,16 @@ +using Convey.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class OrganizationEventCommentDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationEventId { get; set; } + public Guid OrganizationId { get; set; } + public IEnumerable Comments { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentExtensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentExtensions.cs new file mode 100644 index 000000000..d61d75f0b --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentExtensions.cs @@ -0,0 +1,41 @@ +using MongoDB.Driver; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + public static class OrganizationEventCommentExtensions + { + public static async Task<(IEnumerable data, int totalElements, int totalPages)> AggregateByPage( + this IMongoCollection collection, + FilterDefinition filter, + SortDefinition sort, + int pageNumber, + int pageSize) + { + var totalElements = await collection.CountDocumentsAsync(filter); + var totalPages = (int)Math.Ceiling((double)totalElements / pageSize); + + var data = await collection.Find(filter) + .Sort(sort) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToListAsync(); + + return (data, (int)totalElements, totalPages); + } + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string sortDirection) + { + var builder = Builders.Sort; + var sort = builder.Combine(sortBy.Select(sortField => + sortDirection.Equals("asc", StringComparison.OrdinalIgnoreCase) + ? builder.Ascending(sortField) + : builder.Descending(sortField))); + return sort; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs new file mode 100644 index 000000000..0709cede2 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs @@ -0,0 +1,16 @@ +using Convey.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class OrganizationPostCommentDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationPostId { get; set; } + public Guid OrganizationId { get; set; } + public IEnumerable Comments { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentExtensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentExtensions.cs new file mode 100644 index 000000000..cc505bdb4 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentExtensions.cs @@ -0,0 +1,41 @@ +using MongoDB.Driver; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + public static class OrganizationPostCommentExtensions + { + public static async Task<(IEnumerable data, int totalElements, int totalPages)> AggregateByPage( + this IMongoCollection collection, + FilterDefinition filter, + SortDefinition sort, + int pageNumber, + int pageSize) + { + var totalElements = await collection.CountDocumentsAsync(filter); + var totalPages = (int)Math.Ceiling((double)totalElements / pageSize); + + var data = await collection.Find(filter) + .Sort(sort) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToListAsync(); + + return (data, (int)totalElements, totalPages); + } + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string sortDirection) + { + var builder = Builders.Sort; + var sort = builder.Combine(sortBy.Select(sortField => + sortDirection.Equals("asc", StringComparison.OrdinalIgnoreCase) + ? builder.Ascending(sortField) + : builder.Descending(sortField))); + return sort; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs new file mode 100644 index 000000000..885d77fe6 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs @@ -0,0 +1,18 @@ +using Convey.Types; +using MiniSpace.Services.Comments.Core.Entities; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class ReplyDocument + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid CommentId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/StudentDocument.cs deleted file mode 100644 index 65394473d..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/StudentDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Convey.Types; -using System.Diagnostics.CodeAnalysis; - -namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public class StudentDocument : IIdentifiable - { - public Guid Id { get; set; } - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs new file mode 100644 index 000000000..0f650ae5c --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs @@ -0,0 +1,16 @@ +using Convey.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class UserEventCommentDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserEventId { get; set; } + public Guid UserId { get; set; } + public IEnumerable Comments { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentExtensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentExtensions.cs new file mode 100644 index 000000000..cdb9f0574 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentExtensions.cs @@ -0,0 +1,41 @@ +using MongoDB.Driver; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + public static class UserEventCommentExtensions + { + public static async Task<(IEnumerable data, int totalElements, int totalPages)> AggregateByPage( + this IMongoCollection collection, + FilterDefinition filter, + SortDefinition sort, + int pageNumber, + int pageSize) + { + var totalElements = await collection.CountDocumentsAsync(filter); + var totalPages = (int)Math.Ceiling((double)totalElements / pageSize); + + var data = await collection.Find(filter) + .Sort(sort) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToListAsync(); + + return (data, (int)totalElements, totalPages); + } + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string sortDirection) + { + var builder = Builders.Sort; + var sort = builder.Combine(sortBy.Select(sortField => + sortDirection.Equals("asc", StringComparison.OrdinalIgnoreCase) + ? builder.Ascending(sortField) + : builder.Descending(sortField))); + return sort; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs new file mode 100644 index 000000000..710e7b990 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs @@ -0,0 +1,16 @@ +using Convey.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class UserPostCommentDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserPostId { get; set; } + public Guid UserId { get; set; } + public IEnumerable Comments { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentExtensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentExtensions.cs new file mode 100644 index 000000000..7d10fd1a6 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentExtensions.cs @@ -0,0 +1,41 @@ +using MongoDB.Driver; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents +{ + public static class UserPostCommentExtensions + { + public static async Task<(IEnumerable data, int totalElements, int totalPages)> AggregateByPage( + this IMongoCollection collection, + FilterDefinition filter, + SortDefinition sort, + int pageNumber, + int pageSize) + { + var totalElements = await collection.CountDocumentsAsync(filter); + var totalPages = (int)Math.Ceiling((double)totalElements / pageSize); + + var data = await collection.Find(filter) + .Sort(sort) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToListAsync(); + + return (data, (int)totalElements, totalPages); + } + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string sortDirection) + { + var builder = Builders.Sort; + var sort = builder.Combine(sortBy.Select(sortField => + sortDirection.Equals("asc", StringComparison.OrdinalIgnoreCase) + ? builder.Ascending(sortField) + : builder.Descending(sortField))); + return sort; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs index 158baec2d..c91c936ad 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs @@ -1,11 +1,12 @@ using Convey.Persistence.MongoDB; -using Microsoft.Extensions.Hosting; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; using MongoDB.Driver; using MongoDB.Driver.Linq; using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Wrappers; namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories { @@ -18,62 +19,55 @@ public CommentMongoRepository(IMongoRepository repository { _repository = repository; } - + public async Task GetAsync(Guid id) { var comment = await _repository.GetAsync(p => p.Id == id); - return comment?.AsEntity(); } - + public Task AddAsync(Comment comment) - => _repository.AddAsync(comment.AsDocument()); + => _repository.AddAsync(comment.ToDocument()); public Task UpdateAsync(Comment comment) - => _repository.UpdateAsync(comment.AsDocument()); + => _repository.UpdateAsync(comment.ToDocument()); public Task DeleteAsync(Guid id) => _repository.DeleteAsync(id); public async Task> GetByEventIdAsync(Guid eventId) { - var comments = _repository.Collection.AsQueryable(); - var commentsByEventId = await comments.Where(e =>e.CommentContext == CommentContext.Event && e.ContextId == eventId).ToListAsync(); - return commentsByEventId.Select(e => e.AsEntity()); + var comments = await _repository.Collection + .AsQueryable() + .Where(e => e.CommentContext == CommentContext.OrganizationEvent && e.ContextId == eventId) + .ToListAsync(); + return comments.Select(e => e.AsEntity()); } public async Task> GetByPostIdAsync(Guid postId) { - var comments = _repository.Collection.AsQueryable(); - var commentsByEventId = await comments.Where(e => e.CommentContext == CommentContext.Post && e.ContextId == postId).ToListAsync(); - return commentsByEventId.Select(e => e.AsEntity()); + var comments = await _repository.Collection + .AsQueryable() + .Where(e => e.CommentContext == CommentContext.OrganizationPost && e.ContextId == postId) + .ToListAsync(); + return comments.Select(e => e.AsEntity()); } - - private async Task<(int totalPages, int totalElements, IEnumerable data)> BrowseAsync( - FilterDefinition filterDefinition, SortDefinition sortDefinition, - int pageNumber, int pageSize) + + public async Task> BrowseCommentsAsync(BrowseCommentsRequest request) { + var filterDefinition = request.ParentId == Guid.Empty + ? Extensions.ToFilterDefinition(request.ContextId, request.CommentContext).AddParentFilter() + : Extensions.ToFilterDefinition(request.ContextId, request.CommentContext).AddChildrenFilter(request.ParentId); + var sortDefinition = Extensions.ToSortDefinition(request.SortBy, request.SortDirection); + var pagedEvents = await _repository.Collection.AggregateByPage( filterDefinition, sortDefinition, - pageNumber, - pageSize); + request.PageNumber, + request.PageSize); - return pagedEvents; - } - - public async Task<(IEnumerable comments, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseCommentsAsync(int pageNumber, int pageSize, - Guid contextId, CommentContext context, Guid parentId, IEnumerable sortBy, string direction) - { - var filterDefinition = parentId == Guid.Empty - ? Extensions.ToFilterDefinition(contextId, context).AddParentFilter() - : Extensions.ToFilterDefinition(contextId, context).AddChildrenFilter(parentId); - var sortDefinition = Extensions.ToSortDefinition(sortBy, direction); - - var pagedEvents = await BrowseAsync(filterDefinition, sortDefinition, pageNumber, pageSize); - - return (pagedEvents.data.Select(e => e.AsEntity()), pageNumber, pageSize, - pagedEvents.totalPages, pagedEvents.totalElements); + var comments = pagedEvents.data.Select(e => e.AsEntity()); + return new PagedResponse(comments, request.PageNumber, request.PageSize, pagedEvents.totalElements); } - } + } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs new file mode 100644 index 000000000..1d595f634 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs @@ -0,0 +1,83 @@ +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Core.Wrappers; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories +{ + public class OrganizationEventsCommentRepository : IOrganizationEventsCommentRepository + { + private readonly IMongoRepository _repository; + + public OrganizationEventsCommentRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var doc = await _repository.GetAsync(d => d.Comments.Any(c => c.Id == id)); + return doc?.Comments.FirstOrDefault(c => c.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Comment comment) + { + var filter = Builders.Filter.Eq(d => d.OrganizationEventId, comment.ContextId); + var update = Builders.Update.Push(d => d.Comments, comment.ToDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + } + + public async Task UpdateAsync(Comment comment) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.OrganizationEventId, comment.ContextId), + Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == comment.Id) + ); + + var update = Builders.Update + .Set($"{nameof(OrganizationEventCommentDocument.Comments)}.$.{nameof(CommentDocument.TextContent)}", comment.TextContent) + .Set($"{nameof(OrganizationEventCommentDocument.Comments)}.$.{nameof(CommentDocument.LastUpdatedAt)}", comment.LastUpdatedAt) + .Set($"{nameof(OrganizationEventCommentDocument.Comments)}.$.{nameof(CommentDocument.IsDeleted)}", comment.IsDeleted); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == id); + var update = Builders.Update.PullFilter(d => d.Comments, c => c.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var doc = await _repository.GetAsync(d => d.OrganizationEventId == eventId); + return doc?.Comments.Select(c => c.AsEntity()) ?? Enumerable.Empty(); + } + + public async Task> BrowseCommentsAsync(BrowseCommentsRequest request) + { + var filterDefinition = Builders.Filter.Eq(d => d.OrganizationEventId, request.ContextId); + var sortDefinition = OrganizationEventCommentExtensions.ToSortDefinition(request.SortBy, request.SortDirection); + + var pagedEvents = await _repository.Collection.AggregateByPage( + filterDefinition, + sortDefinition, + request.PageNumber, + request.PageSize + ); + + var comments = pagedEvents.data.SelectMany(d => d.Comments.Select(c => c.AsEntity())); + return new PagedResponse(comments, request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs new file mode 100644 index 000000000..13988ceed --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs @@ -0,0 +1,83 @@ +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Core.Wrappers; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories +{ + public class OrganizationPostsCommentRepository : IOrganizationPostsCommentRepository + { + private readonly IMongoRepository _repository; + + public OrganizationPostsCommentRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var doc = await _repository.GetAsync(d => d.Comments.Any(c => c.Id == id)); + return doc?.Comments.FirstOrDefault(c => c.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Comment comment) + { + var filter = Builders.Filter.Eq(d => d.OrganizationPostId, comment.ContextId); + var update = Builders.Update.Push(d => d.Comments, comment.ToDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + } + + public async Task UpdateAsync(Comment comment) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.OrganizationPostId, comment.ContextId), + Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == comment.Id) + ); + + var update = Builders.Update + .Set($"{nameof(OrganizationPostCommentDocument.Comments)}.$.{nameof(CommentDocument.TextContent)}", comment.TextContent) + .Set($"{nameof(OrganizationPostCommentDocument.Comments)}.$.{nameof(CommentDocument.LastUpdatedAt)}", comment.LastUpdatedAt) + .Set($"{nameof(OrganizationPostCommentDocument.Comments)}.$.{nameof(CommentDocument.IsDeleted)}", comment.IsDeleted); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == id); + var update = Builders.Update.PullFilter(d => d.Comments, c => c.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task> GetByPostIdAsync(Guid postId) + { + var doc = await _repository.GetAsync(d => d.OrganizationPostId == postId); + return doc?.Comments.Select(c => c.AsEntity()) ?? Enumerable.Empty(); + } + + public async Task> BrowseCommentsAsync(BrowseCommentsRequest request) + { + var filterDefinition = Builders.Filter.Eq(d => d.OrganizationPostId, request.ContextId); + var sortDefinition = OrganizationPostCommentExtensions.ToSortDefinition(request.SortBy, request.SortDirection); + + var pagedEvents = await _repository.Collection.AggregateByPage( + filterDefinition, + sortDefinition, + request.PageNumber, + request.PageSize + ); + + var comments = pagedEvents.data.SelectMany(d => d.Comments.Select(c => c.AsEntity())); + return new PagedResponse(comments, request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs deleted file mode 100644 index d35bc4519..000000000 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Comments.Core.Entities; -using MiniSpace.Services.Comments.Core.Repositories; -using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; -using System.Diagnostics.CodeAnalysis; - -namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories -{ - [ExcludeFromCodeCoverage] - public class StudentMongoRepository : IStudentRepository - { - private readonly IMongoRepository _repository; - - public StudentMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(Guid id) - { - var student = await _repository.GetAsync(s => s.Id == id); - - return student?.AsEntity(); - } - - public Task ExistsAsync(Guid id) - => _repository.ExistsAsync(s => s.Id == id); - - public Task AddAsync(Student student) - => _repository.AddAsync(student.AsDocument()); - - public Task DeleteAsync(Guid id) - => _repository.DeleteAsync(id); - } -} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs new file mode 100644 index 000000000..c82166a86 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs @@ -0,0 +1,83 @@ +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Core.Wrappers; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories +{ + public class UserEventsCommentRepository : IUserEventsCommentRepository + { + private readonly IMongoRepository _repository; + + public UserEventsCommentRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var doc = await _repository.GetAsync(d => d.Comments.Any(c => c.Id == id)); + return doc?.Comments.FirstOrDefault(c => c.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Comment comment) + { + var filter = Builders.Filter.Eq(d => d.UserEventId, comment.ContextId); + var update = Builders.Update.Push(d => d.Comments, comment.ToDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + } + + public async Task UpdateAsync(Comment comment) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.UserEventId, comment.ContextId), + Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == comment.Id) + ); + + var update = Builders.Update + .Set($"{nameof(UserEventCommentDocument.Comments)}.$.{nameof(CommentDocument.TextContent)}", comment.TextContent) + .Set($"{nameof(UserEventCommentDocument.Comments)}.$.{nameof(CommentDocument.LastUpdatedAt)}", comment.LastUpdatedAt) + .Set($"{nameof(UserEventCommentDocument.Comments)}.$.{nameof(CommentDocument.IsDeleted)}", comment.IsDeleted); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == id); + var update = Builders.Update.PullFilter(d => d.Comments, c => c.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var doc = await _repository.GetAsync(d => d.UserEventId == eventId); + return doc?.Comments.Select(c => c.AsEntity()) ?? Enumerable.Empty(); + } + + public async Task> BrowseCommentsAsync(BrowseCommentsRequest request) + { + var filterDefinition = Builders.Filter.Eq(d => d.UserEventId, request.ContextId); + var sortDefinition = UserEventCommentExtensions.ToSortDefinition(request.SortBy, request.SortDirection); + + var pagedEvents = await _repository.Collection.AggregateByPage( + filterDefinition, + sortDefinition, + request.PageNumber, + request.PageSize + ); + + var comments = pagedEvents.data.SelectMany(d => d.Comments.Select(c => c.AsEntity())); + return new PagedResponse(comments, request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs new file mode 100644 index 000000000..7084b0335 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs @@ -0,0 +1,83 @@ +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Entities; +using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Core.Wrappers; +using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories +{ + public class UserPostsCommentRepository : IUserPostsCommentRepository + { + private readonly IMongoRepository _repository; + + public UserPostsCommentRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var doc = await _repository.GetAsync(d => d.Comments.Any(c => c.Id == id)); + return doc?.Comments.FirstOrDefault(c => c.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Comment comment) + { + var filter = Builders.Filter.Eq(d => d.UserPostId, comment.ContextId); + var update = Builders.Update.Push(d => d.Comments, comment.ToDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + } + + public async Task UpdateAsync(Comment comment) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.UserPostId, comment.ContextId), + Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == comment.Id) + ); + + var update = Builders.Update + .Set($"{nameof(UserPostCommentDocument.Comments)}.$.{nameof(CommentDocument.TextContent)}", comment.TextContent) + .Set($"{nameof(UserPostCommentDocument.Comments)}.$.{nameof(CommentDocument.LastUpdatedAt)}", comment.LastUpdatedAt) + .Set($"{nameof(UserPostCommentDocument.Comments)}.$.{nameof(CommentDocument.IsDeleted)}", comment.IsDeleted); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Comments, c => c.Id == id); + var update = Builders.Update.PullFilter(d => d.Comments, c => c.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task> GetByPostIdAsync(Guid postId) + { + var doc = await _repository.GetAsync(d => d.UserPostId == postId); + return doc?.Comments.Select(c => c.AsEntity()) ?? Enumerable.Empty(); + } + + public async Task> BrowseCommentsAsync(BrowseCommentsRequest request) + { + var filterDefinition = Builders.Filter.Eq(d => d.UserPostId, request.ContextId); + var sortDefinition = UserPostCommentExtensions.ToSortDefinition(request.SortBy, request.SortDirection); + + var pagedEvents = await _repository.Collection.AggregateByPage( + filterDefinition, + sortDefinition, + request.PageNumber, + request.PageSize + ); + + var comments = pagedEvents.data.SelectMany(d => d.Comments.Select(c => c.AsEntity())); + return new PagedResponse(comments, request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/CommentService.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/CommentService.cs index b25d2e07f..557809844 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/CommentService.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/CommentService.cs @@ -3,10 +3,11 @@ using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; -using MiniSpace.Services.Comments.Application.Wrappers; +using MiniSpace.Services.Comments.Core.Wrappers; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; -using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; +using System.Linq; +using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Services @@ -21,7 +22,7 @@ public CommentService(ICommentRepository commentRepository) _commentRepository = commentRepository; } - public async Task>> BrowseCommentsAsync(SearchComments command) + public async Task> BrowseCommentsAsync(SearchComments command) { if (!Enum.TryParse(command.CommentContext, true, out var context)) { @@ -31,15 +32,27 @@ public async Task>> BrowseCommentsAsync(Se var pageNumber = command.Pageable.Page < 1 ? 1 : command.Pageable.Page; var pageSize = command.Pageable.Size > 10 ? 10 : command.Pageable.Size; - var result = await _commentRepository.BrowseCommentsAsync( - pageNumber, pageSize, command.ContextId, context, command.ParentId, - command.Pageable.Sort.SortBy, command.Pageable.Sort.Direction); + var request = new BrowseCommentsRequest( + pageNumber, + pageSize, + command.ContextId, + context, + command.ParentId, + command.Pageable.Sort.SortBy, + command.Pageable.Sort.Direction + ); - var pagedEvents = new PagedResponse>( - result.comments.Select(c => new CommentDto(c)), - result.pageNumber, result.pageSize, result.totalPages, result.totalElements); + var result = await _commentRepository.BrowseCommentsAsync(request); - return pagedEvents; + var commentDtos = result.Items.Select(c => new CommentDto(c)); + var pagedResponse = new PagedResponse( + commentDtos, + result.Page, + result.PageSize, + result.TotalItems + ); + + return pagedResponse; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs index e8a82c7fb..3c5a3d788 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs @@ -1,6 +1,7 @@ using Convey.CQRS.Events; using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Core; +using MiniSpace.Services.Comments.Core.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Services diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs index 18b396ebb..86510e4a9 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs @@ -36,157 +36,158 @@ public CreateEventHandler(IEventRepository eventRepository, IMessageBroker messa } public async Task HandleAsync(CreateEvent command, CancellationToken cancellationToken) -{ - try - { - Console.WriteLine("--------------------------------------------"); - var options = new JsonSerializerOptions - { - WriteIndented = true - }; - var commandJson = JsonSerializer.Serialize(command, options); - Console.WriteLine("Received CreateEvent command: "); - Console.WriteLine(commandJson); - - var identity = _appContext.Identity; - - if (command.EventId == Guid.Empty || await _eventRepository.ExistsAsync(command.EventId)) { - throw new InvalidEventIdException(command.EventId); - } - - if (!Enum.TryParse(command.OrganizerType, true, out var organizerType)) - { - throw new ArgumentException($"Invalid OrganizerType value: {command.OrganizerType}"); - } - - if (!Enum.TryParse(command.Visibility, true, out var visibility)) - { - throw new ArgumentException($"Invalid Visibility value: {command.Visibility}"); - } - - PaymentMethod? paymentMethod = null; - if (command.Settings != null) - { - if (!Enum.TryParse(command.Settings.PaymentMethod, true, out var parsedPaymentMethod)) + try { - throw new ArgumentException($"Invalid PaymentMethod value: {command.Settings.PaymentMethod}"); + var options = new JsonSerializerOptions { WriteIndented = true }; + var commandJson = JsonSerializer.Serialize(command, options); + Console.WriteLine("Received CreateEvent command: "); + Console.WriteLine(commandJson); + + var identity = _appContext.Identity; + + // Validate Event ID + if (command.EventId == Guid.Empty || await _eventRepository.ExistsAsync(command.EventId)) + { + throw new InvalidEventIdException(command.EventId); + } + + // Validate Organizer Type + if (!Enum.TryParse(command.OrganizerType, true, out var organizerType)) + { + throw new ArgumentException($"Invalid OrganizerType value: {command.OrganizerType}"); + } + + // Validate Visibility + if (!Enum.TryParse(command.Visibility, true, out var visibility)) + { + throw new ArgumentException($"Invalid Visibility value: {command.Visibility}"); + } + + PaymentMethod? paymentMethod = null; + if (command.Settings != null && command.Settings.RequiresPayment && !string.IsNullOrWhiteSpace(command.Settings.PaymentMethod)) + { + if (!Enum.TryParse(command.Settings.PaymentMethod, true, out var parsedPaymentMethod)) + { + throw new ArgumentException($"Invalid PaymentMethod value: {command.Settings.PaymentMethod}"); + } + paymentMethod = parsedPaymentMethod; + } + + _eventValidator.ValidateName(command.Name); + _eventValidator.ValidateDescription(command.Description); + var startDate = _eventValidator.ParseDate(command.StartDate, "event_start_date"); + var endDate = _eventValidator.ParseDate(command.EndDate, "event_end_date"); + var now = _dateTimeProvider.Now; + _eventValidator.ValidateDates(now, startDate, "now", "event_start_date"); + _eventValidator.ValidateDates(startDate, endDate, "event_start_date", "event_end_date"); + + // Create Address object + var address = new Address(command.BuildingName, command.Street, command.BuildingNumber, command.ApartmentNumber, command.City, command.ZipCode, command.Country); + + // Validate Capacity and Fee + _eventValidator.ValidateCapacity(command.Capacity); + _eventValidator.ValidateFee(command.Fee); + + // Parse and Validate Category + var category = _eventValidator.ParseCategory(command.Category); + + // Determine Publish Date and State + var publishDate = now; + var state = State.Published; + if (!string.IsNullOrEmpty(command.PublishDate)) + { + publishDate = _eventValidator.ParseDate(command.PublishDate, "event_publish_date"); + _eventValidator.ValidateDates(now, publishDate, "now", "event_publish_date"); + _eventValidator.ValidateDates(publishDate, startDate, "event_publish_date", "event_start_date"); + state = State.ToBePublished; + } + + // Determine Organizer + Organizer organizer; + if (organizerType == OrganizerType.Organization) + { + if (command.OrganizationId == null) + { + throw new ArgumentNullException(nameof(command.OrganizationId), "OrganizationId cannot be null for Organization-type events."); + } + + var organization = await _organizationsServiceClient.GetAsync(command.OrganizationId.Value); + if (organization == null) + { + throw new OrganizationNotFoundException(command.OrganizationId.Value); + } + + // Store both organization ID and the user ID creating the event + organizer = new Organizer(command.OrganizationId.Value, OrganizerType.Organization, userId: command.OrganizerId, organizationId: command.OrganizationId.Value); + } + else + { + organizer = new Organizer(command.OrganizerId, OrganizerType.User, userId: command.OrganizerId); + } + + var settings = command.Settings != null + ? new EventSettings + { + RequiresApproval = command.Settings.RequiresApproval, + IsOnlineEvent = command.Settings.IsOnlineEvent, + IsPrivate = command.Settings.IsPrivate, + RequiresRSVP = command.Settings.RequiresRSVP, + AllowsGuests = command.Settings.AllowsGuests, + ShowAttendeesPublicly = command.Settings.ShowAttendeesPublicly, + SendReminders = command.Settings.SendReminders, + ReminderDaysBefore = command.Settings.ReminderDaysBefore, + EnableChat = command.Settings.EnableChat, + AllowComments = command.Settings.AllowComments, + RequiresPayment = command.Settings.RequiresPayment, + PaymentMethod = paymentMethod ?? PaymentMethod.Offline, + PaymentReceiverDetails = command.Settings.PaymentReceiverDetails, + PaymentGateway = command.Settings.PaymentGateway, + IssueTickets = command.Settings.IssueTickets, + MaxTicketsPerPerson = command.Settings.MaxTicketsPerPerson, + TicketPrice = command.Settings.TicketPrice, + RecordEvent = command.Settings.RecordEvent, + CustomTermsAndConditions = command.Settings.CustomTermsAndConditions, + CustomFields = command.Settings.CustomFields + } + : null; + + var @event = Event.Create( + command.EventId, + command.Name, + command.Description, + organizer, + startDate, + endDate, + address, + command.MediaFilesUrl.ToList(), + command.BannerUrl, + command.Capacity, + command.Fee, + category, + state, + publishDate, + now, + visibility, + settings); + + await _eventRepository.AddAsync(@event); + await _messageBroker.PublishAsync(new EventCreated( + @event.Id, + @event.Organizer.OrganizerType, + @event.Organizer.Id, + @event.MediaFiles)); } - paymentMethod = parsedPaymentMethod; - } - - _eventValidator.ValidateName(command.Name); - _eventValidator.ValidateDescription(command.Description); - var startDate = _eventValidator.ParseDate(command.StartDate, "event_start_date"); - var endDate = _eventValidator.ParseDate(command.EndDate, "event_end_date"); - var now = _dateTimeProvider.Now; - _eventValidator.ValidateDates(now, startDate, "now", "event_start_date"); - _eventValidator.ValidateDates(startDate, endDate, "event_start_date", "event_end_date"); - var address = new Address(command.BuildingName, command.Street, command.BuildingNumber, - command.ApartmentNumber, command.City, command.ZipCode, command.Country); - _eventValidator.ValidateCapacity(command.Capacity); - _eventValidator.ValidateFee(command.Fee); - var category = _eventValidator.ParseCategory(command.Category); - - var publishDate = now; - var state = State.Published; - if (!string.IsNullOrEmpty(command.PublishDate)) - { - publishDate = _eventValidator.ParseDate(command.PublishDate, "event_publish_date"); - _eventValidator.ValidateDates(now, publishDate, "now", "event_publish_date"); - _eventValidator.ValidateDates(publishDate, startDate, "event_publish_date", "event_start_date"); - state = State.ToBePublished; - } - - Organizer organizer; - if (organizerType == OrganizerType.Organization) - { - if (command.OrganizationId == null) + catch (ArgumentException argEx) { - throw new ArgumentNullException(nameof(command.OrganizationId), "OrganizationId cannot be null for Organization-type events."); + Console.WriteLine($"Validation error: {argEx.Message}"); + throw; } - - var organization = await _organizationsServiceClient.GetAsync(command.OrganizationId.Value); - if (organization == null) - { - throw new OrganizationNotFoundException(command.OrganizationId.Value); - } - - if (!organization.Organizers.Contains(command.OrganizerId)) + catch (Exception ex) { - throw new OrganizerDoesNotBelongToOrganizationException(command.OrganizerId, command.OrganizationId.Value); + Console.WriteLine($"Unhandled exception: {ex.Message}"); + throw; } - - organizer = new Organizer(command.OrganizationId.Value, OrganizerType.Organization, organizationId: command.OrganizationId.Value); - } - else - { - organizer = new Organizer(command.OrganizerId, OrganizerType.User, userId: command.OrganizerId); } - - var settings = command.Settings != null - ? new EventSettings - { - RequiresApproval = command.Settings.RequiresApproval, - IsOnlineEvent = command.Settings.IsOnlineEvent, - IsPrivate = command.Settings.IsPrivate, - RequiresRSVP = command.Settings.RequiresRSVP, - AllowsGuests = command.Settings.AllowsGuests, - ShowAttendeesPublicly = command.Settings.ShowAttendeesPublicly, - SendReminders = command.Settings.SendReminders, - ReminderDaysBefore = command.Settings.ReminderDaysBefore, - EnableChat = command.Settings.EnableChat, - AllowComments = command.Settings.AllowComments, - RequiresPayment = command.Settings.RequiresPayment, - PaymentMethod = paymentMethod ?? PaymentMethod.Offline, // Use the parsed PaymentMethod - PaymentReceiverDetails = command.Settings.PaymentReceiverDetails, - PaymentGateway = command.Settings.PaymentGateway, - IssueTickets = command.Settings.IssueTickets, - MaxTicketsPerPerson = command.Settings.MaxTicketsPerPerson, - TicketPrice = command.Settings.TicketPrice, - RecordEvent = command.Settings.RecordEvent, - CustomTermsAndConditions = command.Settings.CustomTermsAndConditions, - CustomFields = command.Settings.CustomFields - } - : new EventSettings(); // or set to null if EventSettings can be optional - - var @event = Event.Create( - command.EventId, - command.Name, - command.Description, - organizer, - startDate, - endDate, - address, - command.MediaFilesUrl.ToList(), - command.BannerUrl, - command.Capacity, - command.Fee, - category, - state, - publishDate, - now, - visibility, - settings); - - await _eventRepository.AddAsync(@event); - await _messageBroker.PublishAsync(new EventCreated( - @event.Id, - @event.Organizer.OrganizerType, - @event.Organizer.Id, - @event.MediaFiles)); } - catch (ArgumentException argEx) - { - Console.WriteLine($"Validation error: {argEx.Message}"); - throw; // Re-throw to be handled by middleware - } - catch (Exception ex) - { - Console.WriteLine($"Unhandled exception: {ex.Message}"); - throw; // Re-throw to be handled by middleware - } -} - } - } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/InvitationDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/InvitationDto.cs new file mode 100644 index 000000000..3558e6772 --- /dev/null +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/InvitationDto.cs @@ -0,0 +1,11 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Events.Application.DTO +{ + [ExcludeFromCodeCoverage] + public class InvitationDto + { + public Guid UserId { get; set; } + } +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDetailsDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDetailsDto.cs new file mode 100644 index 000000000..0a210aefe --- /dev/null +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDetailsDto.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Events.Application.DTO +{ + [ExcludeFromCodeCoverage] + public class OrganizationDetailsDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + public Guid? ParentOrganizationId { get; set; } + public IEnumerable SubOrganizations { get; set; } + public IEnumerable Invitations { get; set; } + public IEnumerable Users { get; set; } + public IEnumerable Roles { get; set; } + // public IEnumerable Gallery { get; set; } + public OrganizationSettingsDto Settings { get; set; } + public string DefaultRoleName { get; set; } + + // New fields + public string Address { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Telephone { get; set; } + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationSettingsDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationSettingsDto.cs new file mode 100644 index 000000000..5d786faf6 --- /dev/null +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationSettingsDto.cs @@ -0,0 +1,22 @@ +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Events.Application.DTO +{ + [ExcludeFromCodeCoverage] + public class OrganizationSettingsDto + { + public bool IsVisible { get; set; } + public bool IsPublic { get; set; } + public bool IsPrivate { get; set; } + public bool CanAddComments { get; set; } + public bool CanAddReactions { get; set; } + public bool CanPostPosts { get; set; } + public bool CanPostEvents { get; set; } + public bool CanMakeReposts { get; set; } + public bool CanAddCommentsToPosts { get; set; } + public bool CanAddReactionsToPosts { get; set; } + public bool CanAddCommentsToEvents { get; set; } + public bool CanAddReactionsToEvents { get; set; } + public bool DisplayFeedInMainOrganization { get; set; } + } +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizerDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizerDto.cs index 806e7cf21..f7262a2ba 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizerDto.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizerDto.cs @@ -7,10 +7,10 @@ namespace MiniSpace.Services.Events.Application.DTO [ExcludeFromCodeCoverage] public class OrganizerDto { - public Guid Id { get; set; } - public Guid? UserId { get; set; } - public Guid? OrganizationId { get; set; } - public OrganizerType OrganizerType { get; set; } + public Guid Id { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public OrganizerType OrganizerType { get; set; } public OrganizerDto() { @@ -22,14 +22,14 @@ public OrganizerDto(Organizer organizer) if (organizer.OrganizerType == OrganizerType.User) { - Id = organizer.Id; - UserId = organizer.Id; + Id = organizer.Id; + UserId = organizer.UserId; OrganizationId = null; } else if (organizer.OrganizerType == OrganizerType.Organization) { - Id = organizer.OrganizationId ?? Guid.Empty; - UserId = null; + Id = organizer.OrganizationId ?? Guid.Empty; + UserId = organizer.UserId; OrganizationId = organizer.OrganizationId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/RoleDto.cs similarity index 69% rename from MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs rename to MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/RoleDto.cs index d375efcb6..86ef62a8e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/RoleDto.cs @@ -1,14 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application.DTO { [ExcludeFromCodeCoverage] - public class OrganizationDto + public class RoleDto { public Guid Id { get; set; } public string Name { get; set; } - public IEnumerable Organizers { get; set; } + public string Description { get; set; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/SubOrganizationDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/SubOrganizationDto.cs new file mode 100644 index 000000000..683a2648c --- /dev/null +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/SubOrganizationDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Events.Application.DTO +{ + [ExcludeFromCodeCoverage] + public class SubOrganizationDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + + } +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserDto.cs new file mode 100644 index 000000000..2d7491666 --- /dev/null +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/UserDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Events.Application.DTO +{ + [ExcludeFromCodeCoverage] + public class UserDto + { + public Guid Id { get; set; } + public RoleDto Role { get; set; } + } +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IOrganizationsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IOrganizationsServiceClient.cs index 056c3ffca..699b9dd2e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IOrganizationsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IOrganizationsServiceClient.cs @@ -7,7 +7,7 @@ namespace MiniSpace.Services.Events.Application.Services.Clients { public interface IOrganizationsServiceClient { - Task GetAsync(Guid organizationId); + Task GetAsync(Guid organizationId); Task> GetAllChildrenOrganizations(Guid organizationId); } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Organizer.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Organizer.cs index d4e15c9df..b9f9a4ffd 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Organizer.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Organizer.cs @@ -4,10 +4,10 @@ namespace MiniSpace.Services.Events.Core.Entities { public class Organizer { - public Guid Id { get; set; } + public Guid Id { get; set; } public Guid? UserId { get; set; } - public Guid? OrganizationId { get; set; } - public OrganizerType OrganizerType { get; set; } + public Guid? OrganizationId { get; set; } + public OrganizerType OrganizerType { get; set; } public Organizer(Guid id, OrganizerType organizerType, Guid? userId = null, Guid? organizationId = null) { @@ -21,7 +21,7 @@ public Organizer(Guid id, OrganizerType organizerType, Guid? userId = null, Guid } else if (organizerType == OrganizerType.Organization) { - UserId = null; + UserId = userId; OrganizationId = organizationId ?? id; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs index 29bfd358e..48c12954e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Convey.CQRS.Queries; @@ -26,6 +27,11 @@ public GetPaginatedSearchEventsHandler(IEventRepository eventRepository, IAppCon public async Task> HandleAsync(GetSearchEvents query, CancellationToken cancellationToken) { + var jsonOptionsx = new JsonSerializerOptions { WriteIndented = true }; + var queryJson = JsonSerializer.Serialize(query, jsonOptionsx); + Console.WriteLine("Query Object: "); + Console.WriteLine(queryJson); + // Convert string values to corresponding enum types Category? category = null; State? state = null; @@ -64,20 +70,27 @@ public GetPaginatedSearchEventsHandler(IEventRepository eventRepository, IAppCon dateTo: query.DateTo ?? default(DateTime), category: category, state: state, - organizations: query.OrganizationId.HasValue ? new List { query.OrganizationId.Value } : Enumerable.Empty(), + organizations: query.OrganizationId.HasValue ? new List { query.OrganizationId.Value } : Enumerable.Empty(), // Filter by OrganizationId friends: query.Friends ?? Enumerable.Empty(), friendsEngagementType: engagementType, sortBy: sortBy, direction: sortDirection ); - // Map events to DTOs var studentId = _appContext.Identity.Id; var eventDtos = events.Select(e => e.AsDto(studentId)).ToList(); + var pagedResult = new MiniSpace.Services.Events.Application.DTO.PagedResult(eventDtos, pageNumber, pageSize, totalElements); + + // Serialize the result to JSON and log it + var jsonOptions = new JsonSerializerOptions { WriteIndented = true }; + var jsonResult = JsonSerializer.Serialize(pagedResult, jsonOptions); + Console.WriteLine("Search Results: "); + Console.WriteLine(jsonResult); + // Return the paginated result - return new MiniSpace.Services.Events.Application.DTO.PagedResult(eventDtos, pageNumber, pageSize, totalElements); + return pagedResult; } } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs index 07e539eef..00e980776 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs @@ -19,7 +19,7 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers public class GetUserEventsHandler : IQueryHandler> { private readonly IEventRepository _eventRepository; - private readonly IStudentsServiceClient _studentsServiceClient; // Keeping it as StudentsServiceClient + private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventValidator _eventValidator; private readonly IAppContext _appContext; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs index 0e83bc995..d0f83e0d9 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs @@ -53,7 +53,7 @@ public async Task> GetAllAsync() return pagedEvents; } - public async Task<(IEnumerable events, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseEventsAsync( + public async Task<(IEnumerable events, int pageNumber, int pageSize, int totalPages, int totalElements)> BrowseEventsAsync( int pageNumber, int pageSize, string name, string organizer, DateTime dateFrom, DateTime dateTo, Category? category, State? state, IEnumerable organizations, IEnumerable friends, EventEngagementType? friendsEngagementType, IEnumerable sortBy, string direction) @@ -63,11 +63,14 @@ public async Task> GetAllAsync() .AddCategoryFilter(category) .AddRestrictedStateFilter(state) .AddFriendsFilter(friends, friendsEngagementType) - .AddOrganizationsIdFilter(organizations); + .AddOrganizationsIdFilter(organizations); + + + var sortDefinition = Extensions.ToSortDefinition(sortBy, direction); var pagedEvents = await BrowseAsync(filterDefinition, sortDefinition, pageNumber, pageSize); - + return (pagedEvents.data.Select(e => e.AsEntity()), pageNumber, pageSize, pagedEvents.totalPages, pagedEvents.totalElements); } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs index 9aa040f85..d592e450f 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs @@ -7,6 +7,7 @@ using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Infrastructure.Mongo.Documents; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Repositories @@ -137,7 +138,7 @@ public static FilterDefinition AddRestrictedStateFilter (this Fil } else { - filterDefinition &= FilterDefinitionBuilder.In(x => x.State, new[] { State.Published, State.Archived }); + filterDefinition &= FilterDefinitionBuilder.In(x => x.State, new[] { State.Published, State.ToBePublished, State.Archived }); } return filterDefinition; @@ -172,14 +173,19 @@ public static FilterDefinition AddOrganizationsIdFilter(this Filt IEnumerable organizationsEnumerable) { var organizations = organizationsEnumerable.ToList(); + if (organizations.Count > 0) { - filterDefinition &= FilterDefinitionBuilder.In(nameof(EventDocument.Organizer) + "." + nameof(OrganizerDocument.OrganizationId), organizations); + var organizationFilter = Builders.Filter.And( + Builders.Filter.Ne(e => e.Organizer.OrganizationId, null), + Builders.Filter.In(e => (Guid)e.Organizer.OrganizationId, organizations) + ); + + filterDefinition &= organizationFilter; } return filterDefinition; } - public static FilterDefinition AddEventIdFilter(this FilterDefinition filterDefinition, IEnumerable eventIds) @@ -206,4 +212,11 @@ public static SortDefinition ToSortDefinition(IEnumerable return sortCombined; } } + + internal class FieldDefinitionBuilder + { + public FieldDefinitionBuilder() + { + } + } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs index 5ccc66949..601086ac5 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs @@ -22,6 +22,5 @@ public FriendsServiceClient(IHttpClient httpClient, HttpClientOptions options) public Task> GetAsync(Guid studentId) => _httpClient.GetAsync>($"{_url}/friends/{studentId}"); - } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs index c6d747c5b..f4a7f1977 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs @@ -20,8 +20,8 @@ public OrganizationsServiceClient(IHttpClient httpClient, HttpClientOptions opti _url = options.Services["organizations"]; } - public Task GetAsync(Guid organizationId) - => _httpClient.GetAsync($"{_url}/organizations/{organizationId}/details"); + public Task GetAsync(Guid organizationId) + => _httpClient.GetAsync($"{_url}/organizations/{organizationId}/details"); public Task> GetAllChildrenOrganizations(Guid organizationId) => _httpClient.GetAsync>($"{_url}/organizations/{organizationId}/children/all"); diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs index 1d722dd00..119b249d9 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs @@ -66,7 +66,10 @@ await _messageBroker.PublishAsync(new MediaFileDeleted( fileSourceInfo.SourceId, fileSourceInfo.SourceType.ToString(), fileSourceInfo.UploaderId, - fileSourceInfo.OrganizationId)); + fileSourceInfo.OrganizationId, + fileSourceInfo.EventId, + fileSourceInfo.PostId + )); } } } diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs index f7e7d793a..9cec78317 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs @@ -9,18 +9,22 @@ public class UploadFile : ICommand public Guid SourceId { get; set; } public string SourceType { get; set; } public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public Guid? PostId { get; set; } public Guid UploaderId { get; set; } public string FileName { get; set; } public string FileContentType { get; set; } public byte[] FileData { get; set; } - public UploadFile(Guid fileId, Guid sourceId, string sourceType, Guid? organizationId, - Guid uploaderId, string fileName, string fileContentType, byte[] fileData) + public UploadFile(Guid fileId, Guid sourceId, string sourceType, Guid? organizationId, Guid? eventId, + Guid? postId, Guid uploaderId, string fileName, string fileContentType, byte[] fileData) { FileId = fileId == Guid.Empty ? Guid.NewGuid() : fileId; SourceId = sourceId; SourceType = sourceType; OrganizationId = organizationId; + EventId = eventId; + PostId = postId; UploaderId = uploaderId; FileName = fileName; FileContentType = fileContentType; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs index 2a4b8f650..71e638828 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs @@ -10,15 +10,20 @@ public class MediaFileDeleted : IEvent public string Source { get; } public Guid UploaderId { get; } public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public Guid? PostId { get; } public MediaFileDeleted(string mediaFileUrl, Guid sourceId, string source, - Guid uploaderId, Guid? organizationId) + Guid uploaderId, Guid? organizationId, + Guid? eventId = null, Guid? postId = null) { MediaFileUrl = mediaFileUrl; SourceId = sourceId; Source = source; UploaderId = uploaderId; OrganizationId = organizationId; + EventId = eventId; + PostId = postId; } } } diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs index 0e854084c..374b1b79b 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs @@ -7,17 +7,22 @@ public class PostFileUploaded : IEvent { public Guid FileId { get; } public Guid PostId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } public string FileName { get; } public string FileUrl { get; } public string ContentType { get; } public DateTime UploadDate { get; } public Guid UploaderId { get; } - public PostFileUploaded(Guid fileId, Guid postId, string fileName, string fileUrl, - string contentType, DateTime uploadDate, Guid uploaderId) + public PostFileUploaded(Guid fileId, Guid postId, Guid? organizationId, Guid? eventId, + string fileName, string fileUrl, string contentType, + DateTime uploadDate, Guid uploaderId) { FileId = fileId; PostId = postId; + OrganizationId = organizationId; + EventId = eventId; FileName = fileName; FileUrl = fileUrl; ContentType = contentType; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/ContextType.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/ContextType.cs index eddad10a3..fbb40acf0 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/ContextType.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/ContextType.cs @@ -3,8 +3,12 @@ public enum ContextType { EventBanner, - EventFile, + EventFile, EventGalleryImage, + PostFileUserEvent, + PostFileUser, + PostFileOrganizationEvent, + PostFileOrganization, PostFile, StudentProfileImage, StudentBannerImage, diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/FileSourceInfo.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/FileSourceInfo.cs index 8e16a6682..4c8d4864b 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/FileSourceInfo.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Core/Entities/FileSourceInfo.cs @@ -14,10 +14,12 @@ public class FileSourceInfo : AggregateRoot public string FileUrl { get; set; } public string FileName { get; set; } public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public Guid? PostId { get; set; } public FileSourceInfo(Guid id, Guid sourceId, ContextType sourceType, Guid uploaderId, State state, DateTime createdAt, string originalFileUrl, string originalFileContentType, string fileUrl, string fileName, - Guid? organizationId = null) + Guid? organizationId = null, Guid? eventId = null, Guid? postId = null) { Id = id; SourceId = sourceId; @@ -30,9 +32,10 @@ public FileSourceInfo(Guid id, Guid sourceId, ContextType sourceType, Guid uploa FileUrl = fileUrl; FileName = fileName; OrganizationId = organizationId; + EventId = eventId; + PostId = postId; } - public void Associate() { State = State.Associated; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/Extensions.cs index 60a6066fa..4972f3215 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/Extensions.cs @@ -18,7 +18,9 @@ public static FileSourceInfo AsEntity(this FileSourceInfoDocument document) document.OriginalFileContentType, document.FileUrl, document.FileName, - document.OrganizationId + document.OrganizationId, + document.EventId, + document.PostId ); public static FileSourceInfoDocument AsDocument(this FileSourceInfo entity) @@ -34,8 +36,9 @@ public static FileSourceInfoDocument AsDocument(this FileSourceInfo entity) OriginalFileContentType = entity.OriginalFileContentType, FileUrl = entity.FileUrl, FileName = entity.FileName, - OrganizationId = entity.OrganizationId + OrganizationId = entity.OrganizationId, + EventId = entity.EventId, + PostId = entity.PostId }; - } } diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs index 8bd018179..67022786e 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs @@ -18,5 +18,7 @@ public class FileSourceInfoDocument : IIdentifiable public string FileUrl { get; set; } public string FileName { get; set; } public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public Guid? PostId { get; set; } } } diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MediaFilesService.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MediaFilesService.cs index c0e5a5340..c1b41f21b 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MediaFilesService.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MediaFilesService.cs @@ -156,116 +156,120 @@ public async Task UploadAsync(UploadMediaFile command) } public async Task UploadFileAsync(UploadFile command) -{ - try - { - // Validate identity - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != command.UploaderId) { - throw new UnauthorizedMediaFileUploadException(identity.Id, command.UploaderId); - } + try + { + // Validate identity + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != command.UploaderId) + { + throw new UnauthorizedMediaFileUploadException(identity.Id, command.UploaderId); + } - // Validate context type - if (!Enum.TryParse(command.SourceType, out ContextType sourceType)) - { - throw new InvalidContextTypeException(command.SourceType); - } + // Validate context type + if (!Enum.TryParse(command.SourceType, out ContextType sourceType)) + { + throw new InvalidContextTypeException(command.SourceType); + } - // Validate file size - _fileValidator.ValidateFileSize(command.FileData.Length); + // Validate file size + _fileValidator.ValidateFileSize(command.FileData.Length); - // Validate file extensions - byte[] buffer = new byte[8]; - Array.Copy(command.FileData, 0, buffer, 0, Math.Min(buffer.Length, command.FileData.Length)); - _fileValidator.ValidateFileExtensions(buffer, command.FileContentType); + // Validate file extensions + byte[] buffer = new byte[8]; + Array.Copy(command.FileData, 0, buffer, 0, Math.Min(buffer.Length, command.FileData.Length)); + _fileValidator.ValidateFileExtensions(buffer, command.FileContentType); - // Prepare the file for upload - using var fileStream = new MemoryStream(command.FileData); - string fileName = GenerateUniqueFileName(command.SourceType, command.UploaderId, command.FileName); - - // Upload the file to S3 - string fileUrl = await _s3Service.UploadFileAsync("files", fileName, fileStream); + // Prepare the file for upload + using var fileStream = new MemoryStream(command.FileData); + string fileName = GenerateUniqueFileName(command.SourceType, command.UploaderId, command.FileName); - // Check if the file URL is valid - if (string.IsNullOrEmpty(fileUrl)) - { - throw new Exception("File upload failed. Received a null or empty file URL."); - } + // Upload the file to S3 + string fileUrl = await _s3Service.UploadFileAsync("files", fileName, fileStream); - // Record the upload date - var uploadDate = _dateTimeProvider.Now; - - // Create file source info record - var fileSourceInfo = new FileSourceInfo( - command.FileId, - command.SourceId, - sourceType, - command.UploaderId, - State.Associated, - uploadDate, - fileUrl, - command.FileContentType, - fileUrl, // Assuming the processedUrl is the same as the fileUrl in this context - command.FileName, - command.OrganizationId - ); - - // Save file source info to the repository - await _fileSourceInfoRepository.AddAsync(fileSourceInfo); - - // Publish specific events based on the context type - if (sourceType == ContextType.EventBanner || sourceType == ContextType.EventGalleryImage || sourceType == ContextType.EventFile) - { - await _messageBroker.PublishAsync(new EventFileUploaded( - command.FileId, - command.SourceId, - command.FileName, - fileUrl, - command.FileContentType, - uploadDate, - command.UploaderId - )); - } - else if (sourceType == ContextType.PostFile) - { - await _messageBroker.PublishAsync(new PostFileUploaded( - command.FileId, - command.SourceId, - command.FileName, - fileUrl, - command.FileContentType, - uploadDate, - command.UploaderId - )); - } - else - { - await _messageBroker.PublishAsync(new GeneralFileUploaded( - command.FileId, - command.FileName, - fileUrl, - Path.GetExtension(command.FileName)?.ToLower(), - command.FileContentType, - uploadDate, - command.OrganizationId, - command.UploaderId - )); + // Check if the file URL is valid + if (string.IsNullOrEmpty(fileUrl)) + { + throw new Exception("File upload failed. Received a null or empty file URL."); + } + + // Record the upload date + var uploadDate = _dateTimeProvider.Now; + + // Create file source info record + var fileSourceInfo = new FileSourceInfo( + command.FileId, + command.SourceId, + sourceType, + command.UploaderId, + State.Associated, + uploadDate, + fileUrl, + command.FileContentType, + fileUrl, // Assuming the processedUrl is the same as the fileUrl in this context + command.FileName, + command.OrganizationId + ); + + // Save file source info to the repository + await _fileSourceInfoRepository.AddAsync(fileSourceInfo); + + // Publish specific events based on the context type + if (sourceType == ContextType.EventBanner || sourceType == ContextType.EventGalleryImage || sourceType == ContextType.EventFile) + { + await _messageBroker.PublishAsync(new EventFileUploaded( + command.FileId, + command.SourceId, + command.FileName, + fileUrl, + command.FileContentType, + uploadDate, + command.UploaderId + )); + } + else if (sourceType == ContextType.PostFile) + { + await _messageBroker.PublishAsync(new PostFileUploaded( + command.FileId, + command.PostId ?? Guid.Empty, // Passing the PostId, defaulting to empty Guid if null + command.OrganizationId, + command.EventId, // Adding EventId to the event + command.FileName, + fileUrl, + command.FileContentType, + uploadDate, + command.UploaderId + )); + } + else + { + await _messageBroker.PublishAsync(new GeneralFileUploaded( + command.FileId, + command.FileName, + fileUrl, + Path.GetExtension(command.FileName)?.ToLower(), + command.FileContentType, + uploadDate, + command.OrganizationId, + command.UploaderId + )); + } + + // Return the response with the correct file URL + var responseDto = new GeneralFileUploadResponseDto(fileSourceInfo.Id, fileUrl); + Console.WriteLine($"Returning response: {JsonSerializer.Serialize(responseDto)}"); + + return responseDto; + } + catch (Exception ex) + { + // Log the error for debugging purposes + Console.WriteLine($"Error in UploadFileAsync: {ex.Message}"); + throw; + } } - // Return the response with the correct file URL - var responseDto = new GeneralFileUploadResponseDto(fileSourceInfo.Id, fileUrl); - Console.WriteLine($"Returning response: {JsonSerializer.Serialize(responseDto)}"); - return responseDto; - } - catch (Exception ex) - { - // Log the error for debugging purposes - Console.WriteLine($"Error in UploadFileAsync: {ex.Message}"); - throw; - } -} diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs index 3a6e3c807..a5614c320 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs @@ -48,7 +48,6 @@ public async Task HandleAsync(CreateOrganization command, CancellationToken canc if (command.ParentId == null) { - // Create as a root organization organization = new Organization( command.OrganizationId, command.Name, @@ -70,7 +69,6 @@ public async Task HandleAsync(CreateOrganization command, CancellationToken canc } else { - // Handle creation of a sub-organization var root = await _organizationRepository.GetAsync(command.RootId.Value); if (root == null) { @@ -110,8 +108,8 @@ public async Task HandleAsync(CreateOrganization command, CancellationToken canc await _organizationRolesRepository.AddRoleAsync(organization.Id, role); } - // Initialize an empty gallery for the organization - await _organizationGalleryRepository.AddImageAsync(organization.Id, new GalleryImage(Guid.NewGuid(), "Default Image URL", DateTime.UtcNow)); + // We do not have to initialize gallery in the momentum organization is created + // await _organizationGalleryRepository.AddImageAsync(organization.Id, new GalleryImage(Guid.NewGuid(), "Default Image URL", DateTime.UtcNow)); // Add the creator as a member with the "Creator" role var creatorRole = defaultRoles.SingleOrDefault(r => r.Name == "Creator"); diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/UserOrganizationsDto.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/UserOrganizationsDto.cs index 4b306d924..10e95efae 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/UserOrganizationsDto.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/UserOrganizationsDto.cs @@ -16,12 +16,13 @@ public class UserOrganizationsDto public string BannerUrl { get; set; } public string ImageUrl { get; set; } public IEnumerable SubOrganizations { get; set; } + public IEnumerable Users { get; set; } public UserOrganizationsDto() { } - public UserOrganizationsDto(Organization organization) + public UserOrganizationsDto(Organization organization, IEnumerable users = null) { Id = organization.Id; Name = organization.Name; @@ -30,6 +31,7 @@ public UserOrganizationsDto(Organization organization) BannerUrl = organization.BannerUrl; ImageUrl = organization.ImageUrl; SubOrganizations = organization.SubOrganizations?.Select(o => new UserOrganizationsDto(o)).ToList(); + Users = users?.Select(u => new UserDto(u)).ToList(); } } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs index 949bf2dad..52edd870a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs @@ -8,6 +8,5 @@ namespace MiniSpace.Services.Organizations.Application.Queries public class GetOrganizationDetails : IQuery { public Guid OrganizationId { get; set; } - public Guid RootId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs index 01a4dc82b..9cb5894b6 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs @@ -1,282 +1,282 @@ -using MiniSpace.Services.Organizations.Application.DTO; -using MiniSpace.Services.Organizations.Core.Entities; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; + using MiniSpace.Services.Organizations.Application.DTO; + using MiniSpace.Services.Organizations.Core.Entities; + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; -namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public static class Extensions + namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents { - public static Organization AsEntity(this OrganizationDocument document) - => new Organization( - document.Id, - document.Name, - document.Description, - document.Settings, - document.OwnerId, - document.BannerUrl, - document.ImageUrl, - document.ParentOrganizationId, - document.DefaultRoleName, - document.Address, // New fields - document.Country, - document.City, - document.Telephone, - document.Email, - document.SubOrganizations?.Select(o => o.AsEntity()) - ); + [ExcludeFromCodeCoverage] + public static class Extensions + { + public static Organization AsEntity(this OrganizationDocument document) + => new Organization( + document.Id, + document.Name, + document.Description, + document.Settings, + document.OwnerId, + document.BannerUrl, + document.ImageUrl, + document.ParentOrganizationId, + document.DefaultRoleName, + document.Address, // New fields + document.Country, + document.City, + document.Telephone, + document.Email, + document.SubOrganizations?.Select(o => o.AsEntity()) + ); - public static OrganizationDocument AsDocument(this Organization entity) - => new OrganizationDocument - { - Id = entity.Id, - Name = entity.Name, - Description = entity.Description, - Settings = entity.Settings, - BannerUrl = entity.BannerUrl, - ImageUrl = entity.ImageUrl, - OwnerId = entity.OwnerId, - ParentOrganizationId = entity.ParentOrganizationId, - SubOrganizations = entity.SubOrganizations?.Select(o => o.AsDocument()).ToList(), - DefaultRoleName = entity.DefaultRoleName, - Address = entity.Address, // New fields - Country = entity.Country, - City = entity.City, - Telephone = entity.Telephone, - Email = entity.Email - }; + public static OrganizationDocument AsDocument(this Organization entity) + => new OrganizationDocument + { + Id = entity.Id, + Name = entity.Name, + Description = entity.Description, + Settings = entity.Settings, + BannerUrl = entity.BannerUrl, + ImageUrl = entity.ImageUrl, + OwnerId = entity.OwnerId, + ParentOrganizationId = entity.ParentOrganizationId, + SubOrganizations = entity.SubOrganizations?.Select(o => o.AsDocument()).ToList(), + DefaultRoleName = entity.DefaultRoleName, + Address = entity.Address, // New fields + Country = entity.Country, + City = entity.City, + Telephone = entity.Telephone, + Email = entity.Email + }; - public static OrganizationDto AsDto(this OrganizationDocument document) - => new OrganizationDto - { - Id = document.Id, - Name = document.Name, - Description = document.Description, - BannerUrl = document.BannerUrl, - ImageUrl = document.ImageUrl, - OwnerId = document.OwnerId, - DefaultRoleName = document.DefaultRoleName - }; + public static OrganizationDto AsDto(this OrganizationDocument document) + => new OrganizationDto + { + Id = document.Id, + Name = document.Name, + Description = document.Description, + BannerUrl = document.BannerUrl, + ImageUrl = document.ImageUrl, + OwnerId = document.OwnerId, + DefaultRoleName = document.DefaultRoleName + }; - public static OrganizationDetailsDto AsDetailsDto(this OrganizationDocument document) - => new OrganizationDetailsDto - { - Id = document.Id, - Name = document.Name, - Description = document.Description, - BannerUrl = document.BannerUrl, - ImageUrl = document.ImageUrl, - OwnerId = document.OwnerId, - ParentOrganizationId = document.ParentOrganizationId, - SubOrganizations = document.SubOrganizations?.Select(o => new SubOrganizationDto + public static OrganizationDetailsDto AsDetailsDto(this OrganizationDocument document) + => new OrganizationDetailsDto { - Id = o.Id, - Name = o.Name, - Description = o.Description, - BannerUrl = o.BannerUrl, - ImageUrl = o.ImageUrl, - OwnerId = o.OwnerId - }).ToList(), - DefaultRoleName = document.DefaultRoleName - }; + Id = document.Id, + Name = document.Name, + Description = document.Description, + BannerUrl = document.BannerUrl, + ImageUrl = document.ImageUrl, + OwnerId = document.OwnerId, + ParentOrganizationId = document.ParentOrganizationId, + SubOrganizations = document.SubOrganizations?.Select(o => new SubOrganizationDto + { + Id = o.Id, + Name = o.Name, + Description = o.Description, + BannerUrl = o.BannerUrl, + ImageUrl = o.ImageUrl, + OwnerId = o.OwnerId + }).ToList(), + DefaultRoleName = document.DefaultRoleName + }; - public static OrganizationDto AsSubDto(this OrganizationDocument document) - => new OrganizationDto - { - Id = document.Id, - Name = document.Name, - Description = document.Description, - BannerUrl = document.BannerUrl, - ImageUrl = document.ImageUrl, - OwnerId = document.OwnerId, - DefaultRoleName = document.DefaultRoleName - }; + public static OrganizationDto AsSubDto(this OrganizationDocument document) + => new OrganizationDto + { + Id = document.Id, + Name = document.Name, + Description = document.Description, + BannerUrl = document.BannerUrl, + ImageUrl = document.ImageUrl, + OwnerId = document.OwnerId, + DefaultRoleName = document.DefaultRoleName + }; - public static Invitation AsEntity(this InvitationEntry document) - => new Invitation(document.UserId); + public static Invitation AsEntity(this InvitationEntry document) + => new Invitation(document.UserId); - public static InvitationEntry AsDocument(this Invitation entity) - => new InvitationEntry - { - UserId = entity.UserId - }; + public static InvitationEntry AsDocument(this Invitation entity) + => new InvitationEntry + { + UserId = entity.UserId + }; - public static OrganizationInvitationDocument AsInvitationDocument(this IEnumerable entities, Guid organizationId) - => new OrganizationInvitationDocument - { - Id = Guid.NewGuid(), - OrganizationId = organizationId, - Invitations = entities.Select(e => e.AsDocument()).ToList() - }; + public static OrganizationInvitationDocument AsInvitationDocument(this IEnumerable entities, Guid organizationId) + => new OrganizationInvitationDocument + { + Id = Guid.NewGuid(), + OrganizationId = organizationId, + Invitations = entities.Select(e => e.AsDocument()).ToList() + }; - public static IEnumerable AsInvitationEntities(this OrganizationInvitationDocument document) - => document.Invitations.Select(i => i.AsEntity()); + public static IEnumerable AsInvitationEntities(this OrganizationInvitationDocument document) + => document.Invitations.Select(i => i.AsEntity()); - public static Role AsEntity(this RoleEntry document) - { - return new Role( - document.Id, - document.Name, - document.Description, - document.Permissions.ToDictionary( - kvp => Enum.Parse(kvp.Key), - kvp => kvp.Value) - ); - } - - public static RoleEntry AsDocument(this Role entity) - { - return new RoleEntry + public static Role AsEntity(this RoleEntry document) { - Id = entity.Id, - Name = entity.Name, - Description = entity.Description, - Permissions = entity.Permissions.ToDictionary( - kvp => kvp.Key.ToString(), - kvp => kvp.Value) - }; - } - + return new Role( + document.Id, + document.Name, + document.Description, + document.Permissions.ToDictionary( + kvp => Enum.Parse(kvp.Key), + kvp => kvp.Value) + ); + } - public static OrganizationRolesDocument AsRoleDocument(this IEnumerable entities, Guid organizationId) - => new OrganizationRolesDocument + public static RoleEntry AsDocument(this Role entity) { - Id = Guid.NewGuid(), - OrganizationId = organizationId, - Roles = entities.Select(e => e.AsDocument()).ToList() - }; + return new RoleEntry + { + Id = entity.Id, + Name = entity.Name, + Description = entity.Description, + Permissions = entity.Permissions.ToDictionary( + kvp => kvp.Key.ToString(), + kvp => kvp.Value) + }; + } - public static IEnumerable AsRoleEntities(this OrganizationRolesDocument document) - => document.Roles.Select(r => r.AsEntity()); + public static OrganizationRolesDocument AsRoleDocument(this IEnumerable entities, Guid organizationId) + => new OrganizationRolesDocument + { + Id = Guid.NewGuid(), + OrganizationId = organizationId, + Roles = entities.Select(e => e.AsDocument()).ToList() + }; - public static OrganizationGalleryImageDocument AsGalleryImageDocument(this IEnumerable entities, Guid organizationId) - => new OrganizationGalleryImageDocument - { - Id = Guid.NewGuid(), - OrganizationId = organizationId, - Gallery = entities.Select(e => e.AsDocument()).ToList() - }; + public static IEnumerable AsRoleEntities(this OrganizationRolesDocument document) + => document.Roles.Select(r => r.AsEntity()); - public static IEnumerable AsGalleryImageEntities(this OrganizationGalleryImageDocument document) - => document.Gallery.Select(g => g.AsEntity()); - - public static User AsEntity(this UserEntry document) - { - return new User( - document.UserId, - new Role( - document.Role.RoleId, - document.Role.RoleName, - string.Empty, - new Dictionary() - ) - ); - } + public static OrganizationGalleryImageDocument AsGalleryImageDocument(this IEnumerable entities, Guid organizationId) + => new OrganizationGalleryImageDocument + { + Id = Guid.NewGuid(), + OrganizationId = organizationId, + Gallery = entities.Select(e => e.AsDocument()).ToList() + }; + public static IEnumerable AsGalleryImageEntities(this OrganizationGalleryImageDocument document) + => document.Gallery.Select(g => g.AsEntity()); - public static UserEntry AsDocument(this User entity) - => new UserEntry + + public static User AsEntity(this UserEntry document) { - UserId = entity.Id, - Role = new RoleAssignment + return new User( + document.UserId, + new Role( + document.Role.RoleId, + document.Role.RoleName, + string.Empty, + new Dictionary() + ) + ); + } + + + public static UserEntry AsDocument(this User entity) + => new UserEntry { - RoleId = entity.Role.Id, - RoleName = entity.Role.Name - } - }; + UserId = entity.Id, + Role = new RoleAssignment + { + RoleId = entity.Role.Id, + RoleName = entity.Role.Name + } + }; - public static OrganizationMembersDocument AsUserDocument(this IEnumerable entities, Guid organizationId) - => new OrganizationMembersDocument - { - Id = Guid.NewGuid(), - OrganizationId = organizationId, - Users = entities.Select(e => e.AsDocument()).ToList() - }; + public static OrganizationMembersDocument AsUserDocument(this IEnumerable entities, Guid organizationId) + => new OrganizationMembersDocument + { + Id = Guid.NewGuid(), + OrganizationId = organizationId, + Users = entities.Select(e => e.AsDocument()).ToList() + }; - public static IEnumerable AsUserEntities(this OrganizationMembersDocument document) - => document.Users.Select(u => u.AsEntity()); + public static IEnumerable AsUserEntities(this OrganizationMembersDocument document) + => document.Users.Select(u => u.AsEntity()); - public static GalleryImage AsEntity(this GalleryImageEntry document) - { - return new GalleryImage(document.ImageId, document.ImageUrl, document.DateAdded); - } + public static GalleryImage AsEntity(this GalleryImageEntry document) + { + return new GalleryImage(document.ImageId, document.ImageUrl, document.DateAdded); + } - public static GalleryImageEntry AsDocument(this GalleryImage entity) - { - return new GalleryImageEntry + public static GalleryImageEntry AsDocument(this GalleryImage entity) { - ImageId = entity.ImageId, - ImageUrl = entity.ImageUrl, - DateAdded = entity.DateAdded - }; - } + return new GalleryImageEntry + { + ImageId = entity.ImageId, + ImageUrl = entity.ImageUrl, + DateAdded = entity.DateAdded + }; + } - public static OrganizationRequest AsEntity(this RequestDocument document) - { - return OrganizationRequest.CreateExisting( - document.RequestId, - document.UserId, - document.RequestDate, - Enum.Parse(document.State), - document.Reason - ); - } + public static OrganizationRequest AsEntity(this RequestDocument document) + { + return OrganizationRequest.CreateExisting( + document.RequestId, + document.UserId, + document.RequestDate, + Enum.Parse(document.State), + document.Reason + ); + } - public static RequestDocument AsDocument(this OrganizationRequest entity) - { - return new RequestDocument + public static RequestDocument AsDocument(this OrganizationRequest entity) { - RequestId = entity.Id, - UserId = entity.UserId, - RequestDate = entity.RequestDate, - State = entity.State.ToString(), - Reason = entity.Reason - }; - } + return new RequestDocument + { + RequestId = entity.Id, + UserId = entity.UserId, + RequestDate = entity.RequestDate, + State = entity.State.ToString(), + Reason = entity.Reason + }; + } - public static OrganizationRequests AsEntity(this OrganizationRequestsDocument document) - { - var requests = document.Requests?.Select(r => r.AsEntity()).ToList(); - return OrganizationRequests.CreateExisting( - document.Id, - document.OrganizationId, - requests - ); - } + public static OrganizationRequests AsEntity(this OrganizationRequestsDocument document) + { + var requests = document.Requests?.Select(r => r.AsEntity()).ToList(); + return OrganizationRequests.CreateExisting( + document.Id, + document.OrganizationId, + requests + ); + } - public static OrganizationRequestsDocument AsDocument(this OrganizationRequests entity) - { - return new OrganizationRequestsDocument + public static OrganizationRequestsDocument AsDocument(this OrganizationRequests entity) { - Id = entity.Id, - OrganizationId = entity.OrganizationId, - Requests = entity.Requests.Select(r => r.AsDocument()).ToList() - }; - } + return new OrganizationRequestsDocument + { + Id = entity.Id, + OrganizationId = entity.OrganizationId, + Requests = entity.Requests.Select(r => r.AsDocument()).ToList() + }; + } - public static UserOrganizations AsEntity(this UserOrganizationsDocument document) - { - var organizations = document.Organizations?.Select(o => new UserOrganizationEntry(o.OrganizationId, o.JoinDate)).ToList(); - return UserOrganizations.CreateExisting(document.Id, document.UserId, organizations); - } + public static UserOrganizations AsEntity(this UserOrganizationsDocument document) + { + var organizations = document.Organizations?.Select(o => new UserOrganizationEntry(o.OrganizationId, o.JoinDate)).ToList(); + return UserOrganizations.CreateExisting(document.Id, document.UserId, organizations); + } - public static UserOrganizationsDocument AsDocument(this UserOrganizations entity) - { - return new UserOrganizationsDocument + public static UserOrganizationsDocument AsDocument(this UserOrganizations entity) { - Id = entity.Id, - UserId = entity.UserId, - Organizations = entity.Organizations.Select(o => new UserOrganizationEntryDocument + return new UserOrganizationsDocument { - OrganizationId = o.OrganizationId, - JoinDate = o.JoinDate - }).ToList() - }; + Id = entity.Id, + UserId = entity.UserId, + Organizations = entity.Organizations.Select(o => new UserOrganizationEntryDocument + { + OrganizationId = o.OrganizationId, + JoinDate = o.JoinDate + }).ToList() + }; + } } } -} diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs index cfdd4e51f..b2d695cda 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs @@ -1,29 +1,58 @@ using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; -using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; -using MongoDB.Driver; -using System.Diagnostics.CodeAnalysis; - +using MiniSpace.Services.Organizations.Core.Repositories; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Queries.Handlers { - [ExcludeFromCodeCoverage] public class GetOrganizationDetailsHandler : IQueryHandler { - private readonly IMongoRepository _repository; + private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationGalleryRepository _galleryRepository; + private readonly IOrganizationRolesRepository _organizationRolesRepository; + private readonly IOrganizationMembersRepository _organizationMembersRepository; - public GetOrganizationDetailsHandler(IMongoRepository repository) + public GetOrganizationDetailsHandler( + IOrganizationRepository organizationRepository, + IOrganizationGalleryRepository galleryRepository, + IOrganizationRolesRepository organizationRolesRepository, + IOrganizationMembersRepository organizationMembersRepository) { - _repository = repository; + _organizationRepository = organizationRepository; + _galleryRepository = galleryRepository; + _organizationRolesRepository = organizationRolesRepository; + _organizationMembersRepository = organizationMembersRepository; } public async Task HandleAsync(GetOrganizationDetails query, CancellationToken cancellationToken) { - var root = await _repository.GetAsync(o => o.Id == query.RootId); - var organization = root?.AsEntity().GetSubOrganization(query.OrganizationId); - return organization == null ? null : new OrganizationDetailsDto(organization); + var organization = await _organizationRepository.GetAsync(query.OrganizationId); + if (organization == null) + { + return null; + } + + var galleryImages = await _galleryRepository.GetGalleryAsync(organization.Id); + var roles = await _organizationRolesRepository.GetRolesAsync(organization.Id); + var users = await _organizationMembersRepository.GetMembersAsync(organization.Id); + + var userDtos = users?.Select(u => new UserDto(u)).ToList() ?? new List(); + + var organizationDto = new OrganizationDetailsDto(organization) + { + SubOrganizations = organization.SubOrganizations?.Select(subOrg => new SubOrganizationDto(subOrg)).ToList(), + Invitations = organization.Invitations?.Select(invite => new InvitationDto(invite)).ToList(), + Users = userDtos, + Roles = roles?.Select(role => new RoleDto(role)).ToList() ?? new List(), + Gallery = galleryImages?.Select(galleryImage => new GalleryImageDto(galleryImage)).ToList() ?? new List(), + Settings = organization.Settings != null ? new OrganizationSettingsDto(organization.Settings) : null + }; + + return organizationDto; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs index ae917b228..89438b13f 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs @@ -16,15 +16,18 @@ public class GetOrganizationWithGalleryAndUsersHandler : IQueryHandler HandleAsync(GetOrganizationWithGalleryAndUsers query, CancellationToken cancellationToken) @@ -36,25 +39,31 @@ public async Task HandleAsync(GetOrganizationWithGa } var galleryImages = await _galleryRepository.GetGalleryAsync(organization.Id); - if (galleryImages == null) { - Console.WriteLine("Gallery Images Retrieved: null"); galleryImages = Enumerable.Empty(); } - else - { - Console.WriteLine("Gallery Images Retrieved:"); - Console.WriteLine(JsonSerializer.Serialize(galleryImages, new JsonSerializerOptions { WriteIndented = true })); - } var roles = await _organizationRolesRepository.GetRolesAsync(organization.Id); + var users = await _organizationMembersRepository.GetMembersAsync(organization.Id); + + var userDtos = users?.Select(u => new UserDto + { + Id = u.Id, + Role = new RoleDto + { + Id = u.Role?.Id ?? Guid.Empty, + Name = u.Role?.Name ?? "Unknown", + Description = u.Role?.Description ?? string.Empty, + Permissions = u.Role?.Permissions ?? new Dictionary() + } + }).ToList() ?? new List(); var settingsDto = organization.Settings != null ? new OrganizationSettingsDto(organization.Settings) : new OrganizationSettingsDto(); - var result = new OrganizationGalleryUsersDto(organization, galleryImages, organization.Users) + var result = new OrganizationGalleryUsersDto(organization, galleryImages, users) { OrganizationDetails = new OrganizationDetailsDto(organization) { @@ -62,9 +71,13 @@ public async Task HandleAsync(GetOrganizationWithGa Roles = roles?.Select(r => new RoleDto(r)).ToList() ?? new List() }, Gallery = galleryImages.Select(g => new GalleryImageDto(g)).ToList(), - Users = organization.Users?.Select(u => new UserDto(u)).ToList() ?? new List() + Users = userDtos }; + // Console.WriteLine("Result Object:"); + // var resultJson = JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }); + // Console.WriteLine(resultJson); + return result; } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs index a089ca30a..9e82705d9 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs @@ -12,16 +12,28 @@ namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Queries.Handlers public class GetUserOrganizationsHandler : IQueryHandler> { private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationMembersRepository _organizationMembersRepository; - public GetUserOrganizationsHandler(IOrganizationRepository organizationRepository) + public GetUserOrganizationsHandler( + IOrganizationRepository organizationRepository, + IOrganizationMembersRepository organizationMembersRepository) { _organizationRepository = organizationRepository; + _organizationMembersRepository = organizationMembersRepository; } public async Task> HandleAsync(GetUserOrganizations query, CancellationToken cancellationToken) { var organizations = await _organizationRepository.GetOrganizationsByUserAsync(query.UserId); - return organizations.Select(org => new UserOrganizationsDto(org)); + var organizationDtos = new List(); + + foreach (var org in organizations) + { + var users = await _organizationMembersRepository.GetMembersAsync(org.Id); + organizationDtos.Add(new UserOrganizationsDto(org, users)); + } + + return organizationDtos; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs index d13754769..4561f84a3 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs @@ -16,6 +16,7 @@ using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; using MiniSpace.Services.Posts.Application.Services; +using MiniSpace.Services.Posts.Core.Wrappers; using MiniSpace.Services.Posts.Infrastructure; namespace MiniSpace.Services.Posts.Api @@ -33,16 +34,11 @@ public static async Task Main(string[] args) .Build()) .Configure(app => app .UseInfrastructure() - .UseEndpoints(endpoints => endpoints - .Post("posts/search", async (cmd, ctx) => - { - var pagedResult = await ctx.RequestServices.GetService().BrowsePostsAsync(cmd); - await ctx.Response.WriteJsonAsync(pagedResult); - })) + .UseDispatcherEndpoints(endpoints => endpoints .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) .Get("posts/{postId}") - .Get>("posts") + .Get>("posts/search") .Get>("posts/organizer/{organizerId}") .Put("posts/{postId}") .Delete("posts/{postId}") diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs index 211e6572f..4f49f0910 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs @@ -1,18 +1,28 @@ using Convey.CQRS.Commands; +using MiniSpace.Services.Posts.Core.Entities; +using System; namespace MiniSpace.Services.Posts.Application.Commands { public class ChangePostState : ICommand { public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } public string State { get; } public DateTime? PublishDate { get; } + public PostContext Context { get; } - public ChangePostState(Guid postId, string state, DateTime? publishDate) + public ChangePostState(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string state, DateTime? publishDate, PostContext context) { PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; State = state; PublishDate = publishDate; + Context = context; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs index fdb630f37..139d008f6 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs @@ -1,27 +1,36 @@ using Convey.CQRS.Commands; +using MiniSpace.Services.Posts.Core.Entities; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Application.Commands { public class CreatePost : ICommand { public Guid PostId { get; } - public Guid EventId { get; } - public Guid OrganizerId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } public string TextContent { get; } - public IEnumerable MediaFiles { get; } + public IEnumerable MediaFiles { get; } public string State { get; } public DateTime? PublishDate { get; } + public PostContext Context { get; } + public string Visibility { get; set; } - public CreatePost(Guid postId, Guid eventId, Guid organizerId, string textContent, - IEnumerable mediaFiles, string state, DateTime? publishDate) + public CreatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFiles, string state, DateTime? publishDate, PostContext context, string visibility) { PostId = postId; + UserId = userId; + OrganizationId = organizationId; EventId = eventId; - OrganizerId = organizerId; TextContent = textContent; MediaFiles = mediaFiles; State = state; PublishDate = publishDate; + Context = context; + Visibility = visibility; } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs index 8fab824c7..b397106f5 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs @@ -1,11 +1,24 @@ using Convey.CQRS.Commands; +using MiniSpace.Services.Posts.Core.Entities; +using System; namespace MiniSpace.Services.Posts.Application.Commands { public class DeletePost : ICommand { public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string Context { get; } - public DeletePost(Guid postId) => PostId = postId; - } + public DeletePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string context) + { + PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + Context = context; + } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs index 63d896bac..dcaebba7e 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs @@ -5,39 +5,76 @@ using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Exceptions; using MiniSpace.Services.Posts.Core.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class ChangePostStateHandler : ICommandHandler { - private readonly IPostRepository _postRepository; + private readonly IUserPostRepository _userPostRepository; + private readonly IOrganizationPostRepository _organizationPostRepository; + private readonly IUserEventPostRepository _userEventPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; private readonly IAppContext _appContext; private readonly IDateTimeProvider _dateTimeProvider; private readonly IMessageBroker _messageBroker; - - public ChangePostStateHandler(IPostRepository postRepository, IAppContext appContext, - IDateTimeProvider dateTimeProvider, IMessageBroker messageBroker) + + public ChangePostStateHandler( + IUserPostRepository userPostRepository, + IOrganizationPostRepository organizationPostRepository, + IUserEventPostRepository userEventPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IAppContext appContext, + IDateTimeProvider dateTimeProvider, + IMessageBroker messageBroker) { - _postRepository = postRepository; + _userPostRepository = userPostRepository; + _organizationPostRepository = organizationPostRepository; + _userEventPostRepository = userEventPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; _appContext = appContext; _dateTimeProvider = dateTimeProvider; _messageBroker = messageBroker; } - + public async Task HandleAsync(ChangePostState command, CancellationToken cancellationToken = default) { - var post = await _postRepository.GetAsync(command.PostId); + Post post = null; + + // Determine which repository to use based on the context + if (command.Context == PostContext.UserPage) + { + post = await _userPostRepository.GetAsync(command.PostId); + } + else if (command.Context == PostContext.OrganizationPage) + { + post = await _organizationPostRepository.GetAsync(command.PostId); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + post = await _userEventPostRepository.GetAsync(command.PostId); + } + else + { + post = await _organizationEventPostRepository.GetAsync(command.PostId); + } + } + if (post is null) { throw new PostNotFoundException(command.PostId); } - + var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != post.OrganizerId && !identity.IsAdmin) + if (identity.IsAuthenticated && identity.Id != post.UserId && identity.Id != post.OrganizationId && !identity.IsAdmin) { throw new UnauthorizedPostAccessException(command.PostId, identity.Id); } - + if (!Enum.TryParse(command.State, true, out var newState)) { throw new InvalidPostStateException(command.State); @@ -47,12 +84,12 @@ public async Task HandleAsync(ChangePostState command, CancellationToken cancell { throw new UnauthorizedPostOperationException(command.PostId, identity.Id); } - + if (post.State == newState && post.State != State.ToBePublished) { throw new PostStateAlreadySetException(post.Id, newState); } - + var previousState = post.State.ToString().ToLowerInvariant(); var now = _dateTimeProvider.Now; @@ -74,10 +111,29 @@ public async Task HandleAsync(ChangePostState command, CancellationToken cancell default: throw new InvalidPostStateException(post.State.ToString().ToLowerInvariant()); } - - await _postRepository.UpdateAsync(post); + + // Update the post in the correct repository + if (command.Context == PostContext.UserPage) + { + await _userPostRepository.UpdateAsync(post); + } + else if (command.Context == PostContext.OrganizationPage) + { + await _organizationPostRepository.UpdateAsync(post); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + await _userEventPostRepository.UpdateAsync(post); + } + else + { + await _organizationEventPostRepository.UpdateAsync(post); + } + } await _messageBroker.PublishAsync(new PostStateChanged(post.Id, command.State, previousState)); } - } -} \ No newline at end of file + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs index d07860d2f..37046bde9 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs @@ -5,22 +5,39 @@ using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Exceptions; using MiniSpace.Services.Posts.Core.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class CreatePostHandler : ICommandHandler { private readonly IPostRepository _postRepository; - private readonly IEventRepository _eventRepository; + private readonly IUserPostRepository _userPostRepository; + private readonly IOrganizationPostRepository _organizationPostRepository; + private readonly IUserEventPostRepository _userEventPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; private readonly IDateTimeProvider _dateTimeProvider; private readonly IMessageBroker _messageBroker; private readonly IAppContext _appContext; - public CreatePostHandler(IPostRepository postRepository, IEventRepository eventRepository, - IDateTimeProvider dateTimeProvider, IMessageBroker messageBroker, IAppContext appContext) + public CreatePostHandler( + IPostRepository postRepository, + IUserPostRepository userPostRepository, + IOrganizationPostRepository organizationPostRepository, + IUserEventPostRepository userEventPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IDateTimeProvider dateTimeProvider, + IMessageBroker messageBroker, + IAppContext appContext) { _postRepository = postRepository; - _eventRepository = eventRepository; + _userPostRepository = userPostRepository; + _organizationPostRepository = organizationPostRepository; + _userEventPostRepository = userEventPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; _dateTimeProvider = dateTimeProvider; _messageBroker = messageBroker; _appContext = appContext; @@ -28,19 +45,13 @@ public CreatePostHandler(IPostRepository postRepository, IEventRepository eventR public async Task HandleAsync(CreatePost command, CancellationToken cancellationToken = default) { - var @event = await _eventRepository.GetAsync(command.EventId); - if (@event is null) - { - throw new EventNotFoundException(command.EventId); - } - var identity = _appContext.Identity; - if (identity.IsAuthenticated && (identity.Id != command.OrganizerId || identity.Id != @event.OrganizerId)) + if (identity.IsAuthenticated && identity.Id != command.UserId && identity.Id != command.OrganizationId) { - throw new UnauthorizedPostCreationAttemptException(identity.Id, command.EventId); + throw new UnauthorizedPostCreationAttemptException(identity.Id, command.EventId ?? Guid.Empty); } - - if(command.PostId == Guid.Empty || await _postRepository.ExistsAsync(command.PostId)) + + if (command.PostId == Guid.Empty || await _postRepository.ExistsAsync(command.PostId)) { throw new InvalidPostIdException(command.PostId); } @@ -49,9 +60,14 @@ public async Task HandleAsync(CreatePost command, CancellationToken cancellation { throw new InvalidPostStateException(command.State); } - + + if (!Enum.TryParse(command.Visibility, true, out var visibilityStatus)) + { + throw new InvalidVisibilityStatusException(command.Visibility); + } + var mediaFiles = command.MediaFiles.ToList(); - if(mediaFiles.Count > 3) + if (mediaFiles.Count > 12) { throw new InvalidNumberOfPostMediaFilesException(command.PostId, mediaFiles.Count); } @@ -63,12 +79,43 @@ public async Task HandleAsync(CreatePost command, CancellationToken cancellation case State.ToBePublished when command.PublishDate is null: throw new PublishDateNullException(command.PostId, newState); } - - var post = Post.Create(command.PostId, command.EventId, command.OrganizerId, command.TextContent, - command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate); - await _postRepository.AddAsync(post); + + Post post; + + if (command.Context == PostContext.UserPage) + { + post = Post.CreateForUser(command.PostId, command.UserId.Value, command.TextContent, command.MediaFiles, + _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _userPostRepository.AddAsync(post); + } + else if (command.Context == PostContext.OrganizationPage) + { + post = Post.CreateForOrganization(command.PostId, command.OrganizationId.Value, command.UserId, command.TextContent, command.MediaFiles, + _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _organizationPostRepository.AddAsync(post); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + post = Post.CreateForEvent(command.PostId, command.EventId.Value, command.UserId, command.OrganizationId, command.TextContent, + command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _userEventPostRepository.AddAsync(post); + } + else + { + post = Post.CreateForEvent(command.PostId, command.EventId.Value, null, command.OrganizationId, command.TextContent, + command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _organizationEventPostRepository.AddAsync(post); + } + } + else + { + throw new InvalidPostContextException(command.Context.ToString()); + } await _messageBroker.PublishAsync(new PostCreated(command.PostId, post.MediaFiles)); } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs index 1430155a5..811a2923f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs @@ -4,45 +4,98 @@ using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class DeletePostHandler : ICommandHandler { - private readonly IPostRepository _postRepository; + private readonly IUserPostRepository _userPostRepository; + private readonly IOrganizationPostRepository _organizationPostRepository; + private readonly IUserEventPostRepository _userEventPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; - - public DeletePostHandler(IPostRepository postRepository, IAppContext appContext, + + public DeletePostHandler( + IUserPostRepository userPostRepository, + IOrganizationPostRepository organizationPostRepository, + IUserEventPostRepository userEventPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IAppContext appContext, IMessageBroker messageBroker) { - _postRepository = postRepository; + _userPostRepository = userPostRepository; + _organizationPostRepository = organizationPostRepository; + _userEventPostRepository = userEventPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; _appContext = appContext; _messageBroker = messageBroker; } - + public async Task HandleAsync(DeletePost command, CancellationToken cancellationToken = default) { - var post = await _postRepository.GetAsync(command.PostId); - if (post is null) + Post post = null; + + switch (command.Context.ToLowerInvariant()) + { + case "userpage": + post = await _userPostRepository.GetAsync(command.PostId); + break; + case "organizationpage": + post = await _organizationPostRepository.GetAsync(command.PostId); + break; + case "eventpage" when command.UserId.HasValue: + post = (await _userEventPostRepository.GetByUserEventIdAsync(command.UserId.Value, command.EventId.Value)) + .FirstOrDefault(p => p.Id == command.PostId); + break; + case "eventpage" when command.OrganizationId.HasValue: + post = (await _organizationEventPostRepository.GetByOrganizationEventIdAsync(command.OrganizationId.Value, command.EventId.Value)) + .FirstOrDefault(p => p.Id == command.PostId); + break; + default: + throw new InvalidPostContextException(command.Context); + } + + if (post == null) { throw new PostNotFoundException(command.PostId); } - + var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != post.OrganizerId && !identity.IsAdmin) + if (identity.IsAuthenticated && identity.Id != (post.UserId ?? post.OrganizationId) && !identity.IsAdmin) { throw new UnauthorizedPostAccessException(command.PostId, identity.Id); } - + if (!identity.IsAdmin && post.State == State.Reported) { throw new UnauthorizedPostOperationException(command.PostId, identity.Id); } - - await _postRepository.DeleteAsync(command.PostId); + switch (command.Context.ToLowerInvariant()) + { + case "userpage": + await _userPostRepository.DeleteAsync(command.PostId); + break; + case "organizationpage": + await _organizationPostRepository.DeleteAsync(command.PostId); + break; + case "eventpage" when command.UserId.HasValue: + await _userEventPostRepository.DeleteAsync(command.PostId); + break; + case "eventpage" when command.OrganizationId.HasValue: + await _organizationEventPostRepository.DeleteAsync(command.PostId); + break; + default: + throw new InvalidPostContextException(command.Context); + } + + // Publish the post deleted event await _messageBroker.PublishAsync(new PostDeleted(command.PostId)); } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs index 081ba3c9c..fc9bd548f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs @@ -3,55 +3,112 @@ using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Exceptions; using MiniSpace.Services.Posts.Core.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class UpdatePostHandler : ICommandHandler { private readonly IPostRepository _postRepository; + private readonly IUserPostRepository _userPostRepository; + private readonly IOrganizationPostRepository _organizationPostRepository; + private readonly IUserEventPostRepository _userEventPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; private readonly IDateTimeProvider _dateTimeProvider; - - public UpdatePostHandler(IPostRepository postRepository, IAppContext appContext, - IMessageBroker messageBroker, IDateTimeProvider dateTimeProvider) + + public UpdatePostHandler(IPostRepository postRepository, + IUserPostRepository userPostRepository, + IOrganizationPostRepository organizationPostRepository, + IUserEventPostRepository userEventPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IAppContext appContext, + IMessageBroker messageBroker, + IDateTimeProvider dateTimeProvider) { _postRepository = postRepository; + _userPostRepository = userPostRepository; + _organizationPostRepository = organizationPostRepository; + _userEventPostRepository = userEventPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; _appContext = appContext; _messageBroker = messageBroker; _dateTimeProvider = dateTimeProvider; } - + public async Task HandleAsync(UpdatePost command, CancellationToken cancellationToken = default) { + // Fetch the post based on its ID var post = await _postRepository.GetAsync(command.PostId); if (post is null) { throw new PostNotFoundException(command.PostId); } - + + // Check if the identity has the right to update this post var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != post.OrganizerId && !identity.IsAdmin) + if (identity.IsAuthenticated && identity.Id != post.UserId && identity.Id != post.OrganizationId && !identity.IsAdmin) { throw new UnauthorizedPostAccessException(command.PostId, identity.Id); } - + if (!identity.IsAdmin && post.State == State.Reported) { throw new UnauthorizedPostOperationException(command.PostId, identity.Id); } - + + // Validate the state if provided + State? newState = null; + if (!string.IsNullOrWhiteSpace(command.State)) + { + if (!Enum.TryParse(command.State, true, out var parsedState)) + { + throw new InvalidPostStateException(command.State); + } + newState = parsedState; + } + + // Validate the visibility if provided + VisibilityStatus? newVisibility = null; + if (!string.IsNullOrWhiteSpace(command.Visibility)) + { + if (!Enum.TryParse(command.Visibility, true, out var parsedVisibility)) + { + throw new InvalidVisibilityStatusException(command.Visibility); + } + newVisibility = parsedVisibility; + } + + // Validate the media files count var mediaFiles = command.MediaFiles.ToList(); - if(mediaFiles.Count > 3) + if (mediaFiles.Count > 12) { throw new InvalidNumberOfPostMediaFilesException(post.Id, mediaFiles.Count); } + // Update the post fields post.Update(command.TextContent, command.MediaFiles, _dateTimeProvider.Now); + + if (newState.HasValue) + { + post.ChangeState(newState.Value, command.PublishDate, _dateTimeProvider.Now); + } + + if (newVisibility.HasValue) + { + post.SetVisibility(newVisibility.Value, _dateTimeProvider.Now); + } + await _postRepository.UpdateAsync(post); - await _messageBroker.PublishAsync(new PostUpdated(command.PostId, post.MediaFiles)); + // Publish the post updated event + await _messageBroker.PublishAsync(new PostUpdated(post.Id, post.MediaFiles.Select(mf => new Guid(mf)))); } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/SearchPosts.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/SearchPosts.cs deleted file mode 100644 index e6776a5a0..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/SearchPosts.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Convey.CQRS.Commands; -using MiniSpace.Services.Posts.Application.Dto; - -namespace MiniSpace.Services.Posts.Application.Commands -{ - public class SearchPosts : ICommand - { - public Guid StudentId { get; set; } - public PageableDto Pageable { get; set; } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs index e180055a3..ff6f50f15 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs @@ -1,18 +1,36 @@ using Convey.CQRS.Commands; +using MiniSpace.Services.Posts.Core.Entities; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Application.Commands { public class UpdatePost : ICommand { public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } public string TextContent { get; } - public IEnumerable MediaFiles { get; } + public IEnumerable MediaFiles { get; } + public string State { get; } + public DateTime? PublishDate { get; } + public PostContext Context { get; } + public string Visibility { get; } - public UpdatePost(Guid postId, string textContent, IEnumerable mediaFiles) + public UpdatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFiles, string state, DateTime? publishDate, PostContext context, string visibility) { PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; TextContent = textContent; MediaFiles = mediaFiles; + State = state; + PublishDate = publishDate; + Context = context; + Visibility = visibility; } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs index 2003cd5e3..c2dafca90 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using MiniSpace.Services.Posts.Core.Entities; @@ -7,14 +9,17 @@ namespace MiniSpace.Services.Posts.Application.Dto public class PostDto { public Guid Id { get; set; } - public Guid EventId { get; set; } - public Guid OrganizerId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } public string TextContent { get; set; } - public IEnumerable MediaFiles { get; set; } + public IEnumerable MediaFiles { get; set; } public string State { get; set; } public DateTime? PublishDate { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } + public string Context { get; set; } + public string Visibility { get; set; } public PostDto() { @@ -23,14 +28,17 @@ public PostDto() public PostDto(Post post) { Id = post.Id; + UserId = post.UserId; + OrganizationId = post.OrganizationId; EventId = post.EventId; - OrganizerId = post.OrganizerId; TextContent = post.TextContent; MediaFiles = post.MediaFiles; State = post.State.ToString(); PublishDate = post.PublishDate; CreatedAt = post.CreatedAt; UpdatedAt = post.UpdatedAt; + Context = post.Context.ToString(); + Visibility = post.Visibility.ToString(); } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventCreated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventCreated.cs deleted file mode 100644 index 73a98a944..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventCreated.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Posts.Application.Events.External -{ - [Message("events")] - public class EventCreated : IEvent - { - public Guid EventId { get; } - public Guid OrganizerId { get; } - - public EventCreated(Guid eventId, Guid organizerId) - { - EventId = eventId; - OrganizerId = organizerId; - } - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventCreatedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventCreatedHandler.cs deleted file mode 100644 index bcbc02e10..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventCreatedHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Convey.CQRS.Events; -using MiniSpace.Services.Posts.Application.Exceptions; -using MiniSpace.Services.Posts.Core.Entities; -using MiniSpace.Services.Posts.Core.Repositories; - -namespace MiniSpace.Services.Posts.Application.Events.External.Handlers -{ - public class EventCreatedHandler : IEventHandler - { - private readonly IEventRepository _eventRepository; - - public EventCreatedHandler(IEventRepository eventRepository) - { - _eventRepository = eventRepository; - } - - public async Task HandleAsync(EventCreated @event, CancellationToken cancellationToken = default) - { - if (await _eventRepository.ExistsAsync(@event.EventId)) - { - throw new EventAlreadyAddedException(@event.EventId); - } - - await _eventRepository.AddAsync(new Event(@event.EventId, @event.OrganizerId)); - } - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs index 0272f096c..9fbb88231 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs @@ -1,39 +1,42 @@ using Convey.CQRS.Commands; using Convey.CQRS.Events; using MiniSpace.Services.Posts.Application.Commands; -using MiniSpace.Services.Posts.Application.Exceptions; +using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Application.Events.External.Handlers { public class EventDeletedHandler : IEventHandler { - private readonly IEventRepository _eventRepository; - private readonly IPostRepository _postRepository; + private readonly IUserEventPostRepository _userEventPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; private readonly ICommandDispatcher _commandDispatcher; - public EventDeletedHandler(IEventRepository eventRepository, IPostRepository postRepository, + public EventDeletedHandler( + IUserEventPostRepository userEventPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, ICommandDispatcher commandDispatcher) { - _eventRepository = eventRepository; - _postRepository = postRepository; + _userEventPostRepository = userEventPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; _commandDispatcher = commandDispatcher; } - + public async Task HandleAsync(EventDeleted @event, CancellationToken cancellationToken = default) { - if (!(await _eventRepository.ExistsAsync(@event.EventId))) + var userPosts = await _userEventPostRepository.GetByEventIdAsync(@event.EventId); + foreach (var post in userPosts) { - throw new EventNotFoundException(@event.EventId); + await _commandDispatcher.SendAsync(new DeletePost(post.Id, post.UserId, null, post.EventId, PostContext.EventPage.ToString())); } - - var posts = await _postRepository.GetByEventIdAsync(@event.EventId); - foreach (var post in posts) + + var organizationPosts = await _organizationEventPostRepository.GetByEventIdAsync(@event.EventId); + foreach (var post in organizationPosts) { - await _commandDispatcher.SendAsync(new DeletePost(post.Id)); + await _commandDispatcher.SendAsync(new DeletePost(post.Id, null, post.OrganizationId, post.EventId, PostContext.EventPage.ToString())); } - - await _eventRepository.DeleteAsync(@event.EventId); } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs index 0918818e5..9e195b729 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs @@ -4,7 +4,7 @@ namespace MiniSpace.Services.Posts.Application.Events.External.Handlers { - public class MediaFileDeletedHandler: IEventHandler + public class MediaFileDeletedHandler : IEventHandler { private readonly IPostRepository _postRepository; private readonly IDateTimeProvider _dateTimeProvider; @@ -17,17 +17,26 @@ public MediaFileDeletedHandler(IPostRepository postRepository, IDateTimeProvider public async Task HandleAsync(MediaFileDeleted @event, CancellationToken cancellationToken) { - if(@event.Source.ToLowerInvariant() != "post") + var relevantContextTypes = new[] + { + "PostFileUserEvent", + "PostFileUser", + "PostFileOrganizationEvent", + "PostFileOrganization", + "PostFile" + }; + + if (!relevantContextTypes.Contains(@event.Source) || !@event.PostId.HasValue) { return; } - var post = await _postRepository.GetAsync(@event.SourceId); + var post = await _postRepository.GetAsync(@event.PostId.Value); if (post != null) { - post.RemoveMediaFile(@event.MediaFileId, _dateTimeProvider.Now); + post.RemoveMediaFile(@event.MediaFileUrl, _dateTimeProvider.Now); await _postRepository.UpdateAsync(post); } } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs index f79ca8647..8f48b76ee 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs @@ -4,17 +4,27 @@ namespace MiniSpace.Services.Posts.Application.Events.External { [Message("mediafiles")] - public class MediaFileDeleted: IEvent + public class MediaFileDeleted : IEvent { - public Guid MediaFileId { get; } + public string MediaFileUrl { get; } public Guid SourceId { get; } public string Source { get; } + public Guid UploaderId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public Guid? PostId { get; } - public MediaFileDeleted(Guid mediaFileId, Guid sourceId, string source) + public MediaFileDeleted(string mediaFileUrl, Guid sourceId, string source, + Guid uploaderId, Guid? organizationId, + Guid? eventId = null, Guid? postId = null) { - MediaFileId = mediaFileId; + MediaFileUrl = mediaFileUrl; SourceId = sourceId; Source = source; + UploaderId = uploaderId; + OrganizationId = organizationId; + EventId = eventId; + PostId = postId; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs index bf44c36ed..c707f4ef7 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs @@ -5,12 +5,12 @@ namespace MiniSpace.Services.Posts.Application.Events public class PostCreated : IEvent { public Guid PostId { get; } - public IEnumerable MediaFilesIds { get; } + public IEnumerable MediaFilesUrls { get; } - public PostCreated(Guid postId, IEnumerable mediaFilesIds) + public PostCreated(Guid postId, IEnumerable mediaFilesUrls) { PostId = postId; - MediaFilesIds = mediaFilesIds; + MediaFilesUrls = mediaFilesUrls; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidBrowseRequestException.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidBrowseRequestException.cs new file mode 100644 index 000000000..7a1fe67b0 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidBrowseRequestException.cs @@ -0,0 +1,12 @@ +namespace MiniSpace.Services.Posts.Application.Exceptions +{ + public class InvalidBrowseRequestException : AppException + { + public override string Code { get; } = "invalid_browse_request"; + + public InvalidBrowseRequestException(string message) + : base(message) + { + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidPostContextException.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidPostContextException.cs new file mode 100644 index 000000000..50514c0e2 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidPostContextException.cs @@ -0,0 +1,17 @@ +using System; +using MiniSpace.Services.Posts.Application.Exceptions; + +namespace MiniSpace.Services.Posts.Application.Exceptions +{ + public class InvalidPostContextException : AppException + { + public override string Code { get; } = "invalid_post_context"; + public string Context { get; } + + public InvalidPostContextException(string context) + : base($"The post context '{context}' is invalid.") + { + Context = context; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidVisibilityStatusException.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidVisibilityStatusException.cs new file mode 100644 index 000000000..545bcefa5 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/InvalidVisibilityStatusException.cs @@ -0,0 +1,14 @@ +namespace MiniSpace.Services.Posts.Application.Exceptions +{ + public class InvalidVisibilityStatusException : AppException + { + public override string Code { get; } = "invalid_visibility_status"; + public string Visibility { get; } + + public InvalidVisibilityStatusException(string visibility) + : base($"Invalid visibility status: '{visibility}'. It must be either 'Visible' or 'Invisible'.") + { + Visibility = visibility; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/IIdentityContext.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/IIdentityContext.cs index 6c3cc1963..bd2aaaf8b 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/IIdentityContext.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/IIdentityContext.cs @@ -9,7 +9,6 @@ public interface IIdentityContext bool IsAuthenticated { get; } bool IsAdmin { get; } bool IsBanned { get; } - bool IsOrganizer { get; } IDictionary Claims { get; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.sln b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.sln new file mode 100644 index 000000000..ef566b8c3 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Posts.Application", "MiniSpace.Services.Posts.Application.csproj", "{443F4D7C-8847-43BE-9300-3FD3324426C4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {443F4D7C-8847-43BE-9300-3FD3324426C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {443F4D7C-8847-43BE-9300-3FD3324426C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443F4D7C-8847-43BE-9300-3FD3324426C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {443F4D7C-8847-43BE-9300-3FD3324426C4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0A5601A8-B8F9-4622-B9DA-6FE229EF142A} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs index a06a7d95f..027485e29 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs @@ -1,12 +1,19 @@ -using System.Diagnostics.CodeAnalysis; using Convey.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; +using MiniSpace.Services.Posts.Core.Wrappers; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Application.Queries { - [ExcludeFromCodeCoverage] - public class GetPosts : IQuery> + public class GetPosts : IQuery> { - public Guid EventId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public int PageNumber { get; set; } = 1; + public int PageSize { get; set; } = 10; + public string SortBy { get; set; } = "PublishDate"; + public string Direction { get; set; } = "asc"; } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs index 6c2dc71ce..6f5c9baf8 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs @@ -1,11 +1,16 @@ -using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Dto; +using MiniSpace.Services.Posts.Core.Requests; using MiniSpace.Services.Posts.Core.Wrappers; namespace MiniSpace.Services.Posts.Application.Services { public interface IPostsService { - Task>> BrowsePostsAsync(SearchPosts command); + /// + /// Browses posts based on the given request parameters. + /// + /// The browsing request containing filtering, sorting, and pagination information. + /// A paged response containing the posts matching the criteria. + Task> BrowsePostsAsync(BrowseRequest request); } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Event.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Event.cs deleted file mode 100644 index 8bd629cb5..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Event.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MiniSpace.Services.Posts.Core.Entities -{ - public class Event - { - public Guid Id { get; private set; } - public Guid OrganizerId { get; private set; } - - public Event(Guid id, Guid organizerId) - { - Id = id; - OrganizerId = organizerId; - } - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationEventPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationEventPost.cs new file mode 100644 index 000000000..fbe69d184 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationEventPost.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Services.Posts.Core.Events; + +namespace MiniSpace.Services.Posts.Core.Entities +{ + public class OrganizationEventPost : AggregateRoot + { + public Guid OrganizationId { get; private set; } + public List EventPosts { get; private set; } + + public OrganizationEventPost(Guid id, Guid organizationId, List eventPosts) + { + Id = id; + OrganizationId = organizationId; + EventPosts = eventPosts; + } + + public void AddEventPost(Post post) + { + EventPosts.Add(post); + AddEvent(new OrganizationEventPostAddedEvent(Id, post.Id)); + } + + public void RemoveEventPost(Guid postId) + { + var post = EventPosts.FirstOrDefault(p => p.Id == postId); + if (post != null) + { + EventPosts.Remove(post); + AddEvent(new OrganizationEventPostRemovedEvent(Id, postId)); + } + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationPost.cs new file mode 100644 index 000000000..8917513c4 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/OrganizationPost.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Services.Posts.Core.Events; + +namespace MiniSpace.Services.Posts.Core.Entities +{ + public class OrganizationPost : AggregateRoot + { + public Guid OrganizationId { get; private set; } + public List OrganizationPosts { get; private set; } + + public OrganizationPost(Guid id, Guid organizationId, List organizationPosts) + { + Id = id; + OrganizationId = organizationId; + OrganizationPosts = organizationPosts; + } + + public void AddOrganizationPost(Post post) + { + OrganizationPosts.Add(post); + AddEvent(new OrganizationEventPostAddedEvent(Id, post.Id)); + } + + public void RemoveOrganizationPost(Guid postId) + { + var post = OrganizationPosts.FirstOrDefault(p => p.Id == postId); + if (post != null) + { + OrganizationPosts.Remove(post); + AddEvent(new OrganizationPostRemovedEvent(Id, postId)); + } + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs index f9a9533e3..0dc5b9df5 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs @@ -1,31 +1,43 @@ +using MiniSpace.Services.Posts.Core.Events; using MiniSpace.Services.Posts.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; namespace MiniSpace.Services.Posts.Core.Entities { public class Post : AggregateRoot { - public Guid EventId { get; private set; } - public Guid OrganizerId { get; private set; } + public Guid? UserId { get; private set; } + public Guid? OrganizationId { get; private set; } + public Guid? EventId { get; private set; } public string TextContent { get; private set; } - public IEnumerable MediaFiles { get; private set; } + public IEnumerable MediaFiles { get; private set; } public State State { get; private set; } public DateTime? PublishDate { get; private set; } public DateTime CreatedAt { get; private set; } public DateTime? UpdatedAt { get; private set; } - - public Post(Guid id, Guid eventId, Guid organizerId, string textContent, - IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, - DateTime? updatedAt = null) + public PostContext Context { get; private set; } + public VisibilityStatus Visibility { get; private set; } // New visibility status property + + public Post(Guid id, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, PostContext context, DateTime? publishDate, + VisibilityStatus visibility = VisibilityStatus.Visible, DateTime? updatedAt = null) { Id = id; + UserId = userId; + OrganizationId = organizationId; EventId = eventId; - OrganizerId = organizerId; TextContent = textContent; - MediaFiles = mediaFiles; + MediaFiles = mediaFiles ?? new List(); CreatedAt = createdAt; UpdatedAt = updatedAt; State = state; PublishDate = publishDate; + Context = context; + Visibility = visibility; + + AddEvent(new PostCreatedEvent(Id)); } public void SetToBePublished(DateTime publishDate, DateTime now) @@ -34,15 +46,19 @@ public void SetToBePublished(DateTime publishDate, DateTime now) State = State.ToBePublished; PublishDate = publishDate; UpdatedAt = now; + + AddEvent(new PostPublishedEvent(Id)); } - + public void SetPublished(DateTime now) { State = State.Published; PublishDate = now; UpdatedAt = now; + + AddEvent(new PostPublishedEvent(Id)); } - + public void SetInDraft(DateTime now) { State = State.InDraft; @@ -57,6 +73,15 @@ public void SetReported(DateTime now) UpdatedAt = now; } + public void SetVisibility(VisibilityStatus visibility, DateTime now) + { + Visibility = visibility; + UpdatedAt = now; + + AddEvent(new PostVisibilityChangedEvent(Id, visibility, now)); + } + + public bool UpdateState(DateTime now) { if (State == State.ToBePublished && PublishDate <= now) @@ -64,37 +89,86 @@ public bool UpdateState(DateTime now) SetPublished(now); return true; } - + return false; } - - public static Post Create(AggregateId id, Guid eventId, Guid studentId, string textContent, - IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate) + + public void ChangeState(State newState, DateTime? publishDate, DateTime now) + { + if (newState == State.ToBePublished && publishDate.HasValue) + { + SetToBePublished(publishDate.Value, now); + } + else if (newState == State.Published) + { + SetPublished(now); + } + else if (newState == State.InDraft) + { + SetInDraft(now); + } + else if (newState == State.Reported) + { + SetReported(now); + } + else + { + throw new InvalidPostStateException(newState.ToString()); + } + } + + public static Post CreateForUser(Guid id, Guid userId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) + { + CheckTextContent(id, textContent); + + return new Post(id, userId, null, null, textContent, mediaFiles, createdAt, state, PostContext.UserPage, + publishDate ?? createdAt, visibility); + } + + public static Post CreateForOrganization(Guid id, Guid organizationId, Guid? userId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) { CheckTextContent(id, textContent); - return new Post(id, eventId, studentId, textContent, mediaFiles, createdAt, state, - publishDate ?? createdAt); + return new Post(id, userId, organizationId, null, textContent, mediaFiles, createdAt, state, PostContext.OrganizationPage, + publishDate ?? createdAt, visibility); } - public void Update(string textContent, IEnumerable mediaFiles, DateTime now) + public static Post CreateForEvent(Guid id, Guid eventId, Guid? userId, Guid? organizationId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) + { + CheckTextContent(id, textContent); + + return new Post(id, userId, organizationId, eventId, textContent, mediaFiles, createdAt, state, PostContext.EventPage, + publishDate ?? createdAt, visibility); + } + + + public void Update(string textContent, IEnumerable mediaFiles, DateTime now) { CheckTextContent(Id, textContent); TextContent = textContent; MediaFiles = mediaFiles; UpdatedAt = now; + + AddEvent(new PostUpdatedEvent(Id)); } - - public void RemoveMediaFile(Guid mediaFileId, DateTime now) + + public void RemoveMediaFile(string mediaFileUrl, DateTime now) { - var mediaFile = MediaFiles.SingleOrDefault(mf => mf == mediaFileId); - if (mediaFile == Guid.Empty) + if (!MediaFiles.Contains(mediaFileUrl)) { - throw new MediaFileNotFoundException(mediaFileId, Id); + throw new MediaFileNotFoundException(mediaFileUrl, Id); } + + MediaFiles = MediaFiles.Where(mf => mf != mediaFileUrl).ToList(); + UpdatedAt = now; + + // Raise an event if necessary (e.g., PostMediaFileRemovedEvent) } - + private static void CheckTextContent(AggregateId id, string textContent) { if (string.IsNullOrWhiteSpace(textContent) || textContent.Length > 5000) @@ -110,5 +184,5 @@ private static void CheckPublishDate(AggregateId id, State state, DateTime publi throw new InvalidPostPublishDateException(id, state, publishDate, now); } } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostContext.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostContext.cs new file mode 100644 index 000000000..94c528048 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostContext.cs @@ -0,0 +1,9 @@ +namespace MiniSpace.Services.Posts.Core.Entities +{ + public enum PostContext + { + UserPage, + OrganizationPage, + EventPage + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserEventPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserEventPost.cs new file mode 100644 index 000000000..9ca272596 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserEventPost.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Services.Posts.Core.Events; + +namespace MiniSpace.Services.Posts.Core.Entities +{ + public class UserEventPost : AggregateRoot + { + public Guid UserId { get; private set; } + public List UserEventPosts { get; private set; } + + public UserEventPost(Guid id, Guid userId, List userEventPosts) + { + Id = id; + UserId = userId; + UserEventPosts = userEventPosts; + } + + public void AddUserEventPost(Post post) + { + UserEventPosts.Add(post); + AddEvent(new UserEventPostAddedEvent(Id, post.Id)); + } + + public void RemoveUserEventPost(Guid postId) + { + var post = UserEventPosts.FirstOrDefault(p => p.Id == postId); + if (post != null) + { + UserEventPosts.Remove(post); + AddEvent(new UserEventPostRemovedEvent(Id, postId)); + } + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserPost.cs new file mode 100644 index 000000000..ea967e7ec --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/UserPost.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Services.Posts.Core.Events; + +namespace MiniSpace.Services.Posts.Core.Entities +{ + public class UserPost : AggregateRoot + { + public Guid UserId { get; private set; } + public List UserPosts { get; private set; } + + public UserPost(Guid id, Guid userId, List userPosts) + { + Id = id; + UserId = userId; + UserPosts = userPosts; + } + + public void AddUserPost(Post post) + { + UserPosts.Add(post); + AddEvent(new UserPostAddedEvent(Id, post.Id)); + } + + public void RemoveUserPost(Guid postId) + { + var post = UserPosts.FirstOrDefault(p => p.Id == postId); + if (post != null) + { + UserPosts.Remove(post); + AddEvent(new UserPostRemovedEvent(Id, postId)); + } + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/VisibilityStatus.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/VisibilityStatus.cs new file mode 100644 index 000000000..bda017c4b --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/VisibilityStatus.cs @@ -0,0 +1,8 @@ +namespace MiniSpace.Services.Posts.Core.Entities +{ + public enum VisibilityStatus + { + Visible, + Invisible + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostAddedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostAddedEvent.cs new file mode 100644 index 000000000..88a551897 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostAddedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class OrganizationEventPostAddedEvent : IDomainEvent + { + public Guid OrganizationEventPostId { get; } + public Guid PostId { get; } + + public OrganizationEventPostAddedEvent(Guid organizationEventPostId, Guid postId) + { + OrganizationEventPostId = organizationEventPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostRemovedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostRemovedEvent.cs new file mode 100644 index 000000000..7df293e93 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationEventPostRemovedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class OrganizationEventPostRemovedEvent : IDomainEvent + { + public Guid OrganizationEventPostId { get; } + public Guid PostId { get; } + + public OrganizationEventPostRemovedEvent(Guid organizationEventPostId, Guid postId) + { + OrganizationEventPostId = organizationEventPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationPostRemovedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationPostRemovedEvent.cs new file mode 100644 index 000000000..c1dc46b2e --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/OrganizationPostRemovedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class OrganizationPostRemovedEvent : IDomainEvent + { + public Guid OrganizationPostId { get; } + public Guid PostId { get; } + + public OrganizationPostRemovedEvent(Guid organizationPostId, Guid postId) + { + OrganizationPostId = organizationPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostCreatedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostCreatedEvent.cs new file mode 100644 index 000000000..245a6e6da --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostCreatedEvent.cs @@ -0,0 +1,14 @@ +namespace MiniSpace.Services.Posts.Core.Events +{ + public class PostCreatedEvent : IDomainEvent + { + public Guid PostId { get; } + public DateTime OccurredOn { get; } + + public PostCreatedEvent(Guid postId) + { + PostId = postId; + OccurredOn = DateTime.UtcNow; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostPublishedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostPublishedEvent.cs new file mode 100644 index 000000000..0e1fc1715 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostPublishedEvent.cs @@ -0,0 +1,14 @@ +namespace MiniSpace.Services.Posts.Core.Events +{ + public class PostPublishedEvent : IDomainEvent + { + public Guid PostId { get; } + public DateTime OccurredOn { get; } + + public PostPublishedEvent(Guid postId) + { + PostId = postId; + OccurredOn = DateTime.UtcNow; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostUpdatedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostUpdatedEvent.cs new file mode 100644 index 000000000..22316cf57 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostUpdatedEvent.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class PostUpdatedEvent : IDomainEvent + { + public Guid PostId { get; } + public DateTime OccurredOn { get; } + + public PostUpdatedEvent(Guid postId) + { + PostId = postId; + OccurredOn = DateTime.UtcNow; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostVisibilityChangedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostVisibilityChangedEvent.cs new file mode 100644 index 000000000..c3dda76ca --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostVisibilityChangedEvent.cs @@ -0,0 +1,19 @@ +using System; +using MiniSpace.Services.Posts.Core.Entities; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class PostVisibilityChangedEvent : IDomainEvent + { + public Guid PostId { get; } + public VisibilityStatus Visibility { get; } + public DateTime ChangedAt { get; } + + public PostVisibilityChangedEvent(Guid postId, VisibilityStatus visibility, DateTime changedAt) + { + PostId = postId; + Visibility = visibility; + ChangedAt = changedAt; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostAddedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostAddedEvent.cs new file mode 100644 index 000000000..0e6a93558 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostAddedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class UserEventPostAddedEvent : IDomainEvent + { + public Guid UserEventPostId { get; } + public Guid PostId { get; } + + public UserEventPostAddedEvent(Guid userEventPostId, Guid postId) + { + UserEventPostId = userEventPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostRemovedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostRemovedEvent.cs new file mode 100644 index 000000000..5218bff02 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserEventPostRemovedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class UserEventPostRemovedEvent : IDomainEvent + { + public Guid UserEventPostId { get; } + public Guid PostId { get; } + + public UserEventPostRemovedEvent(Guid userEventPostId, Guid postId) + { + UserEventPostId = userEventPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostAddedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostAddedEvent.cs new file mode 100644 index 000000000..65b363544 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostAddedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class UserPostAddedEvent : IDomainEvent + { + public Guid UserPostId { get; } + public Guid PostId { get; } + + public UserPostAddedEvent(Guid userPostId, Guid postId) + { + UserPostId = userPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostRemovedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostRemovedEvent.cs new file mode 100644 index 000000000..46eb55961 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/UserPostRemovedEvent.cs @@ -0,0 +1,16 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class UserPostRemovedEvent : IDomainEvent + { + public Guid UserPostId { get; } + public Guid PostId { get; } + + public UserPostRemovedEvent(Guid userPostId, Guid postId) + { + UserPostId = userPostId; + PostId = postId; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/MediaFileNotFoundException.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/MediaFileNotFoundException.cs index 4a68fdb63..d46d5d15f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/MediaFileNotFoundException.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/MediaFileNotFoundException.cs @@ -3,13 +3,13 @@ public class MediaFileNotFoundException : DomainException { public override string Code { get; } = "media_file_not_found"; - public Guid MediaFileId { get; } + public string MediaFileUrl { get; } public Guid PostId { get; } - public MediaFileNotFoundException(Guid mediaFileId, Guid postId) - : base($"Media file with ID: '{mediaFileId}' for post with ID: {postId} was not found.") + public MediaFileNotFoundException(string mediaFileUrl, Guid postId) + : base($"Media file with ID: '{mediaFileUrl}' for post with ID: {postId} was not found.") { - MediaFileId = mediaFileId; + MediaFileUrl = mediaFileUrl; PostId = postId; } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/MiniSpace.Services.Posts.Core.sln b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/MiniSpace.Services.Posts.Core.sln new file mode 100644 index 000000000..686f20093 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/MiniSpace.Services.Posts.Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Posts.Core", "MiniSpace.Services.Posts.Core.csproj", "{627DB8C1-593C-4D99-8821-A831E8F985D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {627DB8C1-593C-4D99-8821-A831E8F985D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {627DB8C1-593C-4D99-8821-A831E8F985D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {627DB8C1-593C-4D99-8821-A831E8F985D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {627DB8C1-593C-4D99-8821-A831E8F985D0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD65EA7D-A693-4CF3-BB6C-608ABCAD87AC} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IEventRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IEventRepository.cs deleted file mode 100644 index 7ce4f9310..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IEventRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using MiniSpace.Services.Posts.Core.Entities; - -namespace MiniSpace.Services.Posts.Core.Repositories -{ - public interface IEventRepository - { - Task GetAsync(Guid id); - Task ExistsAsync(Guid id); - Task AddAsync(Event @event); - Task DeleteAsync(Guid id); - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationEventPostRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationEventPostRepository.cs new file mode 100644 index 000000000..f1e6fccbc --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationEventPostRepository.cs @@ -0,0 +1,12 @@ +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; + +namespace MiniSpace.Services.Posts.Core.Repositories +{ + public interface IOrganizationEventPostRepository : IPostRepository + { + Task> GetByOrganizationEventIdAsync(Guid organizationId, Guid eventId); + Task> BrowseOrganizationEventPostsAsync(BrowseRequest request); + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationPostRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationPostRepository.cs new file mode 100644 index 000000000..a6aa6eab5 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IOrganizationPostRepository.cs @@ -0,0 +1,12 @@ +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; + +namespace MiniSpace.Services.Posts.Core.Repositories +{ + public interface IOrganizationPostRepository : IPostRepository + { + Task> GetByOrganizationIdAsync(Guid organizationId); + Task> BrowseOrganizationPostsAsync(BrowseRequest request); + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IPostRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IPostRepository.cs index 96ef96f4d..d66940bf6 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IPostRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IPostRepository.cs @@ -1,4 +1,6 @@ using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; namespace MiniSpace.Services.Posts.Core.Repositories { @@ -11,7 +13,6 @@ public interface IPostRepository Task UpdateAsync(Post post); Task DeleteAsync(Guid id); Task ExistsAsync(Guid id); - Task<(IEnumerable posts, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseCommentsAsync(int pageNumber, int pageSize, - IEnumerable eventsIds, IEnumerable sortBy, string direction); + Task> BrowsePostsAsync(BrowseRequest request); } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserEventPostRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserEventPostRepository.cs new file mode 100644 index 000000000..e83364102 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserEventPostRepository.cs @@ -0,0 +1,12 @@ +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; + +namespace MiniSpace.Services.Posts.Core.Repositories +{ + public interface IUserEventPostRepository : IPostRepository + { + Task> GetByUserEventIdAsync(Guid userId, Guid eventId); + Task> BrowseUserEventPostsAsync(BrowseRequest request); + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserPostRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserPostRepository.cs new file mode 100644 index 000000000..b7a33e569 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Repositories/IUserPostRepository.cs @@ -0,0 +1,12 @@ +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; + +namespace MiniSpace.Services.Posts.Core.Repositories +{ + public interface IUserPostRepository : IPostRepository + { + Task> GetByUserIdAsync(Guid userId); + Task> BrowseUserPostsAsync(BrowseRequest request); + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/BrowseRequest.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/BrowseRequest.cs new file mode 100644 index 000000000..e8f8b987b --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/BrowseRequest.cs @@ -0,0 +1,13 @@ +namespace MiniSpace.Services.Posts.Core.Requests +{ + public class BrowseRequest + { + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public Guid? UserId { get; set; } + public int PageNumber { get; set; } = 1; + public int PageSize { get; set; } = 10; + public IEnumerable SortBy { get; set; } + public string Direction { get; set; } = "asc"; + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/PagedResponse.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/PagedResponse.cs index bc82a2d4a..edd83eebe 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/PagedResponse.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Wrappers/PagedResponse.cs @@ -1,28 +1,28 @@ namespace MiniSpace.Services.Posts.Core.Wrappers { - public class PagedResponse : Response + public class PagedResponse { + public IEnumerable Items { get; } public int TotalPages { get; } - public int TotalElements { get; } - public int Size { get; } - public int Number { get; } + public int TotalItems { get; } + public int PageSize { get; } + public int Page { get; } public bool First { get; } public bool Last { get; } public bool Empty { get; } + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; - public PagedResponse(T content, int pageNumber, int pageSize, int totalPages, int totalElements) + public PagedResponse(IEnumerable items, int page, int pageSize, int totalItems) { - Content = content; - TotalPages = totalPages; - TotalElements = totalElements; - Size = pageSize; - Number = pageNumber; - First = pageNumber == 0; - Last = pageNumber == totalPages - 1; - Empty = totalElements == 0; - Succeeded = true; - Errors = null; - Message = null; + Items = items; + PageSize = pageSize; + TotalItems = totalItems; + TotalPages = pageSize > 0 ? (int)Math.Ceiling((decimal)totalItems / pageSize) : 0; + Page = page; + First = page == 1; + Last = page == TotalPages; + Empty = !items.Any(); } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs index 61531b2c8..f57c4672a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs @@ -50,13 +50,16 @@ public static class Extensions { public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) { - builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); @@ -78,7 +81,10 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) .AddMetrics() .AddJaeger() .AddHandlersLogging() - .AddMongoRepository("events") + .AddMongoRepository("organization_events_posts") + .AddMongoRepository("organization_posts") + .AddMongoRepository("user_events_posts") + .AddMongoRepository("user_posts") .AddMongoRepository("posts") .AddWebApiSwaggerDocs() .AddCertificateAuthentication() @@ -100,8 +106,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .SubscribeCommand() .SubscribeCommand() .SubscribeCommand() - .SubscribeEvent() - .SubscribeEvent(); + .SubscribeEvent(); return app; } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs index a3aa98da6..c67be8004 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -36,12 +36,6 @@ private static IReadOnlyDictionary MessageTemplates After = "Changed a post with id: {PostId} state to: {State}." } }, - { - typeof(EventCreated), new HandlerLogTemplate - { - After = "Created a new event with id: {EventId}." - } - }, { typeof(EventDeleted), new HandlerLogTemplate { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.sln b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.sln new file mode 100644 index 000000000..3a4df4ab2 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Posts.Infrastructure", "MiniSpace.Services.Posts.Infrastructure.csproj", "{C82B3C2F-CE38-4F86-BDE3-4503C2DF65BA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C82B3C2F-CE38-4F86-BDE3-4503C2DF65BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C82B3C2F-CE38-4F86-BDE3-4503C2DF65BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C82B3C2F-CE38-4F86-BDE3-4503C2DF65BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C82B3C2F-CE38-4F86-BDE3-4503C2DF65BA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C897CEDC-4408-4CED-A046-7FE644C53E5E} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/EventDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/EventDocument.cs deleted file mode 100644 index be59fb7b1..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/EventDocument.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Types; - -namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public class EventDocument : IIdentifiable - { - public Guid Id { get; set; } - public Guid OrganizerId { get; set; } - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs index c1e3ec5c9..2a40e02a2 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs @@ -8,45 +8,51 @@ namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents public static class Extensions { public static Post AsEntity(this PostDocument document) - => new Post(document.Id, document.EventId, document.OrganizerId, document.TextContent, - document.MediaFiles, document.CreatedAt, document.State, document.PublishDate, document.UpdatedAt); + => new Post( + document.Id, + document.UserId, + document.OrganizationId, + document.EventId, + document.TextContent, + document.MediaFiles, + document.CreatedAt, + document.State, + document.Context, + document.PublishDate, + document.Visibility, + document.UpdatedAt); public static PostDocument AsDocument(this Post entity) => new PostDocument() { Id = entity.Id, + UserId = entity.UserId, + OrganizationId = entity.OrganizationId, EventId = entity.EventId, - OrganizerId = entity.OrganizerId, TextContent = entity.TextContent, MediaFiles = entity.MediaFiles, CreatedAt = entity.CreatedAt, UpdatedAt = entity.UpdatedAt, State = entity.State, - PublishDate = entity.PublishDate + PublishDate = entity.PublishDate, + Context = entity.Context, + Visibility = entity.Visibility }; public static PostDto AsDto(this PostDocument document) => new PostDto() { Id = document.Id, + UserId = document.UserId, + OrganizationId = document.OrganizationId, EventId = document.EventId, - OrganizerId = document.OrganizerId, TextContent = document.TextContent, MediaFiles = document.MediaFiles, CreatedAt = document.CreatedAt, UpdatedAt = document.UpdatedAt, State = document.State.ToString().ToLowerInvariant(), - PublishDate = document.PublishDate + PublishDate = document.PublishDate, + Visibility = document.Visibility.ToString().ToLowerInvariant() }; - - public static Event AsEntity(this EventDocument document) - => new Event(document.Id, document.OrganizerId); - - public static EventDocument AsDocument(this Event entity) - => new EventDocument - { - Id = entity.Id, - OrganizerId = entity.OrganizerId - }; - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs new file mode 100644 index 000000000..79de08c61 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Types; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class OrganizationEventPostDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationId { get; set; } + public IEnumerable EventPosts { get; set; } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostExtensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostExtensions.cs new file mode 100644 index 000000000..916336c96 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostExtensions.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Posts.Core.Entities; +using System.Collections.Generic; +using MongoDB.Driver; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public static class OrganizationEventPostExtensions + { + public static OrganizationEventPost AsEntity(this OrganizationEventPostDocument document) + => new OrganizationEventPost(document.Id, document.OrganizationId, document.EventPosts.Select(p => p.AsEntity()).ToList()); + + public static OrganizationEventPostDocument AsDocument(this OrganizationEventPost entity) + => new OrganizationEventPostDocument + { + Id = entity.Id, + OrganizationId = entity.OrganizationId, + EventPosts = entity.EventPosts.Select(p => p.AsDocument()).ToList() + }; + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string direction) + { + var builder = Builders.Sort; + var sortDefinitions = new List>(); + + foreach (var sortField in sortBy) + { + var fieldSort = direction.ToLower() == "desc" ? builder.Descending(sortField) : builder.Ascending(sortField); + sortDefinitions.Add(fieldSort); + } + + return builder.Combine(sortDefinitions); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs new file mode 100644 index 000000000..3aabf819d --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Types; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class OrganizationPostDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationId { get; set; } + public IEnumerable OrganizationPosts { get; set; } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostExtensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostExtensions.cs new file mode 100644 index 000000000..fc3b4f33f --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostExtensions.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Posts.Core.Entities; +using System.Collections.Generic; +using MongoDB.Driver; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public static class OrganizationPostExtensions + { + public static OrganizationPost AsEntity(this OrganizationPostDocument document) + => new OrganizationPost(document.Id, document.OrganizationId, document.OrganizationPosts.Select(p => p.AsEntity()).ToList()); + + public static OrganizationPostDocument AsDocument(this OrganizationPost entity) + => new OrganizationPostDocument + { + Id = entity.Id, + OrganizationId = entity.OrganizationId, + OrganizationPosts = entity.OrganizationPosts.Select(p => p.AsDocument()).ToList() + }; + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string direction) + { + var builder = Builders.Sort; + var sortDefinitions = new List>(); + + foreach (var sortField in sortBy) + { + var fieldSort = direction.ToLower() == "desc" ? builder.Descending(sortField) : builder.Ascending(sortField); + sortDefinitions.Add(fieldSort); + } + + return builder.Combine(sortDefinitions); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs index 030aa8c02..ab49a1ca6 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Convey.Types; using MiniSpace.Services.Posts.Core.Entities; +using System; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents { @@ -8,13 +9,16 @@ namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents public class PostDocument : IIdentifiable { public Guid Id { get; set; } - public Guid EventId { get; set; } - public Guid OrganizerId { get; set; } + public Guid? EventId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } public string TextContent { get; set; } - public IEnumerable MediaFiles { get; set; } + public IEnumerable MediaFiles { get; set; } public State State { get; set; } public DateTime? PublishDate { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } - } + public PostContext Context { get; set; } + public VisibilityStatus Visibility { get; set; } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs new file mode 100644 index 000000000..ebff8b3c9 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Types; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class UserEventPostDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public IEnumerable UserEventPosts { get; set; } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostExtensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostExtensions.cs new file mode 100644 index 000000000..0e66065e3 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostExtensions.cs @@ -0,0 +1,39 @@ +using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Posts.Core.Entities; +using System.Collections.Generic; +using MongoDB.Driver; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public static class UserEventPostExtensions + { + public static UserEventPost AsEntity(this UserEventPostDocument document) + => new UserEventPost(document.Id, document.UserId, document.UserEventPosts.Select(p => p.AsEntity()).ToList()); + + public static UserEventPostDocument AsDocument(this UserEventPost entity) + => new UserEventPostDocument + { + Id = entity.Id, + UserId = entity.UserId, + UserEventPosts = entity.UserEventPosts.Select(p => p.AsDocument()).ToList() + }; + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string direction) + { + var builder = Builders.Sort; + var sortDefinitions = new List>(); + + foreach (var sortField in sortBy) + { + var fieldSort = direction.ToLower() == "desc" ? builder.Descending(sortField) : builder.Ascending(sortField); + sortDefinitions.Add(fieldSort); + } + + return builder.Combine(sortDefinitions); + } + + + + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs new file mode 100644 index 000000000..97dd514e3 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs @@ -0,0 +1,15 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Types; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public class UserPostDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public IEnumerable UserPosts { get; set; } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostExtensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostExtensions.cs new file mode 100644 index 000000000..a47450a5e --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostExtensions.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using MiniSpace.Services.Posts.Core.Entities; +using System.Collections.Generic; +using MongoDB.Driver; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents +{ + [ExcludeFromCodeCoverage] + public static class UserPostExtensions + { + public static UserPost AsEntity(this UserPostDocument document) + => new UserPost(document.Id, document.UserId, document.UserPosts.Select(p => p.AsEntity()).ToList()); + + public static UserPostDocument AsDocument(this UserPost entity) + => new UserPostDocument + { + Id = entity.Id, + UserId = entity.UserId, + UserPosts = entity.UserPosts.Select(p => p.AsDocument()).ToList() + }; + + public static SortDefinition ToSortDefinition(IEnumerable sortBy, string direction) + { + var builder = Builders.Sort; + var sortDefinitions = new List>(); + + foreach (var sortField in sortBy) + { + var fieldSort = direction.ToLower() == "desc" ? builder.Descending(sortField) : builder.Ascending(sortField); + sortDefinitions.Add(fieldSort); + } + + return builder.Combine(sortDefinitions); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs index 6ccc9cb59..82e083af5 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs @@ -31,7 +31,7 @@ public async Task> HandleAsync(GetOrganizerPosts query, Can } var documents = _postRepository.Collection.AsQueryable(); - documents = documents.Where(p => p.OrganizerId == query.OrganizerId); + documents = documents.Where(p => p.UserId == query.OrganizerId); var posts = await documents.ToListAsync(); return posts.Select(p => p.AsDto()); diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs index 38085f674..4abe231ed 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs @@ -1,32 +1,50 @@ -using System.Diagnostics.CodeAnalysis; using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; -using MiniSpace.Services.Posts.Core.Entities; -using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; -using MongoDB.Driver; -using MongoDB.Driver.Linq; +using MiniSpace.Services.Posts.Application.Services; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Queries.Handlers { - [ExcludeFromCodeCoverage] - public class GetPostsHandler : IQueryHandler> + public class GetPostsHandler : IQueryHandler> { - private readonly IMongoRepository _postRepository; + private readonly IPostsService _postsService; + private readonly ILogger _logger; - public GetPostsHandler(IMongoRepository postRepository) + public GetPostsHandler(IPostsService postsService, ILogger logger) { - _postRepository = postRepository; + _postsService = postsService; + _logger = logger; } - - public async Task> HandleAsync(GetPosts query, CancellationToken cancellationToken) + + public async Task> HandleAsync(GetPosts query, CancellationToken cancellationToken) { - var documents = _postRepository.Collection.AsQueryable(); - documents = documents.Where(p => p.EventId == query.EventId && p.State == State.Published); + var sortByList = new List { query.SortBy }; + + var request = new BrowseRequest + { + UserId = query.UserId, + OrganizationId = query.OrganizationId, + EventId = query.EventId, + PageNumber = query.PageNumber, + PageSize = query.PageSize, + SortBy = sortByList, + Direction = query.Direction + }; + + var result = await _postsService.BrowsePostsAsync(request); + + // Log the serialized JSON object + _logger.LogInformation("Fetched posts: {JsonResult}", JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })); - var posts = await documents.ToListAsync(); - return posts.Select(p => p.AsDto()); + return result; } - } + } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/EventMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/EventMongoRepository.cs deleted file mode 100644 index d98f1633b..000000000 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/EventMongoRepository.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Posts.Core.Entities; -using MiniSpace.Services.Posts.Core.Repositories; -using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; - -namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories -{ - [ExcludeFromCodeCoverage] - public class EventMongoRepository : IEventRepository - { - private readonly IMongoRepository _repository; - - public EventMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(Guid id) - { - var @event = await _repository.GetAsync(s => s.Id == id); - - return @event?.AsEntity(); - } - - public Task ExistsAsync(Guid id) - => _repository.ExistsAsync(s => s.Id == id); - - public Task AddAsync(Event @event) - => _repository.AddAsync(@event.AsDocument()); - - public Task DeleteAsync(Guid id) - => _repository.DeleteAsync(id); - } -} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/Extensions.cs index e3a640fdd..e6f98b2c4 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/Extensions.cs @@ -10,6 +10,7 @@ namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories public static class Extensions { private static readonly FilterDefinitionBuilder FilterDefinitionBuilder = Builders.Filter; + public static async Task<(int totalPages, int totalElements, IReadOnlyList data)> AggregateByPage( this IMongoCollection collection, FilterDefinition filterDefinition, @@ -31,7 +32,6 @@ public static class Extensions PipelineStageDefinitionBuilder.Limit(pageSize), })); - var aggregation = await collection.Aggregate() .Match(filterDefinition) .Facet(countFacet, dataFacet) @@ -60,7 +60,7 @@ public static FilterDefinition ToFilterDefinition(IEnumerable p.EventId, eventsIds); + filterDefinition &= FilterDefinitionBuilder.In("EventId", eventsIds); filterDefinition &= FilterDefinitionBuilder.Eq(p => p.State, State.Published); return filterDefinition; @@ -69,7 +69,7 @@ public static FilterDefinition ToFilterDefinition(IEnumerable ToSortDefinition(IEnumerable sortByArguments, string direction) { var sort = sortByArguments.ToList(); - if(sort.Count == 0) + if (sort.Count == 0) { sort.Add("PublishDate"); } @@ -82,4 +82,4 @@ public static SortDefinition ToSortDefinition(IEnumerable return sortCombined; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs new file mode 100644 index 000000000..a609cec66 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs @@ -0,0 +1,135 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories +{ + [ExcludeFromCodeCoverage] + public class OrganizationEventPostMongoRepository : IOrganizationEventPostRepository + { + private readonly IMongoRepository _repository; + + public OrganizationEventPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var organizationEventPost = await _repository.GetAsync(e => e.Id == id); + return organizationEventPost?.EventPosts.FirstOrDefault(p => p.Id == id)?.AsEntity(); + } + + public async Task> GetToUpdateAsync() + { + var postsToUpdate = await _repository.Collection.AsQueryable() + .SelectMany(o => o.EventPosts) + .Where(e => e.State == State.ToBePublished || e.State == State.Published) + .ToListAsync(); + + return postsToUpdate.Select(p => p.AsEntity()); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.EventPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.EventPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public async Task> GetByOrganizationEventIdAsync(Guid organizationId, Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.OrganizationId == organizationId && o.EventPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.EventPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public Task AddAsync(Post post) + { + var organizationEventPost = new OrganizationEventPost(post.Id, post.OrganizationId.Value, new List { post }); + return _repository.AddAsync(organizationEventPost.AsDocument()); + } + + public Task UpdateAsync(Post post) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(o => o.OrganizationId, post.OrganizationId), + Builders.Filter.ElemMatch(o => o.EventPosts, p => p.Id == post.Id)); + + var update = Builders.Update.Set("EventPosts.$", post.AsDocument()); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(o => o.EventPosts, p => p.Id == id); + var update = Builders.Update.PullFilter(o => o.EventPosts, p => p.Id == id); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task ExistsAsync(Guid id) + { + return _repository.ExistsAsync(o => o.EventPosts.Any(p => p.Id == id)); + } + + public async Task> BrowsePostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Empty; + + if (request.OrganizationId.HasValue) + { + filterDefinition &= Builders.Filter.Eq(o => o.OrganizationId, request.OrganizationId.Value); + } + + if (request.EventId.HasValue) + { + filterDefinition &= Builders.Filter.ElemMatch(o => o.EventPosts, p => p.EventId == request.EventId.Value); + } + + var sortDefinition = OrganizationEventPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedEvents = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedEvents.data.SelectMany(o => o.EventPosts.Where(p => p.EventId == request.EventId)).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + + public async Task> BrowseOrganizationEventPostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Empty; + + if (request.OrganizationId.HasValue) + { + filterDefinition &= Builders.Filter.Eq(o => o.OrganizationId, request.OrganizationId.Value); + } + + if (request.EventId.HasValue) + { + filterDefinition &= Builders.Filter.ElemMatch(o => o.EventPosts, p => p.EventId == request.EventId.Value); + } + + var sortDefinition = OrganizationEventPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedEvents = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedEvents.data.SelectMany(o => o.EventPosts.Where(p => p.EventId == request.EventId)).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs new file mode 100644 index 000000000..5c06fc11c --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs @@ -0,0 +1,120 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories +{ + [ExcludeFromCodeCoverage] + public class OrganizationPostMongoRepository : IOrganizationPostRepository + { + private readonly IMongoRepository _repository; + + public OrganizationPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var organizationPost = await _repository.GetAsync(e => e.Id == id); + return organizationPost?.OrganizationPosts.FirstOrDefault(p => p.Id == id)?.AsEntity(); + } + + public async Task> GetToUpdateAsync() + { + var postsToUpdate = await _repository.Collection.AsQueryable() + .SelectMany(o => o.OrganizationPosts) + .Where(e => e.State == State.ToBePublished || e.State == State.Published) + .ToListAsync(); + + return postsToUpdate.Select(p => p.AsEntity()); + } + + public async Task> GetByOrganizationIdAsync(Guid organizationId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.OrganizationId == organizationId) + .SelectMany(o => o.OrganizationPosts) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public Task AddAsync(Post post) + { + var organizationPost = new OrganizationPost(post.Id, post.OrganizationId.Value, new List { post }); + return _repository.AddAsync(organizationPost.AsDocument()); + } + + public Task UpdateAsync(Post post) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(o => o.OrganizationId, post.OrganizationId), + Builders.Filter.ElemMatch(o => o.OrganizationPosts, p => p.Id == post.Id)); + + var update = Builders.Update.Set("OrganizationPosts.$", post.AsDocument()); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(o => o.OrganizationPosts, p => p.Id == id); + var update = Builders.Update.PullFilter(o => o.OrganizationPosts, p => p.Id == id); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task ExistsAsync(Guid id) + { + return _repository.ExistsAsync(o => o.OrganizationPosts.Any(p => p.Id == id)); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.OrganizationPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.OrganizationPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public async Task> BrowsePostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Empty; + + if (request.OrganizationId.HasValue) + { + filterDefinition &= Builders.Filter.Eq(o => o.OrganizationId, request.OrganizationId.Value); + } + + var sortDefinition = OrganizationPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedPosts = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedPosts.data.SelectMany(o => o.OrganizationPosts).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedPosts.totalElements); + } + + public async Task> BrowseOrganizationPostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Where(o => o.OrganizationId == request.OrganizationId); + + var sortDefinition = OrganizationPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedEvents = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedEvents.data.SelectMany(o => o.OrganizationPosts).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs index 4c74b785a..cb6fd7614 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs @@ -2,6 +2,8 @@ using Convey.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -65,16 +67,23 @@ public Task ExistsAsync(Guid id) return pagedEvents; } - public async Task<(IEnumerable posts, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseCommentsAsync(int pageNumber, int pageSize, - IEnumerable eventsIds, IEnumerable sortBy, string direction) + public async Task> BrowsePostsAsync(BrowseRequest request) { - var filterDefinition = Extensions.ToFilterDefinition(eventsIds); - var sortDefinition = Extensions.ToSortDefinition(sortBy, direction); + var filterDefinition = Builders.Filter.Empty; + var sortDefinition = Extensions.ToSortDefinition(request.SortBy, request.Direction); - var pagedEvents = await BrowseAsync(filterDefinition, sortDefinition, pageNumber, pageSize); + var totalItems = await _repository.Collection.CountDocumentsAsync(filterDefinition); - return (pagedEvents.data.Select(e => e.AsEntity()), pageNumber, pageSize, - pagedEvents.totalPages, pagedEvents.totalElements); + var posts = await _repository.Collection + .Find(filterDefinition) + .Sort(sortDefinition) + .Skip((request.PageNumber - 1) * request.PageSize) + .Limit(request.PageSize) + .ToListAsync(); + + var postEntities = posts.Select(p => p.AsEntity()); + + return new PagedResponse(postEntities, request.PageNumber, request.PageSize, (int)totalItems); } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs new file mode 100644 index 000000000..fe5b417d1 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs @@ -0,0 +1,131 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories +{ + [ExcludeFromCodeCoverage] + public class UserEventPostMongoRepository : IUserEventPostRepository + { + private readonly IMongoRepository _repository; + + public UserEventPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var userEventPost = await _repository.GetAsync(e => e.Id == id); + return userEventPost?.UserEventPosts.FirstOrDefault(p => p.Id == id)?.AsEntity(); + } + + public async Task> GetToUpdateAsync() + { + var postsToUpdate = await _repository.Collection.AsQueryable() + .SelectMany(o => o.UserEventPosts) + .Where(e => e.State == State.ToBePublished || e.State == State.Published) + .ToListAsync(); + + return postsToUpdate.Select(p => p.AsEntity()); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.UserEventPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.UserEventPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public async Task> GetByUserEventIdAsync(Guid userId, Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.UserId == userId && o.UserEventPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.UserEventPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public Task UpdateAsync(Post post) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(o => o.UserId, post.UserId), + Builders.Filter.Eq("UserEventPosts.Id", post.Id)); + + var update = Builders.Update + .Set("UserEventPosts.$", post.AsDocument()); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + + + public Task AddAsync(Post post) + { + var userEventPost = new UserEventPost(post.Id, post.UserId.Value, new List { post }); + return _repository.AddAsync(userEventPost.AsDocument()); + } + + + public Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(o => o.UserEventPosts, p => p.Id == id); + var update = Builders.Update.PullFilter(o => o.UserEventPosts, p => p.Id == id); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task ExistsAsync(Guid id) + { + return _repository.ExistsAsync(o => o.UserEventPosts.Any(p => p.Id == id)); + } + + public async Task> BrowsePostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Empty; + + if (request.UserId.HasValue) + { + filterDefinition &= Builders.Filter.Eq(o => o.UserId, request.UserId.Value); + } + + if (request.EventId.HasValue) + { + filterDefinition &= Builders.Filter.ElemMatch(o => o.UserEventPosts, p => p.EventId == request.EventId.Value); + } + + var sortDefinition = UserEventPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedPosts = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedPosts.data.SelectMany(o => o.UserEventPosts).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedPosts.totalElements); + } + + + public async Task> BrowseUserEventPostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Where( + o => o.UserId == request.UserId && o.UserEventPosts.Any(p => p.EventId == request.EventId)); + + var sortDefinition = UserEventPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedEvents = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedEvents.data.SelectMany(o => o.UserEventPosts.Where(p => p.EventId == request.EventId)).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs new file mode 100644 index 000000000..56d7b5fc1 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs @@ -0,0 +1,126 @@ +using System.Diagnostics.CodeAnalysis; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Posts.Core.Entities; +using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories +{ + [ExcludeFromCodeCoverage] + public class UserPostMongoRepository : IUserPostRepository + { + private readonly IMongoRepository _repository; + + public UserPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var userPost = await _repository.GetAsync(e => e.Id == id); + return userPost?.UserPosts.FirstOrDefault(p => p.Id == id)?.AsEntity(); + } + + public async Task> GetToUpdateAsync() + { + var postsToUpdate = await _repository.Collection.AsQueryable() + .SelectMany(o => o.UserPosts) + .Where(e => e.State == State.ToBePublished || e.State == State.Published) + .ToListAsync(); + + return postsToUpdate.Select(p => p.AsEntity()); + } + + public async Task> GetByEventIdAsync(Guid eventId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.UserPosts.Any(p => p.EventId == eventId)) + .SelectMany(o => o.UserPosts.Where(p => p.EventId == eventId)) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public async Task> GetByUserIdAsync(Guid userId) + { + var posts = await _repository.Collection.AsQueryable() + .Where(o => o.UserId == userId) + .SelectMany(o => o.UserPosts) + .ToListAsync(); + + return posts.Select(p => p.AsEntity()); + } + + public Task AddAsync(Post post) + { + var userPost = new UserPost(post.Id, post.UserId.Value, new List { post }); + return _repository.AddAsync(userPost.AsDocument()); + } + + public Task UpdateAsync(Post post) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(o => o.UserId, post.UserId), + Builders.Filter.ElemMatch(o => o.UserPosts, p => p.Id == post.Id)); + + var update = Builders.Update.Set("UserPosts.$", post.AsDocument()); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(o => o.UserPosts, p => p.Id == id); + var update = Builders.Update.PullFilter(o => o.UserPosts, p => p.Id == id); + + return _repository.Collection.UpdateOneAsync(filter, update); + } + + public Task ExistsAsync(Guid id) + { + return _repository.ExistsAsync(o => o.UserPosts.Any(p => p.Id == id)); + } + + public async Task> BrowsePostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Empty; + + if (request.UserId.HasValue) + { + filterDefinition &= Builders.Filter.Eq(o => o.UserId, request.UserId.Value); + } + + if (request.EventId.HasValue) + { + filterDefinition &= Builders.Filter.ElemMatch(o => o.UserPosts, p => p.EventId == request.EventId.Value); + } + + var sortDefinition = UserPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedPosts = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedPosts.data.SelectMany(o => o.UserPosts).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedPosts.totalElements); + } + + public async Task> BrowseUserPostsAsync(BrowseRequest request) + { + var filterDefinition = Builders.Filter.Where(o => o.UserId == request.UserId); + + var sortDefinition = UserPostExtensions.ToSortDefinition(request.SortBy, request.Direction); + + var pagedEvents = await _repository.Collection.AggregateByPage(filterDefinition, sortDefinition, request.PageNumber, request.PageSize); + + var posts = pagedEvents.data.SelectMany(o => o.UserPosts).ToList(); + + return new PagedResponse(posts.Select(p => p.AsEntity()), request.PageNumber, request.PageSize, pagedEvents.totalElements); + } + + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs index 0398fc769..fdbac5102 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs @@ -1,57 +1,105 @@ using MiniSpace.Services.Posts.Application; -using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; -using MiniSpace.Services.Posts.Application.Services.Clients; -using MiniSpace.Services.Posts.Core.Wrappers; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Exceptions; using MiniSpace.Services.Posts.Core.Repositories; +using MiniSpace.Services.Posts.Core.Requests; +using MiniSpace.Services.Posts.Core.Wrappers; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace MiniSpace.Services.Posts.Infrastructure.Services { public class PostsService : IPostsService { + private readonly IOrganizationPostRepository _organizationPostRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; + private readonly IUserPostRepository _userPostRepository; + private readonly IUserEventPostRepository _userEventPostRepository; private readonly IPostRepository _postRepository; - private readonly IStudentsServiceClient _studentsServiceClient; private readonly IAppContext _appContext; - public PostsService(IPostRepository postRepository, IStudentsServiceClient studentsServiceClient, + public PostsService( + IOrganizationPostRepository organizationPostRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IUserPostRepository userPostRepository, + IUserEventPostRepository userEventPostRepository, + IPostRepository postRepository, IAppContext appContext) { + _organizationPostRepository = organizationPostRepository; + _organizationEventPostRepository = organizationEventPostRepository; + _userPostRepository = userPostRepository; + _userEventPostRepository = userEventPostRepository; _postRepository = postRepository; - _studentsServiceClient = studentsServiceClient; _appContext = appContext; } - public async Task>> BrowsePostsAsync(SearchPosts command) + public async Task> BrowsePostsAsync(BrowseRequest request) { var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != command.StudentId) - { - throw new UnauthorizedPostSearchException(command.StudentId, identity.Id); - } - var studentEvents = await _studentsServiceClient.GetAsync(command.StudentId); - if (studentEvents is null) + if (request.UserId.HasValue && identity.IsAuthenticated && identity.Id != request.UserId.Value) { - throw new InvalidStudentServiceClientResponseException(command.StudentId); + throw new UnauthorizedPostSearchException(request.UserId.Value, identity.Id); } - var eventsIds = studentEvents.InterestedInEvents.Union(studentEvents.SignedUpEvents).ToList(); + PagedResponse pagedResponse = null; - var pageNumber = command.Pageable.Page < 1 ? 1 : command.Pageable.Page; - var pageSize = command.Pageable.Size > 10 ? 10 : command.Pageable.Size; + if (request.OrganizationId.HasValue && request.EventId.HasValue) + { + // Browsing posts by organization event + pagedResponse = await _organizationEventPostRepository.BrowseOrganizationEventPostsAsync(request); + } + else if (request.OrganizationId.HasValue) + { + // Browsing posts by organization + pagedResponse = await _organizationPostRepository.BrowseOrganizationPostsAsync(request); + } + else if (request.UserId.HasValue && request.EventId.HasValue) + { + // Browsing posts by user event + pagedResponse = await _userEventPostRepository.BrowseUserEventPostsAsync(request); + } + else if (request.UserId.HasValue) + { + // Browsing posts by user + pagedResponse = await _userPostRepository.BrowseUserPostsAsync(request); + } + else + { + // Return posts from all repositories + var orgEventPosts = await _organizationEventPostRepository.BrowsePostsAsync(request); + var orgPosts = await _organizationPostRepository.BrowsePostsAsync(request); + var userEventPosts = await _userEventPostRepository.BrowsePostsAsync(request); + var userPosts = await _userPostRepository.BrowsePostsAsync(request); - var result = await _postRepository.BrowseCommentsAsync( - pageNumber, pageSize, eventsIds, command.Pageable.Sort.SortBy, command.Pageable.Sort.Direction); + var allPosts = orgEventPosts.Items + .Concat(orgPosts.Items) + .Concat(userEventPosts.Items) + .Concat(userPosts.Items) + .OrderByDescending(p => p.CreatedAt) + .Skip((request.PageNumber - 1) * request.PageSize) + .Take(request.PageSize) + .ToList(); - var pagedEvents = new PagedResponse>( - result.posts.Select(p => new PostDto(p)), - result.pageNumber, result.pageSize, result.totalPages, result.totalElements); + pagedResponse = new PagedResponse( + allPosts, + request.PageNumber, + request.PageSize, + orgEventPosts.TotalItems + orgPosts.TotalItems + userEventPosts.TotalItems + userPosts.TotalItems + ); + } - return pagedEvents; + return new PagedResponse( + pagedResponse.Items.Select(p => new PostDto(p)), + pagedResponse.Page, + pagedResponse.PageSize, + pagedResponse.TotalItems + ); } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs index c262f375e..0f8cc01fb 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs @@ -1,5 +1,4 @@ -using System.Data; -using System.Threading.Tasks.Dataflow; +using System; using Convey.CQRS.Commands; using MiniSpace.Services.Reactions.Core.Entities; @@ -7,19 +6,21 @@ namespace MiniSpace.Services.Reactions.Application.Commands { public class CreateReaction : ICommand { - public Guid ReactionId {get;} - public Guid StudentId { get; } - public string ReactionType { get; } - public Guid ContentId {get;} - public string ContentType { get; } + public Guid ReactionId { get; } + public Guid UserId { get; } + public string ReactionType { get; } + public Guid ContentId { get; } + public string ContentType { get; } + public string TargetType { get; } - public CreateReaction(Guid reactionId, Guid studentId, Guid contentId, string reactionType, string contentType) + public CreateReaction(Guid reactionId, Guid userId, Guid contentId, string reactionType, string contentType, string targetType) { ReactionId = reactionId == Guid.Empty ? Guid.NewGuid() : reactionId; - StudentId = studentId; + UserId = userId; ContentId = contentId; ReactionType = reactionType; ContentType = contentType; + TargetType = targetType; } } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs index 65dde4920..de5991a05 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs @@ -1,81 +1,189 @@ using Convey.CQRS.Commands; using MiniSpace.Services.Reactions.Application.Events; using MiniSpace.Services.Reactions.Application.Exceptions; -using MiniSpace.Services.Reactions.Application.Services; using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Exceptions; using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Application.Services.Clients; +using System; +using System.Threading; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Exceptions; +using MiniSpace.Services.Reactions.Application.Services; namespace MiniSpace.Services.Reactions.Application.Commands.Handlers { - public class CreateReactionHandler(IReactionRepository reactionRepository, - IPostRepository postRepository, - IEventRepository eventRepository, - IStudentRepository studentRepository, - IAppContext appContext, - IMessageBroker messageBroker - ) : ICommandHandler + public class CreateReactionHandler : ICommandHandler { - private readonly IReactionRepository _reactionRepository = reactionRepository; - private readonly IPostRepository _postRepository = postRepository; - private readonly IStudentRepository _studentRepository = studentRepository; - private readonly IEventRepository _eventRepository = eventRepository; - private readonly IMessageBroker _messageBroker = messageBroker; - private readonly IAppContext _appContext = appContext; + private readonly IReactionRepository _reactionRepository; + private readonly IReactionsOrganizationsEventRepository _orgEventRepository; + private readonly IReactionsOrganizationsPostRepository _orgPostRepository; + private readonly IReactionsUserEventRepository _userEventRepository; + private readonly IReactionsUserPostRepository _userPostRepository; + private readonly IReactionsOrganizationsEventCommentsRepository _orgEventCommentsRepository; + private readonly IReactionsOrganizationsPostCommentsRepository _orgPostCommentsRepository; + private readonly IReactionsUserEventCommentsRepository _userEventCommentsRepository; + private readonly IReactionsUserPostCommentsRepository _userPostCommentsRepository; + private readonly ICommentServiceClient _commentServiceClient; + private readonly IStudentsServiceClient _studentsServiceClient; + private readonly IMessageBroker _messageBroker; + private readonly IAppContext _appContext; + + public CreateReactionHandler( + IReactionRepository reactionRepository, + IReactionsOrganizationsEventRepository orgEventRepository, + IReactionsOrganizationsPostRepository orgPostRepository, + IReactionsUserEventRepository userEventRepository, + IReactionsUserPostRepository userPostRepository, + IReactionsOrganizationsEventCommentsRepository orgEventCommentsRepository, + IReactionsOrganizationsPostCommentsRepository orgPostCommentsRepository, + IReactionsUserEventCommentsRepository userEventCommentsRepository, + IReactionsUserPostCommentsRepository userPostCommentsRepository, + ICommentServiceClient commentServiceClient, + IStudentsServiceClient studentsServiceClient, + IAppContext appContext, + IMessageBroker messageBroker) + { + _reactionRepository = reactionRepository; + _orgEventRepository = orgEventRepository; + _orgPostRepository = orgPostRepository; + _userEventRepository = userEventRepository; + _userPostRepository = userPostRepository; + _orgEventCommentsRepository = orgEventCommentsRepository; + _orgPostCommentsRepository = orgPostCommentsRepository; + _userEventCommentsRepository = userEventCommentsRepository; + _userPostCommentsRepository = userPostCommentsRepository; + _commentServiceClient = commentServiceClient; + _studentsServiceClient = studentsServiceClient; + _messageBroker = messageBroker; + _appContext = appContext; + } public async Task HandleAsync(CreateReaction command, CancellationToken cancellationToken = default) { - var identity = _appContext.Identity; + ValidateStudentIdentity(command); - if (identity.IsAuthenticated && identity.Id != command.StudentId) { - throw new UnauthorizedReactionAccessException(command.ReactionId, command.StudentId); - } + await EnsureStudentExistsAsync(command.UserId); + + var contentType = ParseContentType(command.ContentType); + var targetType = ParseTargetType(command.TargetType); + var reactionType = ParseReactionType(command.ReactionType); - if (!await _studentRepository.ExistsAsync(command.StudentId)) + await EnsureReactionDoesNotExistAsync(command.ContentId, contentType, command.UserId); + + // Check if the comment exists using the CommentServiceClient + if (contentType == ReactionContentType.Comment) { - throw new StudentNotFoundException(command.StudentId); + if (!await _commentServiceClient.CommentExistsAsync(command.ContentId)) + { + throw new CommentNotFoundException(command.ContentId); + } } - // Check the content type - if (!Enum.TryParse(command.ContentType, true, out var contentType)) { - throw new InvalidReactionContentTypeException(command.ContentType); - } + var reaction = Reaction.Create(command.ReactionId, command.UserId, reactionType, + command.ContentId, contentType, targetType); + + // Add the reaction to the appropriate repository based on content and target types + switch (contentType) + { + case ReactionContentType.Event when targetType == ReactionTargetType.Organization: + await _orgEventRepository.AddAsync(reaction); + break; - // Check the content - switch (contentType) { - case ReactionContentType.Event: - if (!await _eventRepository.ExistsAsync(command.ContentId)) { - throw new EventNotFoundException(command.ContentId); + case ReactionContentType.Event when targetType == ReactionTargetType.User: + await _userEventRepository.AddAsync(reaction); + break; + + case ReactionContentType.Post when targetType == ReactionTargetType.Organization: + await _orgPostRepository.AddAsync(reaction); + break; + + case ReactionContentType.Post when targetType == ReactionTargetType.User: + await _userPostRepository.AddAsync(reaction); + break; + + case ReactionContentType.Comment when targetType == ReactionTargetType.Organization: + if (await _orgEventCommentsRepository.ExistsAsync(command.ContentId)) + { + await _orgEventCommentsRepository.AddAsync(reaction); + } + else if (await _orgPostCommentsRepository.ExistsAsync(command.ContentId)) + { + await _orgPostCommentsRepository.AddAsync(reaction); } break; - case ReactionContentType.Post: - if (!await _postRepository.ExistsAsync(command.ContentId)) { - throw new PostNotFoundException(command.ContentId); + + case ReactionContentType.Comment when targetType == ReactionTargetType.User: + if (await _userEventCommentsRepository.ExistsAsync(command.ContentId)) + { + await _userEventCommentsRepository.AddAsync(reaction); + } + else if (await _userPostCommentsRepository.ExistsAsync(command.ContentId)) + { + await _userPostCommentsRepository.AddAsync(reaction); } break; + default: - break; + throw new InvalidReactionContentTypeException(command.ContentType); } - // check the reaction type - // case-sensitive - if (!Enum.TryParse(command.ReactionType, false, out var reactionType)) + await _messageBroker.PublishAsync(new ReactionCreated(command.ReactionId)); + } + + private void ValidateStudentIdentity(CreateReaction command) + { + var identity = _appContext.Identity; + + if (identity.IsAuthenticated && identity.Id != command.UserId) { - throw new InvalidReactionTypeException(command.ReactionType); + throw new UnauthorizedReactionAccessException(command.ReactionId, command.UserId); } + } - if (await _reactionRepository.ExistsAsync(command.ContentId, contentType, command.StudentId)) + private async Task EnsureStudentExistsAsync(Guid studentId) + { + if (!await _studentsServiceClient.StudentExistsAsync(studentId)) { - throw new StudentAlreadyGaveReactionException(command.StudentId, command.ContentId, contentType); + throw new StudentNotFoundException(studentId); } + } - string studentFullName = identity.Name; - - var reaction = Reaction.Create(command.ReactionId, command.StudentId, studentFullName, reactionType, - command.ContentId, contentType); - await _reactionRepository.AddAsync(reaction); - - await _messageBroker.PublishAsync(new ReactionCreated(command.ReactionId)); + private ReactionContentType ParseContentType(string contentType) + { + if (!Enum.TryParse(contentType, true, out var parsedContentType)) + { + throw new InvalidReactionContentTypeException(contentType); + } + + return parsedContentType; + } + + private ReactionTargetType ParseTargetType(string targetType) + { + if (!Enum.TryParse(targetType, true, out var parsedTargetType)) + { + throw new InvalidReactionTargetTypeException(targetType); + } + + return parsedTargetType; + } + + private ReactionType ParseReactionType(string reactionType) + { + if (!Enum.TryParse(reactionType, false, out var parsedReactionType)) + { + throw new InvalidReactionTypeException(reactionType); + } + + return parsedReactionType; + } + + private async Task EnsureReactionDoesNotExistAsync(Guid contentId, ReactionContentType contentType, Guid userId) + { + if (await _reactionRepository.ExistsAsync(contentId, contentType, userId)) + { + throw new StudentAlreadyGaveReactionException(userId, contentId, contentType); + } } } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs index c80074cc2..6c7e85348 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs @@ -5,29 +5,55 @@ using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Exceptions; using MiniSpace.Services.Reactions.Core.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Reactions.Application.Commands.Handlers { - public class DeleteReactionHandler(IReactionRepository reactionRepository, - IAppContext appContext, - IMessageBroker messageBroker) : ICommandHandler + public class DeleteReactionHandler : ICommandHandler { - private readonly IReactionRepository _reactionRepository = reactionRepository; - private readonly IMessageBroker _messageBroker = messageBroker; - private readonly IAppContext _appContext = appContext; + private readonly IReactionRepository _reactionRepository; + private readonly IAppContext _appContext; + private readonly IMessageBroker _messageBroker; + + public DeleteReactionHandler(IReactionRepository reactionRepository, + IAppContext appContext, + IMessageBroker messageBroker) + { + _reactionRepository = reactionRepository; + _appContext = appContext; + _messageBroker = messageBroker; + } + public async Task HandleAsync(DeleteReaction command, CancellationToken cancellationToken = default) { - var reaction = await _reactionRepository.GetAsync(command.ReactionId) ?? - throw new ReactionNotFoundException(command.ReactionId); + var reaction = await GetReactionAsync(command.ReactionId); - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != reaction.StudentId) - { - throw new UnauthorizedReactionAccessException(command.ReactionId, identity.Id); - } + ValidateUserIdentity(reaction.UserId); await _reactionRepository.DeleteAsync(command.ReactionId); + await _messageBroker.PublishAsync(new ReactionDeleted(command.ReactionId)); } + + private async Task GetReactionAsync(Guid reactionId) + { + var reaction = await _reactionRepository.GetAsync(reactionId); + if (reaction == null) + { + throw new ReactionNotFoundException(reactionId); + } + return reaction; + } + + private void ValidateUserIdentity(Guid studentId) + { + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != studentId) + { + throw new UnauthorizedReactionAccessException(identity.Id, studentId); + } + } } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionDto.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionDto.cs index 6100e9a58..63875ef26 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionDto.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionDto.cs @@ -1,4 +1,4 @@ - +using System; using System.Diagnostics.CodeAnalysis; using MiniSpace.Services.Reactions.Core.Entities; @@ -8,10 +8,10 @@ namespace MiniSpace.Services.Reactions.Application.Dto public class ReactionDto { public Guid Id { get; set; } - public Guid StudentId { get; set; } - public string StudentFullName { get; set; } - public Guid ContentId{ get; set; } - public ReactionContentType ContentType{ get; set; } - public ReactionType Type { get; set; } - } + public Guid UserId { get; set; } + public Guid ContentId { get; set; } + public ReactionContentType ContentType { get; set; } + public ReactionType ReactionType { get; set; } + public ReactionTargetType TargetType { get; set; } + } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionsSummaryDto.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionsSummaryDto.cs index 79738bad4..76ba0bbf1 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionsSummaryDto.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/ReactionsSummaryDto.cs @@ -5,10 +5,9 @@ namespace MiniSpace.Services.Reactions.Application.Dto { [ExcludeFromCodeCoverage] - public class ReactionsSummaryDto(int nrReactions, ReactionType? dominant, - Guid? authUserReactionId, ReactionType? authUserReactionType) + public class ReactionsSummaryDto(int numerOfReactions, ReactionType? dominant, Guid? authUserReactionId, ReactionType? authUserReactionType) { - public int NumberOfReactions { get; set; } = nrReactions; + public int NumberOfReactions { get; set; } = numerOfReactions; public ReactionType? DominantReaction { get; set; } = dominant; public Guid? AuthUserReactionId { get; set; } = authUserReactionId; public ReactionType? AuthUserReactionType { get; set; } = authUserReactionType; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/UserDto.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/UserDto.cs new file mode 100644 index 000000000..7fdfc53ce --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Dto/UserDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Reactions.Application.Dto +{ + public class UserDto + { + public Guid studentId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/EventCreated.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/EventCreated.cs deleted file mode 100644 index 899f938e2..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/EventCreated.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Reactions.Application.Events.External -{ - [Message("events")] - public class EventCreated : IEvent - { - public Guid EventId { get; } - public Guid OrganizerId { get; } - public EventCreated(Guid eventId, Guid organizerId) - { - EventId = eventId; - OrganizerId = organizerId; - } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/EventCreatedHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/EventCreatedHandler.cs deleted file mode 100644 index 055abcd48..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/EventCreatedHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Convey.CQRS.Events; -using MiniSpace.Services.Reactions.Application.Exceptions; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; - -namespace MiniSpace.Services.Reactions.Application.Events.External.Handlers -{ - public class EventCreatedHandler : IEventHandler - { - private readonly IEventRepository _eventRepository; - - public EventCreatedHandler(IEventRepository eventRepository) - { - _eventRepository = eventRepository; - } - - public async Task HandleAsync(EventCreated @event, CancellationToken cancellationToken = default) - { - if (await _eventRepository.ExistsAsync(@event.EventId)) - { - throw new EventAlreadyAddedException(@event.EventId); - } - - await _eventRepository.AddAsync(new Event(@event.EventId)); - } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/PostCreatedHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/PostCreatedHandler.cs deleted file mode 100644 index 5933153f4..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/PostCreatedHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using MiniSpace.Services.Reactions.Application.Exceptions; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; - -namespace MiniSpace.Services.Reactions.Application.Events.External.Handlers -{ - public class PostCreatedHandler : IEventHandler - { - private readonly IPostRepository _postRepository; - - public PostCreatedHandler(IPostRepository postRepository) - { - _postRepository = postRepository; - } - - public async Task HandleAsync(PostCreated @event, CancellationToken cancellationToken) - { - if (await _postRepository.ExistsAsync(@event.PostId)) - { - throw new PostAlreadyAddedException(@event.PostId); - } - - await _postRepository.AddAsync(new Post(@event.PostId)); - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/StudentCreatedHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/StudentCreatedHandler.cs deleted file mode 100644 index 82e65eb76..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/Handlers/StudentCreatedHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using MiniSpace.Services.Reactions.Application.Exceptions; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; - -namespace MiniSpace.Services.Reactions.Application.Events.External.Handlers -{ - public class StudentCreatedHandler : IEventHandler - { - private readonly IStudentRepository _studentRepository; - - public StudentCreatedHandler(IStudentRepository studentRepository) - { - _studentRepository = studentRepository; - } - - public async Task HandleAsync(StudentCreated @event, CancellationToken cancellationToken) - { - if (await _studentRepository.ExistsAsync(@event.StudentId)) - { - throw new StudentAlreadyAddedException(@event.StudentId); - } - - await _studentRepository.AddAsync(new Student(@event.StudentId)); - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/PostCreated.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/PostCreated.cs deleted file mode 100644 index 605fdf17c..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/PostCreated.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; - - -namespace MiniSpace.Services.Reactions.Application.Events -{ - [Message("posts")] - public class PostCreated : IEvent - { - public Guid PostId { get; } - - public PostCreated(Guid postId) - { - PostId = postId; - } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/StudentCreated.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/StudentCreated.cs deleted file mode 100644 index 6d086acdc..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/External/StudentCreated.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Reactions.Application.Events.External -{ - [Message("students")] - public class StudentCreated : IEvent - { - public Guid StudentId { get; } - public string Name { get; } - - public StudentCreated(Guid studentId, string name) - { - StudentId = studentId; - Name = name; - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Exceptions/CommentNotFoundException.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Exceptions/CommentNotFoundException.cs new file mode 100644 index 000000000..a6e559d13 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Exceptions/CommentNotFoundException.cs @@ -0,0 +1,17 @@ +using System; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Application.Exceptions +{ + public class CommentNotFoundException : AppException + { + public override string Code { get; } = "comment_not_found"; + public Guid CommentId { get; } + + public CommentNotFoundException(Guid commentId) : + base($"Comment with id: {commentId} was not found.") + { + CommentId = commentId; + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/IIdentityContext.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/IIdentityContext.cs index 09394f8d9..41bd7673c 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/IIdentityContext.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/IIdentityContext.cs @@ -9,7 +9,6 @@ public interface IIdentityContext bool IsAuthenticated { get; } bool IsAdmin { get; } bool IsBanned { get; } - bool IsOrganizer { get; } IDictionary Claims { get; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/ICommentServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/ICommentServiceClient.cs new file mode 100644 index 000000000..d6261fd71 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/ICommentServiceClient.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Reactions.Application.Services.Clients +{ + public interface ICommentServiceClient + { + Task CommentExistsAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/IStudentsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/IStudentsServiceClient.cs new file mode 100644 index 000000000..9c951e9e5 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/Clients/IStudentsServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Application.Dto; + +namespace MiniSpace.Services.Reactions.Application.Services.Clients +{ + public interface IStudentsServiceClient + { + Task StudentExistsAsync(Guid id); + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Post.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Post.cs deleted file mode 100644 index b0ba20b4f..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Post.cs +++ /dev/null @@ -1,9 +0,0 @@ - - -namespace MiniSpace.Services.Reactions.Core.Entities -{ - public class Post(Guid id) - { - public Guid Id { get; private set; } = id; - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Reaction.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Reaction.cs index c177ce101..381ac48e6 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Reaction.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Reaction.cs @@ -1,34 +1,58 @@ -using System.Net.Mime; +using System; +using System.Collections.Generic; +using MiniSpace.Services.Reactions.Core.Events; using MiniSpace.Services.Reactions.Core.Exceptions; namespace MiniSpace.Services.Reactions.Core.Entities { public class Reaction : AggregateRoot { - public Guid StudentId { get; private set; } - public string StudentFullName {get;private set;} - public ReactionType ReactionType { get; private set; } + public Guid UserId { get; private set; } + public ReactionType ReactionType { get; private set; } public Guid ContentId { get; private set; } - public ReactionContentType ContentType { get; private set; } - - public Reaction(Guid reactionId, Guid studentId, string studentFullName, ReactionType reactionType, - Guid contentId, ReactionContentType contentType) + public ReactionContentType ContentType { get; private set; } + public ReactionTargetType TargetType { get; private set; } + + private List _domainEvents = new List(); + + public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly(); + + private void AddDomainEvent(IDomainEvent domainEvent) + { + _domainEvents.Add(domainEvent); + } + + public void ClearDomainEvents() { + _domainEvents.Clear(); + } + private Reaction(Guid reactionId, Guid userId, ReactionType reactionType, Guid contentId, + ReactionContentType contentType, ReactionTargetType targetType) + { Id = reactionId; - StudentId = studentId; - StudentFullName = studentFullName; + UserId = userId; ReactionType = reactionType; ContentId = contentId; ContentType = contentType; + TargetType = targetType; + + AddDomainEvent(new ReactionCreatedEvent(reactionId, userId, reactionType, contentId, contentType, targetType)); } - public static Reaction Create(Guid reactionId, Guid studentId, string studentFullName, ReactionType reactionType, - Guid contentId, ReactionContentType contentType) + public static Reaction Create(Guid reactionId, Guid userId, ReactionType reactionType, Guid contentId, + ReactionContentType contentType, ReactionTargetType targetType) { - return new Reaction(reactionId, studentId, studentFullName, reactionType, contentId, contentType); + return new Reaction(reactionId, userId, reactionType, contentId, contentType, targetType); } - + public void UpdateReactionType(ReactionType newReactionType) + { + if (ReactionType != newReactionType) + { + ReactionType = newReactionType; + AddDomainEvent(new ReactionUpdatedEvent(Id, newReactionType)); + } + } } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ContentType.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionContentType.cs similarity index 80% rename from MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ContentType.cs rename to MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionContentType.cs index 72dc6249a..59914da5d 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ContentType.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionContentType.cs @@ -3,6 +3,7 @@ namespace MiniSpace.Services.Reactions.Core.Entities public enum ReactionContentType { Event, - Post + Post, + Comment } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionTargetType.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionTargetType.cs new file mode 100644 index 000000000..0170da468 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/ReactionTargetType.cs @@ -0,0 +1,8 @@ +namespace MiniSpace.Services.Reactions.Core.Entities +{ + public enum ReactionTargetType + { + User, + Organization + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Student.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Student.cs deleted file mode 100644 index 7d7140347..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Student.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MiniSpace.Services.Reactions.Core.Entities -{ - public class Student - { - public Guid Id { get; private set; } - - public Student(Guid id) - { - Id = id; - } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Event.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/User.cs similarity index 74% rename from MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Event.cs rename to MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/User.cs index c9fdd4339..e43f8f862 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/Event.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Entities/User.cs @@ -1,13 +1,12 @@ namespace MiniSpace.Services.Reactions.Core.Entities { - public class Event + public class User { public Guid Id { get; private set; } - public Event(Guid id) + public User(Guid id) { Id = id; } - } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Events/ReactionCreatedEvent.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Events/ReactionCreatedEvent.cs new file mode 100644 index 000000000..5e72bcc80 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Events/ReactionCreatedEvent.cs @@ -0,0 +1,38 @@ +using System; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Events +{ + public class ReactionCreatedEvent : IDomainEvent + { + public Guid ReactionId { get; } + public Guid UserId { get; } + public ReactionType ReactionType { get; } + public Guid ContentId { get; } + public ReactionContentType ContentType { get; } + public ReactionTargetType TargetType { get; } + + public ReactionCreatedEvent(Guid reactionId, Guid userId, ReactionType reactionType, + Guid contentId, ReactionContentType contentType, ReactionTargetType targetType) + { + ReactionId = reactionId; + UserId = userId; + ReactionType = reactionType; + ContentId = contentId; + ContentType = contentType; + TargetType = targetType; + } + } + + public class ReactionUpdatedEvent : IDomainEvent + { + public Guid ReactionId { get; } + public ReactionType NewReactionType { get; } + + public ReactionUpdatedEvent(Guid reactionId, ReactionType newReactionType) + { + ReactionId = reactionId; + NewReactionType = newReactionType; + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Exceptions/InvalidReactionTargetTypeException.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Exceptions/InvalidReactionTargetTypeException.cs new file mode 100644 index 000000000..cd494da5d --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Exceptions/InvalidReactionTargetTypeException.cs @@ -0,0 +1,14 @@ +namespace MiniSpace.Services.Reactions.Core.Exceptions +{ + public class InvalidReactionTargetTypeException : DomainException + { + public override string Code { get; } = "invalid_reaction_target_type"; + public string InvalidReactionTargetType { get; } + + public InvalidReactionTargetTypeException(string invalidReactionTargetType) + : base($"String: {invalidReactionTargetType} cannot be parsed to a valid reaction target type.") + { + InvalidReactionTargetType = invalidReactionTargetType; + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.generated.sln b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.sln similarity index 73% rename from MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.generated.sln rename to MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.sln index d918410c4..cd5b08ae5 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.generated.sln +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/MiniSpace.Services.Reactions.Core.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Reactions.Core", "MiniSpace.Services.Reactions.Core.csproj", "{D4E65D6A-72F8-4016-976E-4C0217AA4CDF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Reactions.Core", "MiniSpace.Services.Reactions.Core.csproj", "{7A3416F2-3F5F-4735-9FB1-623CEF9AA6BF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D4E65D6A-72F8-4016-976E-4C0217AA4CDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4E65D6A-72F8-4016-976E-4C0217AA4CDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4E65D6A-72F8-4016-976E-4C0217AA4CDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4E65D6A-72F8-4016-976E-4C0217AA4CDF}.Release|Any CPU.Build.0 = Release|Any CPU + {7A3416F2-3F5F-4735-9FB1-623CEF9AA6BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A3416F2-3F5F-4735-9FB1-623CEF9AA6BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A3416F2-3F5F-4735-9FB1-623CEF9AA6BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A3416F2-3F5F-4735-9FB1-623CEF9AA6BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IEventRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IEventRepository.cs deleted file mode 100644 index dcd8fe368..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IEventRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using MiniSpace.Services.Reactions.Core.Entities; - -namespace MiniSpace.Services.Reactions.Core.Repositories -{ - public interface IEventRepository - { - Task ExistsAsync(Guid id); - Task AddAsync(Event @event); - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IPostRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IPostRepository.cs deleted file mode 100644 index 780df8001..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IPostRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MiniSpace.Services.Reactions.Core.Entities; - -namespace MiniSpace.Services.Reactions.Core.Repositories -{ - public interface IPostRepository - { - Task ExistsAsync(Guid id); - Task AddAsync(Post post); - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventCommentsRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventCommentsRepository.cs new file mode 100644 index 000000000..064e6c05b --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventCommentsRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsOrganizationsEventCommentsRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventRepository.cs new file mode 100644 index 000000000..c0fcb095e --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsEventRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsOrganizationsEventRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostCommentsRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostCommentsRepository.cs new file mode 100644 index 000000000..f07356bba --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostCommentsRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsOrganizationsPostCommentsRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostRepository.cs new file mode 100644 index 000000000..49d53c68a --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsOrganizationsPostRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsOrganizationsPostRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventCommentsRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventCommentsRepository.cs new file mode 100644 index 000000000..45462e143 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventCommentsRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsUserEventCommentsRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventRepository.cs new file mode 100644 index 000000000..bfe750bc7 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserEventRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsUserEventRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostCommentsRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostCommentsRepository.cs new file mode 100644 index 000000000..5426156c5 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostCommentsRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsUserPostCommentsRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostRepository.cs new file mode 100644 index 000000000..3440bc61b --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IReactionsUserPostRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Core.Repositories +{ + public interface IReactionsUserPostRepository + { + Task ExistsAsync(Guid id); + Task GetByIdAsync(Guid id); + Task AddAsync(Reaction reaction); + Task UpdateAsync(Reaction reaction); + Task DeleteAsync(Guid id); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IStudentRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IStudentRepository.cs deleted file mode 100644 index d487b277f..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Core/Repositories/IStudentRepository.cs +++ /dev/null @@ -1,16 +0,0 @@ -using MiniSpace.Services.Reactions.Core.Entities; - -namespace MiniSpace.Services.Reactions.Core.Repositories -{ - public interface IStudentRepository - { - Task GetAsync(Guid id); - Task ExistsAsync(Guid id); - - Task AddAsync(Student student); - - - - // Task DeleteAsync(Guid id); - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/IdentityContext.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/IdentityContext.cs index 5815655b9..2d1b64aea 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/IdentityContext.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/IdentityContext.cs @@ -15,7 +15,6 @@ internal class IdentityContext : IIdentityContext public bool IsAuthenticated { get; } public bool IsAdmin { get; } public bool IsBanned { get; } - public bool IsOrganizer { get; } public IDictionary Claims { get; } = new Dictionary(); internal IdentityContext() @@ -34,7 +33,6 @@ internal IdentityContext(string id, string role, bool isAuthenticated, IDictiona IsAuthenticated = isAuthenticated; IsAdmin = Role.Equals("admin", StringComparison.InvariantCultureIgnoreCase); IsBanned = Role.Equals("banned", StringComparison.InvariantCultureIgnoreCase); - IsOrganizer = Role.Equals("organizer", StringComparison.InvariantCultureIgnoreCase); Claims = claims ?? new Dictionary(); Name = Claims.TryGetValue("name", out var name) ? name : string.Empty; Email = Claims.TryGetValue("email", out var email) ? email : string.Empty; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 5df6e2a82..ac5154c53 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -2,8 +2,6 @@ using Convey.MessageBrokers.RabbitMQ; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Application.Events.Rejected; - -//using MiniSpace.Services.Reactions.Application.Events.Rejected; using MiniSpace.Services.Reactions.Application.Exceptions; using MiniSpace.Services.Reactions.Core.Exceptions; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs index 463e41475..99fe5159d 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs @@ -40,7 +40,7 @@ using MiniSpace.Services.Reactions.Application.Queries; using Convey.Logging.CQRS; using MiniSpace.Services.Reactions.Application.Events; -using MiniSpace.Services.Reactions.Application.Events.External; + using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Reactions.Infrastructure @@ -50,11 +50,16 @@ public static class Extensions { public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) { - // add repositories builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -64,8 +69,6 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); - // background workers: none - return builder .AddErrorHandler() .AddQueryHandlers() @@ -82,9 +85,15 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) .AddJaeger() .AddHandlersLogging() .AddMongoRepository("reactions") - .AddMongoRepository("posts") - .AddMongoRepository("events") - .AddMongoRepository("students") + .AddMongoRepository("organization_posts") + .AddMongoRepository("organization_events") + .AddMongoRepository("user_posts") + .AddMongoRepository("user_events") + + .AddMongoRepository("organization_posts_comments") + .AddMongoRepository("organization_events_comments") + .AddMongoRepository("user_posts_comments") + .AddMongoRepository("user_events_comments") .AddWebApiSwaggerDocs() .AddCertificateAuthentication() .AddSecurity(); @@ -101,10 +110,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .UseCertificateAuthentication() .UseRabbitMq() .SubscribeCommand() - .SubscribeCommand() - .SubscribeEvent() - .SubscribeEvent() - .SubscribeEvent(); + .SubscribeCommand(); return app; } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 9b0803c60..b81e4e018 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -2,7 +2,6 @@ using Convey.Logging.CQRS; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Application.Events; -using MiniSpace.Services.Reactions.Application.Events.External; namespace MiniSpace.Services.Reactions.Infrastructure.Logging { @@ -23,25 +22,7 @@ private static IReadOnlyDictionary MessageTemplates { After = "Delete the reaction with id: {ReactionId}." } - }, - { - typeof(EventCreated), new HandlerLogTemplate - { - After = "Created a new event with id: {EventId}." - } - }, - { - typeof(PostCreated), new HandlerLogTemplate - { - After = "Created a new post with id: {PostId}." - } - }, - { - typeof(StudentCreated), new HandlerLogTemplate - { - After = "Created a new student with id: {StudentId}." - } - }, + } }; public HandlerLogTemplate Map(TMessage message) where TMessage : class diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/EventDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/EventDocument.cs deleted file mode 100644 index b861ace39..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/EventDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Types; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public class EventDocument : IIdentifiable - { - public Guid Id { get; set; } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/Extensions.cs index 3584e73de..160097a89 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/Extensions.cs @@ -1,58 +1,203 @@ -using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MongoDB.Driver; -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions { - [ExcludeFromCodeCoverage] public static class Extensions { + public static ReactionDocument AsDocument(this Reaction reaction) + { + return new ReactionDocument + { + Id = reaction.Id, + UserId = reaction.UserId, + ReactionType = reaction.ReactionType, + ContentId = reaction.ContentId, + ContentType = reaction.ContentType, + TargetType = reaction.TargetType + }; + } + + public static IEnumerable ToEntities(this UserEventReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } + public static Reaction AsEntity(this ReactionDocument document) - => new Reaction(document.Id, document.StudentId, document.StudentFullName, document.Type, - document.ContentId, document.ContentType); + { + return Reaction.Create( + document.Id, + document.UserId, + document.ReactionType, + document.ContentId, + document.ContentType, + document.TargetType + ); + } - public static ReactionDocument AsDocument(this Reaction entity) - => new ReactionDocument() + public static UserEventReactionDocument ToUserEventDocument(this IEnumerable reactions, Guid userEventId, Guid userId) + { + return new UserEventReactionDocument { - Id = entity.Id, - StudentId = entity.StudentId, - StudentFullName = entity.StudentFullName, - Type = entity.ReactionType, - ContentId = entity.ContentId, - ContentType = entity.ContentType, - + Id = userEventId, + UserEventId = userEventId, + UserId = userId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() }; + } + + public static IEnumerable ToEntities(this UserPostReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } public static ReactionDto AsDto(this ReactionDocument document) - => new ReactionDto() + { + return new ReactionDto { Id = document.Id, - StudentId = document.StudentId, - StudentFullName = document.StudentFullName, - Type = document.Type, + UserId = document.UserId, ContentId = document.ContentId, - ContentType = document.ContentType + ContentType = document.ContentType, + ReactionType = document.ReactionType, + TargetType = document.TargetType + }; + } + + public static UserPostReactionDocument ToUserPostDocument(this IEnumerable reactions, Guid userPostId, Guid userId) + { + return new UserPostReactionDocument + { + Id = userPostId, + UserPostId = userPostId, + UserId = userId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() + }; + } + + public static IEnumerable ToEntities(this OrganizationPostReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } + + public static OrganizationPostReactionDocument ToOrganizationPostDocument(this IEnumerable reactions, Guid organizationPostId, Guid organizationId) + { + return new OrganizationPostReactionDocument + { + Id = organizationPostId, + OrganizationPostId = organizationPostId, + OrganizationId = organizationId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() + }; + } + + public static IEnumerable ToEntities(this OrganizationEventReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } + + public static OrganizationEventReactionDocument ToOrganizationEventDocument(this IEnumerable reactions, Guid organizationEventId, Guid organizationId) + { + return new OrganizationEventReactionDocument + { + Id = organizationEventId, + OrganizationEventId = organizationEventId, + OrganizationId = organizationId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() }; - - public static Student AsEntity(this StudentDocument document) - => new Student(document.Id); + } + + public static IEnumerable ToEntities(this OrganizationEventCommentsReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } - public static StudentDocument AsDocument(this Student entity) - => new StudentDocument + public static OrganizationPostCommentsReactionDocument ToOrganizationPostCommentDocument(this IEnumerable reactions, Guid organizationPostCommentId, Guid organizationId) + { + return new OrganizationPostCommentsReactionDocument { - Id = entity.Id + Id = organizationPostCommentId, + OrganizationPostCommentId = organizationPostCommentId, + OrganizationId = organizationId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() }; + } - public static EventDocument AsDocument(this Event entity) - => new EventDocument + public static IEnumerable ToEntities(this OrganizationPostCommentsReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } + + public static UserEventCommentsReactionDocument ToUserEventCommentDocument(this IEnumerable reactions, Guid userEventCommentId, Guid userId) + { + return new UserEventCommentsReactionDocument { - Id = entity.Id + Id = userEventCommentId, + UserEventCommentId = userEventCommentId, + UserId = userId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() }; + } + + public static IEnumerable ToEntities(this UserEventCommentsReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } - public static PostDocument AsDocument(this Post entity) - => new PostDocument + public static OrganizationEventCommentsReactionDocument ToOrganizationEventCommentDocument(this IEnumerable reactions, Guid organizationEventCommentId, Guid organizationId) + { + return new OrganizationEventCommentsReactionDocument { - Id = entity.Id + Id = organizationEventCommentId, + OrganizationEventCommentId = organizationEventCommentId, + OrganizationId = organizationId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() }; - } + } + + public static UserPostCommentsReactionDocument ToUserPostCommentDocument(this IEnumerable reactions, Guid userPostCommentId, Guid userId) + { + return new UserPostCommentsReactionDocument + { + Id = userPostCommentId, + UserPostCommentId = userPostCommentId, + UserId = userId, + Reactions = reactions.Select(ReactionDocument.FromEntity).ToList() + }; + } + public static IEnumerable ToEntities(this UserPostCommentsReactionDocument document) + { + return document.Reactions.Select(r => r.ToEntity()); + } + + + public static UpdateDefinition Push( + this UpdateDefinitionBuilder builder, + Expression>> field, + TItem value) + { + return builder.AddToSet(field, value); + } + + public static UpdateDefinition Set( + this UpdateDefinitionBuilder builder, + Expression> field, + TField value) + { + return builder.Set(field, value); + } + + public static UpdateDefinition PullFilter( + this UpdateDefinitionBuilder builder, + Expression>> field, + Expression> filter) + { + return builder.PullFilter(field, filter); + } + } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs new file mode 100644 index 000000000..7b965999e --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class OrganizationEventCommentsReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationEventCommentId { get; set; } + public Guid OrganizationId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs new file mode 100644 index 000000000..9bfbde392 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class OrganizationEventReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationEventId { get; set; } + public Guid OrganizationId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs new file mode 100644 index 000000000..2e285f98b --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class OrganizationPostCommentsReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationPostCommentId { get; set; } + public Guid OrganizationId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs new file mode 100644 index 000000000..c050c9077 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class OrganizationPostReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrganizationPostId { get; set; } + public Guid OrganizationId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/PostDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/PostDocument.cs deleted file mode 100644 index 77aff9dbe..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/PostDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Types; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public class PostDocument : IIdentifiable - { - public Guid Id { get; set; } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs index 463303f3e..8edffceb9 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs @@ -1,18 +1,34 @@ -using System.Diagnostics.CodeAnalysis; +using System; using Convey.Types; -using Convey.WebApi.CQRS; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents { - [ExcludeFromCodeCoverage] public class ReactionDocument : IIdentifiable { public Guid Id { get; set; } - public Guid StudentId { get; set; } - public Guid ContentId{ get; set; } - public string StudentFullName { get; set; } - public ReactionContentType ContentType{ get; set; } - public ReactionType Type { get; set; } + public Guid UserId { get; set; } + public ReactionType ReactionType { get; set; } + public Guid ContentId { get; set; } + public ReactionContentType ContentType { get; set; } + public ReactionTargetType TargetType { get; set; } + + public static ReactionDocument FromEntity(Reaction reaction) + { + return new ReactionDocument + { + Id = reaction.Id, + UserId = reaction.UserId, + ReactionType = reaction.ReactionType, + ContentId = reaction.ContentId, + ContentType = reaction.ContentType, + TargetType = reaction.TargetType + }; + } + + public Reaction ToEntity() + { + return Reaction.Create(Id, UserId, ReactionType, ContentId, ContentType, TargetType); + } } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/StudentDocument.cs deleted file mode 100644 index 859d49f98..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/StudentDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Types; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents -{ - [ExcludeFromCodeCoverage] - public class StudentDocument : IIdentifiable - { - public Guid Id { get; set; } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs new file mode 100644 index 000000000..d8e9a99c0 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class UserEventCommentsReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserEventCommentId { get; set; } + public Guid UserId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs new file mode 100644 index 000000000..dc6b60523 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class UserEventReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserEventId { get; set; } + public Guid UserId { get; set; } + public List Reactions { get; set; } = new List(); + + internal bool Set(Func value, ReactionDocument reactionDocument) + { + throw new NotImplementedException(); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs new file mode 100644 index 000000000..7f831c097 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class UserPostCommentsReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserPostCommentId { get; set; } + public Guid UserId { get; set; } + public List Reactions { get; set; } = new List(); + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs new file mode 100644 index 000000000..8acd4aba2 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using MiniSpace.Services.Reactions.Core.Entities; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents +{ + public class UserPostReactionDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid UserPostId { get; set; } + public Guid UserId { get; set; } + public List Reactions { get; set; } = new List(); + + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs index 8463aa226..f94777694 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs @@ -6,6 +6,7 @@ using MiniSpace.Services.Reactions.Application.Queries; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; using MongoDB.Driver; using MongoDB.Driver.Linq; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs index d39a1aa3c..52af13fcf 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs @@ -41,13 +41,13 @@ public async Task Guid? authUserReactionId = null; ReactionType? authUserReactionType = null; - if (identity.IsAuthenticated && reactions.Exists(x => x.StudentId == identity.Id)) { - var reactionDocument = reactions.Find(x => x.StudentId == identity.Id); + if (identity.IsAuthenticated && reactions.Exists(x => x.UserId == identity.Id)) { + var reactionDocument = reactions.Find(x => x.UserId == identity.Id); authUserReactionId = reactionDocument.Id; - authUserReactionType = reactionDocument.Type; + authUserReactionType = reactionDocument.ReactionType; } - ReactionType dominant = reactions.GroupBy(x => x.Type) + ReactionType dominant = reactions.GroupBy(x => x.ReactionType) .OrderBy(x => x.ToList().Count).Last().Key; return new ReactionsSummaryDto(nrReactions, dominant, authUserReactionId, authUserReactionType); } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/EventMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/EventMongoRepository.cs deleted file mode 100644 index 1868abe65..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/EventMongoRepository.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; -using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories -{ - [ExcludeFromCodeCoverage] - public class EventMongoRepository : IEventRepository - { - private readonly IMongoRepository _repository; - - public EventMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public Task AddAsync(Event @event) - => _repository.AddAsync(@event.AsDocument()); - - public Task ExistsAsync(Guid id) - => _repository.ExistsAsync(s => s.Id == id); - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/PostMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/PostMongoRepository.cs deleted file mode 100644 index 8dce852f1..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/PostMongoRepository.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; -using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories -{ - [ExcludeFromCodeCoverage] - public class PostMongoRepository : IPostRepository - { - private readonly IMongoRepository _repository; - - public PostMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public Task AddAsync(Post post) - { - return _repository.AddAsync(post.AsDocument()); - } - - public Task ExistsAsync(Guid id) - => _repository.ExistsAsync(s => s.Id == id); - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs index 011e32b07..2de1d1a73 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs @@ -5,6 +5,7 @@ using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -40,6 +41,6 @@ public Task DeleteAsync(Guid id) public Task ExistsAsync(Guid contentId, ReactionContentType contentType, Guid studentId) => _repository.ExistsAsync(x => x.ContentId == contentId && x.ContentType == contentType - && x.StudentId == studentId); + && x.UserId == studentId); } } diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs new file mode 100644 index 000000000..961b766ac --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsOrganizationsEventCommentsMongoRepository : IReactionsOrganizationsEventCommentsRepository + { + private readonly IMongoRepository _repository; + + public ReactionsOrganizationsEventCommentsMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.OrganizationEventCommentId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(x => x.OrganizationEventCommentId, reaction.ContentId); + var update = Builders.Update.Push(x => x.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.OrganizationEventCommentId, reaction.ContentId), + Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update.Set(x => x.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == id); + var update = Builders.Update.PullFilter(x => x.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs new file mode 100644 index 000000000..4743e54e8 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsOrganizationsEventMongoRepository : IReactionsOrganizationsEventRepository + { + private readonly IMongoRepository _repository; + + public ReactionsOrganizationsEventMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.OrganizationEventId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.ToEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(d => d.OrganizationEventId, reaction.ContentId); + var update = Builders.Update + .Push(d => d.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.OrganizationEventId, reaction.ContentId), + Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update + .Set(d => d.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == id); + var update = Builders.Update + .PullFilter(d => d.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs new file mode 100644 index 000000000..f591d57e7 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsOrganizationsPostCommentsMongoRepository : IReactionsOrganizationsPostCommentsRepository + { + private readonly IMongoRepository _repository; + + public ReactionsOrganizationsPostCommentsMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.OrganizationPostCommentId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(x => x.OrganizationPostCommentId, reaction.ContentId); + var update = Builders.Update.Push(x => x.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.OrganizationPostCommentId, reaction.ContentId), + Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update.Set(x => x.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == id); + var update = Builders.Update.PullFilter(x => x.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs new file mode 100644 index 000000000..3ab31fc7b --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsOrganizationsPostMongoRepository : IReactionsOrganizationsPostRepository + { + private readonly IMongoRepository _repository; + + public ReactionsOrganizationsPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.OrganizationPostId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.ToEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(d => d.OrganizationPostId, reaction.ContentId); + var update = Builders.Update + .Push(d => d.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.OrganizationPostId, reaction.ContentId), + Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update + .Set(d => d.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == id); + var update = Builders.Update + .PullFilter(d => d.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs new file mode 100644 index 000000000..7e298d3de --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsUserEventCommentsMongoRepository : IReactionsUserEventCommentsRepository + { + private readonly IMongoRepository _repository; + + public ReactionsUserEventCommentsMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.UserEventCommentId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(x => x.UserEventCommentId, reaction.ContentId); + var update = Builders.Update.Push(x => x.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.UserEventCommentId, reaction.ContentId), + Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update.Set(x => x.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == id); + var update = Builders.Update.PullFilter(x => x.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs new file mode 100644 index 000000000..91e8881f6 --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsUserEventMongoRepository : IReactionsUserEventRepository + { + private readonly IMongoRepository _repository; + + public ReactionsUserEventMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.UserEventId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.ToEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(d => d.UserEventId, reaction.ContentId); + var update = Builders.Update + .Push(d => d.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.UserEventId, reaction.ContentId), + Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update + .Set(d => d.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(d => d.Reactions, r => r.Id == id); + var update = Builders.Update + .PullFilter(d => d.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs new file mode 100644 index 000000000..73006147d --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsUserPostCommentsMongoRepository : IReactionsUserPostCommentsRepository + { + private readonly IMongoRepository _repository; + + public ReactionsUserPostCommentsMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.UserPostCommentId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(x => x.UserPostCommentId, reaction.ContentId); + var update = Builders.Update.Push(x => x.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.UserPostCommentId, reaction.ContentId), + Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update.Set(x => x.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == id); + var update = Builders.Update.PullFilter(x => x.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs new file mode 100644 index 000000000..66e03855a --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Reactions.Core.Entities; +using MiniSpace.Services.Reactions.Core.Repositories; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; +using MiniSpace.Services.Reactions.Infrastructure.Mongo.Extensions; +using MongoDB.Driver; + +namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories +{ + public class ReactionsUserPostMongoRepository : IReactionsUserPostRepository + { + private readonly IMongoRepository _repository; + + public ReactionsUserPostMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(x => x.UserPostId == id); + + public async Task GetByIdAsync(Guid id) + { + var document = await _repository.GetAsync(x => x.Reactions.Any(r => r.Id == id)); + return document?.Reactions.FirstOrDefault(r => r.Id == id)?.AsEntity(); + } + + public async Task AddAsync(Reaction reaction) + { + var filter = Builders.Filter.Eq(x => x.UserPostId, reaction.ContentId); + var update = Builders.Update.Push(x => x.Reactions, reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task UpdateAsync(Reaction reaction) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.UserPostId, reaction.ContentId), + Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == reaction.Id) + ); + + var update = Builders.Update.Set(x => x.Reactions[-1], reaction.AsDocument()); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task DeleteAsync(Guid id) + { + var filter = Builders.Filter.ElemMatch(x => x.Reactions, r => r.Id == id); + var update = Builders.Update.PullFilter(x => x.Reactions, r => r.Id == id); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs deleted file mode 100644 index a86eefccf..000000000 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Reactions.Core.Entities; -using MiniSpace.Services.Reactions.Core.Repositories; -using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; - -namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories -{ - [ExcludeFromCodeCoverage] - public class StudentMongoRepository : IStudentRepository - { - private readonly IMongoRepository _repository; - - public StudentMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(Guid id) - { - var student = await _repository.GetAsync(s => s.Id == id); - return student?.AsEntity(); - } - - public Task ExistsAsync(Guid id) - => _repository.ExistsAsync(s => s.Id == id); - - - public Task AddAsync(Student student) => _repository.AddAsync(student.AsDocument()); - - - // public Task AddAsync(Student student) - // { - // throw new NotImplementedException(); - // } - - // public Task DeleteAsync(Guid id) - // { - // throw new NotImplementedException(); - // } - } -} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs new file mode 100644 index 000000000..2c85a503e --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; +using Convey.HTTP; +using MiniSpace.Services.Reactions.Application.Services.Clients; + +namespace MiniSpace.Services.Reactions.Infrastructure.Services.Clients +{ + public class CommentServiceClient : ICommentServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public CommentServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["comments"]; + } + + public async Task CommentExistsAsync(Guid id) + { + var response = await _httpClient.GetAsync($"{_url}/comments/{id}"); + return response != null; + } + } +} diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/EventsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/EventsServiceClient.cs new file mode 100644 index 000000000..e69de29bb diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/OrganizationsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/OrganizationsServiceClient.cs new file mode 100644 index 000000000..e69de29bb diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs new file mode 100644 index 000000000..8ba32121d --- /dev/null +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; +using Convey.HTTP; +using MiniSpace.Services.Reactions.Application.Services.Clients; + +namespace MiniSpace.Services.Reactions.Infrastructure.Services.Clients +{ + [ExcludeFromCodeCoverage] + public class StudentsServiceClient : IStudentsServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public StudentsServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["students"]; + } + + public async Task StudentExistsAsync(Guid id) + { + var response = await _httpClient.GetAsync($"{_url}/students/{id}"); + return response != null; + } + + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs index 7a2edfac1..28ff676ea 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs @@ -32,5 +32,9 @@ public class StudentDto public IEnumerable SignedUpEvents { get; set; } public string Country { get; set; } public string City { get; set; } + + public UserSettingsDto UserSettings { get; set; } + + public List GalleryOfImageUrls { get; set; } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs index 8ae4bfdc4..85137644e 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs @@ -2,26 +2,79 @@ using Convey.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; +using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; -using System.Diagnostics.CodeAnalysis; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Queries.Handlers { - [ExcludeFromCodeCoverage] public class GetStudentHandler : IQueryHandler { private readonly IMongoRepository _studentRepository; + private readonly IUserSettingsRepository _userSettingsRepository; + private readonly IUserGalleryRepository _userGalleryRepository; - public GetStudentHandler(IMongoRepository studentRepository) + public GetStudentHandler( + IMongoRepository studentRepository, + IUserSettingsRepository userSettingsRepository, + IUserGalleryRepository userGalleryRepository) { _studentRepository = studentRepository; + _userSettingsRepository = userSettingsRepository; + _userGalleryRepository = userGalleryRepository; } - + public async Task HandleAsync(GetStudent query, CancellationToken cancellationToken) { - var document = await _studentRepository.GetAsync(p => p.Id == query.StudentId); - - return document?.AsDto(); + // Fetch the student document from the repository + var studentDocument = await _studentRepository.GetAsync(p => p.Id == query.StudentId); + if (studentDocument == null) + { + return null; + } + + // Convert the student document to DTO + var studentDto = studentDocument.AsDto(); + + // Fetch the user settings from the repository + var userSettings = await _userSettingsRepository.GetUserSettingsAsync(query.StudentId); + if (userSettings != null) + { + // Map user settings to UserSettingsDto and include them in the StudentDto + studentDto.UserSettings = new UserSettingsDto + { + StudentId = userSettings.StudentId, + CreatedAtVisibility = userSettings.AvailableSettings.CreatedAtVisibility.ToString(), + DateOfBirthVisibility = userSettings.AvailableSettings.DateOfBirthVisibility.ToString(), + InterestedInEventsVisibility = userSettings.AvailableSettings.InterestedInEventsVisibility.ToString(), + SignedUpEventsVisibility = userSettings.AvailableSettings.SignedUpEventsVisibility.ToString(), + EducationVisibility = userSettings.AvailableSettings.EducationVisibility.ToString(), + WorkPositionVisibility = userSettings.AvailableSettings.WorkPositionVisibility.ToString(), + LanguagesVisibility = userSettings.AvailableSettings.LanguagesVisibility.ToString(), + InterestsVisibility = userSettings.AvailableSettings.InterestsVisibility.ToString(), + ContactEmailVisibility = userSettings.AvailableSettings.ContactEmailVisibility.ToString(), + PhoneNumberVisibility = userSettings.AvailableSettings.PhoneNumberVisibility.ToString(), + ProfileImageVisibility = userSettings.AvailableSettings.ProfileImageVisibility.ToString(), + BannerImageVisibility = userSettings.AvailableSettings.BannerImageVisibility.ToString(), + GalleryVisibility = userSettings.AvailableSettings.GalleryVisibility.ToString(), + PreferredLanguage = userSettings.AvailableSettings.PreferredLanguage.ToString(), + FrontendVersion = userSettings.AvailableSettings.FrontendVersion.ToString() + }; + } + + // Fetch the gallery images from the repository + var userGallery = await _userGalleryRepository.GetAsync(query.StudentId); + if (userGallery != null) + { + studentDto.GalleryOfImageUrls = userGallery.GalleryOfImages + .Select(g => new GalleryImageDto(g.ImageId, g.ImageUrl, g.DateAdded)) + .ToList(); + } + + return studentDto; } - } + } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs index 394b59ec0..fb2b7c3b9 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs @@ -7,7 +7,7 @@ using MongoDB.Bson; using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; - +using MiniSpace.Services.Students.Core.Repositories; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Queries.Handlers { @@ -15,11 +15,15 @@ namespace MiniSpace.Services.Students.Infrastructure.Mongo.Queries.Handlers public class GetStudentsHandler : IQueryHandler> { private readonly IMongoRepository _studentRepository; - private const string BaseUrl = "students"; + private readonly IUserSettingsRepository _userSettingsRepository; + private const string BaseUrl = "students"; - public GetStudentsHandler(IMongoRepository studentRepository) + public GetStudentsHandler( + IMongoRepository studentRepository, + IUserSettingsRepository userSettingsRepository) { _studentRepository = studentRepository; + _userSettingsRepository = userSettingsRepository; } public async Task> HandleAsync(GetStudents query, CancellationToken cancellationToken) @@ -29,9 +33,7 @@ public GetStudentsHandler(IMongoRepository studentReposit if (!string.IsNullOrWhiteSpace(query.Name)) { string searchTerm = query.Name.Trim(); - var parts = searchTerm.Split(' ', StringSplitOptions.RemoveEmptyEntries); - var filters = new List>(); if (parts.Length == 1) @@ -63,6 +65,7 @@ public GetStudentsHandler(IMongoRepository studentReposit filter &= Builders.Filter.Or(filters); } + var options = new FindOptions { Limit = query.ResultsPerPage, @@ -72,12 +75,43 @@ public GetStudentsHandler(IMongoRepository studentReposit using (var cursor = await _studentRepository.Collection.FindAsync(filter, options, cancellationToken)) { var documents = await cursor.ToListAsync(cancellationToken); - var dtos = documents.Select(s => s.AsDto()).ToList(); + var dtos = new List(); + + foreach (var document in documents) + { + var studentDto = document.AsDto(); + + // Fetch the user settings for each student and add to DTO + var userSettings = await _userSettingsRepository.GetUserSettingsAsync(document.Id); + if (userSettings != null) + { + studentDto.UserSettings = new UserSettingsDto + { + StudentId = userSettings.StudentId, + CreatedAtVisibility = userSettings.AvailableSettings.CreatedAtVisibility.ToString(), + DateOfBirthVisibility = userSettings.AvailableSettings.DateOfBirthVisibility.ToString(), + InterestedInEventsVisibility = userSettings.AvailableSettings.InterestedInEventsVisibility.ToString(), + SignedUpEventsVisibility = userSettings.AvailableSettings.SignedUpEventsVisibility.ToString(), + EducationVisibility = userSettings.AvailableSettings.EducationVisibility.ToString(), + WorkPositionVisibility = userSettings.AvailableSettings.WorkPositionVisibility.ToString(), + LanguagesVisibility = userSettings.AvailableSettings.LanguagesVisibility.ToString(), + InterestsVisibility = userSettings.AvailableSettings.InterestsVisibility.ToString(), + ContactEmailVisibility = userSettings.AvailableSettings.ContactEmailVisibility.ToString(), + PhoneNumberVisibility = userSettings.AvailableSettings.PhoneNumberVisibility.ToString(), + ProfileImageVisibility = userSettings.AvailableSettings.ProfileImageVisibility.ToString(), + BannerImageVisibility = userSettings.AvailableSettings.BannerImageVisibility.ToString(), + GalleryVisibility = userSettings.AvailableSettings.GalleryVisibility.ToString(), + PreferredLanguage = userSettings.AvailableSettings.PreferredLanguage.ToString(), + FrontendVersion = userSettings.AvailableSettings.FrontendVersion.ToString() + }; + } + + dtos.Add(studentDto); + } + var total = await _studentRepository.Collection.CountDocumentsAsync(filter); return new Application.Queries.PagedResult(dtos, (int)total, query.ResultsPerPage, query.Page, BaseUrl); } } - } -} - +} diff --git a/MiniSpace.Web/src/MiniSpace.Web/.gitignore b/MiniSpace.Web/src/MiniSpace.Web/.gitignore index f3a87d104..f244566cd 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/.gitignore +++ b/MiniSpace.Web/src/MiniSpace.Web/.gitignore @@ -1,3 +1,5 @@ appsettings.local.json appsettings.docker.json appsettings.json + +.fake \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/CreateEventCommand.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/CreateEventCommand.cs index 7b9522515..270fb0e18 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/CreateEventCommand.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/CreateEventCommand.cs @@ -12,7 +12,6 @@ public class CreateEventCommand public Guid EventId { get; set; } = Guid.NewGuid(); public string Name { get; set; } = string.Empty; public string OrganizerType { get; set; } - public Guid OrganizerId { get; set; } public Guid? OrganizationId { get; set; } public string StartDate { get; set; } diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/UpdateEventCommand.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/UpdateEventCommand.cs index 4400b94d5..d383a013e 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/UpdateEventCommand.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Events/CommandsDto/UpdateEventCommand.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using MiniSpace.Web.DTO.Enums; +using MiniSpace.Web.DTO.Events; namespace MiniSpace.Web.Areas.Events.CommandsDto { @@ -8,10 +9,9 @@ public class UpdateEventCommand { public Guid EventId { get; set; } public string Name { get; set; } - public OrganizerType OrganizerType { get; set; } + public string OrganizerType { get; set; } public Guid OrganizerId { get; set; } public Guid OrganizationId { get; set; } - public Guid RootOrganizationId { get; set; } public string StartDate { get; set; } public string EndDate { get; set; } public string BuildingName { get; set; } @@ -26,7 +26,7 @@ public class UpdateEventCommand public string Description { get; set; } public int Capacity { get; set; } public decimal Fee { get; set; } - public string Category { get; set; } + public Category Category { get; set; } public string PublishDate { get; set; } public EventVisibility Visibility { get; set; } public EventSettings Settings { get; set; } diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/CommandsDto/CreatePostCommand.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/CommandsDto/CreatePostCommand.cs new file mode 100644 index 000000000..bddafe679 --- /dev/null +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/CommandsDto/CreatePostCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using MiniSpace.Web.DTO.Enums.Posts; + +namespace MiniSpace.Web.Areas.Posts.CommandsDto +{ + public class CreatePostCommand + { + public Guid PostId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime? PublishDate { get; set; } + public PostContext Context { get; set; } + public string Visibility { get; set; } + } +} diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/IPostsService.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/IPostsService.cs index a7c1c5b93..523e941cb 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/IPostsService.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/IPostsService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MiniSpace.Web.Areas.Posts.CommandsDto; using MiniSpace.Web.Data.Posts; using MiniSpace.Web.DTO; using MiniSpace.Web.DTO.Wrappers; @@ -12,9 +13,8 @@ public interface IPostsService { Task GetPostAsync(Guid postId); Task ChangePostStateAsync(Guid postId, string state, DateTime publishDate); - Task> CreatePostAsync(Guid postId, Guid eventId, Guid organizerId, string textContext, - IEnumerable mediaFiles, string state, DateTime? publishDate); - Task>>> SearchPostsAsync(SearchPosts searchPosts); + Task> CreatePostAsync(CreatePostCommand command); + Task>> SearchPostsAsync(SearchPosts searchPosts); Task DeletePostAsync(Guid postId); Task> GetPostsAsync(Guid eventId); Task> UpdatePostAsync(Guid postId, string textContent, IEnumerable mediaFiles); diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/PostsService.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/PostsService.cs index 11a694318..56ca89505 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/PostsService.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Posts/PostsService.cs @@ -2,10 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; +using System.Web; using MiniSpace.Web.Areas.Identity; +using MiniSpace.Web.Areas.Posts.CommandsDto; using MiniSpace.Web.Data.Events; using MiniSpace.Web.Data.Posts; using MiniSpace.Web.DTO; +using MiniSpace.Web.DTO.Enums.Posts; using MiniSpace.Web.DTO.Wrappers; using MiniSpace.Web.HttpClients; @@ -33,19 +36,49 @@ public Task ChangePostStateAsync(Guid postId, string state, DateTime publishDate return _httpClient.PutAsync($"posts/{postId}/state/{state}", new {postId, state, publishDate}); } - public Task> CreatePostAsync(Guid postId, Guid eventId, Guid organizerId, string textContent, - IEnumerable mediaFiles, string state, DateTime? publishDate) + public Task> CreatePostAsync(CreatePostCommand command) { _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); - return _httpClient.PostAsync("posts", new {postId, eventId, organizerId, textContent, - mediaFiles, state, publishDate}); + return _httpClient.PostAsync("posts", command); } - public Task>>> SearchPostsAsync(SearchPosts searchPosts) + public async Task>> SearchPostsAsync(SearchPosts searchPosts) +{ + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var query = HttpUtility.ParseQueryString(string.Empty); + if (searchPosts.UserId.HasValue) + query["UserId"] = searchPosts.UserId.ToString(); + if (searchPosts.OrganizationId.HasValue) + query["OrganizationId"] = searchPosts.OrganizationId.ToString(); + if (searchPosts.EventId.HasValue) + query["EventId"] = searchPosts.EventId.ToString(); + + query["PageNumber"] = searchPosts.Pageable.Page.ToString(); + query["PageSize"] = searchPosts.Pageable.Size.ToString(); + if (searchPosts.Pageable.Sort?.SortBy != null) + query["SortBy"] = string.Join(",", searchPosts.Pageable.Sort.SortBy); + query["Direction"] = searchPosts.Pageable.Sort?.Direction; + + string queryString = query.ToString(); + string url = $"posts/search?{queryString}"; + + try + { + var result = await _httpClient.GetAsync>(url); + return new HttpResponse>(result); + } + catch (Exception ex) + { + return new HttpResponse>(new ErrorMessage { - _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); - return _httpClient.PostAsync>>("posts/search", searchPosts); - } + Code = ex.Message, + Reason = ex.Message + }); + } +} + + public Task DeletePostAsync(Guid postId) { diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/CommandDto/CreateReactionDto.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/CommandDto/CreateReactionDto.cs new file mode 100644 index 000000000..ba41277ec --- /dev/null +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/CommandDto/CreateReactionDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Web.Areas.Reactions.CommandDto +{ + public class CreateReactionDto + { + public Guid ReactionId { get; set;} + public Guid UserId { get; } + public string ReactionType { get; } + // ReactionType := LoveIt || LikeIt || Wow || ItWasOkay || HateIt + public Guid ContentId { get; } + public string ContentType { get; } + // ContentType := Post || Event || Comment + public string TargetType { get; } + // ReactionTargetType := User || Organization + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/IReactionsService.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/IReactionsService.cs index 4f9e07a07..9a51138b6 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/IReactionsService.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/IReactionsService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MiniSpace.Web.Areas.Reactions.CommandDto; using MiniSpace.Web.DTO; using MiniSpace.Web.DTO.Enums; using MiniSpace.Web.HttpClients; @@ -11,8 +12,7 @@ public interface IReactionsService { Task> GetReactionsAsync(Guid contentId, ReactionContentType contentType); Task GetReactionsSummaryAsync(Guid contentId, ReactionContentType contentType); - Task> CreateReactionAsync(Guid reactionId, Guid studentId, string reactionType, - Guid contentId, string contentType); + Task> CreateReactionAsync(CreateReactionDto command); Task DeleteReactionAsync(Guid reactionId); } } diff --git a/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/ReactionsService.cs b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/ReactionsService.cs index f99350c35..b6f86e0b7 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/ReactionsService.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Areas/Reactions/ReactionsService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using MiniSpace.Web.Areas.Identity; +using MiniSpace.Web.Areas.Reactions.CommandDto; using MiniSpace.Web.DTO; using MiniSpace.Web.DTO.Enums; using MiniSpace.Web.HttpClients; @@ -30,14 +31,13 @@ public Task GetReactionsSummaryAsync(Guid contentId, Reacti return _httpClient.GetAsync($"reactions/summary?contentId={contentId}&contentType={contentType}"); } - public Task> CreateReactionAsync(Guid reactionId, Guid studentId, string reactionType, - Guid contentId, string contentType) + public Task> CreateReactionAsync(CreateReactionDto command) { _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); - return _httpClient.PostAsync("reactions", - new { reactionId, studentId, reactionType, contentId, contentType }); + return _httpClient.PostAsync("reactions", command); } + public Task DeleteReactionAsync(Guid reactionId) { _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/EducationDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/EducationDto.cs index 94a33ff83..e06d3a00e 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/EducationDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/EducationDto.cs @@ -6,8 +6,8 @@ public class EducationDto { public string InstitutionName { get; set; } public string Degree { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } + public DateTime? StartDate { get; set; } = null; + public DateTime? EndDate { get; set; } = null; public string Description { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/Enums/Posts/PostContext.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/Enums/Posts/PostContext.cs new file mode 100644 index 000000000..8fda41dad --- /dev/null +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/Enums/Posts/PostContext.cs @@ -0,0 +1,9 @@ +namespace MiniSpace.Web.DTO.Enums.Posts +{ + public enum PostContext + { + UserPage, + OrganizationPage, + EventPage + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/EventDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/EventDto.cs index 2a96cc18e..a78422b4d 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/EventDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/EventDto.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using MiniSpace.Web.Areas.Events.CommandsDto; +using MiniSpace.Web.DTO.Events; namespace MiniSpace.Web.DTO { @@ -12,12 +14,12 @@ public class EventDto public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public AddressDto Location { get; set; } - public IEnumerable MediaFiles { get; set; } + public IEnumerable MediaFilesUrl { get; set; } public int InterestedStudents { get; set; } public int SignedUpStudents { get; set; } public int Capacity { get; set; } public decimal Fee { get; set; } - public string Category { get; set; } + public Category Category { get; set; } public string Status { get; set; } public DateTime PublishDate { get; set; } public DateTime UpdatedAt { get; set; } @@ -27,6 +29,7 @@ public class EventDto public IEnumerable FriendsInterestedIn { get; set; } public IEnumerable FriendsSignedUp { get; set; } public string BannerUrl { get; set; } + public EventSettings Settings { get; set; } public EventDto() { } } } \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/Notifications/NotificationLinkFactory.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/Notifications/NotificationLinkFactory.cs index f00d6e05b..b97a025d4 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/Notifications/NotificationLinkFactory.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/Notifications/NotificationLinkFactory.cs @@ -40,7 +40,7 @@ public static string GetNotificationLink(NotificationDto notification) // return $"/reports/{notification.RelatedEntityId}"; default: - return $"/student-details/{notification.RelatedEntityId}"; + return $"/user-details/{notification.RelatedEntityId}"; } } } diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/Organizations/UserOrganizationsDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/Organizations/UserOrganizationsDto.cs index bdb639ec4..a6950bdb6 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/Organizations/UserOrganizationsDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/Organizations/UserOrganizationsDto.cs @@ -15,6 +15,9 @@ public class UserOrganizationsDto public IEnumerable SubOrganizations { get; set; } public bool HasSubOrganizations => SubOrganizations != null && SubOrganizations.Any(); + public IEnumerable Users { get; set; } = new List(); + public int UserCount => Users?.Count() ?? 0; + public IEnumerable AllChildrenIds { get; set; } = new List(); public bool IsExpanded { get; set; } = false; } diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/PostDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/PostDto.cs index 47961054e..ae858d075 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/PostDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/PostDto.cs @@ -6,13 +6,16 @@ namespace MiniSpace.Web.DTO public class PostDto { public Guid Id { get; set; } - public Guid EventId { get; set; } - public Guid OrganizerId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } public string TextContent { get; set; } - public IEnumerable MediaFiles { get; set; } + public IEnumerable MediaFiles { get; set; } public string State { get; set; } public DateTime? PublishDate { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } + public string Context { get; set; } + public string Visibility { get; set; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/StudentDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/StudentDto.cs index 52326c689..8fa815d54 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/StudentDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/StudentDto.cs @@ -33,6 +33,7 @@ public class StudentDto public string Country { get; set; } public string City { get; set; } + public AvailableSettingsDto UserSettings { get; set; } public bool IsInvitationPending { get; set; } public bool InvitationSent { get; set; } diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/WorkDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/WorkDto.cs index 8675adaf6..7280fd144 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/WorkDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/WorkDto.cs @@ -6,8 +6,8 @@ public class WorkDto { public string Company { get; set; } public string Position { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } + public DateTime? StartDate { get; set; } = null; + public DateTime? EndDate { get; set; } = null; public string Description { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/DTO/Wrappers/PagedResponseDto.cs b/MiniSpace.Web/src/MiniSpace.Web/DTO/Wrappers/PagedResponseDto.cs index fb5ec85e0..0c16846a1 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/DTO/Wrappers/PagedResponseDto.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/DTO/Wrappers/PagedResponseDto.cs @@ -1,13 +1,24 @@ -namespace MiniSpace.Web.DTO.Wrappers +using System.Collections; +using System.Collections.Generic; + +namespace MiniSpace.Web.DTO.Wrappers { public class PagedResponseDto : ResponseDto { - public int TotalPages { get; set; } - public int TotalElements { get; set; } - public int Size { get; set; } - public int Number { get; set; } - public bool First { get; set; } - public bool Last { get; set; } - public bool Empty { get; set; } + public IEnumerable Items { get; } + public int TotalPages { get; } + public int TotalItems { get; } + public int PageSize { get; } + public int Page { get; } + public bool First { get; } + public bool Last { get; } + public bool Empty { get; } + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; + + public PagedResponseDto() + { + Items = new List(); + } } } \ No newline at end of file diff --git a/MiniSpace.Web/src/MiniSpace.Web/Data/Posts/SearchPosts.cs b/MiniSpace.Web/src/MiniSpace.Web/Data/Posts/SearchPosts.cs index 9c2f5087e..0279766f7 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Data/Posts/SearchPosts.cs +++ b/MiniSpace.Web/src/MiniSpace.Web/Data/Posts/SearchPosts.cs @@ -1,11 +1,28 @@ using System; +using System.Collections.Generic; using MiniSpace.Web.DTO.Wrappers; namespace MiniSpace.Web.Data.Posts { public class SearchPosts { - public Guid StudentId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } public PageableDto Pageable { get; set; } + + public SearchPosts() + { + Pageable = new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new List { "PublishDate" }, + Direction = "asc" + } + }; + } } -} \ No newline at end of file +} diff --git a/MiniSpace.Web/src/MiniSpace.Web/MiniSpace.Web.csproj b/MiniSpace.Web/src/MiniSpace.Web/MiniSpace.Web.csproj index b0776a3ce..5d07a6d5b 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/MiniSpace.Web.csproj +++ b/MiniSpace.Web/src/MiniSpace.Web/MiniSpace.Web.csproj @@ -11,12 +11,11 @@ - + + - - diff --git a/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/GalleryComponent.razor b/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/GalleryComponent.razor index a6106392a..1e873ef96 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/GalleryComponent.razor +++ b/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/GalleryComponent.razor @@ -14,7 +14,7 @@ } else { - +
- - + @if (StudentWithGalleryImagesDto.GalleryImages == null || !StudentWithGalleryImagesDto.GalleryImages.Any()) + { + No images found in the gallery. + } + else + { + + + @foreach (var image in StudentWithGalleryImagesDto.GalleryImages) + { + + + + } + + } } @@ -72,6 +84,16 @@ else + +@if (!string.IsNullOrEmpty(CroppedImageBase64)) +{ +
+ Cropped Image Preview: + +
+} + + @code { [Parameter] public bool IsLoading { get; set; } [Parameter] public StudentWithGalleryImagesDto StudentWithGalleryImagesDto { get; set; } @@ -82,6 +104,24 @@ else private IBrowserFile croppedImageFile; private Guid currentImageId; + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + // Load student's gallery images here (if not already loaded in the parent component) + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + StudentWithGalleryImagesDto.GalleryImages = null; + } + finally + { + IsLoading = false; + } + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -109,7 +149,7 @@ else await stream.CopyToAsync(ms); var buffer = ms.ToArray(); var base64Image = Convert.ToBase64String(buffer); - await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, "profile"); } } @@ -156,9 +196,12 @@ else croppedImageFile.ContentType, fileData); - StateHasChanged(); - await SaveImageAsync.InvokeAsync(null); + if (response.IsSuccessStatusCode) + { + // Refresh gallery images after successful upload + } + StateHasChanged(); NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); } catch (Exception ex) diff --git a/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/ProfileComponent.razor b/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/ProfileComponent.razor index 2fcd6148e..4c3ba0bc7 100644 --- a/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/ProfileComponent.razor +++ b/MiniSpace.Web/src/MiniSpace.Web/Pages/Account/ProfileComponent.razor @@ -7,6 +7,7 @@ @using MiniSpace.Web.Utilities @using System.IO @using MudBlazor +@using System Profile @if (IsLoading) @@ -26,21 +27,19 @@ else
- - Upload Banner Image - - - Remove Banner Image - + +
@@ -53,21 +52,19 @@ else