(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
- ✨(backend) add ServiceProvider #522
- 💄(admin) allow header color customization #552
- ✨(organization) add API endpoints #551
### Fixed

View File

@@ -69,12 +69,22 @@ class OrganizationSerializer(serializers.ModelSerializer):
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):
"""Serialize users."""
timezone = TimeZoneSerializerField(use_pytz=False, required=True)
email = serializers.ReadOnlyField()
name = serializers.ReadOnlyField()
organization = UserOrganizationSerializer(read_only=True)
class Meta:
model = models.User
@@ -83,6 +93,7 @@ class UserSerializer(DynamicFieldsModelSerializer):
"email",
"language",
"name",
"organization",
"timezone",
"is_device",
"is_staff",
@@ -98,6 +109,7 @@ class UserMeSerializer(UserSerializer):
"""
abilities = serializers.SerializerMethodField()
organization = UserOrganizationSerializer(read_only=True)
class Meta:
model = models.User
@@ -108,6 +120,7 @@ class UserMeSerializer(UserSerializer):
"is_staff",
"language",
"name",
"organization",
"timezone",
# added fields
"abilities",

View File

@@ -225,7 +225,9 @@ class UserViewSet(
"""
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
get_me_serializer_class = serializers.UserMeSerializer
throttle_classes = [BurstRateThrottle, SustainedRateThrottle]

View File

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

View File

@@ -51,6 +51,73 @@ def test_api_users_list_authenticated():
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():
"""
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")
dave = factories.UserFactory(email="david.bowman@work.com", name=None)
nicole = factories.UserFactory(email="nicole_foole@work.com", name=None)
frank = factories.UserFactory(email="frank_poole@work.com", name=None)
nicole = factories.UserFactory(
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)
client = APIClient()
@@ -99,6 +170,10 @@ def test_api_users_authenticated_list_by_email():
"is_staff": frank.is_staff,
"language": frank.language,
"timezone": str(frank.timezone),
"organization": {
"id": str(frank.organization.pk),
"name": frank.organization.name,
},
},
{
"id": str(nicole.id),
@@ -108,6 +183,10 @@ def test_api_users_authenticated_list_by_email():
"is_staff": nicole.is_staff,
"language": nicole.language,
"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")
dave = factories.UserFactory(name="Dave bowman", email=None)
nicole = factories.UserFactory(name="nicole foole", email=None)
frank = factories.UserFactory(name="frank poolé", email=None)
nicole = factories.UserFactory(
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)
client = APIClient()
@@ -160,6 +243,10 @@ def test_api_users_authenticated_list_by_name():
"is_staff": frank.is_staff,
"language": frank.language,
"timezone": str(frank.timezone),
"organization": {
"id": str(frank.organization.pk),
"name": frank.organization.name,
},
},
{
"id": str(nicole.id),
@@ -169,6 +256,10 @@ def test_api_users_authenticated_list_by_name():
"is_staff": nicole.is_staff,
"language": nicole.language,
"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():
"""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.force_login(user)
@@ -466,6 +557,10 @@ def test_api_users_retrieve_me_authenticated():
"mailboxes": {"can_create": False, "can_view": False},
"teams": {"can_create": False, "can_view": False},
},
"organization": {
"id": str(user.organization.pk),
"name": user.organization.name,
},
}