(user) add organization data on users API

This will allow the frontend to display data about
organizations when displaying a user or a list of
users.
This commit is contained in:
Quentin BEY
2024-11-22 14:18:50 +01:00
committed by BEY Quentin
parent 5692c50f21
commit edbd1f0061
5 changed files with 119 additions and 14 deletions

View File

@@ -14,6 +14,7 @@ and this project adheres to
- ✨(mailbox) allow to disable mailbox - ✨(mailbox) allow to disable mailbox
- ✨(backend) add ServiceProvider #522 - ✨(backend) add ServiceProvider #522
- 💄(admin) allow header color customization #552 - 💄(admin) allow header color customization #552
- ✨(organization) add API endpoints #551
### Fixed ### Fixed

View File

@@ -69,12 +69,22 @@ class OrganizationSerializer(serializers.ModelSerializer):
return {} return {}
class UserOrganizationSerializer(serializers.ModelSerializer):
"""Serialize organizations for users."""
class Meta:
model = models.Organization
fields = ["id", "name"]
read_only_fields = ["id", "name"]
class UserSerializer(DynamicFieldsModelSerializer): class UserSerializer(DynamicFieldsModelSerializer):
"""Serialize users.""" """Serialize users."""
timezone = TimeZoneSerializerField(use_pytz=False, required=True) timezone = TimeZoneSerializerField(use_pytz=False, required=True)
email = serializers.ReadOnlyField() email = serializers.ReadOnlyField()
name = serializers.ReadOnlyField() name = serializers.ReadOnlyField()
organization = UserOrganizationSerializer(read_only=True)
class Meta: class Meta:
model = models.User model = models.User
@@ -83,6 +93,7 @@ class UserSerializer(DynamicFieldsModelSerializer):
"email", "email",
"language", "language",
"name", "name",
"organization",
"timezone", "timezone",
"is_device", "is_device",
"is_staff", "is_staff",
@@ -98,6 +109,7 @@ class UserMeSerializer(UserSerializer):
""" """
abilities = serializers.SerializerMethodField() abilities = serializers.SerializerMethodField()
organization = UserOrganizationSerializer(read_only=True)
class Meta: class Meta:
model = models.User model = models.User
@@ -108,6 +120,7 @@ class UserMeSerializer(UserSerializer):
"is_staff", "is_staff",
"language", "language",
"name", "name",
"organization",
"timezone", "timezone",
# added fields # added fields
"abilities", "abilities",

View File

@@ -225,7 +225,9 @@ class UserViewSet(
""" """
permission_classes = [permissions.IsSelf] permission_classes = [permissions.IsSelf]
queryset = models.User.objects.all().order_by("-created_at") queryset = (
models.User.objects.select_related("organization").all().order_by("-created_at")
)
serializer_class = serializers.UserSerializer serializer_class = serializers.UserSerializer
get_me_serializer_class = serializers.UserMeSerializer get_me_serializer_class = serializers.UserMeSerializer
throttle_classes = [BurstRateThrottle, SustainedRateThrottle] throttle_classes = [BurstRateThrottle, SustainedRateThrottle]

View File

@@ -359,14 +359,8 @@ class Organization(BaseModel):
""" """
Compute and return abilities for a given user on the organization. Compute and return abilities for a given user on the organization.
""" """
try: # Use the role from queryset annotation will raise on purpose if not used properly
# Use the role from queryset annotation if available is_admin = self.user_role == OrganizationRoleChoices.ADMIN # pylint: disable=no-member
is_admin = self.user_role == OrganizationRoleChoices.ADMIN
except AttributeError:
is_admin = self.organization_accesses.filter(
user=user,
role=OrganizationRoleChoices.ADMIN,
).exists()
return { return {
"get": user.organization_id == self.pk, "get": user.organization_id == self.pk,

View File

@@ -51,6 +51,73 @@ def test_api_users_list_authenticated():
assert len(response.json()["results"]) == 3 assert len(response.json()["results"]) == 3
def test_api_users_list_authenticated_response_content(
client, django_assert_num_queries
):
"""
Authenticated users should be able to list all users with the expected output.
"""
user_organization = factories.OrganizationFactory(
with_registration_id=True, name="HAL 9000"
)
user = factories.UserFactory(
organization=user_organization,
email="kylefields@example.net",
name="Mr. Christopher Curtis",
language="en-us",
)
client.force_login(user)
other_user_organization = factories.OrganizationFactory(
with_registration_id=True, name="Corp. Inc."
)
other_user = factories.UserFactory(
organization=other_user_organization,
email="sara83@example.com",
name="Christopher Thompson",
language="fr-fr",
)
with django_assert_num_queries(3): # get User, Count, Users
response = client.get("/api/v1.0/users/")
assert response.status_code == HTTP_200_OK
assert response.json() == {
"count": 2,
"next": None,
"previous": None,
"results": [
{
"email": "sara83@example.com",
"id": str(other_user.pk),
"is_device": False,
"is_staff": False,
"language": "fr-fr",
"name": "Christopher Thompson",
"organization": {
"id": str(other_user.organization.pk),
"name": "Corp. Inc.",
},
"timezone": "UTC",
},
{
"email": "kylefields@example.net",
"id": str(user.pk),
"is_device": False,
"is_staff": False,
"language": "en-us",
"name": "Mr. Christopher Curtis",
"organization": {
"id": str(user.organization.pk),
"name": "HAL 9000",
},
"timezone": "UTC",
},
],
}
def test_api_users_authenticated_list_by_email(): def test_api_users_authenticated_list_by_email():
""" """
Authenticated users should be able to search users with a case-insensitive and Authenticated users should be able to search users with a case-insensitive and
@@ -58,8 +125,12 @@ def test_api_users_authenticated_list_by_email():
""" """
user = factories.UserFactory(email="tester@ministry.fr", name="john doe") user = factories.UserFactory(email="tester@ministry.fr", name="john doe")
dave = factories.UserFactory(email="david.bowman@work.com", name=None) dave = factories.UserFactory(email="david.bowman@work.com", name=None)
nicole = factories.UserFactory(email="nicole_foole@work.com", name=None) nicole = factories.UserFactory(
frank = factories.UserFactory(email="frank_poole@work.com", name=None) email="nicole_foole@work.com", name=None, with_organization=True
)
frank = factories.UserFactory(
email="frank_poole@work.com", name=None, with_organization=True
)
factories.UserFactory(email="heywood_floyd@work.com", name=None) factories.UserFactory(email="heywood_floyd@work.com", name=None)
client = APIClient() client = APIClient()
@@ -99,6 +170,10 @@ def test_api_users_authenticated_list_by_email():
"is_staff": frank.is_staff, "is_staff": frank.is_staff,
"language": frank.language, "language": frank.language,
"timezone": str(frank.timezone), "timezone": str(frank.timezone),
"organization": {
"id": str(frank.organization.pk),
"name": frank.organization.name,
},
}, },
{ {
"id": str(nicole.id), "id": str(nicole.id),
@@ -108,6 +183,10 @@ def test_api_users_authenticated_list_by_email():
"is_staff": nicole.is_staff, "is_staff": nicole.is_staff,
"language": nicole.language, "language": nicole.language,
"timezone": str(nicole.timezone), "timezone": str(nicole.timezone),
"organization": {
"id": str(nicole.organization.pk),
"name": nicole.organization.name,
},
}, },
] ]
@@ -119,8 +198,12 @@ def test_api_users_authenticated_list_by_name():
""" """
user = factories.UserFactory(email="tester@ministry.fr", name="john doe") user = factories.UserFactory(email="tester@ministry.fr", name="john doe")
dave = factories.UserFactory(name="Dave bowman", email=None) dave = factories.UserFactory(name="Dave bowman", email=None)
nicole = factories.UserFactory(name="nicole foole", email=None) nicole = factories.UserFactory(
frank = factories.UserFactory(name="frank poolé", email=None) name="nicole foole", email=None, with_organization=True
)
frank = factories.UserFactory(
name="frank poolé", email=None, with_organization=True
)
factories.UserFactory(name="heywood floyd", email=None) factories.UserFactory(name="heywood floyd", email=None)
client = APIClient() client = APIClient()
@@ -160,6 +243,10 @@ def test_api_users_authenticated_list_by_name():
"is_staff": frank.is_staff, "is_staff": frank.is_staff,
"language": frank.language, "language": frank.language,
"timezone": str(frank.timezone), "timezone": str(frank.timezone),
"organization": {
"id": str(frank.organization.pk),
"name": frank.organization.name,
},
}, },
{ {
"id": str(nicole.id), "id": str(nicole.id),
@@ -169,6 +256,10 @@ def test_api_users_authenticated_list_by_name():
"is_staff": nicole.is_staff, "is_staff": nicole.is_staff,
"language": nicole.language, "language": nicole.language,
"timezone": str(nicole.timezone), "timezone": str(nicole.timezone),
"organization": {
"id": str(nicole.organization.pk),
"name": nicole.organization.name,
},
}, },
] ]
@@ -437,7 +528,7 @@ def test_api_users_retrieve_me_anonymous():
def test_api_users_retrieve_me_authenticated(): def test_api_users_retrieve_me_authenticated():
"""Authenticated users should be able to retrieve their own user via the "/users/me" path.""" """Authenticated users should be able to retrieve their own user via the "/users/me" path."""
user = factories.UserFactory() user = factories.UserFactory(with_organization=True)
client = APIClient() client = APIClient()
client.force_login(user) client.force_login(user)
@@ -466,6 +557,10 @@ def test_api_users_retrieve_me_authenticated():
"mailboxes": {"can_create": False, "can_view": False}, "mailboxes": {"can_create": False, "can_view": False},
"teams": {"can_create": False, "can_view": False}, "teams": {"can_create": False, "can_view": False},
}, },
"organization": {
"id": str(user.organization.pk),
"name": user.organization.name,
},
} }