(contacts) add "abilities" to API endpoint data

Returns the possible actions to the frontend using the
model's `get_abilities`.
This commit is contained in:
Quentin BEY
2024-12-06 11:57:58 +01:00
committed by BEY Quentin
parent 7154a491f4
commit 6fde76fb46
8 changed files with 37 additions and 14 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to
### Added
- ✨(contacts) add "abilities" to API endpoint data #585
- ✨(contacts) allow filter list API with email
- ✨(contacts) return profile contact from same organization
- ✨(dimail) automate allows requests to dimail

View File

@@ -10,10 +10,13 @@ from core.models import ServiceProvider
class ContactSerializer(serializers.ModelSerializer):
"""Serialize contacts."""
abilities = serializers.SerializerMethodField()
class Meta:
model = models.Contact
fields = [
"id",
"abilities",
"override",
"data",
"full_name",
@@ -21,7 +24,7 @@ class ContactSerializer(serializers.ModelSerializer):
"owner",
"short_name",
]
read_only_fields = ["id", "owner"]
read_only_fields = ["id", "owner", "abilities"]
extra_kwargs = {
"override": {"required": False},
}
@@ -31,6 +34,13 @@ class ContactSerializer(serializers.ModelSerializer):
validated_data.pop("override", None)
return super().update(instance, validated_data)
def get_abilities(self, contact) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return contact.get_abilities(request.user)
return {}
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""

View File

@@ -137,7 +137,7 @@ class ContactViewSet(
"""Contact ViewSet"""
permission_classes = [permissions.AccessPermission]
queryset = models.Contact.objects.all()
queryset = models.Contact.objects.select_related("user").all()
serializer_class = serializers.ContactSerializer
throttle_classes = [BurstRateThrottle, SustainedRateThrottle]
ordering_fields = ["full_name", "short_name", "created_at"]

View File

@@ -196,8 +196,8 @@ class Contact(BaseModel):
For now, we still consider here, a contact without owner is "public"
so we allow access to it.
"""
is_owner = user == self.owner
is_profile_member_or_same_organization = bool(self.user) and (
is_owner = user.pk == self.owner_id
is_profile_member_or_same_organization = bool(self.user_id) and (
self.user.organization_id == user.organization_id
)

View File

@@ -86,6 +86,7 @@ def test_api_contacts_create_authenticated_missing_base():
assert response.json() == {
"override": None,
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
"data": {},
"full_name": "David Bowman",
"id": str(new_contact.pk),
@@ -123,6 +124,7 @@ def test_api_contacts_create_authenticated_successful():
contact = models.Contact.objects.get(owner=user)
assert response.json() == {
"id": str(contact.id),
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
"override": str(base_contact.id),
"data": CONTACT_DATA,
"full_name": "David Bowman",
@@ -195,6 +197,7 @@ def test_api_contacts_create_authenticated_successful_with_notes():
contact = models.Contact.objects.get(owner=user)
assert response.json() == {
"id": str(contact.id),
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
"override": None,
"data": CONTACT_DATA,
"full_name": "David Bowman",

View File

@@ -22,7 +22,7 @@ def test_api_contacts_list_anonymous():
}
def test_api_contacts_list_authenticated_no_query():
def test_api_contacts_list_authenticated_no_query(django_assert_num_queries):
"""
Authenticated users should be able to list contacts without applying a query.
Profile and overridden contacts should be excluded.
@@ -57,12 +57,14 @@ def test_api_contacts_list_authenticated_no_query():
client = APIClient()
client.force_login(user)
response = client.get("/api/v1.0/contacts/")
with django_assert_num_queries(2):
response = client.get("/api/v1.0/contacts/")
assert response.status_code == 200
assert response.json() == [
{
"id": str(user_profile_contact.pk),
"abilities": {"delete": False, "get": True, "patch": True, "put": True},
"override": None,
"owner": str(user.pk),
"data": user_profile_contact.data,
@@ -72,6 +74,7 @@ def test_api_contacts_list_authenticated_no_query():
},
{
"id": str(profile_contact.pk),
"abilities": {"delete": False, "get": True, "patch": False, "put": False},
"override": None,
"owner": str(profile_contact.user.pk),
"data": profile_contact.data,
@@ -81,6 +84,7 @@ def test_api_contacts_list_authenticated_no_query():
},
{
"id": str(override_contact.pk),
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
"override": str(overriden_contact.pk),
"owner": str(user.pk),
"data": override_contact.data,

View File

@@ -22,7 +22,7 @@ def test_api_contacts_retrieve_anonymous():
}
def test_api_contacts_retrieve_authenticated_owned():
def test_api_contacts_retrieve_authenticated_owned(django_assert_num_queries):
"""
Authenticated users should be allowed to retrieve a contact they own.
"""
@@ -32,11 +32,13 @@ def test_api_contacts_retrieve_authenticated_owned():
client = APIClient()
client.force_login(user)
response = client.get(f"/api/v1.0/contacts/{contact.id!s}/")
with django_assert_num_queries(2):
response = client.get(f"/api/v1.0/contacts/{contact.id!s}/")
assert response.status_code == 200
assert response.json() == {
"id": str(contact.id),
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
"override": None,
"owner": str(contact.owner.id),
"data": contact.data,
@@ -60,6 +62,7 @@ def test_api_contacts_retrieve_authenticated_public():
assert response.status_code == 200
assert response.json() == {
"id": str(contact.id),
"abilities": {"delete": False, "get": True, "patch": False, "put": False},
"override": None,
"owner": None,
"data": contact.data,

View File

@@ -35,7 +35,7 @@ def test_api_contacts_update_anonymous():
assert contact_values == old_contact_values
def test_api_contacts_update_authenticated_owned():
def test_api_contacts_update_authenticated_owned(django_assert_num_queries):
"""
Authenticated users should be allowed to update their own contacts.
"""
@@ -52,11 +52,13 @@ def test_api_contacts_update_authenticated_owned():
).data
new_contact_values["override"] = str(factories.ContactFactory().id)
response = client.put(
f"/api/v1.0/contacts/{contact.id!s}/",
new_contact_values,
format="json",
)
with django_assert_num_queries(8):
# user, 2x contact, user, 3x check, update contact
response = client.put(
f"/api/v1.0/contacts/{contact.id!s}/",
new_contact_values,
format="json",
)
assert response.status_code == 200