Преглед изворни кода

Added API views & tests for tags

Jeremy Stretch пре 7 година
родитељ
комит
dc2f1d7c64

+ 13 - 0
netbox/extras/api/serializers.py

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
 
 from django.core.exceptions import ObjectDoesNotExist
 from rest_framework import serializers
+from taggit.models import Tag
 
 from dcim.api.serializers import NestedDeviceSerializer, NestedRackSerializer, NestedSiteSerializer
 from dcim.models import Device, Rack, Site
@@ -62,6 +63,18 @@ class TopologyMapSerializer(ValidatedModelSerializer):
         fields = ['id', 'name', 'slug', 'site', 'device_patterns', 'description']
 
 
+#
+# Tags
+#
+
+class TagSerializer(ValidatedModelSerializer):
+    tagged_items = serializers.IntegerField(read_only=True)
+
+    class Meta:
+        model = Tag
+        fields = ['id', 'name', 'slug', 'tagged_items']
+
+
 #
 # Image attachments
 #

+ 3 - 0
netbox/extras/api/urls.py

@@ -28,6 +28,9 @@ router.register(r'export-templates', views.ExportTemplateViewSet)
 # Topology maps
 router.register(r'topology-maps', views.TopologyMapViewSet)
 
+# Tags
+router.register(r'tags', views.TagViewSet)
+
 # Image attachments
 router.register(r'image-attachments', views.ImageAttachmentViewSet)
 

+ 12 - 0
netbox/extras/api/views.py

@@ -1,12 +1,14 @@
 from __future__ import unicode_literals
 
 from django.contrib.contenttypes.models import ContentType
+from django.db.models import Count
 from django.http import Http404, HttpResponse
 from django.shortcuts import get_object_or_404
 from rest_framework.decorators import detail_route
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.response import Response
 from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
+from taggit.models import Tag
 
 from extras import filters
 from extras.models import CustomField, ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
@@ -109,6 +111,16 @@ class TopologyMapViewSet(ModelViewSet):
         return response
 
 
+#
+# Tags
+#
+
+class TagViewSet(ModelViewSet):
+    queryset = Tag.objects.annotate(tagged_items=Count('taggit_taggeditem_items'))
+    serializer_class = serializers.TagSerializer
+    filter_class = filters.TagFilter
+
+
 #
 # Image attachments
 #

+ 21 - 0
netbox/extras/filters.py

@@ -3,6 +3,8 @@ from __future__ import unicode_literals
 import django_filters
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
+from django.db.models import Q
+from taggit.models import Tag
 
 from dcim.models import Site
 from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
@@ -85,6 +87,25 @@ class ExportTemplateFilter(django_filters.FilterSet):
         fields = ['content_type', 'name']
 
 
+class TagFilter(django_filters.FilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
+
+    class Meta:
+        model = Tag
+        fields = ['name', 'slug']
+
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(slug__icontains=value)
+        )
+
+
 class TopologyMapFilter(django_filters.FilterSet):
     site_id = django_filters.ModelMultipleChoiceFilter(
         name='site',

+ 94 - 0
netbox/extras/tests/test_api.py

@@ -5,6 +5,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.urls import reverse
 from rest_framework import status
 from rest_framework.test import APITestCase
+from taggit.models import Tag
 
 from dcim.models import Device
 from extras.constants import GRAPH_TYPE_SITE
@@ -226,3 +227,96 @@ class ExportTemplateTest(HttpStatusMixin, APITestCase):
 
         self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
         self.assertEqual(ExportTemplate.objects.count(), 2)
+
+
+class TagTest(HttpStatusMixin, APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.tag1 = Tag.objects.create(name='Test Tag 1', slug='test-tag-1')
+        self.tag2 = Tag.objects.create(name='Test Tag 2', slug='test-tag-2')
+        self.tag3 = Tag.objects.create(name='Test Tag 3', slug='test-tag-3')
+
+    def test_get_tag(self):
+
+        url = reverse('extras-api:tag-detail', kwargs={'pk': self.tag1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.tag1.name)
+
+    def test_list_tags(self):
+
+        url = reverse('extras-api:tag-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_tag(self):
+
+        data = {
+            'name': 'Test Tag 4',
+            'slug': 'test-tag-4',
+        }
+
+        url = reverse('extras-api:tag-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Tag.objects.count(), 4)
+        tag4 = Tag.objects.get(pk=response.data['id'])
+        self.assertEqual(tag4.name, data['name'])
+        self.assertEqual(tag4.slug, data['slug'])
+
+    def test_create_tag_bulk(self):
+
+        data = [
+            {
+                'name': 'Test Tag 4',
+                'slug': 'test-tag-4',
+            },
+            {
+                'name': 'Test Tag 5',
+                'slug': 'test-tag-5',
+            },
+            {
+                'name': 'Test Tag 6',
+                'slug': 'test-tag-6',
+            },
+        ]
+
+        url = reverse('extras-api:tag-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Tag.objects.count(), 6)
+        self.assertEqual(response.data[0]['name'], data[0]['name'])
+        self.assertEqual(response.data[1]['name'], data[1]['name'])
+        self.assertEqual(response.data[2]['name'], data[2]['name'])
+
+    def test_update_tag(self):
+
+        data = {
+            'name': 'Test Tag X',
+            'slug': 'test-tag-x',
+        }
+
+        url = reverse('extras-api:tag-detail', kwargs={'pk': self.tag1.pk})
+        response = self.client.put(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(Tag.objects.count(), 3)
+        tag1 = Tag.objects.get(pk=response.data['id'])
+        self.assertEqual(tag1.name, data['name'])
+        self.assertEqual(tag1.slug, data['slug'])
+
+    def test_delete_tag(self):
+
+        url = reverse('extras-api:tag-detail', kwargs={'pk': self.tag1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Tag.objects.count(), 2)