Skip to content

Commit

Permalink
Fix API response
Browse files Browse the repository at this point in the history
- add serializers and return flag object in serialized form when a request of correct form is made.
  • Loading branch information
abhiabhi94 committed Nov 13, 2020
1 parent f26ce46 commit 558aaca
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 44 deletions.
42 changes: 42 additions & 0 deletions flag/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers

from flag.models import Flag


User = get_user_model()


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
lookup_field = 'username'


class FlagSerializer(serializers.ModelSerializer):
class Meta:
model = Flag
fields = ('creator', 'count', 'state', 'verbose_state', 'moderator', 'is_flagged', 'reporters')
read_only_fields = ('creator', 'moderator', 'reporters')

creator = UserSerializer()
moderator = UserSerializer()
verbose_state = serializers.SerializerMethodField()
reporters = serializers.SerializerMethodField()

def __init__(self, *args, **kwargs):
context = kwargs['context']
self.model_obj = context.get('model_obj')
self.user = context.get('user')
super().__init__(*args, **kwargs)

@staticmethod
def get_verbose_state(obj):
return obj.get_verbose_state(obj.state)

def get_reporters(self, obj):
flag_obj = Flag.objects.get_flag(self.model_obj)
return [
{'id': instance.user.id, 'username': instance.user.username} for instance in flag_obj.flags.all()
]
17 changes: 12 additions & 5 deletions flag/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@
from rest_framework.response import Response

from flag.mixins import ContentTypeMixin
from flag.api.serializers import FlagSerializer
from flag.models import Flag
from flag.utils import process_flagging_request


class SetFlag(ContentTypeMixin, APIView):
api = True
permission_classes = (permissions.IsAuthenticated,)

def _get_serializer_context(self):
context = {}
context['model_obj'] = self.model_obj
context['user'] = self.request.user
return context

def post(self, request, *args, **kwargs):
self.validate(request)
self.validate(self.request)
response = process_flagging_request(user=request.user, model_obj=self.model_obj, data=self.data)
detail = {'detail': response['msg']}

if response['status']: # 1 indicates bad request
return Response(status=status.HTTP_400_BAD_REQUEST, data=detail)

if response.get('flag', None):
return Response(status=status.HTTP_201_CREATED, data=detail)

return Response(status=status.HTTP_200_OK, data=detail)
flag = Flag.objects.get_flag(self.model_obj)
serializer = FlagSerializer(instance=flag, context=self._get_serializer_context())
return Response(serializer.data, status=status.HTTP_200_OK)
10 changes: 7 additions & 3 deletions flag/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ def get_flag(self, model_obj):
flag, __ = self.get_or_create(content_type=ctype, object_id=model_obj.id, creator=creator)
return flag

def is_flagged(self, model_obj):
flag = self.get_flag(model_obj)
return flag.is_flagged

class FlagInstanceManager(models.Manager):
def has_flagged(self, user, model_obj):
"""
Returns whether a model object has been flagged by a user or not
Expand All @@ -26,9 +28,11 @@ def has_flagged(self, user, model_obj):
Returns:
bool
"""
ctype = get_content_type(model_obj)
return self.filter(flag__content_type=ctype, flag__object_id=model_obj.id, user=user).exists()
flag = self.get_flag(model_obj)
return flag.flags.filter(user=user).exists()


class FlagInstanceManager(models.Manager):
def _clean_reason(self, reason):
err = ValidationError(
_('%(reason)s is an invalid reason'),
Expand Down
6 changes: 6 additions & 0 deletions flag/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ def get_clean_state(self, state):
raise err
return state

def get_verbose_state(self, state):
state = self.get_clean_state(state)
for item in self.STATE_CHOICES:
if item[0] == state:
return item[1]

def toggle_state(self, state, moderator):
state = self.get_clean_state(state)
# toggle states occurs between rejected and resolved states only
Expand Down
4 changes: 2 additions & 2 deletions flag/templatetags/flag_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _

from flag.models import FlagInstance
from flag.models import Flag, FlagInstance
from flag.conf import settings

register = template.Library()
Expand All @@ -18,7 +18,7 @@ def get_model_name(obj):

def has_flagged(user, obj):
if user.is_authenticated:
return FlagInstance.objects.has_flagged(user, obj)
return Flag.objects.has_flagged(user, obj)

return False

Expand Down
64 changes: 40 additions & 24 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rest_framework import status

from flag.api.serializers import FlagSerializer
from tests.base import BaseFlagAPITest, Flag, FlagInstance


Expand All @@ -8,51 +9,57 @@ class FlagAPIViewsTest(BaseFlagAPITest):
def setUpTestData(cls):
super().setUpTestData()
cls.post = cls.create_post()
cls.flag = Flag.objects.get_flag(cls.post)

def setUp(self):
super().setUp()
self.url = '/api/flag/'
self.init_count = 0
self.context = {
'model_obj': self.post,
'user': self.user_1
}

def test_flagging_successfully_with_url_encoded_form(self):
data = self.data.copy()
post = self.post
data['model_id'] = post.id
self.flag.refresh_from_db()
init_count = self.flag.count

response = self.client.post(self.url, data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
response.json()['detail'],
'The content has been flagged successfully. A moderator will review it shortly.'
)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.flag.refresh_from_db()
self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data)

# check database
flag = Flag.objects.get_flag(post)
__, created = FlagInstance.objects.get_or_create(
flag=flag,
flag=self.flag,
user=response.wsgi_request.user,
reason=data['reason']
)
self.assertEqual(created, False)
self.assertEqual(self.flag.count, init_count + 1)

def test_flagging_successfully_with_json_format(self):
data = self.data.copy()
post = self.post
data['model_id'] = post.id
response = self.client.post(self.url, data=data, format='json')

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
response.json()['detail'],
'The content has been flagged successfully. A moderator will review it shortly.'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.flag.refresh_from_db()
self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data)

# check database
flag = Flag.objects.get_flag(post)
__, created = FlagInstance.objects.get_or_create(
flag=flag,
flag=self.flag,
user=response.wsgi_request.user,
reason=data['reason']
)
self.assertEqual(created, False)
self.assertEqual(self.flag.count, self.init_count + 1)

def test_flagging_flagged_object(self):
data = self.data.copy()
Expand Down Expand Up @@ -83,18 +90,24 @@ def test_flagging_unflagged_object(self):
def test_unflagging_successfully(self):
# un-flag => no reason is passed and the content must be already flagged by the user
post = self.post
self.set_flag(model_obj=post)
self.flag.refresh_from_db()
init_count = self.flag.count
self.set_flag(post)
self.flag.refresh_from_db()
self.assertEqual(self.flag.count, init_count + 1)

data = self.data.copy()
data['model_id'] = post.id
data.pop('reason')
response = self.client.post(self.url, data=data)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()['detail'], 'The content has been unflagged successfully.')
self.flag.refresh_from_db()
self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data)
# check database
flag = Flag.objects.get_flag(post)
self.assertEqual(self.flag.count, init_count)
__, created = FlagInstance.objects.get_or_create(
flag=flag,
flag=self.flag,
user=response.wsgi_request.user,
reason=FlagInstance.reason_values[0]
)
Expand Down Expand Up @@ -132,15 +145,18 @@ def test_choosing_last_reason_with_info(self):
"""Test response when last reason is passed with info"""
data = self.data.copy()
post = self.post_2
flag = Flag.objects.get_flag(post)
init_count = flag.count
reason = FlagInstance.reason_values[-1]
info = 'weird'
data.update({'reason': reason, 'info': info, 'model_id': post.id})
response = self.client.post(self.url, data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
response.json()['detail'],
'The content has been flagged successfully. A moderator will review it shortly.'
)

self.assertEqual(response.status_code, status.HTTP_200_OK)
flag.refresh_from_db()
context = self.context.copy()
context['model_obj'] = post
self.assertEqual(response.data, FlagSerializer(flag, context=context).data)
# check database
flag = Flag.objects.get_flag(post)
self.assertEqual(FlagInstance.objects.get(user=response.wsgi_request.user, flag=flag).info, info)
self.assertEqual(flag.count, init_count + 1)
53 changes: 43 additions & 10 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ def test_get_clean_state(self):
# None
self.assertRaises(ValidationError, flag.get_clean_state, None)

def test_is_flagged(self):
post = self.post_1
flag = Flag.objects.get_flag(post)
flag.count = 0
flag.save()

self.assertEqual(False, flag.is_flagged)

self.set_flag(post)
flag.refresh_from_db()
self.assertEqual(True, flag.is_flagged)

@patch('flag.models.Flag.get_clean_state')
def test_get_verbose_state(self, mocked_get_clean_state):
flag = self.flag
state = flag.State.FLAGGED
mocked_get_clean_state.return_value = state

self.assertEqual(flag.get_verbose_state(state), flag.STATE_CHOICES[state-1][1])

mocked_get_clean_state.return_value = 100
self.assertIsNone(flag.get_verbose_state(100))

def test_toggle_state(self):
post = self.content_object_2
flag = self.create_flag(post)
Expand Down Expand Up @@ -88,6 +111,26 @@ def setUp(self):
def test_get_flag(self):
self.assertEqual(Flag.objects.get_flag(self.post_2), self.flag)

def test_has_flagged(self):
user = self.user_2
post = self.post_2
self.assertEqual(Flag.objects.has_flagged(user, post), False)

self.set_flag(self.post_2, user=user)

self.assertEqual(Flag.objects.has_flagged(user, post), True)

def test_is_flagged(self):
post = self.post_1
flag = Flag.objects.get_flag(post)
flag.count = 0
flag.save()

self.assertEqual(False, Flag.objects.is_flagged(post))

self.flag.increase_count()
self.assertEqual(False, Flag.objects.is_flagged(post))


class FlagInstanceModelTest(BaseFlagModelTest):
def test_create_flag_instance(self):
Expand All @@ -101,16 +144,6 @@ def test_create_flag_instance(self):


class TestFlagInstanceManager(BaseFlagModelTest):

def test_has_flagged(self):
user = self.user_2
post = self.post_2
self.assertEqual(FlagInstance.objects.has_flagged(user, post), False)

self.set_flag(self.post_2, user=user)

self.assertEqual(FlagInstance.objects.has_flagged(user, post), True)

def test_clean_when_last_reason_is_used(self):
flag = self.flag

Expand Down

0 comments on commit 558aaca

Please sign in to comment.