This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
people/src/backend/core/api/serializers.py

169 lines
5.0 KiB
Python
Raw Normal View History

"""Client serializers for the People core app."""
from rest_framework import exceptions, serializers
from timezone_field.rest_framework import TimeZoneSerializerField
from core import models
class ContactSerializer(serializers.ModelSerializer):
"""Serialize contacts."""
class Meta:
model = models.Contact
fields = [
"id",
"base",
"data",
"full_name",
"owner",
"short_name",
]
read_only_fields = ["id", "owner"]
def update(self, instance, validated_data):
"""Make "base" field readonly but only for update/patch."""
validated_data.pop("base", None)
return super().update(instance, validated_data)
class UserSerializer(serializers.ModelSerializer):
"""Serialize users."""
timezone = TimeZoneSerializerField(use_pytz=False, required=True)
class Meta:
model = models.User
fields = [
"id",
✨(api) search users by email (#16) * ✨(api) search users by email The front end should be able to search users by email. To that goal, we added a list method to the users viewset thus creating the /users/ endpoint. Results are filtered based on similarity with the query, based on what preexisted for the /contacts/ endpoint. * ✅(api) test list users by email Test search when complete, partial query, accentuated and capital. Also, lower similarity threshold for user search by email as it was too high for some tests to pass. * 💡(api) improve documentation and test comments Improve user viewset documentation and comments describing tests sections Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com> Co-authored-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr> * 🛂(api) set isAuthenticated as base requirements Instead of checking permissions or adding decorators to every viewset, isAuthenticated is set as base requirement. * 🛂(api) define throttle limits in settings Use of Djando Rest Framework's throttle options, now set globally to avoid duplicate code. * 🩹(api) add email to user serializer email field added to serializer. Tests modified accordingly. I added the email field as "read only" to pass tests, but we need to discuss that point in review. * 🧱(api) move search logic to queryset User viewset "list" method was overridden to allow search by email. This removed the pagination. Instead of manually re-adding pagination at the end of this method, I moved the search/filter logic to get_queryset, to leave DRF handle pagination. * ✅(api) test throttle protection Test that throttle protection succesfully blocks too many requests. * 📝(tests) improve tests comment Fix typos on comments and clarify which setting are tested on test_throttle test (setting import required disabling pylint false positive error) Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com> --------- Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com> Co-authored-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
2024-01-29 10:14:17 +01:00
"email",
"language",
"timezone",
"is_device",
"is_staff",
]
read_only_fields = ["id", "email", "is_device", "is_staff"]
class TeamAccessSerializer(serializers.ModelSerializer):
"""Serialize team accesses."""
abilities = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.TeamAccess
fields = ["id", "user", "role", "abilities"]
read_only_fields = ["id", "abilities"]
def update(self, instance, validated_data):
"""Make "user" field is readonly but only on update."""
validated_data.pop("user", None)
return super().update(instance, validated_data)
def get_abilities(self, access) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return access.get_abilities(request.user)
return {}
def validate(self, attrs):
"""
Check access rights specific to writing (create/update)
"""
request = self.context.get("request")
user = getattr(request, "user", None)
role = attrs.get("role")
# Update
if self.instance:
can_set_role_to = self.instance.get_abilities(user)["set_role_to"]
if role and role not in can_set_role_to:
message = (
f"You are only allowed to set role to {', '.join(can_set_role_to)}"
if can_set_role_to
else "You are not allowed to set this role for this team."
)
raise exceptions.PermissionDenied(message)
# Create
else:
try:
team_id = self.context["team_id"]
except KeyError as exc:
raise exceptions.ValidationError(
"You must set a team ID in kwargs to create a new team access."
) from exc
if not models.TeamAccess.objects.filter(
team=team_id,
user=user,
role__in=[models.RoleChoices.OWNER, models.RoleChoices.ADMIN],
).exists():
raise exceptions.PermissionDenied(
"You are not allowed to manage accesses for this team."
)
if (
role == models.RoleChoices.OWNER
and not models.TeamAccess.objects.filter(
team=team_id,
user=user,
role=models.RoleChoices.OWNER,
).exists()
):
raise exceptions.PermissionDenied(
"Only owners of a team can assign other users as owners."
)
attrs["team_id"] = self.context["team_id"]
return attrs
class TeamSerializer(serializers.ModelSerializer):
"""Serialize teams."""
abilities = serializers.SerializerMethodField(read_only=True)
accesses = TeamAccessSerializer(many=True, read_only=True)
slug = serializers.SerializerMethodField()
class Meta:
model = models.Team
fields = [
"id",
"name",
"accesses",
"abilities",
"slug",
"created_at",
"updated_at",
]
read_only_fields = [
"id",
"accesses",
"abilities",
"slug",
"created_at",
"updated_at",
]
def get_abilities(self, team) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return team.get_abilities(request.user)
return {}
def get_slug(self, instance):
"""Return slug from the team's name."""
return instance.get_slug()
class InvitationSerializer(serializers.ModelSerializer):
"""Serialize invitations."""
class Meta:
model = models.Invitation
fields = ["email", "team", "role", "issuer"]
read_only_fields = ["team", "issuer"]