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