✨(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
|
### Added
|
||||||
|
|
||||||
|
- ✨(contacts) add "abilities" to API endpoint data #585
|
||||||
- ✨(contacts) allow filter list API with email
|
- ✨(contacts) allow filter list API with email
|
||||||
- ✨(contacts) return profile contact from same organization
|
- ✨(contacts) return profile contact from same organization
|
||||||
- ✨(dimail) automate allows requests to dimail
|
- ✨(dimail) automate allows requests to dimail
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ from core.models import ServiceProvider
|
|||||||
class ContactSerializer(serializers.ModelSerializer):
|
class ContactSerializer(serializers.ModelSerializer):
|
||||||
"""Serialize contacts."""
|
"""Serialize contacts."""
|
||||||
|
|
||||||
|
abilities = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Contact
|
model = models.Contact
|
||||||
fields = [
|
fields = [
|
||||||
"id",
|
"id",
|
||||||
|
"abilities",
|
||||||
"override",
|
"override",
|
||||||
"data",
|
"data",
|
||||||
"full_name",
|
"full_name",
|
||||||
@@ -21,7 +24,7 @@ class ContactSerializer(serializers.ModelSerializer):
|
|||||||
"owner",
|
"owner",
|
||||||
"short_name",
|
"short_name",
|
||||||
]
|
]
|
||||||
read_only_fields = ["id", "owner"]
|
read_only_fields = ["id", "owner", "abilities"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"override": {"required": False},
|
"override": {"required": False},
|
||||||
}
|
}
|
||||||
@@ -31,6 +34,13 @@ class ContactSerializer(serializers.ModelSerializer):
|
|||||||
validated_data.pop("override", None)
|
validated_data.pop("override", None)
|
||||||
return super().update(instance, validated_data)
|
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):
|
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class ContactViewSet(
|
|||||||
"""Contact ViewSet"""
|
"""Contact ViewSet"""
|
||||||
|
|
||||||
permission_classes = [permissions.AccessPermission]
|
permission_classes = [permissions.AccessPermission]
|
||||||
queryset = models.Contact.objects.all()
|
queryset = models.Contact.objects.select_related("user").all()
|
||||||
serializer_class = serializers.ContactSerializer
|
serializer_class = serializers.ContactSerializer
|
||||||
throttle_classes = [BurstRateThrottle, SustainedRateThrottle]
|
throttle_classes = [BurstRateThrottle, SustainedRateThrottle]
|
||||||
ordering_fields = ["full_name", "short_name", "created_at"]
|
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"
|
For now, we still consider here, a contact without owner is "public"
|
||||||
so we allow access to it.
|
so we allow access to it.
|
||||||
"""
|
"""
|
||||||
is_owner = user == self.owner
|
is_owner = user.pk == self.owner_id
|
||||||
is_profile_member_or_same_organization = bool(self.user) and (
|
is_profile_member_or_same_organization = bool(self.user_id) and (
|
||||||
self.user.organization_id == user.organization_id
|
self.user.organization_id == user.organization_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ def test_api_contacts_create_authenticated_missing_base():
|
|||||||
|
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"override": None,
|
"override": None,
|
||||||
|
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
|
||||||
"data": {},
|
"data": {},
|
||||||
"full_name": "David Bowman",
|
"full_name": "David Bowman",
|
||||||
"id": str(new_contact.pk),
|
"id": str(new_contact.pk),
|
||||||
@@ -123,6 +124,7 @@ def test_api_contacts_create_authenticated_successful():
|
|||||||
contact = models.Contact.objects.get(owner=user)
|
contact = models.Contact.objects.get(owner=user)
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"id": str(contact.id),
|
"id": str(contact.id),
|
||||||
|
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
|
||||||
"override": str(base_contact.id),
|
"override": str(base_contact.id),
|
||||||
"data": CONTACT_DATA,
|
"data": CONTACT_DATA,
|
||||||
"full_name": "David Bowman",
|
"full_name": "David Bowman",
|
||||||
@@ -195,6 +197,7 @@ def test_api_contacts_create_authenticated_successful_with_notes():
|
|||||||
contact = models.Contact.objects.get(owner=user)
|
contact = models.Contact.objects.get(owner=user)
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"id": str(contact.id),
|
"id": str(contact.id),
|
||||||
|
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
|
||||||
"override": None,
|
"override": None,
|
||||||
"data": CONTACT_DATA,
|
"data": CONTACT_DATA,
|
||||||
"full_name": "David Bowman",
|
"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.
|
Authenticated users should be able to list contacts without applying a query.
|
||||||
Profile and overridden contacts should be excluded.
|
Profile and overridden contacts should be excluded.
|
||||||
@@ -57,12 +57,14 @@ def test_api_contacts_list_authenticated_no_query():
|
|||||||
client = APIClient()
|
client = APIClient()
|
||||||
client.force_login(user)
|
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.status_code == 200
|
||||||
assert response.json() == [
|
assert response.json() == [
|
||||||
{
|
{
|
||||||
"id": str(user_profile_contact.pk),
|
"id": str(user_profile_contact.pk),
|
||||||
|
"abilities": {"delete": False, "get": True, "patch": True, "put": True},
|
||||||
"override": None,
|
"override": None,
|
||||||
"owner": str(user.pk),
|
"owner": str(user.pk),
|
||||||
"data": user_profile_contact.data,
|
"data": user_profile_contact.data,
|
||||||
@@ -72,6 +74,7 @@ def test_api_contacts_list_authenticated_no_query():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": str(profile_contact.pk),
|
"id": str(profile_contact.pk),
|
||||||
|
"abilities": {"delete": False, "get": True, "patch": False, "put": False},
|
||||||
"override": None,
|
"override": None,
|
||||||
"owner": str(profile_contact.user.pk),
|
"owner": str(profile_contact.user.pk),
|
||||||
"data": profile_contact.data,
|
"data": profile_contact.data,
|
||||||
@@ -81,6 +84,7 @@ def test_api_contacts_list_authenticated_no_query():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": str(override_contact.pk),
|
"id": str(override_contact.pk),
|
||||||
|
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
|
||||||
"override": str(overriden_contact.pk),
|
"override": str(overriden_contact.pk),
|
||||||
"owner": str(user.pk),
|
"owner": str(user.pk),
|
||||||
"data": override_contact.data,
|
"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.
|
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 = APIClient()
|
||||||
client.force_login(user)
|
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.status_code == 200
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"id": str(contact.id),
|
"id": str(contact.id),
|
||||||
|
"abilities": {"delete": True, "get": True, "patch": True, "put": True},
|
||||||
"override": None,
|
"override": None,
|
||||||
"owner": str(contact.owner.id),
|
"owner": str(contact.owner.id),
|
||||||
"data": contact.data,
|
"data": contact.data,
|
||||||
@@ -60,6 +62,7 @@ def test_api_contacts_retrieve_authenticated_public():
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"id": str(contact.id),
|
"id": str(contact.id),
|
||||||
|
"abilities": {"delete": False, "get": True, "patch": False, "put": False},
|
||||||
"override": None,
|
"override": None,
|
||||||
"owner": None,
|
"owner": None,
|
||||||
"data": contact.data,
|
"data": contact.data,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def test_api_contacts_update_anonymous():
|
|||||||
assert contact_values == old_contact_values
|
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.
|
Authenticated users should be allowed to update their own contacts.
|
||||||
"""
|
"""
|
||||||
@@ -52,11 +52,13 @@ def test_api_contacts_update_authenticated_owned():
|
|||||||
).data
|
).data
|
||||||
new_contact_values["override"] = str(factories.ContactFactory().id)
|
new_contact_values["override"] = str(factories.ContactFactory().id)
|
||||||
|
|
||||||
response = client.put(
|
with django_assert_num_queries(8):
|
||||||
f"/api/v1.0/contacts/{contact.id!s}/",
|
# user, 2x contact, user, 3x check, update contact
|
||||||
new_contact_values,
|
response = client.put(
|
||||||
format="json",
|
f"/api/v1.0/contacts/{contact.id!s}/",
|
||||||
)
|
new_contact_values,
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user