Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ORG-76] Endpoint to get participants lists #83

Merged
merged 6 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions organizator_api/app/applications/application/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from app.applications.domain.models.application import Application
from app.events.application.response import EventResponse
from app.events.domain.models.event import Event
from app.users.application.response import UserResponse
from app.users.domain.models.user import User


Expand Down Expand Up @@ -33,3 +34,11 @@ def to_dict_without_user(self) -> dict[str, Any]:
"created_at": self.created_at.strftime("%Y-%m-%dT%H:%M:%SZ"),
"updated_at": self.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"),
}

def to_dict_without_event(self) -> dict[str, Any]:
return {
"id": self.id,
"user": UserResponse.from_user(self.user).to_dict(),
"created_at": self.created_at.strftime("%Y-%m-%dT%H:%M:%SZ"),
"updated_at": self.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"),
}
6 changes: 6 additions & 0 deletions organizator_api/app/applications/domain/repositories.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import uuid
from abc import ABC, abstractmethod
from typing import List

from app.applications.domain.models.application import Application
from app.events.domain.models.event import Event
from app.users.domain.models.user import User


Expand All @@ -13,3 +15,7 @@ def create(self, application: Application) -> None:
@abstractmethod
def get_by_user(self, user: User) -> List[Application]:
pass

@abstractmethod
def get_by_event(self, event_id: uuid.UUID) -> List[Application]:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import uuid
from typing import List

from app.applications.domain.models.application import Application
from app.applications.infrastructure.repository_factories import (
ApplicationRepositoryFactory,
)
from app.events.domain.models.event import Event
from app.events.domain.usecases.get_event_use_case import GetEventUseCase
from app.users.domain.exceptions import OnlyAuthorizedToOrganizer
from app.users.domain.models.user import UserRoles
from app.users.domain.usecases.get_role_by_token_use_case import GetRoleByTokenUseCase


class GetApplicationsByEventUseCase:
def __init__(self) -> None:
self.application_repository = ApplicationRepositoryFactory.create()

def execute(self, token: uuid.UUID, event_id: uuid.UUID) -> List[Application]:
role = GetRoleByTokenUseCase().execute(token=token)

if role != UserRoles.ORGANIZER_ADMIN and role != UserRoles.ORGANIZER:
raise OnlyAuthorizedToOrganizer

event = GetEventUseCase().execute(event_id=event_id)

return self.application_repository.get_by_event(event_id=event.id)
2 changes: 2 additions & 0 deletions organizator_api/app/applications/infrastructure/http/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from app.applications.infrastructure.http.views import (
create_new_application,
get_applications_by_token,
get_applications_by_event,
)

urlpatterns = [
path("new", create_new_application),
path("myevents", get_applications_by_token),
path("participants/<uuid:event_id>", get_applications_by_event),
]
36 changes: 35 additions & 1 deletion organizator_api/app/applications/infrastructure/http/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
from app.applications.domain.usecases.create_new_application_use_case import (
CreateNewApplicationUseCase,
)
from app.applications.domain.usecases.get_applications_by_event_use_case import (
GetApplicationsByEventUseCase,
)
from app.applications.domain.usecases.get_applications_by_token_use_case import (
GetApplicationsByTokenUseCase,
)
from app.events.domain.exceptions import EventNotFound
from app.users.domain.exceptions import UserNotFound
from app.users.domain.exceptions import UserNotFound, OnlyAuthorizedToOrganizer


@require_http_methods(["POST"])
Expand Down Expand Up @@ -77,3 +80,34 @@ def get_applications_by_token(request: HttpRequest) -> HttpResponse:
)

return HttpResponse(status=200, content=json.dumps(applications_response))


@require_http_methods(["GET"])
def get_applications_by_event(
request: HttpRequest, event_id: uuid.UUID
) -> HttpResponse:
token = request.headers.get("Authorization")
if not token:
return HttpResponse(status=401, content="Unauthorized")

try:
token_to_uuid = uuid.UUID(token)
except ValueError:
return HttpResponse(status=400, content="Invalid token")

try:
applications = GetApplicationsByEventUseCase().execute(
token=token_to_uuid, event_id=event_id
)
except EventNotFound:
return HttpResponse(status=404, content="Event not found")
except OnlyAuthorizedToOrganizer:
return HttpResponse(status=401, content="Only authorized to organizer")

applications_response = []
for application in applications:
applications_response.append(
ApplicationResponse.from_application(application).to_dict_without_event()
)

return HttpResponse(status=200, content=json.dumps(applications_response))
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import uuid
from typing import List

from django.db import IntegrityError
Expand Down Expand Up @@ -38,6 +39,14 @@ def get_by_user(self, user: User) -> List[Application]:
for application in ORMEventApplication.objects.filter(user=user_orm)
]

def get_by_event(self, event_id: uuid.UUID) -> List[Application]:
event_orm = ORMEvent.objects.get(id=event_id)

return [
self._to_domain_model(application)
for application in ORMEventApplication.objects.filter(event=event_orm)
]

def _to_domain_model(self, orm_application: ORMEventApplication) -> Application:
return Application(
id=orm_application.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import uuid

from app.applications.domain.usecases.get_applications_by_event_use_case import (
GetApplicationsByEventUseCase,
)
from app.events.domain.exceptions import EventNotFound
from app.users.domain.exceptions import OnlyAuthorizedToOrganizer
from app.users.domain.models.user import UserRoles
from tests.api_tests import ApiTests
from tests.applications.domain.ApplicationFactory import ApplicationFactory
from tests.events.domain.EventFactory import EventFactory
from tests.users.domain.UserFactory import UserFactory


class TestGetApplicationsByEventUseCase(ApiTests):
def setUp(self) -> None:
super().setUp()
self.application_repository.clear()
self.user_repository.clear()
self.event_repository.clear()

self.user_token_participant = uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686")
self.user_token_organizer = uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00687")

self.user_participant = UserFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686"),
token=self.user_token_participant,
username="john",
email="john@test.com",
)
self.user_repository.create(self.user_participant)

self.user_organizer = UserFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00687"),
token=self.user_token_organizer,
username="jane",
email="jane@test.com",
role=UserRoles.ORGANIZER,
)
self.user_repository.create(self.user_organizer)

self.event_id = uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686")
self.event = EventFactory().create(new_id=self.event_id, name="HackUPC 2024")
self.event_repository.create(self.event)

def test__given_a_participant_token__when_get_application_by_event_id__then_only_authorized_to_organizer_is_raised(
self,
) -> None:
# When / Then
with self.assertRaises(OnlyAuthorizedToOrganizer):
GetApplicationsByEventUseCase().execute(
event_id=self.event_id, token=self.user_token_participant
)

def test__given_a_non_existing_event__when_get_application_by_event_id__then_event_not_found_is_raised(
self,
) -> None:
# When / Then
with self.assertRaises(EventNotFound):
GetApplicationsByEventUseCase().execute(
event_id=uuid.uuid4(), token=self.user_token_organizer
)

def test__given_applications_in_the_bd__when_get_applications_by_event_id__then_a_list_of_applications_is_returned(
self,
) -> None:
# Given
application1 = ApplicationFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686"),
user=self.user_participant,
event=self.event,
)
application2 = ApplicationFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00687"),
user=self.user_organizer,
event=self.event,
)
self.application_repository.create(application1)
self.application_repository.create(application2)

# When
applications = GetApplicationsByEventUseCase().execute(
event_id=self.event_id, token=self.user_token_organizer
)

# Then
self.assertEqual(len(applications), 2)
self.assertEqual(applications[0].id, application1.id)
self.assertEqual(applications[1].id, application2.id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import uuid
from datetime import datetime, timezone

from app.users.domain.models.user import UserRoles
from tests.api_tests import ApiTests
from tests.applications.domain.ApplicationFactory import ApplicationFactory
from tests.events.domain.EventFactory import EventFactory
from tests.users.domain.UserFactory import UserFactory


class TestViewGetParticipantsInEvents(ApiTests):
def setUp(self) -> None:
super().setUp()
self.application_repository.clear()
self.user_repository.clear()
self.event_repository.clear()

self.user_token_participant = "eb41b762-5988-4fa3-8942-7a91ccb00686"
self.user_participant = UserFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686"),
token=uuid.UUID(self.user_token_participant),
username="john",
email="john@test.com",
)
self.user_repository.create(self.user_participant)

self.user_token_organizer = "eb41b762-5988-4fa3-8942-7a91ccb00687"
user_organizer = UserFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00687"),
token=uuid.UUID(self.user_token_organizer),
username="jane",
email="jane@test.com",
role=UserRoles.ORGANIZER,
)
self.user_repository.create(user_organizer)

self.event_id = uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686")
self.event = EventFactory().create(new_id=self.event_id, name="HackUPC 2024")
self.event_repository.create(self.event)

def test__when_get_participants_in_events_without_header__then_unauthorized_is_returned(
self,
) -> None:
# When
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00686",
content_type="application/json",
)

# Then
self.assertEqual(response.status_code, 401)
self.assertEqual(response.content, b"Unauthorized")

def test__given_a_invalid_token__when_get_participants_in_events__then_invalid_token_is_returned(
self,
) -> None:
# When
headers = {"HTTP_Authorization": "invalid_token"}
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00686",
content_type="application/json",
**headers # type: ignore
)

# Then
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, b"Invalid token")

def test__given_a_non_existing_event_token__when_get_participants_in_events__then_event_not_found_is_returned(
self,
) -> None:
# Given
headers = {"HTTP_Authorization": self.user_token_organizer}

# When
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00685",
content_type="application/json",
**headers # type: ignore
)

# Then
self.assertEqual(response.content, b"Event not found")
self.assertEqual(response.status_code, 404)

def test__given_a_participant_user_and_a_existing_event__when_get_participants_in_events__then_only_authorized_to_organizers_is_returned(
self,
) -> None:
# Given
headers = {"HTTP_Authorization": self.user_token_participant}

# When
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00686",
content_type="application/json",
**headers # type: ignore
)

# Then
self.assertEqual(response.content, b"Only authorized to organizer")
self.assertEqual(response.status_code, 401)

def test__given_a_organizer_user_and_a_existing_event_without_participants__when_get_participants_in_events__then_empty_list_is_returned(
self,
) -> None:
# Given
headers = {"HTTP_Authorization": self.user_token_organizer}

# When
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00686",
content_type="application/json",
**headers # type: ignore
)

# Then
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"[]")

def test__given_a_organizer_user_and_a_existing_event_with_participants__when_get_participants_in_events__then_participants_list_is_returned(
self,
) -> None:
# Given
application1 = ApplicationFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00686"),
user=self.user_participant,
event=self.event,
created_at=datetime(2024, 1, 9, 10, 47, 0, tzinfo=timezone.utc),
updated_at=datetime(2024, 1, 9, 10, 47, 0, tzinfo=timezone.utc),
)
application2 = ApplicationFactory().create(
new_id=uuid.UUID("eb41b762-5988-4fa3-8942-7a91ccb00687"),
event=self.event,
created_at=datetime(2024, 1, 9, 10, 47, 0, tzinfo=timezone.utc),
updated_at=datetime(2024, 1, 9, 10, 47, 0, tzinfo=timezone.utc),
)
self.application_repository.create(application1)
self.application_repository.create(application2)

headers = {"HTTP_Authorization": self.user_token_organizer}

# When
response = self.client.get(
"/organizator-api/applications/participants/eb41b762-5988-4fa3-8942-7a91ccb00686",
content_type="application/json",
**headers # type: ignore
)

# Then
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.content,
b'[{"id": "eb41b762-5988-4fa3-8942-7a91ccb00686", "user": {"id": "eb41b762-5988-4fa3-8942-7a91ccb00686", "username": "john", "email": "john@test.com", "first_name": "Carlota", "last_name": "Catot", "bio": "The user that is using this application", "profile_image": "profile_picture.png", "role": "Participant", "date_of_birth": "07/05/1996", "study": true, "work": false, "university": "Universitat Polit\\u00e8cnica de Catalunya", "degree": "Computer Science", "expected_graduation": "01/05/2024", "current_job_role": "", "tshirt": "", "gender": "", "alimentary_restrictions": "", "github": "", "linkedin": "", "devpost": "", "webpage": ""}, "created_at": "2024-01-09T10:47:00Z", "updated_at": "2024-01-09T10:47:00Z"}, {"id": "eb41b762-5988-4fa3-8942-7a91ccb00687", "user": {"id": "ef6f6fb3-ba12-43dd-a0da-95de8125b1cc", "username": "carlotacb", "email": "carlota@hackupc.com", "first_name": "Carlota", "last_name": "Catot", "bio": "The user that is using this application", "profile_image": "profile_picture.png", "role": "Participant", "date_of_birth": "07/05/1996", "study": true, "work": false, "university": "Universitat Polit\\u00e8cnica de Catalunya", "degree": "Computer Science", "expected_graduation": "01/05/2024", "current_job_role": "", "tshirt": "", "gender": "", "alimentary_restrictions": "", "github": "", "linkedin": "", "devpost": "", "webpage": ""}, "created_at": "2024-01-09T10:47:00Z", "updated_at": "2024-01-09T10:47:00Z"}]',
)
Loading
Loading