✨(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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user