diff --git a/.github/workflows/azure_synchronisation.yml b/.github/workflows/azure_synchronisation.yml
index e49e4980f..b296c030c 100644
--- a/.github/workflows/azure_synchronisation.yml
+++ b/.github/workflows/azure_synchronisation.yml
@@ -29,7 +29,13 @@ jobs:
AZURE_DEVOPS_PASSWORD: ${{ secrets.AZURE_DEVOPS_PASSWORD }}
run: |
# git remote add azure https://$AZURE_DEVOPS_USERNAME:$AZURE_DEVOPS_PASSWORD@dev.azure.com/SocialAppOIPproject/SocialApp_IO/_git/SocialApp_IO
- git remote add azure https://$AZURE_DEVOPS_PAT@dev.azure.com/SocialAppOIPproject/SocialApp_IO/_git/SocialApp_IO
+ # Check if the remote already exists
+ if git remote get-url azure; then
+ echo "Remote 'azure' already exists. Updating URL if needed."
+ git remote set-url azure https://$AZURE_DEVOPS_PAT@dev.azure.com/SocialAppOIPproject/SocialApp_IO/_git/SocialApp_IO
+ else
+ git remote add azure https://$AZURE_DEVOPS_PAT@dev.azure.com/SocialAppOIPproject/SocialApp_IO/_git/SocialApp_IO
+ fi
- name: Push all branches to Azure DevOps
run: git push azure --all --force
diff --git a/.github/workflows/build_microservices.yml b/.github/workflows/build_microservices.yml
index d08bea385..2d60a9355 100644
--- a/.github/workflows/build_microservices.yml
+++ b/.github/workflows/build_microservices.yml
@@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
build-and-test:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.docker.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.docker.yml
index 03b68c692..d5102ddea 100644
--- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.docker.yml
+++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.docker.yml
@@ -97,7 +97,7 @@ modules:
returnValue: Welcome to MiniSpace API [async]!
identity:
- path: identity
+ path: /identity
routes:
- upstream: /users/{userId}
method: GET
@@ -112,7 +112,7 @@ modules:
use: downstream
downstream: identity-service/me
auth: true
-
+
- upstream: /sign-up
method: POST
use: downstream
@@ -124,13 +124,516 @@ modules:
- upstream: /sign-in
method: POST
- auth: false
use: downstream
downstream: identity-service/sign-in
- responseHeaders:
- content-type: application/json
+ auth: false
+
+ - upstream: /users/{userId}/organizer-rights
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/organizer-rights
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
services:
identity-service:
localUrl: localhost:5004
- url: identity-service
\ No newline at end of file
+ url: identity-service
+
+ reports:
+ path: /reports
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: PUT
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /{reportId}
+ method: GET
+ use: downstream
+ downstream: reports-service/reports/{reportId}
+ auth: true
+
+ services:
+ reports-service:
+ localUrl: localhost:5005
+ url: reports-service
+
+ notifications:
+ path: /notifications
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: notifications-service/notifications
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: notifications-service/notifications/{userId}
+ auth: true
+
+ services:
+ notifications-service:
+ localUrl: localhost:5006
+ url: notifications-service
+
+ students:
+ path: /students
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}
+ bind:
+ - studentId:{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: DELETE
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}/state/{state}
+ bind:
+ - studentId:{studentId}
+ - state:{state}
+ auth: true
+ claims:
+ role: admin
+
+ - upstream: /{studentId}/events
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}/events
+ auth: true
+
+ services:
+ students-service:
+ localUrl: localhost:5007
+ url: students-service
+
+ events:
+ path: /events
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: events-service/events
+ auth: true
+
+ - upstream: /{eventId}
+ method: PUT
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/{eventId}
+
+ - upstream: /student/{studentId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/student/{studentId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: events-service/events/search
+
+ - upstream: /search/organizer
+ method: POST
+ use: downstream
+ downstream: events-service/events/search/organizer
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/rate
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/rate
+ auth: true
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/organizer/{organizerId}
+ auth: true
+
+ services:
+ events-service:
+ localUrl: localhost:5008
+ url: events-service
+
+ comments:
+ path: /comments
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: comments-service/comments
+ auth: true
+
+ - upstream: /{commentId}
+ method: PUT
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/search
+
+ services:
+ comments-service:
+ localUrl: localhost:5009
+ url: comments-service
+
+ reactions:
+ path: /reactions
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reactions-service/reactions
+ auth: true
+
+ - upstream: /{reactionId}
+ method: DELETE
+ use: downstream
+ downstream: reactions-service/reactions/{reactionId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions
+
+ - upstream: /summary
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions/summary
+
+ services:
+ reactions-service:
+ localUrl: localhost:5010
+ url: reactions-service
+
+ statistics:
+ path: /statistics
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics
+ auth: true
+
+ - upstream: /rating
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ - upstream: /rating
+ method: POST
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ services:
+ statistics-service:
+ localUrl: localhost:5011
+ url: statistics-service
+
+ friends:
+ path: /friends
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: friends-service/friends
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{userId}/invite
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/{userId}/invite
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{requesterId}/{friendId}/remove
+ method: DELETE
+ use: downstream
+ downstream: friends-service/friends/{requesterId}/{friendId}/remove
+ bind:
+ - friendId: {friendId}
+ - requesterId: {requesterId}
+ auth: true
+
+ - upstream: /requests/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/accept
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/accept
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/decline
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/decline
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /pending
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending
+ auth: true
+
+ - upstream: /pending/all
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending/all
+ auth: true
+
+ - upstream: /requests/sent/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/sent/{userId}
+ auth: true
+
+ services:
+ friends-service:
+ localUrl: localhost:5012
+ url: friends-service
+
+ posts:
+ path: /posts
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: posts-service/posts
+ auth: true
+
+ - upstream: /{postId}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ bind:
+ - postId:{postId}
+ auth: true
+
+ - upstream: /{postId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}/state/{state}
+ bind:
+ - postId:{postId}
+ - state:{state}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: posts-service/posts
+
+ - upstream: /{postId}
+ method: DELETE
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ auth: true
+
+
+ services:
+ posts-service:
+ localUrl: localhost:5013
+ url: posts-service
+
+ organizations:
+ path: /organizations
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer/{organizerId}
+ method: DELETE
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations
+
+ - upstream: /{organizationId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}
+
+ - upstream: /{organizationId}/details
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/details
+
+ - upstream: /root
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/root
+
+ - upstream: /{organizationId}/children
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/children
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/organizer/{organizerId}
+ auth: true
+
+ services:
+ organizations-service:
+ localUrl: localhost:5015
+ url: organizations-service
+
+
diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.yml
index 3bc4ebb7b..8475d0118 100644
--- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.yml
+++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada-async.yml
@@ -95,9 +95,8 @@ modules:
method: GET
use: return_value
returnValue: Welcome to MiniSpace API [async]!
-
identity:
- path: identity
+ path: /identity
routes:
- upstream: /users/{userId}
method: GET
@@ -112,7 +111,7 @@ modules:
use: downstream
downstream: identity-service/me
auth: true
-
+
- upstream: /sign-up
method: POST
use: downstream
@@ -124,12 +123,536 @@ modules:
- upstream: /sign-in
method: POST
- auth: false
use: downstream
downstream: identity-service/sign-in
-
+ auth: false
+
+ - upstream: /users/{userId}/organizer-rights
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/organizer-rights
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
services:
identity-service:
localUrl: localhost:5004
url: identity-service
+
+
+
+ reports:
+ path: /reports
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: PUT
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /{reportId}
+ method: GET
+ use: downstream
+ downstream: reports-service/reports/{reportId}
+ auth: true
+
+ services:
+ reports-service:
+ localUrl: localhost:5005
+ url: reports-service
+
+
+
+ notifications:
+ path: /notifications
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: notifications-service/notifications
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: notifications-service/notifications/{userId}
+ auth: true
+
+ services:
+ notifications-service:
+ localUrl: localhost:5006
+ url: notifications-service
+
+
+
+ students:
+ path: /students
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}
+ bind:
+ - studentId:{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: DELETE
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}/state/{state}
+ bind:
+ - studentId:{studentId}
+ - state:{state}
+ auth: true
+ claims:
+ role: admin
+
+ - upstream: /{studentId}/events
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}/events
+ auth: true
+
+ services:
+ students-service:
+ localUrl: localhost:5007
+ url: students-service
+
+
+
+ events:
+ path: /events
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: events-service/events
+ auth: true
+
+ - upstream: /{eventId}
+ method: PUT
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/{eventId}
+
+ - upstream: /student/{studentId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/student/{studentId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: events-service/events/search
+
+ - upstream: /search/organizer
+ method: POST
+ use: downstream
+ downstream: events-service/events/search/organizer
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/rate
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/rate
+ auth: true
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/organizer/{organizerId}
+ auth: true
+
+ services:
+ events-service:
+ localUrl: localhost:5008
+ url: events-service
+
+
+
+ comments:
+ path: /comments
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: comments-service/comments
+ auth: true
+
+ - upstream: /{commentId}
+ method: PUT
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/search
+
+ services:
+ comments-service:
+ localUrl: localhost:5009
+ url: comments-service
+
+
+
+ reactions:
+ path: /reactions
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reactions-service/reactions
+ auth: true
+
+ - upstream: /{reactionId}
+ method: DELETE
+ use: downstream
+ downstream: reactions-service/reactions/{reactionId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions
+
+ - upstream: /summary
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions/summary
+
+ services:
+ reactions-service:
+ localUrl: localhost:5010
+ url: reactions-service
+
+
+
+ statistics:
+ path: /statistics
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics
+ auth: true
+
+ - upstream: /rating
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ - upstream: /rating
+ method: POST
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ services:
+ statistics-service:
+ localUrl: localhost:5011
+ url: statistics-service
+
+
+
+ friends:
+ path: /friends
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: friends-service/friends
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{userId}/invite
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/{userId}/invite
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{requesterId}/{friendId}/remove
+ method: DELETE
+ use: downstream
+ downstream: friends-service/friends/{requesterId}/{friendId}/remove
+ bind:
+ - friendId: {friendId}
+ - requesterId: {requesterId}
+ auth: true
+
+ - upstream: /requests/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/accept
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/accept
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/decline
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/decline
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /pending
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending
+ auth: true
+
+ - upstream: /pending/all
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending/all
+ auth: true
+
+ - upstream: /requests/sent/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/sent/{userId}
+ auth: true
+
+ services:
+ friends-service:
+ localUrl: localhost:5012
+ url: friends-service
+
+
+
+ posts:
+ path: /posts
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: posts-service/posts
+ auth: true
+
+ - upstream: /{postId}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ bind:
+ - postId:{postId}
+ auth: true
+
+ - upstream: /{postId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}/state/{state}
+ bind:
+ - postId:{postId}
+ - state:{state}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: posts-service/posts
+
+ - upstream: /{postId}
+ method: DELETE
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ auth: true
+
+
+ services:
+ posts-service:
+ localUrl: localhost:5013
+ url: posts-service
+
+
+
+ organizations:
+ path: /organizations
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer/{organizerId}
+ method: DELETE
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations
+
+ - upstream: /{organizationId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}
+
+ - upstream: /{organizationId}/details
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/details
+
+ - upstream: /root
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/root
+
+ - upstream: /{organizationId}/children
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/children
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/organizer/{organizerId}
+ auth: true
+
+ services:
+ organizations-service:
+ localUrl: localhost:5015
+ url: organizations-service
+
+
diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.docker.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.docker.yml
index c77d6aca1..221305f1d 100644
--- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.docker.yml
+++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.docker.yml
@@ -29,9 +29,11 @@ extensions:
allowedOrigins:
- '*'
allowedMethods:
- - post
- - put
- - delete
+ - GET
+ - POST
+ - PUT
+ - DELETE
+ - OPTIONS
allowedHeaders:
- '*'
exposedHeaders:
@@ -42,10 +44,10 @@ extensions:
jwt:
issuerSigningKey: eiquief5phee9pazo0Faegaez9gohThailiur5woy2befiech1oarai4aiLi6ahVecah3ie9Aiz6Peij
- validIssuer: pacco
+ validIssuer: minispace
validateAudience: false
- validateIssuer: true
- validateLifetime: true
+ validateIssuer: false
+ validateLifetime: false
swagger:
name: MiniSpace
@@ -72,7 +74,7 @@ modules:
returnValue: Welcome to MiniSpace API!
identity:
- path: identity
+ path: /identity
routes:
- upstream: /users/{userId}
method: GET
@@ -102,10 +104,518 @@ modules:
use: downstream
downstream: identity-service/sign-in
auth: false
- responseHeaders:
- content-type: application/json
-
+
+ - upstream: /users/{userId}/organizer-rights
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/organizer-rights
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/organizer-rights
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: POST
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
+
+ - upstream: /users/{userId}/ban
+ method: DELETE
+ use: downstream
+ downstream: identity-service/users/{userId}/ban
+ auth: true
+
services:
identity-service:
localUrl: localhost:5004
url: identity-service
+
+ reports:
+ path: /reports
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /
+ method: PUT
+ use: downstream
+ downstream: reports-service/reports
+ auth: true
+
+ - upstream: /{reportId}
+ method: GET
+ use: downstream
+ downstream: reports-service/reports/{reportId}
+ auth: true
+
+ services:
+ reports-service:
+ localUrl: localhost:5005
+ url: reports-service
+
+ notifications:
+ path: /notifications
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: notifications-service/notifications
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: notifications-service/notifications/{userId}
+ auth: true
+
+ services:
+ notifications-service:
+ localUrl: localhost:5006
+ url: notifications-service
+
+ students:
+ path: /students
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}
+ bind:
+ - studentId:{studentId}
+ auth: true
+
+ - upstream: /{studentId}
+ method: DELETE
+ use: downstream
+ downstream: students-service/students/{studentId}
+ auth: true
+
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: students-service/students
+ auth: true
+
+ - upstream: /{studentId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: students-service/students/{studentId}/state/{state}
+ bind:
+ - studentId:{studentId}
+ - state:{state}
+ auth: true
+ claims:
+ role: admin
+
+ - upstream: /{studentId}/events
+ method: GET
+ use: downstream
+ downstream: students-service/students/{studentId}/events
+ auth: true
+
+ services:
+ students-service:
+ localUrl: localhost:5007
+ url: students-service
+
+ events:
+ path: /events
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: events-service/events
+ auth: true
+
+ - upstream: /{eventId}
+ method: PUT
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/{eventId}
+
+ - upstream: /student/{studentId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/student/{studentId}
+ auth: true
+
+ - upstream: /{eventId}
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: events-service/events/search
+
+ - upstream: /search/organizer
+ method: POST
+ use: downstream
+ downstream: events-service/events/search/organizer
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/show-interest
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/sign-up
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
+ - upstream: /{eventId}/rate
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/rate
+ auth: true
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: events-service/events/organizer/{organizerId}
+ auth: true
+
+ services:
+ events-service:
+ localUrl: localhost:5008
+ url: events-service
+
+ comments:
+ path: /comments
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: comments-service/comments
+ auth: true
+
+ - upstream: /{commentId}
+ method: PUT
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /{commentId}/like
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/search
+
+ services:
+ comments-service:
+ localUrl: localhost:5009
+ url: comments-service
+
+ reactions:
+ path: /reactions
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: reactions-service/reactions
+ auth: true
+
+ - upstream: /{reactionId}
+ method: DELETE
+ use: downstream
+ downstream: reactions-service/reactions/{reactionId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions
+
+ - upstream: /summary
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions/summary
+
+ services:
+ reactions-service:
+ localUrl: localhost:5010
+ url: reactions-service
+
+ statistics:
+ path: /statistics
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics
+ auth: true
+
+ - upstream: /rating
+ method: GET
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ - upstream: /rating
+ method: POST
+ use: downstream
+ downstream: statistics-service/statistics/rating
+ auth: true
+
+ services:
+ statistics-service:
+ localUrl: localhost:5011
+ url: statistics-service
+
+ friends:
+ path: /friends
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: friends-service/friends
+ auth: true
+
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{userId}/invite
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/{userId}/invite
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /{requesterId}/{friendId}/remove
+ method: DELETE
+ use: downstream
+ downstream: friends-service/friends/{requesterId}/{friendId}/remove
+ bind:
+ - friendId: {friendId}
+ - requesterId: {requesterId}
+ auth: true
+
+ - upstream: /requests/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/accept
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/accept
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /requests/{userId}/decline
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/decline
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /pending
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending
+ auth: true
+
+ - upstream: /pending/all
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending/all
+ auth: true
+
+ - upstream: /requests/sent/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/sent/{userId}
+ auth: true
+
+ services:
+ friends-service:
+ localUrl: localhost:5012
+ url: friends-service
+
+ posts:
+ path: /posts
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: posts-service/posts
+ auth: true
+
+ - upstream: /{postId}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ bind:
+ - postId:{postId}
+ auth: true
+
+ - upstream: /{postId}/state/{state}
+ method: PUT
+ use: downstream
+ downstream: posts-service/posts/{postId}/state/{state}
+ bind:
+ - postId:{postId}
+ - state:{state}
+ auth: true
+
+ - upstream: /{postId}
+ method: GET
+ use: downstream
+ downstream: posts-service/posts/{postId}
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: posts-service/posts
+
+ - upstream: /{postId}
+ method: DELETE
+ use: downstream
+ downstream: posts-service/posts/{postId}
+ auth: true
+
+
+ services:
+ posts-service:
+ localUrl: localhost:5013
+ url: posts-service
+
+ organizations:
+ path: /organizations
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer/{organizerId}
+ method: DELETE
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations
+
+ - upstream: /{organizationId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}
+
+ - upstream: /{organizationId}/details
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/details
+
+ - upstream: /root
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/root
+
+ - upstream: /{organizationId}/children
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/children
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/organizer/{organizerId}
+ auth: true
+
+ services:
+ organizations-service:
+ localUrl: localhost:5015
+ url: organizations-service
+
+
diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
index 1a9e8a03b..492edb433 100644
--- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
+++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml
@@ -18,7 +18,7 @@ generateTraceId: true
useLocalUrl: true
loadBalancer:
enabled: false
- url: faibo:9999
+ url: fabio:9999
extensions:
customErrors:
@@ -112,32 +112,24 @@ modules:
use: downstream
downstream: identity-service/users/{userId}/organizer-rights
auth: true
- claims:
- role: admin
- upstream: /users/{userId}/organizer-rights
method: DELETE
use: downstream
downstream: identity-service/users/{userId}/organizer-rights
auth: true
- claims:
- role: admin
- upstream: /users/{userId}/ban
method: POST
use: downstream
downstream: identity-service/users/{userId}/ban
auth: true
- claims:
- role: admin
- upstream: /users/{userId}/ban
method: DELETE
use: downstream
downstream: identity-service/users/{userId}/ban
auth: true
- claims:
- role: admin
services:
identity-service:
@@ -210,8 +202,6 @@ modules:
use: downstream
downstream: students-service/students
auth: true
- claims:
- role: admin
- upstream: /{studentId}
method: GET
@@ -299,6 +289,12 @@ modules:
method: POST
use: downstream
downstream: events-service/events/search
+
+ - upstream: /search/organizer
+ method: POST
+ use: downstream
+ downstream: events-service/events/search/organizer
+ auth: true
- upstream: /{eventId}/show-interest
method: POST
@@ -306,12 +302,24 @@ modules:
downstream: events-service/events/{eventId}/show-interest
auth: true
+ - upstream: /{eventId}/show-interest
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/show-interest
+ auth: true
+
- upstream: /{eventId}/sign-up
method: POST
use: downstream
downstream: events-service/events/{eventId}/sign-up
auth: true
+ - upstream: /{eventId}/sign-up
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/sign-up
+ auth: true
+
- upstream: /{eventId}/rate
method: POST
use: downstream
@@ -324,6 +332,24 @@ modules:
downstream: events-service/events/organizer/{organizerId}
auth: true
+ - upstream: /{eventId}/participants
+ method: GET
+ use: downstream
+ downstream: events-service/events/{eventId}/participants
+ auth: true
+
+ - upstream: /{eventId}/participants
+ method: POST
+ use: downstream
+ downstream: events-service/events/{eventId}/participants
+ auth: true
+
+ - upstream: /{eventId}/participants
+ method: DELETE
+ use: downstream
+ downstream: events-service/events/{eventId}/participants
+ auth: true
+
services:
events-service:
localUrl: localhost:5008
@@ -348,12 +374,6 @@ modules:
- commentId:{commentId}
auth: true
- - upstream: /
- method: GET
- use: downstream
- downstream: comments-service/comments
- auth: true
-
- upstream: /{commentId}
method: DELETE
use: downstream
@@ -368,6 +388,19 @@ modules:
- commentId:{commentId}
auth: true
+ - upstream: /{commentId}/like
+ method: DELETE
+ use: downstream
+ downstream: comments-service/comments/{commentId}/like
+ bind:
+ - commentId:{commentId}
+ auth: true
+
+ - upstream: /search
+ method: POST
+ use: downstream
+ downstream: comments-service/comments/search
+
services:
comments-service:
localUrl: localhost:5009
@@ -384,17 +417,21 @@ modules:
downstream: reactions-service/reactions
auth: true
- - upstream: /
+ - upstream: /{reactionId}
method: DELETE
use: downstream
- downstream: reactions-service/reactions
+ downstream: reactions-service/reactions/{reactionId}
auth: true
- upstream: /
method: GET
use: downstream
downstream: reactions-service/reactions
- auth: true
+
+ - upstream: /summary
+ method: GET
+ use: downstream
+ downstream: reactions-service/reactions/summary
services:
reactions-service:
@@ -428,56 +465,89 @@ modules:
statistics-service:
localUrl: localhost:5011
url: statistics-service
-
+
friends:
- path: /friends
- routes:
- - upstream: /
- method: GET
- use: downstream
- downstream: friends-service/friends
- auth: true
+ path: /friends
+ routes:
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: friends-service/friends
+ auth: true
- - upstream: /{userId}
- method: POST
- use: downstream
- downstream: friends-service/friends/{userId}
- bind:
- - userId:{userId}
- auth: true
+ - upstream: /{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
- - upstream: /notYet
- method: GET
- use: downstream
- downstream: friends-service/friends/notYet
- auth: true
+ - upstream: /{userId}/invite
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/{userId}/invite
+ bind:
+ - userId: {userId}
+ auth: true
- - upstream: /pending
- method: POST
- use: downstream
- downstream: friends-service/friends/pending
- auth: true
+ - upstream: /{requesterId}/{friendId}/remove
+ method: DELETE
+ use: downstream
+ downstream: friends-service/friends/{requesterId}/{friendId}/remove
+ bind:
+ - friendId: {friendId}
+ - requesterId: {requesterId}
+ auth: true
- - upstream: /pending
- method: GET
- use: downstream
- downstream: friends-service/friends/pending
- auth: true
+ - upstream: /requests/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}
+ bind:
+ - userId: {userId}
+ auth: true
- - upstream: /{userId}/invite
- method: POST
- use: downstream
- downstream: friends-service/friends/{userId}/invite
- bind:
- - userId:{userId}
- auth: true
+ - upstream: /requests/{userId}/accept
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/accept
+ bind:
+ - userId: {userId}
+ auth: true
- services:
- friends-service:
- localUrl: localhost:5012
- url: friends-service
+ - upstream: /requests/{userId}/decline
+ method: POST
+ use: downstream
+ downstream: friends-service/friends/requests/{userId}/decline
+ bind:
+ - userId: {userId}
+ auth: true
+
+ - upstream: /pending
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending
+ auth: true
+
+ - upstream: /pending/all
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/pending/all
+ auth: true
+
+ - upstream: /requests/sent/{userId}
+ method: GET
+ use: downstream
+ downstream: friends-service/friends/requests/sent/{userId}
+ auth: true
+
+ services:
+ friends-service:
+ localUrl: localhost:5012
+ url: friends-service
@@ -501,12 +571,17 @@ modules:
- upstream: /{postId}/state/{state}
method: PUT
use: downstream
- downstream: students-service/students/{postId}/state/{state}
+ downstream: posts-service/posts/{postId}/state/{state}
bind:
- postId:{postId}
- state:{state}
auth: true
+ - upstream: /{postId}
+ method: GET
+ use: downstream
+ downstream: posts-service/posts/{postId}
+
- upstream: /
method: GET
use: downstream
@@ -522,4 +597,65 @@ modules:
services:
posts-service:
localUrl: localhost:5013
- url: posts-service
\ No newline at end of file
+ url: posts-service
+
+
+
+ organizations:
+ path: /organizations
+ routes:
+ - upstream: /
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer
+ method: POST
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer
+ auth: true
+
+ - upstream: /organizer/{organizationId}/organizer/{organizerId}
+ method: DELETE
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId}
+ auth: true
+
+ - upstream: /
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations
+
+ - upstream: /{organizationId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}
+
+ - upstream: /{organizationId}/details
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/details
+
+ - upstream: /root
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/root
+
+ - upstream: /{organizationId}/children
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/{organizationId}/children
+
+ - upstream: /organizer/{organizerId}
+ method: GET
+ use: downstream
+ downstream: organizations-service/organizations/organizer/{organizerId}
+ auth: true
+
+ services:
+ organizations-service:
+ localUrl: localhost:5015
+ url: organizations-service
+
+
diff --git a/MiniSpace.Services.Comments/.gitignore b/MiniSpace.Services.Comments/.gitignore
new file mode 100644
index 000000000..104b54414
--- /dev/null
+++ b/MiniSpace.Services.Comments/.gitignore
@@ -0,0 +1,484 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from `dotnet new gitignore`
+
+# dotenv files
+.env
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+.idea
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# Vim temporary swap files
+*.swp
diff --git a/MiniSpace.Services.Comments/Dockerfile b/MiniSpace.Services.Comments/Dockerfile
new file mode 100644
index 000000000..357241e47
--- /dev/null
+++ b/MiniSpace.Services.Comments/Dockerfile
@@ -0,0 +1,17 @@
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+WORKDIR /app
+
+COPY . .
+
+RUN dotnet publish src/MiniSpace.Services.Comments.Api -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
+WORKDIR /app
+
+COPY --from=build /app/out .
+
+ENV ASPNETCORE_URLS=http://*:80
+ENV ASPNETCORE_ENVIRONMENT=docker
+ENV NTRADA_CONFIG=ntrada.docker
+
+ENTRYPOINT ["dotnet", "MiniSpace.Services.Comments.Api.dll"]
diff --git a/MiniSpace.Services.Comments/LICENSE b/MiniSpace.Services.Comments/LICENSE
new file mode 100644
index 000000000..b7ea7f0cc
--- /dev/null
+++ b/MiniSpace.Services.Comments/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 DevMentors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/MiniSpace.Services.Comments/MiniSpace.Services.Comments.sln b/MiniSpace.Services.Comments/MiniSpace.Services.Comments.sln
new file mode 100644
index 000000000..b474e20c8
--- /dev/null
+++ b/MiniSpace.Services.Comments/MiniSpace.Services.Comments.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Api", "src\MiniSpace.Services.Comments.Api\MiniSpace.Services.Comments.Api.csproj", "{F0C47524-349D-48B6-9866-9072D9294FF7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Application", "src\MiniSpace.Services.Comments.Application\MiniSpace.Services.Comments.Application.csproj", "{AEA4B1F6-080B-4BAD-94FA-F7489A2D29B7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Core", "src\MiniSpace.Services.Comments.Core\MiniSpace.Services.Comments.Core.csproj", "{2C8AC5D4-011F-4545-9C0C-88D99D13FF82}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Comments.Infrastructure", "src\MiniSpace.Services.Comments.Infrastructure\MiniSpace.Services.Comments.Infrastructure.csproj", "{85941F19-E28E-467B-A338-7D358D32CFC8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F0C47524-349D-48B6-9866-9072D9294FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0C47524-349D-48B6-9866-9072D9294FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0C47524-349D-48B6-9866-9072D9294FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0C47524-349D-48B6-9866-9072D9294FF7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AEA4B1F6-080B-4BAD-94FA-F7489A2D29B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AEA4B1F6-080B-4BAD-94FA-F7489A2D29B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AEA4B1F6-080B-4BAD-94FA-F7489A2D29B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AEA4B1F6-080B-4BAD-94FA-F7489A2D29B7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2C8AC5D4-011F-4545-9C0C-88D99D13FF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2C8AC5D4-011F-4545-9C0C-88D99D13FF82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2C8AC5D4-011F-4545-9C0C-88D99D13FF82}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2C8AC5D4-011F-4545-9C0C-88D99D13FF82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {85941F19-E28E-467B-A338-7D358D32CFC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {85941F19-E28E-467B-A338-7D358D32CFC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {85941F19-E28E-467B-A338-7D358D32CFC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {85941F19-E28E-467B-A338-7D358D32CFC8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {30DA4571-97CC-4095-8214-8558CF1F0539}
+ EndGlobalSection
+EndGlobal
diff --git a/MiniSpace.Services.Comments/scripts/build.sh b/MiniSpace.Services.Comments/scripts/build.sh
new file mode 100644
index 000000000..3affad0eb
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/build.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+dotnet build -c release
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/scripts/dockerize-tag-push.sh b/MiniSpace.Services.Comments/scripts/dockerize-tag-push.sh
new file mode 100755
index 000000000..e10e9e528
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/dockerize-tag-push.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+export ASPNETCORE_ENVIRONMENT=docker
+
+cd ..
+
+docker build -t minispace.services.comments:latest .
+
+docker tag minispace.services.comments:latest adrianvsaint/minispace.services.comments:latest
+
+docker push adrianvsaint/minispace.services.comments:latest
diff --git a/MiniSpace.Services.Comments/scripts/dockerize.sh b/MiniSpace.Services.Comments/scripts/dockerize.sh
new file mode 100644
index 000000000..d3dfa7b19
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/dockerize.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+TAG=''
+VERSION_TAG=
+
+case "$TRAVIS_BRANCH" in
+ "master")
+ TAG=latest
+ VERSION_TAG=$TRAVIS_BUILD_NUMBER
+ ;;
+ "develop")
+ TAG=dev
+ VERSION_TAG=$TAG-$TRAVIS_BUILD_NUMBER
+ ;;
+esac
+
+REPOSITORY=$DOCKER_USERNAME/minispace.services.comments
+
+docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
+docker build -t $REPOSITORY:$TAG -t $REPOSITORY:$VERSION_TAG .
+docker push $REPOSITORY:$TAG
+docker push $REPOSITORY:$VERSION_TAG
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/scripts/start-async.sh b/MiniSpace.Services.Comments/scripts/start-async.sh
new file mode 100755
index 000000000..0b61fec74
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/start-async.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+export ASPNETCORE_ENVIRONMENT=local
+export NTRADA_CONFIG=ntrada-async
+cd src/MiniSpace.Services.Comments.Api
+dotnet run
diff --git a/MiniSpace.Services.Comments/scripts/start.sh b/MiniSpace.Services.Comments/scripts/start.sh
new file mode 100755
index 000000000..2df375d6f
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/start.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+export ASPNETCORE_ENVIRONMENT=local
+cd src/MiniSpace.APIGateway
+dotnet run
diff --git a/MiniSpace.Services.Comments/scripts/test.sh b/MiniSpace.Services.Comments/scripts/test.sh
new file mode 100644
index 000000000..6046c35a0
--- /dev/null
+++ b/MiniSpace.Services.Comments/scripts/test.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+dotnet test
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj
new file mode 100644
index 000000000..c66b12422
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net8.0
+ latest
+ MiniSpace.Services.Identity.Api
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs
new file mode 100644
index 000000000..27a5df313
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Convey;
+using Convey.Secrets.Vault;
+using Convey.Logging;
+using Convey.Types;
+using Convey.WebApi;
+using Convey.WebApi.CQRS;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using MiniSpace.Services.Comments.Application;
+using MiniSpace.Services.Comments.Application.Commands;
+using MiniSpace.Services.Comments.Application.Dto;
+using MiniSpace.Services.Comments.Application.Services;
+using MiniSpace.Services.Comments.Infrastructure;
+
+namespace MiniSpace.Services.Identity.Api
+{
+ public class Program
+ {
+ public static async Task Main(string[] args)
+ => await WebHost.CreateDefaultBuilder(args)
+ .ConfigureServices(services => services
+ .AddConvey()
+ .AddWebApi()
+ .AddApplication()
+ .AddInfrastructure()
+ .Build())
+ .Configure(app => app
+ .UseInfrastructure()
+ .UseEndpoints(endpoints => endpoints
+ .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name))
+ .Post("comments/search", async (cmd, ctx) =>
+ {
+ var pagedResult = await ctx.RequestServices.GetService().BrowseCommentsAsync(cmd);
+ await ctx.Response.WriteJsonAsync(pagedResult);
+ }))
+ .UseDispatcherEndpoints(endpoints => endpoints
+ .Post("comments")
+ .Put("comments/{commentID}")
+ .Delete("comments/{commentID}")
+ .Post("comments/{commentID}/like")
+ .Delete("comments/{commentID}/like")
+ )
+ )
+ .UseLogging()
+ .Build()
+ .RunAsync();
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Properties/launchSettings.json b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Properties/launchSettings.json
new file mode 100644
index 000000000..5c713c378
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Properties/launchSettings.json
@@ -0,0 +1,26 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:5009"
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "local"
+ }
+ },
+ "MiniSpace.Services.Comments": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "applicationUrl": "http://localhost:5009",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "local"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.Development.json b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.Development.json
new file mode 100644
index 000000000..7a73a41bf
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.Development.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.docker.json b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.docker.json
new file mode 100644
index 000000000..e1d06000d
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.docker.json
@@ -0,0 +1,153 @@
+{
+ "app": {
+ "name": "MiniSpace Comments Service",
+ "version": "1"
+ },
+ "consul": {
+ "enabled": true,
+ "url": "http://consul:8500",
+ "service": "comments-service",
+ "address": "comments-service",
+ "port": "80",
+ "pingEnabled": true,
+ "pingEndpoint": "ping",
+ "pingInterval": 3,
+ "removeAfterInterval": 3
+ },
+ "fabio": {
+ "enabled": true,
+ "url": "http://fabio:9999",
+ "service": "comments-service"
+ },
+ "httpClient": {
+ "type": "fabio",
+ "retries": 3,
+ "services": {}
+ },
+ "jwt": {
+ "certificate": {
+ "location": "",
+ "password": "",
+ "rawData": ""
+ },
+ "issuerSigningKey": "eiquief5phee9pazo0Faegaez9gohThailiur5woy2befiech1oarai4aiLi6ahVecah3ie9Aiz6Peij",
+ "expiryMinutes": 60,
+ "issuer": "minispace",
+ "validateAudience": false,
+ "validateIssuer": false,
+ "validateLifetime": true,
+ "allowAnonymousEndpoints": ["/sign-in", "/sign-up"]
+ },
+ "logger": {
+ "console": {
+ "enabled": true
+ },
+ "elk": {
+ "enabled": false,
+ "url": "http://elk:9200"
+ },
+ "file": {
+ "enabled": false,
+ "path": "logs/logs.txt",
+ "interval": "day"
+ },
+ "seq": {
+ "enabled": true,
+ "url": "http://seq:5341",
+ "apiKey": "secret"
+ }
+ },
+ "jaeger": {
+ "enabled": true,
+ "serviceName": "comments",
+ "udpHost": "jaeger",
+ "udpPort": 6831,
+ "maxPacketSize": 0,
+ "sampler": "const",
+ "excludePaths": ["/", "/ping", "/metrics"]
+ },
+ "metrics": {
+ "enabled": true,
+ "influxEnabled": false,
+ "prometheusEnabled": true,
+ "influxUrl": "http://influx:8086",
+ "database": "minispace",
+ "env": "docker",
+ "interval": 5
+ },
+ "mongo": {
+ "connectionString": "mongodb+srv://minispace-user:9vd6IxYWUuuqhzEH@cluster0.mmhq4pe.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0",
+ "database": "comments-service",
+ "seed": false
+ },
+ "rabbitMq": {
+ "connectionName": "comments-service",
+ "retries": 3,
+ "retryInterval": 2,
+ "conventionsCasing": "snakeCase",
+ "logger": {
+ "enabled": true
+ },
+ "username": "guest",
+ "password": "guest",
+ "virtualHost": "/",
+ "port": 5672,
+ "hostnames": [
+ "rabbitmq"
+ ],
+ "requestedConnectionTimeout": "00:00:30",
+ "requestedHeartbeat": "00:01:00",
+ "socketReadTimeout": "00:00:30",
+ "socketWriteTimeout": "00:00:30",
+ "continuationTimeout": "00:00:20",
+ "handshakeContinuationTimeout": "00:00:10",
+ "networkRecoveryInterval": "00:00:05",
+ "exchange": {
+ "declare": true,
+ "durable": true,
+ "autoDelete": false,
+ "type": "topic",
+ "name": "comments"
+ },
+ "queue": {
+ "declare": true,
+ "durable": true,
+ "exclusive": false,
+ "autoDelete": false,
+ "template": "comments-service/{{exchange}}.{{message}}"
+ },
+ "context": {
+ "enabled": true,
+ "header": "message_context"
+ },
+ "spanContextHeader": "span_context"
+ },
+ "redis": {
+ "connectionString": "redis",
+ "instance": "events:"
+ },
+ "swagger": {
+ "enabled": true,
+ "reDocEnabled": false,
+ "name": "v1",
+ "title": "API",
+ "version": "v1",
+ "routePrefix": "docs",
+ "includeSecurity": true
+ },
+ "vault": {
+ "enabled": false,
+ "url": "http://vault:8200",
+ "kv": {
+ "enabled": false
+ },
+ "pki": {
+ "enabled": false
+ },
+ "lease": {
+ "mongo": {
+ "enabled": false
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.json b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.json
new file mode 100644
index 000000000..10f68b8c8
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.local.json b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.local.json
new file mode 100644
index 000000000..e0c12ec2f
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/appsettings.local.json
@@ -0,0 +1,196 @@
+{
+ "app": {
+ "name": "MiniSpace Comments Service",
+ "service": "comments-service",
+ "version": "1"
+ },
+ "consul": {
+ "enabled": true,
+ "url": "http://localhost:8500",
+ "service": "comments-service",
+ "address": "docker.for.win.localhost",
+ "port": "5009",
+ "pingEnabled": false,
+ "pingEndpoint": "ping",
+ "pingInterval": 3,
+ "removeAfterInterval": 3
+ },
+ "fabio": {
+ "enabled": true,
+ "url": "http://localhost:9999",
+ "service": "comments-service"
+ },
+ "httpClient": {
+ "type": "direct",
+ "retries": 3,
+ "services": {
+ "students": "http://localhost:5007"
+ },
+ "requestMasking": {
+ "enabled": true,
+ "maskTemplate": "*****"
+ }
+ },
+ "jwt": {
+ "issuerSigningKey": "eiquief5phee9pazo0Faegaez9gohThailiur5woy2befiech1oarai4aiLi6ahVecah3ie9Aiz6Peij",
+ "expiryMinutes": 60,
+ "issuer": "minispace",
+ "validateAudience": false,
+ "validateIssuer": false,
+ "validateLifetime": false,
+ "allowAnonymousEndpoints": ["/sign-in", "/sign-up"]
+ },
+ "logger": {
+ "level": "information",
+ "excludePaths": ["/", "/ping", "/metrics"],
+ "excludeProperties": [
+ "api_key",
+ "access_key",
+ "ApiKey",
+ "ApiSecret",
+ "ClientId",
+ "ClientSecret",
+ "ConnectionString",
+ "Password",
+ "Email",
+ "Login",
+ "Secret",
+ "Token"
+ ],
+ "console": {
+ "enabled": true
+ },
+ "elk": {
+ "enabled": false,
+ "url": "http://localhost:9200"
+ },
+ "file": {
+ "enabled": true,
+ "path": "logs/logs.txt",
+ "interval": "day"
+ },
+ "seq": {
+ "enabled": true,
+ "url": "http://localhost:5341",
+ "apiKey": "secret"
+ },
+ "tags": {}
+ },
+ "jaeger": {
+ "enabled": true,
+ "serviceName": "comments",
+ "udpHost": "localhost",
+ "udpPort": 6831,
+ "maxPacketSize": 0,
+ "sampler": "const",
+ "excludePaths": ["/", "/ping", "/metrics"]
+ },
+ "metrics": {
+ "enabled": true,
+ "influxEnabled": false,
+ "prometheusEnabled": true,
+ "influxUrl": "http://localhost:8086",
+ "database": "minispace",
+ "env": "local",
+ "interval": 5
+ },
+ "mongo": {
+ "connectionString": "mongodb+srv://minispace-user:9vd6IxYWUuuqhzEH@cluster0.mmhq4pe.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0",
+ "database": "comments-service",
+ "seed": false
+ },
+ "outbox": {
+ "enabled": false,
+ "type": "sequential",
+ "expiry": 3600,
+ "intervalMilliseconds": 2000,
+ "inboxCollection": "inbox",
+ "outboxCollection": "outbox",
+ "disableTransactions": true
+ },
+ "rabbitMq": {
+ "connectionName": "comments-service",
+ "retries": 3,
+ "retryInterval": 2,
+ "conventionsCasing": "snakeCase",
+ "logger": {
+ "enabled": true
+ },
+ "username": "guest",
+ "password": "guest",
+ "virtualHost": "/",
+ "port": 5672,
+ "hostnames": [
+ "localhost"
+ ],
+ "requestedConnectionTimeout": "00:00:30",
+ "requestedHeartbeat": "00:01:00",
+ "socketReadTimeout": "00:00:30",
+ "socketWriteTimeout": "00:00:30",
+ "continuationTimeout": "00:00:20",
+ "handshakeContinuationTimeout": "00:00:10",
+ "networkRecoveryInterval": "00:00:05",
+ "exchange": {
+ "declare": true,
+ "durable": true,
+ "autoDelete": false,
+ "type": "topic",
+ "name": "comments"
+ },
+ "queue": {
+ "declare": true,
+ "durable": true,
+ "exclusive": false,
+ "autoDelete": false,
+ "template": "comments-service/{{exchange}}.{{message}}"
+ },
+ "context": {
+ "enabled": true,
+ "header": "message_context"
+ },
+ "spanContextHeader": "span_context"
+ },
+ "redis": {
+ "connectionString": "localhost",
+ "instance": "comments:"
+ },
+ "swagger": {
+ "enabled": true,
+ "reDocEnabled": false,
+ "name": "v1",
+ "title": "API",
+ "version": "v1",
+ "routePrefix": "docs",
+ "includeSecurity": true
+ },
+ "vault": {
+ "enabled": true,
+ "url": "http://localhost:8200",
+ "authType": "token",
+ "token": "secret",
+ "username": "user",
+ "password": "secret",
+ "kv": {
+ "enabled": true,
+ "engineVersion": 2,
+ "mountPoint": "kv",
+ "path": "comments-service/settings"
+ },
+ "pki": {
+ "enabled": true,
+ "roleName": "comments-service",
+ "commonName": "comments-service.minispace.io"
+ },
+ "lease": {
+ "mongo": {
+ "type": "database",
+ "roleName": "comments-service",
+ "enabled": true,
+ "autoRenewal": true,
+ "templates": {
+ "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017"
+ }
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs
new file mode 100644
index 000000000..440c96dc4
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs
@@ -0,0 +1,15 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ public class AddLike : ICommand
+ {
+ public Guid CommentId { get; set; }
+
+ public AddLike(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
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
new file mode 100644
index 000000000..54519972e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ 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 Guid ParentId { get; set; }
+ public string Comment { get; set; }
+
+
+ public CreateComment(Guid commentId, Guid contextId, string commentContext, Guid studentId, Guid parentId,
+ string comment)
+ {
+ CommentId = commentId == Guid.Empty ? Guid.NewGuid() : commentId;
+ ContextId = contextId;
+ CommentContext = commentContext;
+ StudentId = studentId;
+ ParentId = parentId;
+ Comment = comment;
+ }
+ }
+}
\ 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
new file mode 100644
index 000000000..8ff86829c
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs
@@ -0,0 +1,12 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ public class DeleteComment : ICommand
+ {
+ public Guid CommentId { get; }
+
+ public DeleteComment(Guid commentId) => CommentId = commentId;
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs
new file mode 100644
index 000000000..7d3061195
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs
@@ -0,0 +1,12 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ public class DeleteLike : ICommand
+ {
+ public Guid CommentId { get; }
+
+ public DeleteLike(Guid commentId) => CommentId = commentId;
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs
new file mode 100644
index 000000000..abd04f183
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs
@@ -0,0 +1,48 @@
+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;
+
+namespace MiniSpace.Services.Comments.Application.Commands.Handlers
+{
+ public class AddLikeHandler : ICommandHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+ private readonly IAppContext _appContext;
+ private readonly IMessageBroker _messageBroker;
+
+ public AddLikeHandler(ICommentRepository commentRepository, IAppContext appContext,
+ IMessageBroker messageBroker)
+ {
+ _commentRepository = commentRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(AddLike command, CancellationToken cancellationToken = default)
+ {
+ var comment = await _commentRepository.GetAsync(command.CommentId);
+ if (comment is null)
+ {
+ throw new CommentNotFoundException(command.CommentId);
+ }
+
+ var identity = _appContext.Identity;
+ if (!identity.IsAuthenticated)
+ {
+ throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id);
+ }
+
+ comment.Like(identity.Id);
+ await _commentRepository.UpdateAsync(comment);
+
+ await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId));
+ }
+ }
+}
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
new file mode 100644
index 000000000..ae067c0cb
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs
@@ -0,0 +1,74 @@
+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.Exceptions;
+using MiniSpace.Services.Comments.Core.Repositories;
+
+namespace MiniSpace.Services.Comments.Application.Commands.Handlers
+{
+ public class CreateCommentHandler : ICommandHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+ private readonly IStudentRepository _studentRepository;
+ private readonly IDateTimeProvider _dateTimeProvider;
+ private readonly IMessageBroker _messageBroker;
+ private readonly IAppContext _appContext;
+
+ public CreateCommentHandler(ICommentRepository commentRepository, IStudentRepository studentRepository,
+ IDateTimeProvider dateTimeProvider, IMessageBroker messageBroker, IAppContext appContext)
+ {
+ _commentRepository = commentRepository;
+ _studentRepository = studentRepository;
+ _dateTimeProvider = dateTimeProvider;
+ _messageBroker = messageBroker;
+ _appContext = appContext;
+ }
+
+ public async Task HandleAsync(CreateComment command, CancellationToken cancellationToken = default)
+ {
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && identity.Id != command.StudentId)
+ {
+ throw new UnauthorizedCommentAccessException(command.ContextId, identity.Id);
+ }
+ if (!(await _studentRepository.ExistsAsync(command.StudentId)))
+ {
+ throw new StudentNotFoundException(command.StudentId);
+ }
+
+ if (!Enum.TryParse(command.CommentContext, true, out var newCommentContext))
+ {
+ 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);
+
+ if (command.ParentId != Guid.Empty)
+ {
+ var parentComment = await _commentRepository.GetAsync(command.ParentId);
+ 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);
+ }
+
+ await _commentRepository.AddAsync(comment);
+ await _messageBroker.PublishAsync(new CommentCreated(command.CommentId));
+ }
+
+ }
+}
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
new file mode 100644
index 000000000..447d23985
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs
@@ -0,0 +1,48 @@
+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;
+
+namespace MiniSpace.Services.Comments.Application.Commands.Handlers
+{
+ public class DeleteCommentHandler : ICommandHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+ private readonly IAppContext _appContext;
+ private readonly IMessageBroker _messageBroker;
+
+ public DeleteCommentHandler(ICommentRepository commentRepository, IAppContext appContext,
+ IMessageBroker messageBroker)
+ {
+ _commentRepository = commentRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(DeleteComment command, CancellationToken cancellationToken = default)
+ {
+ var comment = await _commentRepository.GetAsync(command.CommentId);
+ if (comment is null)
+ {
+ throw new CommentNotFoundException(command.CommentId);
+ }
+
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && identity.Id != comment.StudentId && !identity.IsAdmin)
+ {
+ throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id);
+ }
+
+ comment.Delete();
+ await _commentRepository.UpdateAsync(comment);
+
+ 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
new file mode 100644
index 000000000..c17a53909
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs
@@ -0,0 +1,48 @@
+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;
+
+namespace MiniSpace.Services.Comments.Application.Commands.Handlers
+{
+ public class DeleteLikeHandler : ICommandHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+ private readonly IAppContext _appContext;
+ private readonly IMessageBroker _messageBroker;
+
+ public DeleteLikeHandler(ICommentRepository commentRepository, IAppContext appContext,
+ IMessageBroker messageBroker)
+ {
+ _commentRepository = commentRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(DeleteLike command, CancellationToken cancellationToken = default)
+ {
+ var comment = await _commentRepository.GetAsync(command.CommentId);
+ if (comment is null)
+ {
+ throw new CommentNotFoundException(command.CommentId);
+ }
+
+ var identity = _appContext.Identity;
+ if (!identity.IsAuthenticated)
+ {
+ throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id);
+ }
+
+ comment.UnLike(identity.Id);
+ await _commentRepository.UpdateAsync(comment);
+
+ await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId));
+ }
+ }
+}
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
new file mode 100644
index 000000000..879d52563
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs
@@ -0,0 +1,50 @@
+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;
+
+namespace MiniSpace.Services.Comments.Application.Commands.Handlers
+{
+ public class UpdateCommentHandler : ICommandHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+ private readonly IAppContext _appContext;
+ private readonly IMessageBroker _messageBroker;
+ private readonly IDateTimeProvider _dateTimeProvider;
+
+ public UpdateCommentHandler(ICommentRepository commentRepository, IAppContext appContext,
+ IMessageBroker messageBroker, IDateTimeProvider dateTimeProvider)
+ {
+ _commentRepository = commentRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ _dateTimeProvider = dateTimeProvider;
+ }
+
+ public async Task HandleAsync(UpdateComment command, CancellationToken cancellationToken = default)
+ {
+ var comment = await _commentRepository.GetAsync(command.CommentId);
+ if (comment is null)
+ {
+ throw new CommentNotFoundException(command.CommentId);
+ }
+
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && identity.Id != comment.StudentId && !identity.IsAdmin)
+ {
+ throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id);
+ }
+
+ comment.Update(command.TextContent, _dateTimeProvider.Now);
+ await _commentRepository.UpdateAsync(comment);
+
+ await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId));
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/SearchComments.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/SearchComments.cs
new file mode 100644
index 000000000..34e9bf540
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/SearchComments.cs
@@ -0,0 +1,14 @@
+using System;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Comments.Application.Dto;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ public class SearchComments : ICommand
+ {
+ public Guid ContextId { get; set; }
+ public string CommentContext { get; set; }
+ public Guid ParentId { get; set; }
+ public PageableDto Pageable { get; set; }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..e93a0e67d
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Comments.Application.Commands
+{
+ public class UpdateComment : ICommand
+ {
+ public Guid CommentId { get; }
+ public string TextContent { get; }
+
+ public UpdateComment(Guid commentId, string textContent)
+ {
+ CommentId = commentId;
+ TextContent = textContent;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/ContractAttribute.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/ContractAttribute.cs
new file mode 100644
index 000000000..d5b2ceecf
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/ContractAttribute.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application
+{
+ public class ContractAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..72ca781fc
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/CommentDto.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MiniSpace.Services.Comments.Core.Entities;
+
+namespace MiniSpace.Services.Comments.Application.Dto
+{
+ 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 IEnumerable Likes { get; set; }
+ 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 bool IsDeleted { get; set; }
+
+ public CommentDto()
+ {
+ }
+
+ public CommentDto (Comment comment)
+ {
+ Id = comment.Id;
+ ContextId = comment.ContextId;
+ CommentContext = comment.CommentContext.ToString().ToLowerInvariant();
+ StudentId = comment.StudentId;
+ StudentName = comment.StudentName;
+ Likes = comment.Likes;
+ ParentId = comment.ParentId;
+ TextContent = comment.TextContent;
+ CreatedAt = comment.CreatedAt;
+ LastUpdatedAt = comment.LastUpdatedAt;
+ LastReplyAt = comment.LastReplyAt;
+ RepliesCount = comment.RepliesCount;
+ IsDeleted = comment.IsDeleted;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/PageableDto.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/PageableDto.cs
new file mode 100644
index 000000000..0aa0823b7
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/PageableDto.cs
@@ -0,0 +1,9 @@
+namespace MiniSpace.Services.Comments.Application.Dto
+{
+ public class PageableDto
+ {
+ public int Page { get; set; }
+ public int Size { get; set; }
+ public SortDto Sort { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/SortDto.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/SortDto.cs
new file mode 100644
index 000000000..7aaa0fc53
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Dto/SortDto.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Comments.Application.Dto
+{
+ public class SortDto
+ {
+ public IEnumerable SortBy { get; set; }
+ public string Direction { 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
new file mode 100644
index 000000000..2007156a5
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs
@@ -0,0 +1,15 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events
+{
+ public class CommentCreated : IEvent
+ {
+ public Guid CommentId { get; }
+
+ public CommentCreated(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs
new file mode 100644
index 000000000..d30ad3f1b
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs
@@ -0,0 +1,15 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events
+{
+ public class CommentDeleted : IEvent
+ {
+ public Guid CommentId { get; }
+
+ public CommentDeleted(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs
new file mode 100644
index 000000000..360c41cc1
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs
@@ -0,0 +1,15 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events
+{
+ public class CommentUpdated : IEvent
+ {
+ public Guid CommentId { get; }
+
+ public CommentUpdated(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs
new file mode 100644
index 000000000..d902fb896
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events.External
+{
+ [Message("events")]
+ public class EventDeleted : IEvent
+ {
+ public Guid EventId { get; }
+
+ public EventDeleted(Guid eventId)
+ {
+ EventId = eventId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs
new file mode 100644
index 000000000..9392c392b
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs
@@ -0,0 +1,36 @@
+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 EventDeletedHandler : IEventHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+
+ public EventDeletedHandler(ICommentRepository commentRepository)
+ {
+ _commentRepository = commentRepository;
+ }
+
+ public async Task HandleAsync(EventDeleted @event, CancellationToken cancellationToken = default)
+ {
+ //if (!(await _eventRepository.ExistsAsync(@event.EventId)))
+ //{
+ // throw new EventNotFoundException(@event.EventId);
+ //}
+
+ var comments = await _commentRepository.GetByEventIdAsync(@event.EventId);
+ foreach(var comment in comments)
+ {
+ await _commentRepository.DeleteAsync(comment.Id);
+ }
+
+ //await _eventRepository.DeleteAsync(@event.EventId);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs
new file mode 100644
index 000000000..7c69cbf7a
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs
@@ -0,0 +1,36 @@
+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 PostDeletedHandler : IEventHandler
+ {
+ private readonly ICommentRepository _commentRepository;
+
+ public PostDeletedHandler(ICommentRepository commentRepository)
+ {
+ _commentRepository = commentRepository;
+ }
+
+ public async Task HandleAsync(PostDeleted @event, CancellationToken cancellationToken = default)
+ {
+ //if (!(await _studentRepository.ExistsAsync(@event.StudentId)))
+ //{
+ // throw new StudentNotFoundException(@event.StudentId);
+ //}
+
+ var comments = await _commentRepository.GetByPostIdAsync(@event.PostId);
+ foreach (var comment in comments)
+ {
+ await _commentRepository.DeleteAsync(comment.Id);
+ }
+
+ //await _studentRepository.DeleteAsync(@event.StudentId);
+ }
+ }
+}
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
new file mode 100644
index 000000000..19e873bf9
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentCreatedHandler.cs
@@ -0,0 +1,31 @@
+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.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/StudentDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs
similarity index 72%
rename from MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/StudentDeletedHandler.cs
rename to MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs
index b2a2514d7..fe9321fe6 100644
--- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/StudentDeletedHandler.cs
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/StudentDeletedHandler.cs
@@ -1,8 +1,12 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using Convey.CQRS.Events;
-using MiniSpace.Services.Posts.Application.Exceptions;
-using MiniSpace.Services.Posts.Core.Repositories;
+using MiniSpace.Services.Comments.Application.Exceptions;
+using MiniSpace.Services.Comments.Core.Repositories;
-namespace MiniSpace.Services.Posts.Application.Events.External.Handlers
+namespace MiniSpace.Services.Comments.Application.Events.External.Handlers
{
public class StudentDeletedHandler : IEventHandler
{
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs
new file mode 100644
index 000000000..0163b3514
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs
@@ -0,0 +1,17 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events.External
+{
+ [Message("posts")]
+ public class PostDeleted : IEvent
+ {
+ public Guid PostId { get; }
+
+ public PostDeleted(Guid postId)
+ {
+ PostId = postId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentCreated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs
similarity index 82%
rename from MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentCreated.cs
rename to MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs
index f7d769110..b85346e58 100644
--- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentCreated.cs
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentCreated.cs
@@ -1,7 +1,8 @@
using Convey.CQRS.Events;
using Convey.MessageBrokers;
+using System;
-namespace MiniSpace.Services.Posts.Application.Events.External
+namespace MiniSpace.Services.Comments.Application.Events.External
{
[Message("students")]
public class StudentCreated : IEvent
diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs
similarity index 82%
rename from MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentDeleted.cs
rename to MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs
index 844641aa5..75f0176af 100644
--- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/StudentDeleted.cs
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/StudentDeleted.cs
@@ -1,7 +1,8 @@
using Convey.CQRS.Events;
using Convey.MessageBrokers;
+using System;
-namespace MiniSpace.Services.Posts.Application.Events.External
+namespace MiniSpace.Services.Comments.Application.Events.External
{
[Message("students")]
public class StudentDeleted : IEvent
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs
new file mode 100644
index 000000000..3ffbfc7da
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs
@@ -0,0 +1,16 @@
+using Convey.CQRS.Events;
+using System;
+
+
+namespace MiniSpace.Services.Comments.Application.Events
+{
+ public class LikeDeleted : IEvent
+ {
+ public Guid CommentId { get; }
+
+ public LikeDeleted(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs
new file mode 100644
index 000000000..fdd2ff22a
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs
@@ -0,0 +1,16 @@
+using Convey.CQRS.Events;
+using System;
+
+
+namespace MiniSpace.Services.Comments.Application.Events
+{
+ public class LikeUpdated : IEvent
+ {
+ public Guid CommentId { get; }
+
+ public LikeUpdated(Guid commentId)
+ {
+ CommentId = commentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs
new file mode 100644
index 000000000..1ac0d75cc
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events.Rejected
+{
+ public class CreateCommentRejected : IRejectedEvent
+ {
+ public Guid CommentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public CreateCommentRejected(Guid commentId, string reason, string code)
+ {
+ CommentId = commentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs
new file mode 100644
index 000000000..6e2231517
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events.Rejected
+{
+ public class DeleteCommentRejected : IRejectedEvent
+ {
+ public Guid CommentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public DeleteCommentRejected(Guid commentId, string reason, string code)
+ {
+ CommentId = commentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs
new file mode 100644
index 000000000..693a22760
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs
@@ -0,0 +1,19 @@
+using Convey.CQRS.Events;
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Events.Rejected
+{
+ public class UpdateCommentRejected : IRejectedEvent
+ {
+ public Guid CommentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public UpdateCommentRejected(Guid commentId, string reason, string code)
+ {
+ CommentId = commentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/AppException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/AppException.cs
new file mode 100644
index 000000000..a15634a0e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/AppException.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public abstract class AppException : Exception
+ {
+ public virtual string Code { get; }
+
+ protected AppException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/CommentNotFoundException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/CommentNotFoundException.cs
new file mode 100644
index 000000000..ccfbb45ed
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/CommentNotFoundException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class CommentNotFoundException : AppException
+ {
+ public override string Code { get; } = "comment_not_found";
+ public Guid Id { get; }
+
+ public CommentNotFoundException(Guid id) : base($"Comment with id: {id} was not found.")
+ {
+ Id = id;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextEnumException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextEnumException.cs
new file mode 100644
index 000000000..f539b84e7
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextEnumException.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class InvalidCommentContextEnumException : AppException
+ {
+ public override string Code { get; } = "invalid_commentcontext_enum";
+ public string InvalidEnum { get; }
+
+ public InvalidCommentContextEnumException(string invalidEnum) : base(
+ $"String: {invalidEnum} cannot be parsed to valid CommentContext.")
+ {
+ InvalidEnum = invalidEnum;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextException.cs
new file mode 100644
index 000000000..facb2c8b3
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidCommentContextException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class InvalidCommentContextException: AppException
+ {
+ public override string Code { get; } = "invalid_comment_context";
+ public string Context { get; }
+
+ public InvalidCommentContextException(string context) : base($"Invalid comment context: {context}.")
+ {
+ Context = context;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidParentCommentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidParentCommentException.cs
new file mode 100644
index 000000000..5e095cf8a
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/InvalidParentCommentException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class InvalidParentCommentException : AppException
+ {
+ public override string Code { get; } = "invalid_parent_comment";
+ public Guid ParentId { get; }
+
+ public InvalidParentCommentException(Guid parentId) : base($"Invalid parent comment with id: '{parentId}'. It cannot be a child comment.")
+ {
+ ParentId = parentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/ParentComentNotFoundException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/ParentComentNotFoundException.cs
new file mode 100644
index 000000000..22cb3d39b
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/ParentComentNotFoundException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class ParentCommentNotFoundException : AppException
+ {
+ public override string Code { get; } = "parent_comment_not_found";
+ public Guid ParentId { get; }
+
+ public ParentCommentNotFoundException(Guid parentId) : base($"Parent comment with id: '{parentId}' was not found.")
+ {
+ ParentId = parentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentAlreadyExistsException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentAlreadyExistsException.cs
new file mode 100644
index 000000000..2b52f6291
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentAlreadyExistsException.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class StudentAlreadyExistsException : AppException
+ {
+ public override string Code { get; } = "student_already_added";
+ public Guid StudentId { get; }
+
+ public StudentAlreadyExistsException(Guid studentId)
+ : base($"Student with id: {studentId} was already added.")
+ {
+ StudentId = studentId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/StudentNotFoundException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentNotFoundException.cs
similarity index 80%
rename from MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/StudentNotFoundException.cs
rename to MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentNotFoundException.cs
index 1f7d6b593..e7e399c6c 100644
--- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Exceptions/StudentNotFoundException.cs
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/StudentNotFoundException.cs
@@ -1,4 +1,6 @@
-namespace MiniSpace.Services.Posts.Application.Exceptions
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
{
public class StudentNotFoundException : AppException
{
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UnauthorizedCommentAccessException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UnauthorizedCommentAccessException.cs
new file mode 100644
index 000000000..5c0c95898
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Exceptions/UnauthorizedCommentAccessException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Exceptions
+{
+ public class UnauthorizedCommentAccessException : AppException
+ {
+ public override string Code { get; } = "unauthorized_comment_access";
+ public Guid CommentId { get; }
+ public Guid UserId { get; }
+
+ public UnauthorizedCommentAccessException(Guid commentId, Guid userId)
+ : base($"Unauthorized access to comment with id: '{commentId}' by user with id: '{userId}'.")
+ {
+ CommentId = commentId;
+ UserId = userId;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs
new file mode 100644
index 000000000..09dbc4ac6
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs
@@ -0,0 +1,16 @@
+using Convey;
+using Convey.CQRS.Commands;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Comments.Application
+{
+ public static class Extensions
+ {
+ public static IConveyBuilder AddApplication(this IConveyBuilder builder)
+ => builder
+ .AddCommandHandlers()
+ .AddEventHandlers()
+ .AddInMemoryCommandDispatcher()
+ .AddInMemoryEventDispatcher();
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IAppContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IAppContext.cs
new file mode 100644
index 000000000..8c2c9b106
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IAppContext.cs
@@ -0,0 +1,8 @@
+namespace MiniSpace.Services.Comments.Application
+{
+ public interface IAppContext
+ {
+ string RequestId { get; }
+ IIdentityContext Identity { get; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IIdentityContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IIdentityContext.cs
new file mode 100644
index 000000000..c1ade755c
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/IIdentityContext.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Comments.Application
+{
+ public interface IIdentityContext
+ {
+ Guid Id { get; }
+ string Role { get; }
+ string Name { get; }
+ string Email { get; }
+ 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.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj
new file mode 100644
index 000000000..6b085f9b4
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000..3bbad565c
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/ICommentService.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Comments.Application.Commands;
+using MiniSpace.Services.Comments.Application.Dto;
+using MiniSpace.Services.Comments.Application.Wrappers;
+
+namespace MiniSpace.Services.Comments.Application.Services
+{
+ public interface ICommentService
+ {
+ Task>> BrowseCommentsAsync(SearchComments command);
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IDateTimeProvider.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IDateTimeProvider.cs
new file mode 100644
index 000000000..5832fa711
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IDateTimeProvider.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Application.Services
+{
+ public interface IDateTimeProvider
+ {
+ DateTime Now { get; }
+ }
+}
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
new file mode 100644
index 000000000..db27763e1
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs
@@ -0,0 +1,12 @@
+using Convey.CQRS.Events;
+using MiniSpace.Services.Comments.Core;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Comments.Application.Services
+{
+ public interface IEventMapper
+ {
+ IEvent Map(IDomainEvent @event);
+ IEnumerable MapAll(IEnumerable events);
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs
new file mode 100644
index 000000000..64c9d4e65
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs
@@ -0,0 +1,12 @@
+using Convey.CQRS.Events;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Application.Services
+{
+ public interface IMessageBroker
+ {
+ Task PublishAsync(params IEvent[] events);
+ Task PublishAsync(IEnumerable events);
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateId.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateId.cs
new file mode 100644
index 000000000..a58b79062
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateId.cs
@@ -0,0 +1,51 @@
+using MiniSpace.Services.Comments.Core.Exceptions;
+using System;
+
+namespace MiniSpace.Services.Comments.Core.Entities
+{
+ public class AggregateId : IEquatable
+ {
+ public Guid Value { get; }
+
+ public AggregateId()
+ {
+ Value = Guid.NewGuid();
+ }
+
+ public AggregateId(Guid value)
+ {
+ if (value == Guid.Empty)
+ {
+ throw new InvalidAggregateIdException();
+ }
+
+ Value = value;
+ }
+
+ public bool Equals(AggregateId other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ return ReferenceEquals(this, other) || Value.Equals(other.Value);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ return obj.GetType() == GetType() && Equals((AggregateId) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+
+ public static implicit operator Guid(AggregateId id)
+ => id.Value;
+
+ public static implicit operator AggregateId(Guid id)
+ => new AggregateId(id);
+
+ public override string ToString() => Value.ToString();
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..753e23dbf
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/AggregateRoot.cs
@@ -0,0 +1,20 @@
+using MiniSpace.Services.Comments.Core;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Comments.Core.Entities
+{
+ public abstract class AggregateRoot
+ {
+ private readonly List _events = new List();
+ public IEnumerable Events => _events;
+ public AggregateId Id { get; protected set; }
+ public int Version { get; protected set; }
+
+ protected void AddEvent(IDomainEvent @event)
+ {
+ _events.Add(@event);
+ }
+
+ public void ClearEvents() => _events.Clear();
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..cefd9bf3e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Comment.cs
@@ -0,0 +1,104 @@
+using MiniSpace.Services.Comments.Core.Exceptions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+
+namespace MiniSpace.Services.Comments.Core.Entities
+{
+ public class Comment : AggregateRoot
+ {
+ private ISet _likes = 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 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 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,
+ IEnumerable likes, Guid parentId, string textContent, DateTime createdAt, DateTime lastUpdatedAt,
+ DateTime lastReplyAt, int repliesCount, bool isDeleted)
+ {
+ Id = id;
+ ContextId = contextId;
+ CommentContext = commentContext;
+ StudentId = studentId;
+ StudentName = studentName;
+ Likes = likes;
+ ParentId = parentId;
+ TextContent = textContent;
+ CreatedAt = createdAt;
+ LastUpdatedAt = lastUpdatedAt;
+ LastReplyAt = lastReplyAt;
+ RepliesCount = repliesCount;
+ IsDeleted = isDeleted;
+ }
+
+ public void Like(Guid studentId)
+ {
+ if (Likes.Any(id => id == studentId))
+ {
+ throw new StudentAlreadyLikesCommentException(studentId);
+ }
+ _likes.Add(studentId);
+ }
+
+ public void UnLike(Guid studentId)
+ {
+ if (Likes.All(id => id != studentId))
+ {
+ throw new StudentNotLikeCommentException(studentId, Id);
+ }
+ _likes.Remove(studentId);
+ }
+
+ public static Comment Create(AggregateId id, Guid contextId, CommentContext commentContext, Guid studentId,
+ string studentName, 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);
+ }
+
+ public void Update(string textContent, DateTime now)
+ {
+ CheckContent(Id, textContent);
+
+ TextContent = textContent;
+ LastUpdatedAt = now;
+ }
+
+ private static void CheckContent(AggregateId id, string textContent)
+ {
+ if (string.IsNullOrWhiteSpace(textContent) || textContent.Length > 300)
+ {
+ throw new InvalidCommentContentException(id);
+ }
+ }
+
+ public void Delete()
+ {
+ IsDeleted = true;
+ TextContent = "";
+ }
+
+ public void AddReply(DateTime now)
+ {
+ RepliesCount++;
+ LastReplyAt = now;
+ }
+ }
+}
\ 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
new file mode 100644
index 000000000..866c3b3aa
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/CommentContext.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Core.Entities
+{
+ public enum CommentContext
+ {
+ Post,
+ Event
+ }
+}
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
new file mode 100644
index 000000000..b9290f47f
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Entities/Student.cs
@@ -0,0 +1,14 @@
+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/Exceptions/DomainException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/DomainException.cs
new file mode 100644
index 000000000..0482ff69c
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/DomainException.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Core.Exceptions
+{
+ public abstract class DomainException : Exception
+ {
+ public virtual string Code { get; }
+
+ protected DomainException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidAggregateIdException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidAggregateIdException.cs
new file mode 100644
index 000000000..5ec007725
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidAggregateIdException.cs
@@ -0,0 +1,11 @@
+namespace MiniSpace.Services.Comments.Core.Exceptions
+{
+ public class InvalidAggregateIdException : DomainException
+ {
+ public override string Code { get; } = "invalid_aggregate_id";
+
+ public InvalidAggregateIdException() : base($"Invalid aggregate id.")
+ {
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidCommentContentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidCommentContentException.cs
new file mode 100644
index 000000000..4eaddb6df
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/InvalidCommentContentException.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace MiniSpace.Services.Comments.Core.Exceptions
+{
+ public class InvalidCommentContentException : DomainException
+ {
+ public override string Code { get; } = "invalid_comment_content";
+ public Guid Id { get; }
+
+ public InvalidCommentContentException(Guid id) : base(
+ $"Comment with id: {id} has invalid content.")
+ {
+ Id = id;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentAlreadyLikesCommentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentAlreadyLikesCommentException.cs
new file mode 100644
index 000000000..d8f4359df
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentAlreadyLikesCommentException.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Core.Exceptions
+{
+ public class StudentAlreadyLikesCommentException : DomainException
+ {
+ public override string Code { get; } = "student_already_likes_comment";
+ public Guid Id { get; }
+
+ public StudentAlreadyLikesCommentException(Guid id) : base(
+ $"Comment with id: {id} has already liked this comment.")
+ {
+ Id = id;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentNotLikeCommentException.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentNotLikeCommentException.cs
new file mode 100644
index 000000000..ef274af44
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Exceptions/StudentNotLikeCommentException.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Core.Exceptions
+{
+ public class StudentNotLikeCommentException : DomainException
+ {
+ public override string Code { get; } = "student_not_likes_comment";
+ public Guid StudentId { get; }
+ public Guid CommentId { get; }
+
+ public StudentNotLikeCommentException(Guid studentId, Guid commentId) : base(
+ $"Student with id: {studentId} does not like comment with id:.")
+ {
+ StudentId = studentId;
+ 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
new file mode 100644
index 000000000..ce8577480
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/IDomainEvent.cs
@@ -0,0 +1,6 @@
+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.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/MiniSpace.Services.Comments.Core.csproj
new file mode 100644
index 000000000..3c20077e9
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/MiniSpace.Services.Comments.Core.csproj
@@ -0,0 +1,8 @@
+
+
+
+ net8.0
+ disable
+
+
+
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
new file mode 100644
index 000000000..20ea99c79
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/ICommentRepository.cs
@@ -0,0 +1,22 @@
+using MiniSpace.Services.Comments.Core.Entities;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MiniSpace.Services.Comments.Core.Repositories
+{
+ public interface ICommentRepository
+ {
+ Task GetAsync(Guid id);
+ Task AddAsync(Comment comment);
+ Task UpdateAsync(Comment comment);
+ 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
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
new file mode 100644
index 000000000..5043e91dd
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Repositories/IStudentRepository.cs
@@ -0,0 +1,14 @@
+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/Wrappers/PagedResponse.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs
new file mode 100644
index 000000000..8872f116e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/PagedResponse.cs
@@ -0,0 +1,28 @@
+namespace MiniSpace.Services.Comments.Application.Wrappers
+{
+ public class PagedResponse : Response
+ {
+ public int TotalPages { get; }
+ public int TotalElements { get; }
+ public int Size { get; }
+ public int Number { get; }
+ public bool First { get; }
+ public bool Last { get; }
+ public bool Empty { get; }
+
+ public PagedResponse(T content, int pageNumber, int pageSize, int totalPages, int totalElements)
+ {
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/Response.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/Response.cs
new file mode 100644
index 000000000..016d8ffe6
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Core/Wrappers/Response.cs
@@ -0,0 +1,10 @@
+namespace MiniSpace.Services.Comments.Application.Wrappers
+{
+ public class Response
+ {
+ public T Content { get; set; }
+ public bool Succeeded { get; set; }
+ public string[] Errors { get; set; }
+ public string Message { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContext.cs
new file mode 100644
index 000000000..9a23e58cc
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContext.cs
@@ -0,0 +1,27 @@
+using MiniSpace.Services.Comments.Application;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Contexts
+{
+ internal class AppContext : IAppContext
+ {
+ public string RequestId { get; }
+ public IIdentityContext Identity { get; }
+
+ internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty)
+ {
+ }
+
+ internal AppContext(CorrelationContext context) : this(context.CorrelationId,
+ context.User is null ? IdentityContext.Empty : new IdentityContext(context.User))
+ {
+ }
+
+ internal AppContext(string requestId, IIdentityContext identity)
+ {
+ RequestId = requestId;
+ Identity = identity;
+ }
+
+ internal static IAppContext Empty => new AppContext();
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs
new file mode 100644
index 000000000..6278674e3
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs
@@ -0,0 +1,35 @@
+using Convey.MessageBrokers;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using MiniSpace.Services.Comments.Application;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Contexts
+{
+ internal sealed class AppContextFactory : IAppContextFactory
+ {
+ private readonly ICorrelationContextAccessor _contextAccessor;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor)
+ {
+ _contextAccessor = contextAccessor;
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public IAppContext Create()
+ {
+ if (_contextAccessor.CorrelationContext is {})
+ {
+ var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext);
+
+ return string.IsNullOrWhiteSpace(payload)
+ ? AppContext.Empty
+ : new AppContext(JsonConvert.DeserializeObject(payload));
+ }
+
+ var context = _httpContextAccessor.GetCorrelationContext();
+
+ return context is null ? AppContext.Empty : new AppContext(context);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/CorrelationContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/CorrelationContext.cs
new file mode 100644
index 000000000..a86d3ee2d
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/CorrelationContext.cs
@@ -0,0 +1,22 @@
+namespace MiniSpace.Services.Comments.Infrastructure.Contexts
+{
+ internal class CorrelationContext
+ {
+ public string CorrelationId { get; set; }
+ public string SpanContext { get; set; }
+ public UserContext User { get; set; }
+ public string ResourceId { get; set; }
+ public string TraceId { get; set; }
+ public string ConnectionId { get; set; }
+ public string Name { get; set; }
+ public DateTime CreatedAt { get; set; }
+
+ public class UserContext
+ {
+ public string Id { get; set; }
+ public bool IsAuthenticated { get; set; }
+ public string Role { get; set; }
+ public IDictionary Claims { get; set; }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/IdentityContext.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/IdentityContext.cs
new file mode 100644
index 000000000..12d43b7d7
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/IdentityContext.cs
@@ -0,0 +1,41 @@
+using MiniSpace.Services.Comments.Application;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Contexts
+{
+ internal class IdentityContext : IIdentityContext
+ {
+ public Guid Id { get; }
+ public string Role { get; } = string.Empty;
+ public string Name { get; } = string.Empty;
+ public string Email { get; } = string.Empty;
+ public bool IsAuthenticated { get; }
+ public bool IsAdmin { get; }
+ public bool IsBanned { get; }
+ public bool IsOrganizer { get; }
+ public IDictionary Claims { get; } = new Dictionary();
+
+ internal IdentityContext()
+ {
+ }
+
+ internal IdentityContext(CorrelationContext.UserContext context)
+ : this(context.Id, context.Role, context.IsAuthenticated, context.Claims)
+ {
+ }
+
+ internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims)
+ {
+ Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty;
+ Role = role ?? string.Empty;
+ 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;
+ }
+
+ internal static IIdentityContext Empty => new IdentityContext();
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs
new file mode 100644
index 000000000..988cdbf37
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs
@@ -0,0 +1,35 @@
+using Convey.CQRS.Commands;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.Types;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Decorators
+{
+ [Decorator]
+ internal sealed class OutboxCommandHandlerDecorator : ICommandHandler
+ where TCommand : class, ICommand
+ {
+ private readonly ICommandHandler _handler;
+ private readonly IMessageOutbox _outbox;
+ private readonly string _messageId;
+ private readonly bool _enabled;
+
+ public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox,
+ OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor)
+ {
+ _handler = handler;
+ _outbox = outbox;
+ _enabled = outboxOptions.Enabled;
+
+ var messageProperties = messagePropertiesAccessor.MessageProperties;
+ _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId)
+ ? Guid.NewGuid().ToString("N")
+ : messageProperties.MessageId;
+ }
+
+ public Task HandleAsync(TCommand command, CancellationToken cancellationToken)
+ => _enabled
+ ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command))
+ : _handler.HandleAsync(command);
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs
new file mode 100644
index 000000000..00e34126e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs
@@ -0,0 +1,35 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.Types;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Decorators
+{
+ [Decorator]
+ internal sealed class OutboxEventHandlerDecorator : IEventHandler
+ where TEvent : class, IEvent
+ {
+ private readonly IEventHandler _handler;
+ private readonly IMessageOutbox _outbox;
+ private readonly string _messageId;
+ private readonly bool _enabled;
+
+ public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox,
+ OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor)
+ {
+ _handler = handler;
+ _outbox = outbox;
+ _enabled = outboxOptions.Enabled;
+
+ var messageProperties = messagePropertiesAccessor.MessageProperties;
+ _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId)
+ ? Guid.NewGuid().ToString("N")
+ : messageProperties.MessageId;
+ }
+
+ public Task HandleAsync(TEvent @event, CancellationToken cancellationToken)
+ => _enabled
+ ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event))
+ : _handler.HandleAsync(@event);
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
new file mode 100644
index 000000000..fdde1c6f7
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
@@ -0,0 +1,86 @@
+using Convey.MessageBrokers.RabbitMQ;
+using MiniSpace.Services.Comments.Application.Commands;
+using MiniSpace.Services.Comments.Application.Events.Rejected;
+using MiniSpace.Services.Comments.Application.Exceptions;
+using MiniSpace.Services.Comments.Core.Exceptions;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Exceptions
+{
+ internal sealed class ExceptionToMessageMapper : IExceptionToMessageMapper
+ {
+ public object Map(Exception exception, object message)
+ => exception switch
+
+ {
+ /*InvalidPostContentException ex => message switch
+ {
+ CreatePost _ => new CreatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ UpdatePost _ => new UpdatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ _ => null,
+ },
+ InvalidPostPublishDateException ex => message switch
+ {
+ ChangePostState _ => new ChangePostStateRejected(ex.Id,
+ ex.State.ToString().ToLowerInvariant(), ex.Message, ex.Code),
+ _ => null,
+ },
+ NotAllowedPostStateException ex => message switch
+ {
+ CreatePost _ => new CreatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ _ => null,
+ },
+ PostNotFoundException ex => message switch
+ {
+ UpdatePost _ => new UpdatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ DeletePost _ => new DeletePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ _ => null,
+ },
+ PostStateAlreadySetException ex => message switch
+ {
+ ChangePostState _ => new ChangePostStateRejected(ex.Id,
+ ex.State.ToString().ToLowerInvariant(), ex.Message, ex.Code),
+ _ => null,
+ },
+ PublishDateNullException ex => message switch
+ {
+ CreatePost _ => new CreatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ ChangePostState _ => new ChangePostStateRejected(ex.Id,
+ ex.State.ToString().ToLowerInvariant(), ex.Message, ex.Code),
+ _ => null,
+ },
+ StudentNotFoundException ex => message switch
+ {
+ CreatePost _ => new CreatePostRejected(ex.Id, ex.Message,
+ ex.Code),
+ _ => null,
+ },
+ UnauthorizedPostAccessException ex => message switch
+ {
+ UpdatePost _ => new UpdatePostRejected(ex.PostId, ex.Message,
+ ex.Code),
+ DeletePost _ => new DeletePostRejected(ex.PostId, ex.Message,
+ ex.Code),
+ ChangePostState _ => new ChangePostStateRejected(ex.PostId,
+ "unknown", ex.Message, ex.Code),
+ _ => null,
+ },
+ UnauthorizedPostOperationException ex => message switch
+ {
+ UpdatePost _ => new UpdatePostRejected(ex.PostId, ex.Message,
+ ex.Code),
+ DeletePost _ => new DeletePostRejected(ex.PostId, ex.Message,
+ ex.Code),
+ ChangePostState _ => new ChangePostStateRejected(ex.PostId,
+ "unknown", ex.Message, ex.Code),
+ _ => null,
+ },*/
+ _ => null
+ };
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs
new file mode 100644
index 000000000..cf4883382
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs
@@ -0,0 +1,46 @@
+using System.Collections.Concurrent;
+using System.Net;
+using Convey;
+using Convey.WebApi.Exceptions;
+using MiniSpace.Services.Comments.Application.Exceptions;
+using MiniSpace.Services.Comments.Core.Exceptions;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Exceptions
+{
+ internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper
+ {
+ private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary();
+
+ public ExceptionResponse Map(Exception exception)
+ => exception switch
+ {
+ DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message},
+ HttpStatusCode.BadRequest),
+ AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message},
+ HttpStatusCode.BadRequest),
+ _ => new ExceptionResponse(new {code = "error", reason = "There was an error."},
+ HttpStatusCode.BadRequest)
+ };
+
+ private static string GetCode(Exception exception)
+ {
+ var type = exception.GetType();
+ if (Codes.TryGetValue(type, out var code))
+ {
+ return code;
+ }
+
+ var exceptionCode = exception switch
+ {
+ DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException
+ .Code,
+ AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code,
+ _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty)
+ };
+
+ Codes.TryAdd(type, exceptionCode);
+
+ return exceptionCode;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs
new file mode 100644
index 000000000..38e07cf93
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs
@@ -0,0 +1,136 @@
+using System.Text;
+using Convey;
+using Convey.CQRS.Commands;
+using Convey.CQRS.Events;
+using Convey.CQRS.Queries;
+using Convey.Discovery.Consul;
+using Convey.Docs.Swagger;
+using Convey.HTTP;
+using Convey.LoadBalancing.Fabio;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.CQRS;
+using Convey.MessageBrokers.Outbox;
+using Convey.MessageBrokers.Outbox.Mongo;
+using Convey.MessageBrokers.RabbitMQ;
+using Convey.Metrics.AppMetrics;
+using Convey.Persistence.MongoDB;
+using Convey.Persistence.Redis;
+using Convey.Security;
+using Convey.Tracing.Jaeger;
+using Convey.Tracing.Jaeger.RabbitMQ;
+using Convey.WebApi;
+using Convey.WebApi.CQRS;
+using Convey.WebApi.Security;
+using Convey.WebApi.Swagger;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using MiniSpace.Services.Comments.Application;
+using MiniSpace.Services.Comments.Application.Commands;
+using MiniSpace.Services.Comments.Application.Events.External;
+using MiniSpace.Services.Comments.Application.Services;
+using MiniSpace.Services.Comments.Core.Repositories;
+using MiniSpace.Services.Comments.Infrastructure.Contexts;
+using MiniSpace.Services.Comments.Infrastructure.Decorators;
+using MiniSpace.Services.Comments.Infrastructure.Exceptions;
+using MiniSpace.Services.Comments.Infrastructure.Logging;
+using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents;
+using MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories;
+using MiniSpace.Services.Comments.Infrastructure.Services;
+
+namespace MiniSpace.Services.Comments.Infrastructure
+{
+ 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(ctx => ctx.GetRequiredService().Create());
+ builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>));
+ builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>));
+
+ return builder
+ .AddErrorHandler()
+ .AddQueryHandlers()
+ .AddInMemoryQueryDispatcher()
+ .AddHttpClient()
+ .AddConsul()
+ .AddFabio()
+ .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin())
+ .AddMessageOutbox(o => o.AddMongo())
+ .AddExceptionToMessageMapper()
+ .AddMongo()
+ .AddRedis()
+ .AddMetrics()
+ .AddJaeger()
+ .AddHandlersLogging()
+ .AddMongoRepository("students")
+ .AddMongoRepository("comments")
+ .AddWebApiSwaggerDocs()
+ .AddCertificateAuthentication()
+ .AddSecurity();
+ }
+
+ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app)
+ {
+ app.UseErrorHandler()
+ .UseSwaggerDocs()
+ .UseJaeger()
+ .UseConvey()
+ .UsePublicContracts()
+ .UseMetrics()
+ .UseCertificateAuthentication()
+ .UseRabbitMq()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeEvent()
+ .SubscribeEvent();
+
+ return app;
+ }
+
+ internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor)
+ => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true
+ ? JsonConvert.DeserializeObject(json.FirstOrDefault())
+ : null;
+
+ internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties)
+ {
+ const string sagaHeader = "Saga";
+ if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga))
+ {
+ return null;
+ }
+
+ return saga is null
+ ? null
+ : new Dictionary
+ {
+ [sagaHeader] = saga
+ };
+ }
+
+ internal static string GetSpanContext(this IMessageProperties messageProperties, string header)
+ {
+ if (messageProperties is null)
+ {
+ return string.Empty;
+ }
+
+ if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes)
+ {
+ return Encoding.UTF8.GetString(spanBytes);
+ }
+
+ return string.Empty;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/IAppContextFactory.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/IAppContextFactory.cs
new file mode 100644
index 000000000..a66e221ae
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/IAppContextFactory.cs
@@ -0,0 +1,9 @@
+using MiniSpace.Services.Comments.Application;
+
+namespace MiniSpace.Services.Comments.Infrastructure
+{
+ public interface IAppContextFactory
+ {
+ IAppContext Create();
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs
new file mode 100644
index 000000000..777426b5e
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs
@@ -0,0 +1,21 @@
+using Convey;
+using Convey.Logging.CQRS;
+using Microsoft.Extensions.DependencyInjection;
+using MiniSpace.Services.Comments.Application.Commands;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Logging
+{
+ internal static class Extensions
+ {
+ public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder)
+ {
+ var assembly = typeof(UpdateComment).Assembly;
+
+ builder.Services.AddSingleton(new MessageToLogTemplateMapper());
+
+ return builder
+ .AddCommandHandlersLogging(assembly)
+ .AddEventHandlersLogging(assembly);
+ }
+ }
+}
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
new file mode 100644
index 000000000..77c86f651
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs
@@ -0,0 +1,63 @@
+using Convey.Logging.CQRS;
+using MiniSpace.Services.Comments.Application.Commands;
+using MiniSpace.Services.Comments.Application.Events;
+using MiniSpace.Services.Comments.Application.Events.External;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Logging
+{
+ internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper
+ {
+ private static IReadOnlyDictionary MessageTemplates
+ => new Dictionary
+ {
+ {
+ typeof(CreateComment), new HandlerLogTemplate
+ {
+ After = "Created the comment with id: {CommentId}."
+ }
+ },
+ {
+ typeof(UpdateComment), new HandlerLogTemplate
+ {
+ After = "Updated the comment with id: {CommentId}."
+ }
+ },
+ {
+ typeof(DeleteComment), new HandlerLogTemplate
+ {
+ 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
+ {
+ After = "Added like in the comment with id: {CommentId}."
+ }
+ },
+ {
+ typeof(DeleteLike), new HandlerLogTemplate
+ {
+ After = "Removed like in the comment with id: {CommentId}."
+ }
+ },
+ };
+
+ public HandlerLogTemplate Map(TMessage message) where TMessage : class
+ {
+ var key = message.GetType();
+ return MessageTemplates.TryGetValue(key, out var template) ? template : null;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj
new file mode 100644
index 000000000..11a87d0b2
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj
@@ -0,0 +1,40 @@
+
+
+
+ net8.0
+ enable
+ disable
+ MiniSpace.Services.Comments.Infrastructure
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000..d6e3e9066
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs
@@ -0,0 +1,22 @@
+using Convey.Types;
+using MiniSpace.Services.Comments.Core.Entities;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents
+{
+ public class CommentDocument : IIdentifiable
+ {
+ 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 DateTime LastUpdatedAt { get; set; }
+ public DateTime LastReplyAt { get; set; }
+ public int RepliesCount { get; set; }
+ 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
new file mode 100644
index 000000000..5d02210e9
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/Extensions.cs
@@ -0,0 +1,58 @@
+using MiniSpace.Services.Comments.Application.Dto;
+using MiniSpace.Services.Comments.Core.Entities;
+
+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);
+
+ public static CommentDocument AsDocument(this Comment entity)
+ => new CommentDocument()
+ {
+ Id = entity.Id,
+ ContextId = entity.ContextId,
+ CommentContext = entity.CommentContext,
+ StudentId = entity.StudentId,
+ StudentName = entity.StudentName,
+ Likes = entity.Likes,
+ ParentId = entity.ParentId,
+ TextContent = entity.TextContent,
+ CreatedAt = entity.CreatedAt,
+ LastUpdatedAt = entity.LastUpdatedAt,
+ LastReplyAt = entity.LastReplyAt,
+ RepliesCount = entity.RepliesCount,
+ IsDeleted = entity.IsDeleted,
+ };
+
+ public static CommentDto AsDto(this CommentDocument document)
+ => new CommentDto()
+ {
+ Id = document.Id,
+ ContextId = document.ContextId,
+ CommentContext = document.CommentContext.ToString().ToLowerInvariant(),
+ StudentId = document.StudentId,
+ StudentName = document.StudentName,
+ Likes = document.Likes,
+ ParentId = document.ParentId,
+ TextContent = document.TextContent,
+ CreatedAt = document.CreatedAt,
+ LastUpdatedAt = document.LastUpdatedAt,
+ LastReplyAt = document.LastReplyAt,
+ RepliesCount = document.RepliesCount,
+ IsDeleted= document.IsDeleted,
+ };
+
+ public static Student AsEntity(this StudentDocument document)
+ => new Student(document.Id);
+
+ public static StudentDocument AsDocument(this Student entity)
+ => new StudentDocument
+ {
+ Id = entity.Id,
+ };
+ }
+}
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
new file mode 100644
index 000000000..eea1b15dd
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/StudentDocument.cs
@@ -0,0 +1,9 @@
+using Convey.Types;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Documents
+{
+ public class StudentDocument : IIdentifiable
+ {
+ public Guid Id { get; set; }
+ }
+}
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
new file mode 100644
index 000000000..14afb264f
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs
@@ -0,0 +1,77 @@
+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;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories
+{
+ public class CommentMongoRepository : ICommentRepository
+ {
+ private readonly IMongoRepository _repository;
+
+ 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());
+
+ public Task UpdateAsync(Comment comment)
+ => _repository.UpdateAsync(comment.AsDocument());
+
+ 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());
+ }
+
+ 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());
+ }
+
+ private async Task<(int totalPages, int totalElements, IEnumerable data)> BrowseAsync(
+ FilterDefinition filterDefinition, SortDefinition sortDefinition,
+ int pageNumber, int pageSize)
+ {
+ var pagedEvents = await _repository.Collection.AggregateByPage(
+ filterDefinition,
+ sortDefinition,
+ pageNumber,
+ 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);
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/Extensions.cs
new file mode 100644
index 000000000..713d611db
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/Extensions.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using MiniSpace.Services.Comments.Core.Entities;
+using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents;
+using MongoDB.Bson;
+using MongoDB.Driver;
+
+namespace MiniSpace.Services.Comments.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,
+ SortDefinition sortDefinition,
+ int page,
+ int pageSize)
+ {
+ var countFacet = AggregateFacet.Create("count",
+ PipelineDefinition.Create(new[]
+ {
+ PipelineStageDefinitionBuilder.Count()
+ }));
+
+ var dataFacet = AggregateFacet.Create("data",
+ PipelineDefinition.Create(new[]
+ {
+ PipelineStageDefinitionBuilder.Sort(sortDefinition),
+ PipelineStageDefinitionBuilder.Skip((page - 1) * pageSize),
+ PipelineStageDefinitionBuilder.Limit(pageSize),
+ }));
+
+
+ var aggregation = await collection.Aggregate()
+ .Match(filterDefinition)
+ .Facet(countFacet, dataFacet)
+ .ToListAsync();
+
+ var count = aggregation.First()
+ .Facets.First(x => x.Name == "count")
+ .Output()
+ ?.FirstOrDefault()
+ ?.Count;
+
+ if (count == null)
+ {
+ return (0, 0, Array.Empty());
+ }
+ var totalPages = (int)Math.Ceiling((double)count / pageSize);
+
+ var data = aggregation.First()
+ .Facets.First(x => x.Name == "data")
+ .Output();
+
+ return (totalPages, (int)count, data);
+ }
+
+ public static FilterDefinition ToFilterDefinition(Guid contextId, CommentContext context)
+ {
+ var filterDefinition = FilterDefinitionBuilder.Empty;
+
+ filterDefinition &= FilterDefinitionBuilder.Eq(x => x.ContextId, contextId);
+ filterDefinition &= FilterDefinitionBuilder.Eq(x => x.CommentContext, context);
+
+ return filterDefinition;
+ }
+
+ public static FilterDefinition AddParentFilter (this FilterDefinition filterDefinition)
+ {
+ filterDefinition &= FilterDefinitionBuilder.Eq(x => x.ParentId, Guid.Empty);
+ return filterDefinition;
+ }
+
+ public static FilterDefinition AddChildrenFilter (this FilterDefinition filterDefinition,
+ Guid parentId)
+ {
+ filterDefinition &= FilterDefinitionBuilder.Eq(x => x.ParentId, parentId);
+ return filterDefinition;
+ }
+
+ public static SortDefinition ToSortDefinition(IEnumerable sortByArguments, string direction)
+ {
+ var sort = sortByArguments.ToList();
+ if(!sort.Any())
+ {
+ sort.Add("LastReplyAt");
+ sort.Add("LastUpdatedAt");
+ }
+ var sortDefinitionBuilder = Builders.Sort;
+ var sortDefinition = sort
+ .Select(sortBy => direction == "asc"
+ ? sortDefinitionBuilder.Ascending(sortBy)
+ : sortDefinitionBuilder.Descending(sortBy));
+ var sortCombined = sortDefinitionBuilder.Combine(sortDefinition);
+ return sortCombined;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs
similarity index 77%
rename from MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs
rename to MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs
index 4f4e84eaf..6fd9a4fe2 100644
--- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs
@@ -1,9 +1,9 @@
using Convey.Persistence.MongoDB;
-using MiniSpace.Services.Posts.Core.Entities;
-using MiniSpace.Services.Posts.Core.Repositories;
-using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents;
+using MiniSpace.Services.Comments.Core.Entities;
+using MiniSpace.Services.Comments.Core.Repositories;
+using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents;
-namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Repositories
+namespace MiniSpace.Services.Comments.Infrastructure.Mongo.Repositories
{
public class StudentMongoRepository : IStudentRepository
{
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
new file mode 100644
index 000000000..b51edaa99
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/CommentService.cs
@@ -0,0 +1,43 @@
+using MiniSpace.Services.Comments.Application;
+using MiniSpace.Services.Comments.Application.Commands;
+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.Entities;
+using MiniSpace.Services.Comments.Core.Repositories;
+using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Services
+{
+ public class CommentService : ICommentService
+ {
+ private readonly ICommentRepository _commentRepository;
+
+ public CommentService(ICommentRepository commentRepository)
+ {
+ _commentRepository = commentRepository;
+ }
+
+ public async Task>> BrowseCommentsAsync(SearchComments command)
+ {
+ if (!Enum.TryParse(command.CommentContext, true, out var context))
+ {
+ throw new InvalidCommentContextException(command.CommentContext);
+ }
+
+ 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 pagedEvents = new PagedResponse>(
+ result.comments.Select(c => new CommentDto(c)),
+ result.pageNumber, result.pageSize, result.totalPages, result.totalElements);
+
+ return pagedEvents;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/DateTimeProvider.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/DateTimeProvider.cs
new file mode 100644
index 000000000..4b356528a
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/DateTimeProvider.cs
@@ -0,0 +1,9 @@
+using MiniSpace.Services.Comments.Application.Services;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Services
+{
+ internal sealed class DateTimeProvider : IDateTimeProvider
+ {
+ public DateTime Now => DateTime.UtcNow;
+ }
+}
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
new file mode 100644
index 000000000..357db5bef
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs
@@ -0,0 +1,22 @@
+using Convey.CQRS.Events;
+using MiniSpace.Services.Comments.Application.Services;
+using MiniSpace.Services.Comments.Core;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Services
+{
+ public class EventMapper : IEventMapper
+ {
+ public IEnumerable MapAll(IEnumerable events)
+ => events.Select(Map);
+
+ public IEvent Map(IDomainEvent @event)
+ {
+ switch (@event)
+ {
+
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs
new file mode 100644
index 000000000..c4355c2a4
--- /dev/null
+++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs
@@ -0,0 +1,84 @@
+using Convey.CQRS.Events;
+using Convey.MessageBrokers;
+using Convey.MessageBrokers.Outbox;
+using Convey.MessageBrokers.RabbitMQ;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using OpenTracing;
+using MiniSpace.Services.Comments.Application.Services;
+
+namespace MiniSpace.Services.Comments.Infrastructure.Services
+{
+ internal sealed class MessageBroker : IMessageBroker
+ {
+ private const string DefaultSpanContextHeader = "span_context";
+ private readonly IBusPublisher _busPublisher;
+ private readonly IMessageOutbox _outbox;
+ private readonly ICorrelationContextAccessor _contextAccessor;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly IMessagePropertiesAccessor _messagePropertiesAccessor;
+ private readonly ITracer _tracer;
+ private readonly ILogger _logger;
+ private readonly string _spanContextHeader;
+
+ public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox,
+ ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor,
+ IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer,
+ ILogger logger)
+ {
+ _busPublisher = busPublisher;
+ _outbox = outbox;
+ _contextAccessor = contextAccessor;
+ _httpContextAccessor = httpContextAccessor;
+ _messagePropertiesAccessor = messagePropertiesAccessor;
+ _tracer = tracer;
+ _logger = logger;
+ _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader)
+ ? DefaultSpanContextHeader
+ : options.SpanContextHeader;
+ }
+
+ public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable());
+
+ public async Task PublishAsync(IEnumerable events)
+ {
+ if (events is null)
+ {
+ return;
+ }
+
+ var messageProperties = _messagePropertiesAccessor.MessageProperties;
+ var originatedMessageId = messageProperties?.MessageId;
+ var correlationId = messageProperties?.CorrelationId;
+ var spanContext = messageProperties?.GetSpanContext(_spanContextHeader);
+ if (string.IsNullOrWhiteSpace(spanContext))
+ {
+ spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString();
+ }
+
+ var headers = messageProperties.GetHeadersToForward();
+ var correlationContext = _contextAccessor.CorrelationContext ??
+ _httpContextAccessor.GetCorrelationContext();
+
+ foreach (var @event in events)
+ {
+ if (@event is null)
+ {
+ continue;
+ }
+
+ var messageId = Guid.NewGuid().ToString("N");
+ _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}'].");
+ if (_outbox.Enabled)
+ {
+ await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext,
+ correlationContext, headers);
+ continue;
+ }
+
+ await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext,
+ headers);
+ }
+ }
+ }
+}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
index baf7c85bc..c1b8e8662 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs
@@ -36,21 +36,29 @@ public static async Task Main(string[] args)
.Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name))
.Post("events/search", async (cmd, ctx) =>
{
- var pagedResult = await ctx.RequestServices.GetService().SignInAsync(cmd);
+ var pagedResult = await ctx.RequestServices.GetService().BrowseEventsAsync(cmd);
+ await ctx.Response.WriteJsonAsync(pagedResult);
+ })
+ .Post("events/search/organizer", async (cmd, ctx) =>
+ {
+ var pagedResult = await ctx.RequestServices.GetService().BrowseOrganizerEventsAsync(cmd);
await ctx.Response.WriteJsonAsync(pagedResult);
}))
.UseDispatcherEndpoints(endpoints => endpoints
.Get("events/{eventId}")
- //.Get>("events/organizer/{organizerId}")
- //.Put("events/{eventId}")
+ .Put("events/{eventId}")
.Post("events",
afterDispatch: (cmd, ctx) => ctx.Response.Created($"events/{cmd.EventId}"))
+ .Delete("events/{eventId}")
.Post("events/{eventId}/sign-up")
+ .Delete("events/{eventId}/sign-up")
.Post("events/{eventId}/show-interest")
+ .Delete("events/{eventId}/show-interest")
.Post("events/{eventId}/rate")
- // TODO: Add query for student latest enrolled events
.Get>>("events/student/{studentId}")
- .Delete("events/{eventId}")
+ .Get("events/{eventId}/participants")
+ .Post("events/{eventId}/participants")
+ .Delete("events/{eventId}/participants")
)
)
.UseLogging()
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/appsettings.local.json b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/appsettings.local.json
index 187d80747..18ad866e0 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/appsettings.local.json
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/appsettings.local.json
@@ -24,7 +24,9 @@
"type": "direct",
"retries": 3,
"services": {
- "students": "http://localhost:5007"
+ "students": "http://localhost:5007",
+ "friends": "http://localhost:5012",
+ "organizations": "http://localhost:5015"
},
"requestMasking": {
"enabled": true,
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEvent.cs
index cfb4d59ae..8573c7592 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEvent.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEvent.cs
@@ -9,6 +9,7 @@ public class AddEvent : ICommand
public Guid EventId { get; }
public string Name { get; }
public Guid OrganizerId { get; }
+ public Guid OrganizationId { get; }
public string StartDate { get; }
public string EndDate { get; }
public string BuildingName { get; }
@@ -23,13 +24,14 @@ public class AddEvent : ICommand
public string Category { get; }
public string PublishDate { get; }
- public AddEvent(Guid eventId, string name, Guid organizerId, string startDate, string endDate,
- string buildingName, string street, string buildingNumber, string apartmentNumber, string city,
- string zipCode, string description, int capacity, decimal fee, string category, string publishDate)
+ public AddEvent(Guid eventId, string name, Guid organizerId, Guid organizationId, string startDate,
+ string endDate, string buildingName, string street, string buildingNumber, string apartmentNumber,
+ string city, string zipCode, string description, int capacity, decimal fee, string category, string publishDate)
{
EventId = eventId == Guid.Empty ? Guid.NewGuid() : eventId;
Name = name;
OrganizerId = organizerId;
+ OrganizationId = organizationId;
StartDate = startDate;
EndDate = endDate;
BuildingName = buildingName;
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs
new file mode 100644
index 000000000..596a3c2ea
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Core.Entities;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class AddEventParticipant: ICommand
+ {
+ public Guid EventId { get; set; }
+ public Guid StudentId { get; set; }
+ public string StudentName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs
new file mode 100644
index 000000000..c88c1157e
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class CancelInterestInEvent: ICommand
+ {
+ public Guid EventId { get; set; }
+ public Guid StudentId { get; set; }
+
+ public CancelInterestInEvent(Guid eventId, Guid studentId)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs
new file mode 100644
index 000000000..4ca8d5a6f
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class CancelSignUpToEvent : ICommand
+ {
+ public Guid EventId { get; set; }
+ public Guid StudentId { get; set; }
+
+ public CancelSignUpToEvent(Guid eventId, Guid studentId)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventHandler.cs
index 9dfebeb32..9c284e67b 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventHandler.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventHandler.cs
@@ -6,6 +6,7 @@
using MiniSpace.Services.Events.Application.Events;
using MiniSpace.Services.Events.Application.Exceptions;
using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Application.Services.Clients;
using MiniSpace.Services.Events.Core.Entities;
using MiniSpace.Services.Events.Core.Repositories;
@@ -15,17 +16,18 @@ public class AddEventHandler: ICommandHandler
{
private readonly IEventRepository _eventRepository;
private readonly IMessageBroker _messageBroker;
- private readonly IEventMapper _eventMapper;
+ private readonly IOrganizationsServiceClient _organizationsServiceClient;
private readonly IDateTimeProvider _dateTimeProvider;
private readonly IEventValidator _eventValidator;
private readonly IAppContext _appContext;
- public AddEventHandler(IEventRepository eventRepository, IMessageBroker messageBroker, IEventMapper eventMapper,
- IDateTimeProvider dateTimeProvider, IEventValidator eventValidator, IAppContext appContext)
+ public AddEventHandler(IEventRepository eventRepository, IMessageBroker messageBroker,
+ IOrganizationsServiceClient organizationsServiceClient, IDateTimeProvider dateTimeProvider,
+ IEventValidator eventValidator, IAppContext appContext)
{
_eventRepository = eventRepository;
_messageBroker = messageBroker;
- _eventMapper = eventMapper;
+ _organizationsServiceClient = organizationsServiceClient;
_dateTimeProvider = dateTimeProvider;
_eventValidator = eventValidator;
_appContext = appContext;
@@ -39,27 +41,43 @@ public async Task HandleAsync(AddEvent command, CancellationToken cancellationTo
if(identity.Id != command.OrganizerId)
throw new OrganizerCannotAddEventForAnotherOrganizerException(identity.Id, command.OrganizerId);
- var category = _eventValidator.ParseCategory(command.Category);
+ _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);
+ _eventValidator.ValidateCapacity(command.Capacity);
+ _eventValidator.ValidateFee(command.Fee);
+ var category = _eventValidator.ParseCategory(command.Category);
var publishDate = now;
- var status = State.Published;
- if (command.PublishDate != null)
+ var state = State.Published;
+ if (command.PublishDate != string.Empty)
{
publishDate = _eventValidator.ParseDate(command.PublishDate, "event_publish_date");
_eventValidator.ValidateDates(now, publishDate, "now", "event_publish_date");
- status = State.ToBePublished;
+ _eventValidator.ValidateDates(publishDate, startDate, "event_publish_date", "event_start_date");
+ state = State.ToBePublished;
}
- var address = new Address(command.BuildingName, command.Street, command.BuildingNumber,
- command.ApartmentNumber, command.City, command.ZipCode);
- var organizer = new Organizer(command.OrganizerId, identity.Name, identity.Email, string.Empty);
+ var organization = await _organizationsServiceClient.GetAsync(command.OrganizationId);
+ if (organization == null)
+ {
+ throw new OrganizationNotFoundException(command.OrganizationId);
+ }
+
+ if (!organization.Organizers.Contains(command.OrganizerId))
+ {
+ throw new OrganizerDoesNotBelongToOrganizationException(command.OrganizerId, command.OrganizationId);
+ }
+
+ var organizer = new Organizer(command.OrganizerId, identity.Name, identity.Email, command.OrganizerId, organization.Name);
var @event = Event.Create(command.EventId, command.Name, command.Description, startDate, endDate,
- address, command.Capacity, command.Fee, category, status, publishDate, organizer);
+ address, command.Capacity, command.Fee, category, state, publishDate, organizer, now);
await _eventRepository.AddAsync(@event);
await _messageBroker.PublishAsync(new EventCreated(@event.Id, @event.Organizer.Id));
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs
new file mode 100644
index 000000000..48c4fe35a
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Exceptions;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Core.Entities;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class AddEventParticipantHandler: ICommandHandler
+ {
+ private readonly IEventRepository _eventRepository;
+ private readonly IStudentRepository _studentRepository;
+ private readonly IAppContext _appContext;
+ private IMessageBroker _messageBroker;
+
+ public AddEventParticipantHandler(IEventRepository eventRepository, IStudentRepository studentRepository,
+ IAppContext appContext, IMessageBroker messageBroker)
+ {
+ _eventRepository = eventRepository;
+ _studentRepository = studentRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(AddEventParticipant command, CancellationToken cancellationToken)
+ {
+ var @event = await _eventRepository.GetAsync(command.EventId);
+ if(@event is null)
+ {
+ throw new EventNotFoundException(command.EventId);
+ }
+
+ var student = await _studentRepository.GetAsync(command.StudentId);
+ if(student is null)
+ {
+ throw new StudentNotFoundException(command.StudentId);
+ }
+
+ var identity = _appContext.Identity;
+ if(identity.IsAuthenticated && @event.Organizer.Id != identity.Id && !identity.IsAdmin)
+ {
+ throw new UnauthorizedEventAccessException(@event.Id, identity.Id);
+ }
+
+ @event.AddParticipant(new Participant(command.StudentId, command.StudentName));
+ await _eventRepository.UpdateAsync(@event);
+ await _messageBroker.PublishAsync(new EventParticipantAdded(@event.Id,
+ command.StudentId, command.StudentName));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs
new file mode 100644
index 000000000..e92c5834b
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs
@@ -0,0 +1,44 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Exceptions;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class CancelInterestInEventHandler : ICommandHandler
+ {
+ private readonly IEventRepository _eventRepository;
+ private readonly IMessageBroker _messageBroker;
+ private readonly IAppContext _appContext;
+
+ public CancelInterestInEventHandler(IEventRepository eventRepository, IMessageBroker messageBroker,
+ IAppContext appContext)
+ {
+ _eventRepository = eventRepository;
+ _messageBroker = messageBroker;
+ _appContext = appContext;
+ }
+
+ public async Task HandleAsync(CancelInterestInEvent command, CancellationToken cancellationToken)
+ {
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && identity.Id != command.StudentId)
+ {
+ throw new UnauthorizedEventAccessException(command.EventId, command.StudentId);
+ }
+
+ var @event = await _eventRepository.GetAsync(command.EventId);
+ if (@event is null)
+ {
+ throw new EventNotFoundException(command.EventId);
+ }
+
+ @event.CancelInterest(command.StudentId);
+ await _eventRepository.UpdateAsync(@event);
+ await _messageBroker.PublishAsync(new StudentCancelledInterestInEvent(@event.Id, command.StudentId));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs
new file mode 100644
index 000000000..a73533d97
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs
@@ -0,0 +1,44 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Exceptions;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class CancelSignUpToEventHandler : ICommandHandler
+ {
+ private readonly IEventRepository _eventRepository;
+ private readonly IMessageBroker _messageBroker;
+ private readonly IAppContext _appContext;
+
+ public CancelSignUpToEventHandler(IEventRepository eventRepository, IMessageBroker messageBroker,
+ IAppContext appContext)
+ {
+ _eventRepository = eventRepository;
+ _messageBroker = messageBroker;
+ _appContext = appContext;
+ }
+
+ public async Task HandleAsync(CancelSignUpToEvent command, CancellationToken cancellationToken)
+ {
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && identity.Id != command.StudentId)
+ {
+ throw new UnauthorizedEventAccessException(command.EventId, command.StudentId);
+ }
+
+ var @event = await _eventRepository.GetAsync(command.EventId);
+ if (@event is null)
+ {
+ throw new EventNotFoundException(command.EventId);
+ }
+
+ @event.CancelSignUp(command.StudentId);
+ await _eventRepository.UpdateAsync(@event);
+ await _messageBroker.PublishAsync(new StudentCancelledSignUpToEvent(@event.Id, command.StudentId));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs
new file mode 100644
index 000000000..297384d56
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Exceptions;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class RemoveEventParticipantHandler: ICommandHandler
+ {
+ private readonly IEventRepository _eventRepository;
+ private readonly IAppContext _appContext;
+ private IMessageBroker _messageBroker;
+
+ public RemoveEventParticipantHandler(IEventRepository eventRepository, IAppContext appContext, IMessageBroker messageBroker)
+ {
+ _eventRepository = eventRepository;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ }
+
+ public async Task HandleAsync(RemoveEventParticipant command, CancellationToken cancellationToken)
+ {
+ var @event = await _eventRepository.GetAsync(command.EventId);
+ if(@event is null)
+ {
+ throw new EventNotFoundException(command.EventId);
+ }
+
+ var identity = _appContext.Identity;
+ if(identity.IsAuthenticated && @event.Organizer.Id != identity.Id && !identity.IsAdmin)
+ {
+ throw new UnauthorizedEventAccessException(@event.Id, identity.Id);
+ }
+
+ @event.RemoveParticipant(command.ParticipantId);
+ await _eventRepository.UpdateAsync(@event);
+ await _messageBroker.PublishAsync(new EventParticipantRemoved(@event.Id, command.ParticipantId));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs
index 559a995c2..3e7a84ccb 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs
@@ -46,7 +46,7 @@ public async Task HandleAsync(ShowInterestInEvent command, CancellationToken can
throw new StudentNotFoundException(command.StudentId);
}
- var participant = new Participant(student.Id, identity.Name, identity.Email);
+ var participant = new Participant(student.Id, identity.Name);
@event.ShowStudentInterest(participant);
await _eventRepository.UpdateAsync(@event);
await _messageBroker.PublishAsync(new StudentShowedInterestInEvent(@event.Id, student.Id));
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs
index cc94246eb..3a3b697ab 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs
@@ -47,7 +47,7 @@ public async Task HandleAsync(SignUpToEvent command, CancellationToken cancellat
throw new StudentNotFoundException(command.StudentId);
}
- var participant = new Participant(student.Id, identity.Name, identity.Email);
+ var participant = new Participant(student.Id, identity.Name);
@event.SignUpStudent(participant);
await _eventRepository.UpdateAsync(@event);
await _messageBroker.PublishAsync(new StudentSignedUpToEvent(@event.Id, student.Id));
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs
new file mode 100644
index 000000000..de8ea2789
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs
@@ -0,0 +1,77 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Exceptions;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Core.Entities;
+using MiniSpace.Services.Events.Core.Repositories;
+
+namespace MiniSpace.Services.Events.Application.Commands.Handlers
+{
+ public class UpdateEventHandler : ICommandHandler
+ {
+ private readonly IEventRepository _eventRepository;
+ private readonly IEventValidator _eventValidator;
+ private readonly IAppContext _appContext;
+ private readonly IMessageBroker _messageBroker;
+ private readonly IDateTimeProvider _dateTimeProvider;
+
+ public UpdateEventHandler(IEventRepository eventRepository, IEventValidator eventValidator,
+ IAppContext appContext, IMessageBroker messageBroker, IDateTimeProvider dateTimeProvider)
+ {
+ _eventRepository = eventRepository;
+ _eventValidator = eventValidator;
+ _appContext = appContext;
+ _messageBroker = messageBroker;
+ _dateTimeProvider = dateTimeProvider;
+ }
+
+ public async Task HandleAsync(UpdateEvent command, CancellationToken cancellationToken)
+ {
+ var @event = await _eventRepository.GetAsync(command.EventId);
+ if (@event is null)
+ {
+ throw new EventNotFoundException(command.EventId);
+ }
+
+ var identity = _appContext.Identity;
+ if (identity.IsAuthenticated && !@event.IsOrganizer(identity.Id) && !identity.IsAdmin)
+ {
+ throw new UnauthorizedEventAccessException(@event.Id, identity.Id);
+ }
+
+ var name = command.Name == string.Empty ? @event.Name : command.Name;
+ var description = command.Description == string.Empty ? @event.Description : command.Description;
+ var category = command.Category == string.Empty ? @event.Category : _eventValidator.ParseCategory(command.Category);
+ var startDate = command.StartDate == string.Empty ? @event.StartDate
+ : _eventValidator.ParseDate(command.StartDate, "event_start_date");
+ var endDate = command.EndDate == string.Empty ? @event.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 = @event.Location.Update(command.BuildingName, command.Street, command.BuildingNumber,
+ command.ApartmentNumber, command.City, command.ZipCode);
+ var capacity = command.Capacity == 0 ? @event.Capacity : command.Capacity;
+ _eventValidator.ValidateUpdatedCapacity(capacity, @event.Capacity);
+ var fee = command.Fee == 0 ? @event.Fee : command.Fee;
+ _eventValidator.ValidateUpdatedFee(fee, @event.Fee);
+
+ var publishDate = @event.PublishDate;
+ var state = @event.State;
+ if (command.PublishDate != string.Empty)
+ {
+ 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;
+ }
+
+ @event.Update(name, description, startDate, endDate, address, capacity, fee, category, state, publishDate, now);
+ await _eventRepository.UpdateAsync(@event);
+ await _messageBroker.PublishAsync(new EventUpdated(@event.Id, _dateTimeProvider.Now, identity.Id));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs
new file mode 100644
index 000000000..679c46efb
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class RemoveEventParticipant: ICommand
+ {
+ public Guid EventId { get; set; }
+ public Guid ParticipantId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs
index 1b9ef6791..96f8ea8fa 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs
@@ -1,4 +1,6 @@
-using Convey.CQRS.Commands;
+using System;
+using System.Collections.Generic;
+using Convey.CQRS.Commands;
using MiniSpace.Services.Events.Application.DTO;
namespace MiniSpace.Services.Events.Application.Commands
@@ -7,6 +9,10 @@ public class SearchEvents : ICommand
{
public string Name { get; set; }
public string Organizer { get; set; }
+ public string Category { get; set; }
+ public string State { get; set; }
+ public IEnumerable Friends { get; set; }
+ public string FriendsEngagementType { get; set; }
public string DateFrom { get; set; }
public string DateTo { get; set; }
public PageableDto Pageable { get; set; }
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs
new file mode 100644
index 000000000..e51c824fd
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs
@@ -0,0 +1,16 @@
+using System;
+using Convey.CQRS.Commands;
+using MiniSpace.Services.Events.Application.DTO;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class SearchOrganizerEvents : ICommand
+ {
+ public string Name { get; set; }
+ public Guid OrganizerId { get; set; }
+ public string DateFrom { get; set; }
+ public string DateTo { get; set; }
+ public string State { get; set; }
+ public PageableDto Pageable { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs
new file mode 100644
index 000000000..7cc856df7
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs
@@ -0,0 +1,47 @@
+using System;
+using Convey.CQRS.Commands;
+
+namespace MiniSpace.Services.Events.Application.Commands
+{
+ public class UpdateEvent : ICommand
+ {
+ public Guid EventId { get; }
+ public string Name { get; }
+ public Guid OrganizerId { get; }
+ public string StartDate { get; }
+ public string EndDate { get; }
+ public string BuildingName { get; }
+ public string Street { get; }
+ public string BuildingNumber { get; }
+ public string ApartmentNumber { get; }
+ public string City { get; }
+ public string ZipCode { get; }
+ public string Description { get; }
+ public int Capacity { get; }
+ public decimal Fee { get; }
+ public string Category { get; }
+ public string PublishDate { get; }
+
+ public UpdateEvent(Guid eventId, string name, Guid organizerId, string startDate, string endDate,
+ string buildingName, string street, string buildingNumber, string apartmentNumber, string city,
+ string zipCode, string description, int capacity, decimal fee, string category, string publishDate)
+ {
+ EventId = eventId;
+ Name = name;
+ OrganizerId = organizerId;
+ StartDate = startDate;
+ EndDate = endDate;
+ BuildingName = buildingName;
+ Street = street;
+ BuildingNumber = buildingNumber;
+ ApartmentNumber = apartmentNumber;
+ City = city;
+ ZipCode = zipCode;
+ Description = description;
+ Capacity = capacity;
+ Fee = fee;
+ Category = category;
+ PublishDate = publishDate;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs
index 0f104d13b..aa1352062 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs
@@ -14,7 +14,6 @@ public class EventDto
public OrganizerDto Organizer { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
- public IEnumerable CoOrganizers { get; set; }
public AddressDto Location { get; set; }
//public string Image { get; set; }
public int InterestedStudents { get; set; }
@@ -24,12 +23,18 @@ public class EventDto
public string Category { get; set; }
public string Status { get; set; }
public DateTime PublishDate { get; set; }
+ public DateTime UpdatedAt { get; set; }
+ public bool IsSignedUp { get; set; }
+ public bool IsInterested { get; set; }
+ public bool HasRated { get; set; }
+ public IEnumerable FriendsInterestedIn { get; set; }
+ public IEnumerable FriendsSignedUp { get; set; }
public EventDto()
{
}
- public EventDto(Event @event)
+ public EventDto(Event @event, Guid studentId)
{
Id = @event.Id;
Name = @event.Name;
@@ -37,7 +42,6 @@ public EventDto(Event @event)
Organizer = new OrganizerDto(@event.Organizer);
StartDate = @event.StartDate;
EndDate = @event.EndDate;
- CoOrganizers = @event.CoOrganizers.Select(x => new OrganizerDto(x));
Location = new AddressDto(@event.Location);
InterestedStudents = @event.InterestedStudents.Count();
SignedUpStudents = @event.SignedUpStudents.Count();
@@ -46,6 +50,11 @@ public EventDto(Event @event)
Category = @event.Category.ToString();
Status = @event.State.ToString();
PublishDate = @event.PublishDate;
+ IsSignedUp = @event.SignedUpStudents.Any(x => x.StudentId == studentId);
+ IsInterested = @event.InterestedStudents.Any(x => x.StudentId == studentId);
+ HasRated = @event.Ratings.Any(x => x.StudentId == studentId);
+ FriendsInterestedIn = Enumerable.Empty();
+ FriendsSignedUp = Enumerable.Empty();
}
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventParticipantsDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventParticipantsDto.cs
new file mode 100644
index 000000000..81771c496
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventParticipantsDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class EventParticipantsDto
+ {
+ public Guid EventId { get; set; }
+ public IEnumerable InterestedStudents { get; set; }
+ public IEnumerable SignedUpStudents { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs
new file mode 100644
index 000000000..0858caaae
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/FriendDto.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class FriendDto
+ {
+ public Guid Id { get; set; }
+ public Guid StudentId { get; set; }
+ public Guid FriendId { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public string FriendState { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs
new file mode 100644
index 000000000..23d101a9f
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/OrganizationDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class OrganizationDto
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public IEnumerable Organizers { get; set; }
+ }
+}
\ No newline at end of file
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 bbdb13ef7..d7ee683d3 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
@@ -8,7 +8,8 @@ public class OrganizerDto
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
- public string Organization { get; set; }
+ public Guid OrganizationId { get; set; }
+ public string OrganizationName { get; set; }
public OrganizerDto()
{
@@ -19,7 +20,8 @@ public OrganizerDto(Organizer organizer)
Id = organizer.Id;
Name = organizer.Name;
Email = organizer.Email;
- Organization = organizer.Organization;
+ OrganizationId = organizer.OrganizationId;
+ OrganizationName = organizer.OrganizationName;
}
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs
new file mode 100644
index 000000000..dbf2c39eb
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.DTO
+{
+ public class ParticipantDto
+ {
+ public Guid StudentId { get; set; }
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs
new file mode 100644
index 000000000..4dba38c16
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events
+{
+ public class EventParticipantAdded: IEvent
+ {
+ public Guid EventId { get; }
+ public Guid ParticipantId { get; }
+ public string ParticipantName { get; }
+
+ public EventParticipantAdded(Guid eventId, Guid participantId, string participantName)
+ {
+ EventId = eventId;
+ ParticipantId = participantId;
+ ParticipantName = participantName;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs
new file mode 100644
index 000000000..7a4a8d091
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events
+{
+ public class EventParticipantRemoved: IEvent
+ {
+ public Guid EventId { get; }
+ public Guid Participant { get; }
+
+ public EventParticipantRemoved(Guid eventId, Guid participant)
+ {
+ EventId = eventId;
+ Participant = participant;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs
new file mode 100644
index 000000000..c0e79da44
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs
@@ -0,0 +1,12 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events
+{
+ public class EventUpdated(Guid eventId, DateTime updatedAt, Guid updatedBy) : IEvent
+ {
+ public Guid EventId { get; set; } = eventId;
+ public DateTime UpdatedAt { get; set; } = updatedAt;
+ public Guid UpdatedBy { get; set; } = updatedBy;
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs
new file mode 100644
index 000000000..896285dc6
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs
@@ -0,0 +1,21 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class AddEventParticipantRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public AddEventParticipantRejected(Guid eventId, Guid studentId, string reason, string code)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventRejected.cs
index 0184bbe9d..54e972866 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventRejected.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventRejected.cs
@@ -5,9 +5,9 @@
namespace MiniSpace.Services.Events.Application.Events.Rejected
{
- public class AddEventRejected(Guid userId, string reason, string code) : IRejectedEvent
+ public class AddEventRejected(Guid organizerId, string reason, string code) : IRejectedEvent
{
- public Guid UserId { get; } = userId;
+ public Guid OrganizerId { get; } = organizerId;
public string Reason { get; } = reason;
public string Code { get; } = code;
}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs
new file mode 100644
index 000000000..ced107d1c
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class CancelInterestInEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public CancelInterestInEventRejected(Guid eventId, string reason, string code)
+ {
+ EventId = eventId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs
new file mode 100644
index 000000000..f5d0ddb8f
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class CancelSignUpToEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public CancelSignUpToEventRejected(Guid eventId, string reason, string code)
+ {
+ EventId = eventId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs
new file mode 100644
index 000000000..082010765
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class DeleteEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public DeleteEventRejected(Guid eventId, string reason, string code)
+ {
+ EventId = eventId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs
new file mode 100644
index 000000000..2a610238a
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs
@@ -0,0 +1,21 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class RateEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public RateEventRejected(Guid eventId, Guid studentId, string reason, string code)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs
new file mode 100644
index 000000000..5e05cbdbf
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class RemoveEventParticipantRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public RemoveEventParticipantRejected(Guid eventId, string reason, string code)
+ {
+ EventId = eventId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs
new file mode 100644
index 000000000..2dbd42582
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs
@@ -0,0 +1,16 @@
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class SearchEventsRejected: IRejectedEvent
+ {
+ public string Reason { get; }
+ public string Code { get; }
+
+ public SearchEventsRejected(string reason, string code)
+ {
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs
new file mode 100644
index 000000000..53c49333b
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs
@@ -0,0 +1,16 @@
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class SearchOrganizerEventsRejected: IRejectedEvent
+ {
+ public string Reason { get; }
+ public string Code { get; }
+
+ public SearchOrganizerEventsRejected(string reason, string code)
+ {
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs
new file mode 100644
index 000000000..bb6e599b0
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs
@@ -0,0 +1,21 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class ShowInterestInEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public ShowInterestInEventRejected(Guid eventId, Guid studentId, string reason, string code)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs
new file mode 100644
index 000000000..537014622
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs
@@ -0,0 +1,21 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class SignUpToEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public SignUpToEventRejected(Guid eventId, Guid studentId, string reason, string code)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs
new file mode 100644
index 000000000..db2c3eb71
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs
@@ -0,0 +1,19 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events.Rejected
+{
+ public class UpdateEventRejected: IRejectedEvent
+ {
+ public Guid EventId { get; }
+ public string Reason { get; }
+ public string Code { get; }
+
+ public UpdateEventRejected(Guid eventId, string reason, string code)
+ {
+ EventId = eventId;
+ Reason = reason;
+ Code = code;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs
new file mode 100644
index 000000000..a993018b5
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events
+{
+ public class StudentCancelledInterestInEvent: IEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+
+ public StudentCancelledInterestInEvent(Guid eventId, Guid studentId)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs
new file mode 100644
index 000000000..fdfd41431
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs
@@ -0,0 +1,17 @@
+using System;
+using Convey.CQRS.Events;
+
+namespace MiniSpace.Services.Events.Application.Events
+{
+ public class StudentCancelledSignUpToEvent: IEvent
+ {
+ public Guid EventId { get; }
+ public Guid StudentId { get; }
+
+ public StudentCancelledSignUpToEvent(Guid eventId, Guid studentId)
+ {
+ EventId = eventId;
+ StudentId = studentId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventCapacityException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventCapacityException.cs
new file mode 100644
index 000000000..01083e0d8
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventCapacityException.cs
@@ -0,0 +1,13 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventCapacityException : AppException
+ {
+ public override string Code { get; } = "invalid_event_capacity";
+ public int Capacity { get; }
+
+ public InvalidEventCapacityException(int capacity) : base($"Invalid event capacity: {capacity}. It must be between 1 and 1000.")
+ {
+ Capacity = capacity;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventDescriptionException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventDescriptionException.cs
new file mode 100644
index 000000000..6934dbcb3
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventDescriptionException.cs
@@ -0,0 +1,13 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventDescriptionException : AppException
+ {
+ public override string Code { get; } = "invalid_event_description";
+ public string Description { get; }
+
+ public InvalidEventDescriptionException(string description): base("Invalid event description. It cannot be empty or longer than 5000 characters.")
+ {
+ Description = description;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventEngagementTypeException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventEngagementTypeException.cs
new file mode 100644
index 000000000..727634904
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventEngagementTypeException.cs
@@ -0,0 +1,14 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventEngagementTypeException : AppException
+ {
+ public override string Code { get; } = "invalid_event_engagement_type";
+ public string EngagementType { get; }
+
+ public InvalidEventEngagementTypeException(string engagementType)
+ : base($"Invalid event engagement type: {engagementType}.")
+ {
+ EngagementType = engagementType;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventFeeException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventFeeException.cs
new file mode 100644
index 000000000..c6b99f883
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventFeeException.cs
@@ -0,0 +1,13 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventFeeException : AppException
+ {
+ public override string Code { get; } = "invalid_event_fee";
+ public decimal Fee { get; }
+
+ public InvalidEventFeeException(decimal fee) : base($"Invalid event fee: {fee}. It must be between 0 and 1000.")
+ {
+ Fee = fee;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventNameException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventNameException.cs
new file mode 100644
index 000000000..c2db3eaee
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventNameException.cs
@@ -0,0 +1,13 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventNameException : AppException
+ {
+ public override string Code { get; } = "invalid_event_name";
+ public string Name { get; }
+
+ public InvalidEventNameException(string name): base("Invalid event name. It cannot be empty or longer than 300 characters.")
+ {
+ Name = name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventStateException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventStateException.cs
new file mode 100644
index 000000000..ca4f6b106
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidEventStateException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidEventStateException : AppException
+ {
+ public override string Code { get; } = "invalid_event_state";
+ public string State { get; }
+
+ public InvalidEventStateException(string state) : base($"Event State property is invalid: {state}.")
+ {
+ State = state;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventCapacityException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventCapacityException.cs
new file mode 100644
index 000000000..65adf652d
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventCapacityException.cs
@@ -0,0 +1,16 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidUpdatedEventCapacityException : AppException
+ {
+ public override string Code { get; } = "invalid_updated_event_capacity";
+ public int CurrentCapacity { get; }
+ public int NewCapacity { get; }
+
+ public InvalidUpdatedEventCapacityException(int currentCapacity, int newCapacity)
+ : base($"Invalid updated event capacity: {newCapacity}. It has to be greater than the current capacity: {currentCapacity}.")
+ {
+ CurrentCapacity = currentCapacity;
+ NewCapacity = newCapacity;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventFeeException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventFeeException.cs
new file mode 100644
index 000000000..9a068f813
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/InvalidUpdatedEventFeeException.cs
@@ -0,0 +1,16 @@
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class InvalidUpdatedEventFeeException : AppException
+ {
+ public override string Code { get; } = "invalid_updated_event_fee";
+ public decimal CurrentFee { get; }
+ public decimal NewFee { get; }
+
+ public InvalidUpdatedEventFeeException(decimal currentFee, decimal newFee)
+ : base($"Updated fee: {newFee} cannot be greater than current fee: {currentFee}.")
+ {
+ CurrentFee = currentFee;
+ NewFee = newFee;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizationNotFoundException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizationNotFoundException.cs
new file mode 100644
index 000000000..83e7bb79a
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizationNotFoundException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class OrganizationNotFoundException: AppException
+ {
+ public override string Code { get; } = "organization_not_found";
+ public Guid OrganizationId { get; }
+
+ public OrganizationNotFoundException(Guid organizationId): base($"Organization with id: '{organizationId}' was not found.")
+ {
+ OrganizationId = organizationId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizerDoesNotBelongToOrganizationException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizerDoesNotBelongToOrganizationException.cs
new file mode 100644
index 000000000..7e5388802
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/OrganizerDoesNotBelongToOrganizationException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class OrganizerDoesNotBelongToOrganizationException : AppException
+ {
+ public override string Code => "organizer_does_not_belong_to_organization";
+ public Guid OrganizerId { get; }
+ public Guid OrganizationId { get; }
+
+ public OrganizerDoesNotBelongToOrganizationException(Guid organizerId, Guid organizationId)
+ : base($"Organizer with ID: {organizerId} does not belong to organization with ID: {organizationId}.")
+ {
+ OrganizerId = organizerId;
+ OrganizationId = organizationId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedEventAccesException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedEventAccessException.cs
similarity index 100%
rename from MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedEventAccesException.cs
rename to MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedEventAccessException.cs
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedOrganizerEventsAccessException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedOrganizerEventsAccessException.cs
new file mode 100644
index 000000000..a542aadef
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Exceptions/UnauthorizedOrganizerEventsAccessException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MiniSpace.Services.Events.Application.Exceptions
+{
+ public class UnauthorizedOrganizerEventsAccessException : AppException
+ {
+ public override string Code { get; } = "unauthorized_event_access";
+ public Guid OrganizerId { get; }
+ public Guid UserId { get; }
+
+ public UnauthorizedOrganizerEventsAccessException(Guid organizerId, Guid userId)
+ : base($"Unauthorized access to organizer events with ID: '{organizerId}' by user with ID: '{userId}'.")
+ {
+ OrganizerId = organizerId;
+ UserId = userId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs
new file mode 100644
index 000000000..8101e4e4c
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs
@@ -0,0 +1,11 @@
+using System;
+using Convey.CQRS.Queries;
+using MiniSpace.Services.Events.Application.DTO;
+
+namespace MiniSpace.Services.Events.Application.Queries
+{
+ public class GetEventParticipants : IQuery
+ {
+ public Guid EventId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs
new file mode 100644
index 000000000..860aba8f2
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IFriendsServiceClient.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MiniSpace.Services.Events.Application.DTO;
+
+namespace MiniSpace.Services.Events.Application.Services.Clients
+{
+ public interface IFriendsServiceClient
+ {
+ Task> GetAsync(Guid studentId);
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..49d592182
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Clients/IOrganizationsServiceClient.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Threading.Tasks;
+using MiniSpace.Services.Events.Application.DTO;
+
+namespace MiniSpace.Services.Events.Application.Services.Clients
+{
+ public interface IOrganizationsServiceClient
+ {
+ Task GetAsync(Guid id);
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Events/EventService.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Events/EventService.cs
deleted file mode 100644
index ff8bc3d7a..000000000
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/Events/EventService.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MiniSpace.Services.Events.Application.Commands;
-using MiniSpace.Services.Events.Application.DTO;
-using MiniSpace.Services.Events.Application.Wrappers;
-using MiniSpace.Services.Events.Core.Entities;
-using MiniSpace.Services.Events.Core.Repositories;
-
-namespace MiniSpace.Services.Events.Application.Services.Events
-{
- public class EventService : IEventService
- {
- private readonly IEventRepository _eventRepository;
- private readonly IEventValidator _eventValidator;
- private readonly IMessageBroker _messageBroker;
-
- public EventService(IEventRepository eventRepository, IEventValidator eventValidator, IMessageBroker messageBroker)
- {
- _eventRepository = eventRepository;
- _eventValidator = eventValidator;
- _messageBroker = messageBroker;
- }
-
- public async Task>> SignInAsync(SearchEvents command)
- {
- var dateTo = DateTime.MinValue;
- var dateFrom = DateTime.MinValue;
- if(command.DateTo != string.Empty)
- {
- _eventValidator.ParseDate(command.DateTo, "DateTo");
- }
- if(command.DateFrom != string.Empty)
- {
- _eventValidator.ParseDate(command.DateFrom, "DateFrom");
- }
- (int pageNumber, int pageSize) = _eventValidator.PageFilter(command.Pageable.Page, command.Pageable.Size);
-
- var result = await _eventRepository.BrowseAsync(
- pageNumber, pageSize, command.Name, command.Organizer, dateFrom, dateTo,
- command.Pageable.Sort.SortBy, command.Pageable.Sort.Direction, State.Published);
-
- var pagedEvents = new PagedResponse>(result.Item1.Select(e => new EventDto(e)),
- result.Item2, result.Item3, result.Item4, result.Item5);
-
- return pagedEvents;
- }
- }
-}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
index 5bde55989..7a3a9429c 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventService.cs
@@ -8,6 +8,7 @@ namespace MiniSpace.Services.Events.Application.Services
{
public interface IEventService
{
- Task>> SignInAsync(SearchEvents command);
+ Task>> BrowseEventsAsync(SearchEvents command);
+ Task>> BrowseOrganizerEventsAsync(SearchOrganizerEvents command);
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventValidator.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventValidator.cs
index 1ed5a713a..cf1ad68b6 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventValidator.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventValidator.cs
@@ -7,7 +7,16 @@ public interface IEventValidator
{
Category ParseCategory(string categoryString);
DateTime ParseDate(string dateString, string fieldName);
+ State ParseState(string stateString);
+ EventEngagementType ParseEngagementType(string engagementTypeString);
void ValidateDates(DateTime earlierDate, DateTime laterDate, string earlierDateString, string endDateString);
(int pageNumber, int pageSize) PageFilter(int pageNumber, int pageSize);
+ void ValidateName(string name);
+ void ValidateDescription(string description);
+ void ValidateCapacity(int capacity);
+ void ValidateFee(decimal fee);
+ void ValidateUpdatedCapacity(int currentCapacity, int newCapacity);
+ void ValidateUpdatedFee(decimal currentFee, decimal newFee);
+ State? RestrictState(State? state);
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Address.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Address.cs
index 6da0ed1dc..a00173b0f 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Address.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Address.cs
@@ -14,5 +14,17 @@ public class Address(
public string ApartmentNumber { get; set; } = apartmentNumber;
public string City { get; set; } = city;
public string ZipCode { get; set; } = zipCode;
+
+ public Address Update(string buildingName, string street, string buildingNumber, string apartmentNumber,
+ string city, string zipCode)
+ {
+ BuildingName = buildingName == string.Empty ? BuildingName : buildingName;
+ Street = street == string.Empty ? Street : street;
+ BuildingNumber = buildingNumber == string.Empty ? BuildingNumber : buildingNumber;
+ ApartmentNumber = apartmentNumber == string.Empty ? ApartmentNumber : apartmentNumber;
+ City = city == string.Empty ? City : city;
+ ZipCode = zipCode == string.Empty ? ZipCode : zipCode;
+ return this;
+ }
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs
index b9bf64a09..da9c0ffe0 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs
@@ -8,7 +8,6 @@ namespace MiniSpace.Services.Events.Core.Entities
{
public class Event: AggregateRoot
{
- private ISet _coOrganizers = new HashSet();
private ISet _interestedStudents = new HashSet();
private ISet _signedUpStudents = new HashSet();
private ISet _ratings = new HashSet();
@@ -24,12 +23,7 @@ public class Event: AggregateRoot
public Category Category { get; private set; }
public State State { get; private set; }
public DateTime PublishDate { get; private set; }
-
- public IEnumerable CoOrganizers
- {
- get => _coOrganizers;
- private set => _coOrganizers = new HashSet(value);
- }
+ public DateTime UpdatedAt { get; private set; }
public IEnumerable InterestedStudents
{
@@ -51,7 +45,7 @@ public IEnumerable Ratings
public Event(AggregateId id, string name, string description, DateTime startDate, DateTime endDate,
Address location, int capacity, decimal fee, Category category, State state, DateTime publishDate,
- Organizer organizer, IEnumerable coOrganizers = null, IEnumerable interestedStudents = null,
+ Organizer organizer, DateTime updatedAt, IEnumerable interestedStudents = null,
IEnumerable signedUpStudents = null, IEnumerable ratings = null)
{
Id = id;
@@ -65,32 +59,48 @@ public Event(AggregateId id, string name, string description, DateTime startDat
Category = category;
State = state;
Organizer = organizer;
- CoOrganizers = coOrganizers ?? Enumerable.Empty();
InterestedStudents = interestedStudents ?? Enumerable.Empty();
SignedUpStudents = signedUpStudents ?? Enumerable.Empty();
Ratings = ratings ?? Enumerable.Empty();
PublishDate = publishDate;
+ UpdatedAt = updatedAt;
}
public static Event Create(AggregateId id, string name, string description, DateTime startDate, DateTime endDate,
- Address location, int capacity, decimal fee, Category category, State state, DateTime publishDate, Organizer organizer)
+ Address location, int capacity, decimal fee, Category category, State state, DateTime publishDate,
+ Organizer organizer, DateTime now)
{
var @event = new Event(id, name, description, startDate, endDate, location, capacity, fee, category,
- state, publishDate, organizer);
+ state, publishDate, organizer, now);
return @event;
}
- public void AddOrganizer(Organizer organizer)
+ public void Update(string name, string description, DateTime startDate, DateTime endDate, Address location,
+ int capacity, decimal fee, Category category, State state, DateTime publishDate, DateTime now)
+ {
+ Name = name;
+ Description = description;
+ StartDate = startDate;
+ EndDate = endDate;
+ Location = location;
+ Capacity = capacity;
+ Fee = fee;
+ Category = category;
+ State = state;
+ PublishDate = publishDate;
+ UpdatedAt = now;
+ }
+
+ public void SignUpStudent(Participant participant)
{
- if (CoOrganizers.Any(o => o.Id == organizer.Id))
+ if(State != State.Published)
{
- throw new OrganizerAlreadyAddedException(organizer.Id);
+ throw new InvalidEventState(Id, State.Published, State);
}
-
- _coOrganizers.Add(organizer);
+ AddParticipant(participant);
}
- public void SignUpStudent(Participant participant)
+ public void AddParticipant(Participant participant)
{
if (SignedUpStudents.Any(p => p.StudentId == participant.StudentId))
{
@@ -101,10 +111,35 @@ public void SignUpStudent(Participant participant)
{
throw new EventCapacityExceededException(Id, Capacity);
}
+
+ if(participant.StudentId == Organizer.Id)
+ {
+ throw new OrganizerCannotSignUpForOwnEventException(Organizer.Id, Id);
+ }
_signedUpStudents.Add(participant);
}
+ public void CancelSignUp(Guid studentId)
+ {
+ if(State != State.Published)
+ {
+ throw new InvalidEventState(Id, State.Published, State);
+ }
+ RemoveParticipant(studentId);
+ }
+
+ public void RemoveParticipant(Guid studentId)
+ {
+ var participant = _signedUpStudents.SingleOrDefault(p => p.StudentId == studentId);
+ if (participant is null)
+ {
+ throw new StudentNotSignedUpException(studentId, Id);
+ }
+
+ _signedUpStudents.Remove(participant);
+ }
+
public void ShowStudentInterest(Participant participant)
{
if (InterestedStudents.Any(p => p.StudentId == participant.StudentId))
@@ -115,8 +150,24 @@ public void ShowStudentInterest(Participant participant)
_interestedStudents.Add(participant);
}
+ public void CancelInterest(Guid studentId)
+ {
+ var participant = _interestedStudents.SingleOrDefault(p => p.StudentId == studentId);
+ if (participant is null)
+ {
+ throw new StudentNotInterestedInEventException(studentId, Id);
+ }
+
+ _interestedStudents.Remove(participant);
+ }
+
public void Rate(Guid studentId, int rating)
{
+ if(State != State.Archived)
+ {
+ throw new InvalidEventState(Id, State.Archived, State);
+ }
+
if(_signedUpStudents.All(p => p.StudentId != studentId))
{
throw new StudentNotSignedUpForEventException(Id ,studentId);
@@ -148,6 +199,7 @@ public bool UpdateState(DateTime now)
else
return false;
+ UpdatedAt = now;
return true;
}
@@ -160,5 +212,8 @@ private void ChangeState(State state)
State = state;
}
+
+ public bool IsOrganizer(Guid organizerId)
+ => Organizer.Id == organizerId;
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/EventEngagementType.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/EventEngagementType.cs
new file mode 100644
index 000000000..af5592f1d
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/EventEngagementType.cs
@@ -0,0 +1,8 @@
+namespace MiniSpace.Services.Events.Core.Entities
+{
+ public enum EventEngagementType
+ {
+ SignedUp,
+ InterestedIn
+ }
+}
\ 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 a2d988afa..1584b6870 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
@@ -7,14 +7,16 @@ public class Organizer
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
- public string Organization { get; set; }
+ public Guid OrganizationId { get; set; }
+ public string OrganizationName { get; set; }
- public Organizer(Guid id, string name, string email, string organization)
+ public Organizer(Guid id, string name, string email, Guid organizationId, string organizationName)
{
Id = id;
Name = name;
Email = email;
- Organization = organization;
+ OrganizationId = organizationId;
+ OrganizationName = organizationName;
}
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs
index e80fdda37..7b0a03d91 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs
@@ -2,10 +2,9 @@
namespace MiniSpace.Services.Events.Core.Entities
{
- public class Participant(Guid studentId, string name, string email)
+ public class Participant(Guid studentId, string name)
{
public Guid StudentId { get; set; } = studentId;
public string Name { get; set; } = name;
- public string Email { get; set; } = email;
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/InvalidEventState.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/InvalidEventState.cs
new file mode 100644
index 000000000..8d5eaa711
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/InvalidEventState.cs
@@ -0,0 +1,20 @@
+using System;
+using MiniSpace.Services.Events.Core.Entities;
+
+namespace MiniSpace.Services.Events.Core.Exceptions
+{
+ public class InvalidEventState : DomainException
+ {
+ public override string Code { get; } = "invalid_event_state";
+ public Guid EventId { get; }
+ public State RequiredState { get; }
+ public State CurrentState { get; }
+ public InvalidEventState(Guid eventId, State requiredState, State currentState)
+ : base($"Event with id: {eventId} has invalid state: {currentState}. Required state: {requiredState}")
+ {
+ EventId = eventId;
+ RequiredState = requiredState;
+ CurrentState = currentState;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerAlreadyAddedException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerAlreadyAddedException.cs
deleted file mode 100644
index 13bed5f20..000000000
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerAlreadyAddedException.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace MiniSpace.Services.Events.Core.Exceptions
-{
- public class OrganizerAlreadyAddedException : DomainException
- {
- public override string Code { get; } = "organizer_already_added";
-
- public OrganizerAlreadyAddedException(Guid id)
- : base($"Organizer with id: '{id}' has been already added")
- {
- }
- }
-}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerCannotSignUpForOwnEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerCannotSignUpForOwnEventException.cs
new file mode 100644
index 000000000..407b6eac4
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/OrganizerCannotSignUpForOwnEventException.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace MiniSpace.Services.Events.Core.Exceptions
+{
+ public class OrganizerCannotSignUpForOwnEventException : DomainException
+ {
+ public override string Code { get; } = "organizer_cannot_sign_up_for_own_event";
+ public Guid OrganizerId { get; }
+ public Guid EventId { get; }
+ public OrganizerCannotSignUpForOwnEventException(Guid organizerId, Guid eventId)
+ : base($"Organizer with id: {organizerId} cannot sign up for their own event with id: {eventId}.")
+ {
+ OrganizerId = organizerId;
+ EventId = eventId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs
new file mode 100644
index 000000000..626d8269e
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MiniSpace.Services.Events.Core.Exceptions
+{
+ public class StudentNotInterestedInEventException : DomainException
+ {
+ public override string Code { get; } = "student_is_not_interested_in_event";
+ public Guid StudentId { get; }
+ public Guid EventId { get; }
+
+ public StudentNotInterestedInEventException(Guid studentId, Guid eventId)
+ : base($"Student with ID: '{studentId}' is not interested in event with ID: '{eventId}'.")
+ {
+ StudentId = studentId;
+ EventId = eventId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs
new file mode 100644
index 000000000..da58daf97
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace MiniSpace.Services.Events.Core.Exceptions
+{
+ public class StudentNotSignedUpException : DomainException
+ {
+ public override string Code { get; } = "student_not_signed_up";
+ public Guid StudentId { get; }
+ public Guid EventId { get; }
+
+ public StudentNotSignedUpException(Guid studentId, Guid eventId)
+ : base($"Student with ID: '{studentId}' has not signed up to event with ID: '{eventId}'.")
+ {
+ StudentId = studentId;
+ EventId = eventId;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Repositories/IEventRepository.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Repositories/IEventRepository.cs
index 41d28e0f3..bed362306 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Repositories/IEventRepository.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Repositories/IEventRepository.cs
@@ -13,8 +13,12 @@ public interface IEventRepository
Task AddAsync(Event @event);
Task UpdateAsync(Event @event);
Task DeleteAsync(Guid id);
- Task,int,int,int,int>> BrowseAsync(int pageNumber, int pageSize, string name,
- string organizer, DateTime dateFrom, DateTime dateTo, IEnumerable sortBy, string direction,
- State state, IEnumerable eventIds = null);
+ 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 friends, EventEngagementType? friendsEngagementType,
+ IEnumerable sortBy, string direction, IEnumerable eventIds = null);
+ Task<(IEnumerable events, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseOrganizerEventsAsync(
+ int pageNumber, int pageSize, string name, Guid organizerId, DateTime dateFrom, DateTime dateTo,
+ IEnumerable sortBy, string direction, State? state);
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
index 799ba2d49..28d680eea 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs
@@ -1,5 +1,6 @@
using System;
using Convey.MessageBrokers.RabbitMQ;
+using MiniSpace.Services.Events.Application.Commands;
using MiniSpace.Services.Events.Application.Events.Rejected;
using MiniSpace.Services.Events.Application.Exceptions;
@@ -12,10 +13,66 @@ public object Map(Exception exception, object message)
{
// TODO: Add more exceptions
AuthorizedUserIsNotAnOrganizerException ex => new AddEventRejected(ex.UserId, ex.Message, ex.Code),
- OrganizerCannotAddEventForAnotherOrganizerException ex => new AddEventRejected(ex.OrganizerId, ex.Message, ex.Code),
- InvalidEventCategoryException ex => new AddEventRejected(Guid.Empty, ex.Message, ex.Code),
- InvalidEventDateTimeException ex => new AddEventRejected(Guid.Empty, ex.Message, ex.Code),
- InvalidEventDateTimeOrderException ex => new AddEventRejected(Guid.Empty, ex.Message, ex.Code),
+ EventNotFoundException ex
+ => message switch
+ {
+ AddEventParticipant m => new AddEventParticipantRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ CancelInterestInEvent m => new CancelInterestInEventRejected(ex.EventId, ex.Message, ex.Code),
+ CancelSignUpToEvent m => new CancelSignUpToEventRejected(ex.EventId, ex.Message, ex.Code),
+ DeleteEvent m => new DeleteEventRejected(ex.EventId, ex.Message, ex.Code),
+ RateEvent m => new RateEventRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ RemoveEventParticipant m => new RemoveEventParticipantRejected(ex.EventId, ex.Message, ex.Code),
+ ShowInterestInEvent m => new ShowInterestInEventRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ SignUpToEvent m => new SignUpToEventRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ UpdateEvent m => new UpdateEventRejected(ex.EventId, ex.Message, ex.Code),
+ _ => null
+ },
+ InvalidEventCategoryException ex
+ => message switch
+ {
+ AddEvent m => new AddEventRejected(m.OrganizerId, ex.Message, ex.Code),
+ _ => null
+ },
+ InvalidEventDateTimeException ex
+ => message switch
+ {
+ AddEvent m => new AddEventRejected(m.OrganizerId, ex.Message, ex.Code),
+ _ => null
+ },
+ InvalidEventDateTimeOrderException ex
+ => message switch
+ {
+ AddEvent m => new AddEventRejected(m.OrganizerId, ex.Message, ex.Code),
+ _ => null
+ },
+ OrganizerCannotAddEventForAnotherOrganizerException ex
+ => message switch
+ {
+ AddEvent m => new AddEventRejected(m.OrganizerId, ex.Message, ex.Code),
+ _ => null
+ },
+ StudentNotFoundException ex
+ => message switch
+ {
+ AddEventParticipant m => new AddEventParticipantRejected(m.EventId, ex.StudentId, ex.Message, ex.Code),
+ ShowInterestInEvent m => new ShowInterestInEventRejected(m.EventId, ex.StudentId, ex.Message, ex.Code),
+ SignUpToEvent m => new SignUpToEventRejected(m.EventId, ex.StudentId, ex.Message, ex.Code),
+ RateEvent m => new RateEventRejected(m.EventId, ex.StudentId, ex.Message, ex.Code),
+ _ => null
+ },
+ UnauthorizedEventAccessException ex
+ => message switch
+ {
+ AddEventParticipant m => new AddEventParticipantRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ CancelInterestInEvent m => new CancelInterestInEventRejected(ex.EventId, ex.Message, ex.Code),
+ CancelSignUpToEvent m => new CancelSignUpToEventRejected(ex.EventId, ex.Message, ex.Code),
+ DeleteEvent m => new DeleteEventRejected(ex.EventId, ex.Message, ex.Code),
+ RemoveEventParticipant m => new RemoveEventParticipantRejected(ex.EventId, ex.Message, ex.Code),
+ ShowInterestInEvent m => new ShowInterestInEventRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ SignUpToEvent m => new SignUpToEventRejected(ex.EventId, m.StudentId, ex.Message, ex.Code),
+ UpdateEvent m => new UpdateEventRejected(ex.EventId, ex.Message, ex.Code),
+ _ => null
+ },
_ => null,
};
}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs
index 82a642ba3..49a53898c 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs
@@ -37,7 +37,6 @@
using MiniSpace.Services.Events.Application.Events.External;
using MiniSpace.Services.Events.Application.Services;
using MiniSpace.Services.Events.Application.Services.Clients;
-using MiniSpace.Services.Events.Application.Services.Events;
using MiniSpace.Services.Events.Core.Repositories;
using MiniSpace.Services.Events.Infrastructure.Contexts;
using MiniSpace.Services.Events.Infrastructure.Decorators;
@@ -65,6 +64,8 @@ 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(ctx => ctx.GetRequiredService().Create());
builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>));
builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>));
@@ -104,9 +105,14 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app
.UseRabbitMq()
.SubscribeCommand()
.SubscribeCommand()
+ .SubscribeCommand()
.SubscribeCommand()
.SubscribeCommand()
.SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
+ .SubscribeCommand()
.SubscribeEvent();
return app;
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs
index 66a7e31af..f9dec514b 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs
@@ -62,6 +62,20 @@ private static IReadOnlyDictionary MessageTemplates
After = "Events' state updated at: {Now}."
}
},
+ {
+ typeof(AddEventParticipant),
+ new HandlerLogTemplate
+ {
+ After = "Added a participant with id: {StudentId} to event with id: {EventId}.",
+ }
+ },
+ {
+ typeof(RemoveEventParticipant),
+ new HandlerLogTemplate
+ {
+ After = "Removed participant with id: {ParticipantId} from event with id: {EventId}."
+ }
+ },
{
typeof(StudentCreated),
new HandlerLogTemplate
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs
index 9c8c88738..dffb5c2f7 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs
@@ -14,7 +14,6 @@ public class EventDocument : IIdentifiable
public Organizer Organizer { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
- public IEnumerable CoOrganizers { get; set; }
public Address Location { get; set; }
//public string Image { get; set; }
public IEnumerable InterestedStudents { get; set; }
@@ -24,6 +23,7 @@ public class EventDocument : IIdentifiable
public Category Category { get; set; }
public State State { get; set; }
public DateTime PublishDate { get; set; }
+ public DateTime UpdatedAt { get; set; }
public IEnumerable Ratings { get; set; }
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs
index 271978669..5464f8410 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs
@@ -1,4 +1,6 @@
-using System.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using MiniSpace.Services.Events.Application.DTO;
using MiniSpace.Services.Events.Core.Entities;
@@ -6,7 +8,7 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents
{
public static class Extensions
{
- public static EventDto AsDto(this EventDocument document)
+ public static EventDto AsDto(this EventDocument document, Guid studentId)
=> new ()
{
Id = document.Id,
@@ -15,7 +17,6 @@ public static EventDto AsDto(this EventDocument document)
Organizer = document.Organizer.AsDto(),
StartDate = document.StartDate,
EndDate = document.EndDate,
- CoOrganizers = document.CoOrganizers.Select(x => x.AsDto()),
Location = document.Location.AsDto(),
InterestedStudents = document.InterestedStudents.Count(),
SignedUpStudents = document.SignedUpStudents.Count(),
@@ -23,13 +24,29 @@ public static EventDto AsDto(this EventDocument document)
Fee = document.Fee,
Category = document.Category.ToString(),
Status = document.State.ToString(),
- PublishDate = document.PublishDate
+ PublishDate = document.PublishDate,
+ UpdatedAt = document.UpdatedAt,
+ IsSignedUp = document.SignedUpStudents.Any(x => x.StudentId == studentId),
+ IsInterested = document.InterestedStudents.Any(x => x.StudentId == studentId),
+ HasRated = document.Ratings.Any(x => x.StudentId == studentId)
};
+
+ public static EventDto AsDtoWithFriends(this EventDocument document, Guid studentId, IEnumerable friends)
+ {
+ var eventDto = document.AsDto(studentId);
+ eventDto.FriendsInterestedIn = document.InterestedStudents
+ .Where(x => friends.Any(f => f.FriendId == x.StudentId))
+ .Select(p => p.AsDto());
+ eventDto.FriendsSignedUp = document.SignedUpStudents
+ .Where(x => friends.Any(f => f.FriendId == x.StudentId))
+ .Select(p => p.AsDto());
+ return eventDto;
+ }
public static Event AsEntity(this EventDocument document)
=> new (document.Id, document.Name, document.Description, document.StartDate, document.EndDate,
document.Location, document.Capacity, document.Fee, document.Category, document.State, document.PublishDate,
- document.Organizer, document.CoOrganizers, document.InterestedStudents, document.SignedUpStudents, document.Ratings);
+ document.Organizer, document.UpdatedAt,document.InterestedStudents, document.SignedUpStudents, document.Ratings);
public static EventDocument AsDocument(this Event entity)
=> new ()
@@ -41,7 +58,6 @@ public static EventDocument AsDocument(this Event entity)
StartDate = entity.StartDate,
EndDate = entity.EndDate,
Location = entity.Location,
- CoOrganizers = entity.CoOrganizers,
InterestedStudents = entity.InterestedStudents,
SignedUpStudents = entity.SignedUpStudents,
Capacity = entity.Capacity,
@@ -49,8 +65,17 @@ public static EventDocument AsDocument(this Event entity)
Category = entity.Category,
State = entity.State,
PublishDate = entity.PublishDate,
+ UpdatedAt = entity.UpdatedAt,
Ratings = entity.Ratings
};
+
+ public static EventParticipantsDto AsDto(this EventDocument document)
+ => new ()
+ {
+ EventId = document.Id,
+ InterestedStudents = document.InterestedStudents.Select(p => p.AsDto()),
+ SignedUpStudents = document.SignedUpStudents.Select(p => p.AsDto())
+ };
public static AddressDto AsDto(this Address entity)
=> new ()
@@ -72,7 +97,8 @@ public static OrganizerDto AsDto(this Organizer entity)
Id = entity.Id,
Name = entity.Name,
Email = entity.Email,
- Organization = entity.Organization
+ OrganizationId = entity.OrganizationId,
+ OrganizationName = entity.OrganizationName
};
public static StudentDocument AsDocument(this Student entity)
@@ -83,5 +109,12 @@ public static StudentDocument AsDocument(this Student entity)
public static Student AsEntity(this StudentDocument document)
=> new (document.Id);
+
+ public static ParticipantDto AsDto(this Participant entity)
+ => new ()
+ {
+ StudentId = entity.StudentId,
+ Name = entity.Name
+ };
}
}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs
index e840928e4..25e976803 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs
@@ -1,12 +1,16 @@
using System;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Convey.CQRS.Queries;
using Convey.Persistence.MongoDB;
+using MiniSpace.Services.Events.Application;
using MiniSpace.Services.Events.Application.DTO;
using MiniSpace.Services.Events.Application.Events;
using MiniSpace.Services.Events.Application.Queries;
using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Application.Services.Clients;
+using MiniSpace.Services.Events.Core.Entities;
using MiniSpace.Services.Events.Infrastructure.Mongo.Documents;
namespace MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers
@@ -14,20 +18,35 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers
public class GetEventHandler : IQueryHandler
{
private readonly IMongoRepository _eventRepository;
+ private readonly IFriendsServiceClient _friendsServiceClient;
+ private readonly IAppContext _appContext;
private readonly IMessageBroker _messageBroker;
- public GetEventHandler(IMongoRepository eventRepository, IMessageBroker messageBroker)
+ public GetEventHandler(IMongoRepository eventRepository,
+ IFriendsServiceClient friendsServiceClient, IAppContext appContext, IMessageBroker messageBroker)
{
_eventRepository = eventRepository;
+ _friendsServiceClient = friendsServiceClient;
+ _appContext = appContext;
_messageBroker = messageBroker;
}
public async Task HandleAsync(GetEvent query, CancellationToken cancellationToken)
{
var document = await _eventRepository.GetAsync(p => p.Id == query.EventId);
+ if(document is null)
+ {
+ return null;
+ }
+ var identity = _appContext.Identity;
+ var friends = Enumerable.Empty();
+ if(identity.IsAuthenticated)
+ {
+ friends = await _friendsServiceClient.GetAsync(identity.Id);
+ }
await _messageBroker.PublishAsync(new EventViewed(query.EventId));
- return document?.AsDto();
+ return document.AsDtoWithFriends(identity.Id, friends);
}
}
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs
new file mode 100644
index 000000000..2fd84959a
--- /dev/null
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Convey.CQRS.Queries;
+using Convey.Persistence.MongoDB;
+using MiniSpace.Services.Events.Application;
+using MiniSpace.Services.Events.Application.DTO;
+using MiniSpace.Services.Events.Application.Events;
+using MiniSpace.Services.Events.Application.Queries;
+using MiniSpace.Services.Events.Application.Services;
+using MiniSpace.Services.Events.Infrastructure.Mongo.Documents;
+
+namespace MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers
+{
+ public class GetEventParticipantsHandler : IQueryHandler
+ {
+ private readonly IMongoRepository _eventRepository;
+ private readonly IAppContext _appContext;
+
+ public GetEventParticipantsHandler(IMongoRepository eventRepository,
+ IAppContext appContext)
+ {
+ _eventRepository = eventRepository;
+ _appContext = appContext;
+ }
+
+ public async Task HandleAsync(GetEventParticipants query, CancellationToken cancellationToken)
+ {
+ var document = await _eventRepository.GetAsync(p => p.Id == query.EventId);
+ if(document is null)
+ {
+ return null;
+ }
+ var identity = _appContext.Identity;
+ if(identity.IsAuthenticated && identity.Id != document.Organizer.Id && !identity.IsAdmin)
+ {
+ return null;
+ }
+
+ return document.AsDto();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs
index 9d10756e6..14818f556 100644
--- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs
+++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs
@@ -43,11 +43,11 @@ public async Task>> HandleAsync(GetStudentEv
var studentEvents = await _studentsServiceClient.GetAsync(query.StudentId);
var studentEventIds = studentEvents.InterestedInEvents.Union(studentEvents.SignedUpEvents).ToList();
- var result = await _eventRepository.BrowseAsync(1, query.NumberOfResults,
- string.Empty, string.Empty, DateTime.MinValue, DateTime.MinValue,
- Enumerable.Empty(), "asc", State.Published, studentEventIds);
+ var result = await _eventRepository.BrowseEventsAsync(1, query.NumberOfResults,
+ string.Empty, string.Empty, DateTime.MinValue, DateTime.MinValue, null, null,
+ Enumerable.Empty(), null, Enumerable.Empty(), "asc", studentEventIds);
- return new PagedResponse>(result.Item1.Select(e => new EventDto(e)),
+ return new PagedResponse>(result.Item1.Select(e => new EventDto(e, identity.Id)),
result.Item2, result.Item3, result.Item4, result.Item5);;
}
}
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 2fda11c8e..af2c686d7 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
@@ -37,21 +37,50 @@ public async Task> GetAllAsync()
return filteredEvents.Select(e => e.AsEntity());
}
- public async Task,int,int,int,int>> BrowseAsync(int pageNumber, int pageSize, string name, string organizer,
- DateTime dateFrom, DateTime dateTo, IEnumerable sortBy, string direction, State state,
- IEnumerable eventIds = null)
+ private async Task<(int totalPages, int totalElements, IEnumerable data)> BrowseAsync(
+ FilterDefinition filterDefinition, SortDefinition sortDefinition,
+ int pageNumber, int pageSize)
{
- var filterDefinition = Repositories.Extensions.ToFilterDefinition(name, organizer, dateFrom, dateTo, state, eventIds);
- var sortDefinition = Repositories.Extensions.ToSortDefinition(sortBy, direction);
-
var pagedEvents = await _repository.Collection.AggregateByPage(
filterDefinition,
sortDefinition,
pageNumber,
pageSize);
+
+ return pagedEvents;
+ }
+
+ 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 friends, EventEngagementType? friendsEngagementType,
+ IEnumerable