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

User endpoint for updating user's preferred language #1379

Merged
merged 4 commits into from
Nov 14, 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
7 changes: 6 additions & 1 deletion tests/test_gdpr_api/test_gdpr_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from tests.helpers import patch_method
from tilavarauspalvelu.enums import OrderStatus, ReservationStateChoice
from utils.date_utils import DEFAULT_TIMEZONE
from utils.sentry import SentryLogger

from .helpers import get_gdpr_auth_header, patch_oidc_config
Expand Down Expand Up @@ -565,7 +566,11 @@ def test_delete_user_data__has_trailing_slash(api_client, settings):

def test_delete_user_data__dont_anonymize_if_open_payments(api_client, settings):
user = UserFactory.create(username="foo")
reservation = ReservationFactory.create(user=user)
reservation = ReservationFactory.create(
user=user,
begin=datetime.datetime(2020, 1, 1, 12, tzinfo=DEFAULT_TIMEZONE),
end=datetime.datetime(2020, 1, 1, 14, tzinfo=DEFAULT_TIMEZONE),
)
PaymentOrderFactory.create(reservation=reservation, status=OrderStatus.DRAFT, remote_id=uuid.uuid4())

settings.GDPR_API_DELETE_SCOPE = "gdprdelete"
Expand Down
5 changes: 4 additions & 1 deletion tests/test_graphql_api/test_user/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from functools import partial

from graphene_django_extensions.testing import build_query
from graphene_django_extensions.testing import build_mutation, build_query

current_user_query = partial(build_query, "currentUser")

UPDATE_MUTATION = build_mutation("updateCurrentUser", "CurrentUserUpdateMutation")
STAFF_UPDATE_MUTATION = build_mutation("updateStaffUser", "UserStaffUpdateMutation")
46 changes: 33 additions & 13 deletions tests/test_graphql_api/test_user/test_update.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,56 @@
import pytest
from graphene_django_extensions.testing import build_mutation

from tests.factories import UserFactory
from tilavarauspalvelu.enums import ReservationNotification
from tilavarauspalvelu.enums import Language

from .helpers import UPDATE_MUTATION

# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]


UPDATE_MUTATION = build_mutation("updateUser", "UserUpdateMutation")

def test_user__update__language(graphql):
user = UserFactory.create_superuser(preferred_language=Language.EN.value)

def test_user__update(graphql):
user = graphql.login_with_superuser()
data = {
"preferredLanguage": Language.FI.value.upper(),
}

data = {"pk": user.pk, "reservationNotification": ReservationNotification.NONE.value.upper()}
graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.has_errors is False
assert response.first_query_object["pk"] == user.pk

user.refresh_from_db()
assert user.reservation_notification == ReservationNotification.NONE.value
assert user.preferred_language == Language.FI.value


def test_user__update__language_not_available(graphql):
user = UserFactory.create_superuser(preferred_language=Language.EN.value)

data = {
"preferredLanguage": "UK",
}

graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.has_schema_errors is True, response


def test_user__update__pk_user_not_an_input(graphql):
user = UserFactory.create_superuser(preferred_language=Language.EN.value)
other_user = UserFactory.create_superuser(preferred_language=Language.EN.value)

def test_user__update__not_self(graphql):
user = UserFactory.create_superuser()
graphql.login_with_superuser()
data = {
"pk": other_user.pk,
"preferredLanguage": Language.FI.value.upper(),
}

data = {"pk": user.pk, "reservationNotification": ReservationNotification.NONE.value.upper()}
graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."
assert response.has_schema_errors
57 changes: 21 additions & 36 deletions tests/test_graphql_api/test_user/test_update_permissions.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,50 @@
import pytest
from graphene_django_extensions.testing import build_mutation

from tests.factories import UserFactory
from tilavarauspalvelu.enums import UserRoleChoice
from tilavarauspalvelu.enums import Language

from .helpers import UPDATE_MUTATION

# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]


UPDATE_MUTATION = build_mutation("updateUser", "UserUpdateMutation")

def test_user__update__language__superuser(graphql):
user = UserFactory.create_superuser()

def test_user__update__anonymous_user(graphql):
user = UserFactory.create()
data = {
"preferredLanguage": Language.FI.value.upper(),
}

data = {"pk": user.pk, "reservationNotification": "NONE"}
graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."
assert response.has_errors is False


def test_user__update__cannot_update_other_user(graphql):
def test_user__update__language__regular_user(graphql):
user = UserFactory.create()
graphql.login_with_superuser()

data = {"pk": user.pk, "reservationNotification": "NONE"}
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."

data = {
"preferredLanguage": Language.FI.value.upper(),
}

def test_user__update__regular_user(graphql):
user = graphql.login_with_regular_user()

data = {"pk": user.pk, "reservationNotification": "NONE"}
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."


def test_user__update__admin_user(graphql):
user = graphql.login_user_with_role(role=UserRoleChoice.ADMIN)

data = {"pk": user.pk, "reservationNotification": "NONE"}
graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.has_errors is False

user.refresh_from_db()
assert user.reservation_notification == "none"

def test_user__update__language__admin(graphql):
user = UserFactory.create_with_general_role()

def test_user__update__superuser(graphql):
user = graphql.login_with_superuser()
data = {
"preferredLanguage": Language.FI.value.upper(),
}

data = {"pk": user.pk, "reservationNotification": "NONE"}
graphql.force_login(user)
response = graphql(UPDATE_MUTATION, input_data=data)

assert response.has_errors is False

user.refresh_from_db()
assert user.reservation_notification == "none"
29 changes: 29 additions & 0 deletions tests/test_graphql_api/test_user/test_update_staff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest

from tests.factories import UserFactory
from tilavarauspalvelu.enums import ReservationNotification

from .helpers import STAFF_UPDATE_MUTATION

# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]


def test_user__update_staff(graphql):
user = UserFactory.create_superuser(reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED)

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}

graphql.force_login(user)
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.has_errors is False
assert response.first_query_object["pk"] == user.pk

user.refresh_from_db()
assert user.reservation_notification == ReservationNotification.NONE
84 changes: 84 additions & 0 deletions tests/test_graphql_api/test_user/test_update_staff_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import pytest

from tests.factories import UserFactory
from tilavarauspalvelu.enums import ReservationNotification, UserRoleChoice

from .helpers import STAFF_UPDATE_MUTATION

# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]


def test_user__update__superuser(graphql):
user = UserFactory.create_superuser(reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED)
graphql.force_login(user)

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.has_errors is False

user.refresh_from_db()
assert user.reservation_notification == ReservationNotification.NONE


def test_user__update__anonymous_user(graphql):
user = UserFactory.create(reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED)

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."


def test_user__update__cannot_update_other_user(graphql):
user = UserFactory.create_superuser(reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED)
graphql.login_with_regular_user()

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."


def test_user__update__regular_user(graphql):
user = UserFactory.create(reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED)
graphql.force_login(user)

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."


def test_user__update__admin_user(graphql):
user = UserFactory.create_with_general_role(
role=UserRoleChoice.ADMIN,
reservation_notification=ReservationNotification.ONLY_HANDLING_REQUIRED,
)
graphql.force_login(user)

data = {
"pk": user.pk,
"reservationNotification": ReservationNotification.NONE.value.upper(),
}
response = graphql(STAFF_UPDATE_MUTATION, input_data=data)

assert response.has_errors is False

user.refresh_from_db()
assert user.reservation_notification == ReservationNotification.NONE
5 changes: 3 additions & 2 deletions tilavarauspalvelu/api/graphql/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
from .types.resource.mutations import ResourceCreateMutation, ResourceDeleteMutation, ResourceUpdateMutation
from .types.space.mutations import SpaceCreateMutation, SpaceDeleteMutation, SpaceUpdateMutation
from .types.unit.mutations import UnitUpdateMutation
from .types.user.mutations import UserUpdateMutation
from .types.user.mutations import CurrentUserUpdateMutation, UserStaffUpdateMutation

__all__ = [
"AllocatedTimeSlotCreateMutation",
Expand All @@ -91,6 +91,7 @@
"BannerNotificationCreateMutation",
"BannerNotificationDeleteMutation",
"BannerNotificationUpdateMutation",
"CurrentUserUpdateMutation",
"EquipmentCategoryCreateMutation",
"EquipmentCategoryDeleteMutation",
"EquipmentCategoryUpdateMutation",
Expand Down Expand Up @@ -137,5 +138,5 @@
"SpaceDeleteMutation",
"SpaceUpdateMutation",
"UnitUpdateMutation",
"UserUpdateMutation",
"UserStaffUpdateMutation",
]
6 changes: 4 additions & 2 deletions tilavarauspalvelu/api/graphql/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
BannerNotificationCreateMutation,
BannerNotificationDeleteMutation,
BannerNotificationUpdateMutation,
CurrentUserUpdateMutation,
EquipmentCategoryCreateMutation,
EquipmentCategoryDeleteMutation,
EquipmentCategoryUpdateMutation,
Expand Down Expand Up @@ -77,7 +78,7 @@
SpaceDeleteMutation,
SpaceUpdateMutation,
UnitUpdateMutation,
UserUpdateMutation,
UserStaffUpdateMutation,
)
from .queries import (
AgeGroupNode,
Expand Down Expand Up @@ -355,7 +356,8 @@ class Mutation(graphene.ObjectType):
refresh_order = RefreshOrderMutation.Field()
#
# User
update_user = UserUpdateMutation.Field()
update_current_user = CurrentUserUpdateMutation.Field()
update_staff_user = UserStaffUpdateMutation.Field()
#
# Misc.
create_banner_notification = BannerNotificationCreateMutation.Field()
Expand Down
35 changes: 31 additions & 4 deletions tilavarauspalvelu/api/graphql/types/user/mutations.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
from typing import TYPE_CHECKING, Any, Self

from graphene_django_extensions import UpdateMutation
from graphene_django_extensions.bases import DjangoMutation

from tilavarauspalvelu.typing import GQLInfo

from .permissions import UserPermission, UserStaffPermission
from .serializers import CurrentUserUpdateSerializer, UserStaffUpdateSerializer

if TYPE_CHECKING:
from tilavarauspalvelu.models import User

from .permissions import UserPermission
from .serializers import UserUpdateSerializer
__all__ = [
"CurrentUserUpdateMutation",
"UserStaffUpdateMutation",
]


class UserUpdateMutation(UpdateMutation):
class CurrentUserUpdateMutation(DjangoMutation):
class Meta:
serializer_class = UserUpdateSerializer
serializer_class = CurrentUserUpdateSerializer
permission_classes = [UserPermission]

@classmethod
def custom_mutation(cls, info: GQLInfo, input_data: dict[str, Any]) -> Self:
user: User = info.context.user
user.preferred_language = input_data["preferred_language"]
user.save(update_fields=["preferred_language"])
output = cls.get_serializer_output(user)
return cls(**output)


class UserStaffUpdateMutation(UpdateMutation):
class Meta:
serializer_class = UserStaffUpdateSerializer
permission_classes = [UserStaffPermission]
Loading