diff --git a/lists/forms.py b/lists/forms.py index f941b12..fd8ceb7 100644 --- a/lists/forms.py +++ b/lists/forms.py @@ -1,9 +1,15 @@ from django import forms -from lists.models import ListItem +from lists.models import ListItem, Tag class ListItemForm(forms.ModelForm): class Meta: model = ListItem fields = ['entity', 'tags'] + + +class TagForm(forms.ModelForm): + class Meta: + model = Tag + fields = ['name'] diff --git a/lists/models.py b/lists/models.py index 0004675..2f344f9 100644 --- a/lists/models.py +++ b/lists/models.py @@ -31,7 +31,7 @@ class ListItem(Model): """Item de la lista de un usuario""" user = ForeignKey(User, on_delete=CASCADE, related_name='list') entity = ForeignKey(Entity, on_delete=CASCADE) - tags = ManyToManyField(Tag) + tags = ManyToManyField(Tag, related_name='list') class Stars(Model): diff --git a/lists/test.py b/lists/test.py index 43a036f..07699ca 100644 --- a/lists/test.py +++ b/lists/test.py @@ -136,3 +136,58 @@ class TestList(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(ListItem.objects.get(pk=1).tags.count(), 2) + + def test_get_tag(self): + response = self.client.get('/api/lists/tag/1/') + + self.assertEqual(response.status_code, 200) + + tags = response.json()['tags'] + + self.assertEqual(len(tags), 3) + + def test_post_tag_is_protected(self): + response = self.client.post('/api/lists/tag/1/', + HTTP_AUTHORIZATION=self._other_bearer_token()) + self.assertEqual(response.status_code, 403) + + def test_post_tag(self): + tag = { + 'name': 'oh no' + } + + response = self.client.post('/api/lists/tag/1/', json.dumps(tag), + content_type='application/json', + HTTP_AUTHORIZATION=self._user_bearer_token()) + + self.assertEqual(response.status_code, 200) + self.assertEqual(Tag.objects.filter(name='oh no').count(), 1) + + def test_put_tag_is_protected(self): + response = self.client.put('/api/lists/tag/1/1/', + HTTP_AUTHORIZATION=self._other_bearer_token()) + self.assertEqual(response.status_code, 403) + + def test_put_tag(self): + tag = {'name': 'oh no'} + + response = self.client.put('/api/lists/tag/1/1/', json.dumps(tag), + content_type='application/json', + HTTP_AUTHORIZATION=self._user_bearer_token()) + + self.assertEqual(response.status_code, 200) + self.assertEqual(Tag.objects.filter(name='oh no').count(), 1) + + def test_delete_tag_is_protected(self): + response = self.client.put('/api/lists/tag/1/1/', + HTTP_AUTHORIZATION=self._other_bearer_token()) + self.assertEqual(response.status_code, 403) + + def test_delete_tag(self): + response = self.client.delete('/api/lists/tag/1/1/', + HTTP_AUTHORIZATION=self._user_bearer_token()) + + self.assertEqual(response.status_code, 200) + self.assertEqual(Tag.objects.all().count(), 2) + + self.assertEqual(ListItem.objects.get(pk=1).tags.count(), 2) diff --git a/lists/urls.py b/lists/urls.py index 2ce24bc..5e3f011 100644 --- a/lists/urls.py +++ b/lists/urls.py @@ -5,5 +5,6 @@ from lists import views urlpatterns = [ path('list//', views.list_view), path('list///', views.list_item_view), - path('tag//', views.tag_view) + path('tag//', views.tag_view), + path('tag///', views.tag_view) ] diff --git a/lists/views.py b/lists/views.py index c354a5c..c26cf39 100644 --- a/lists/views.py +++ b/lists/views.py @@ -4,8 +4,8 @@ from json.decoder import JSONDecodeError from django.http import JsonResponse from oauth2_provider.decorators import protected_resource -from lists.forms import ListItemForm -from lists.models import ListItem +from lists.forms import ListItemForm, TagForm +from lists.models import ListItem, Tag from users.models import User @@ -55,7 +55,7 @@ def list_item_view(request, user_id, list_item_id): return JsonResponse({'status': 404, 'error': 'La ruta no existe'}, status=404) -def tag_view(request, user_id): +def tag_view(request, user_id, tag_id=None): """ Punto de entrada para las vistas de tags """ @@ -66,10 +66,22 @@ def tag_view(request, user_id): return JsonResponse({'status': 404, 'error': f'No existe un usuario con id {user_id}'}, status=404) user = user[0] + # Tag id puede ser none, pero si no lo es, tiene que existir + tag = None + if tag_id: + tag = Tag.objects.filter(pk=tag_id) + if tag.count() != 1: + return JsonResponse({'status': 404, 'error': f'No existe una tag con id {tag}'}, status=404) + tag = tag[0] + if request.method == 'GET': return _get_tags(request, user) elif request.method == 'POST': return _post_tag(request, user) + elif request.method == 'PUT' and tag: + return _put_tag(request, user, tag) + elif request.method == 'DELETE' and tag: + return _delete_tag(request, user, tag) else: return JsonResponse({'status': 404, 'error': 'La ruta no existe'}, status=404) @@ -185,9 +197,90 @@ def _remove_list_item(request, user, list_item): def _get_tags(request, user): - pass + """Obtiene todas las tags de un usuario""" + + encoded_tags = [] + for tag in user.tags.all(): + encoded_tags.append({ + 'id': tag.id, + 'name': tag.name + }) + + return JsonResponse({'tags': encoded_tags}) @protected_resource() def _post_tag(request, user): - pass + """Crea una nueva tag en un usuario""" + # Solo el dueño o un administrador puede modificar la lista + if request.user.id != user.id and not request.user.is_admin: + return JsonResponse({'status': 403, + 'error': 'El usuario no tiene permiso para hacer esta acción'}, + status=403) + + try: + request_data = json.loads(request.body.decode('utf8')) + except JSONDecodeError: + return JsonResponse({'status': 400, + 'error': 'El body de la request no es json valido'}, + status=400) + + tag = Tag(user=user) + form = TagForm(request_data, instance=tag) + + if not form.is_valid(): + return JsonResponse({'status': 400, 'error': form.errors.as_json()}, status=400) + + if user.tags.filter(name=request_data['name']).count() > 0: + return JsonResponse({'status': 400, 'error': 'El usuario ya tiene un tag con ese nombre'}, status=400) + + form.save() + + return JsonResponse({'status': 200}) + + +@protected_resource() +def _put_tag(request, user, tag): + """Actualiza una tag""" + + # Solo el dueño o un administrador puede modificar la lista + if request.user.id != user.id and not request.user.is_admin: + return JsonResponse({'status': 403, + 'error': 'El usuario no tiene permiso para hacer esta acción'}, + status=403) + + try: + request_data = json.loads(request.body.decode('utf8')) + except JSONDecodeError: + return JsonResponse({'status': 400, + 'error': 'El body de la request no es json valido'}, + status=400) + + form = TagForm(request_data, instance=tag) + + if not form.is_valid(): + return JsonResponse({'status': 400, 'error': form.errors.as_json()}, status=400) + + if user.tags.filter(name=request_data['name']).count() > 0: + return JsonResponse({'status': 400, 'error': 'El usuario ya tiene un tag con ese nombre'}, status=400) + + form.save() + + return JsonResponse({'status': 200}) + + +@protected_resource() +def _delete_tag(request, user, tag): + # Solo el dueño o un administrador puede modificar la lista + if request.user.id != user.id and not request.user.is_admin: + return JsonResponse({'status': 403, + 'error': 'El usuario no tiene permiso para hacer esta acción'}, + status=403) + + # Eliminando la tag de los items que están asociados a ella + for list_item in tag.list.all(): + list_item.tags.remove(tag) + + tag.delete() + + return JsonResponse({'status': 200}, status=200)