diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index a7d081c5c..a5c139c08 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers from dcim.api.serializers import NestedSiteSerializer -from extras.models import ACTION_CHOICES, Graph, GRAPH_TYPE_CHOICES, TopologyMap, UserAction +from extras.models import ACTION_CHOICES, Graph, GRAPH_TYPE_CHOICES, ExportTemplate, TopologyMap, UserAction from users.api.serializers import NestedUserSerializer from utilities.api import ChoiceFieldSerializer @@ -41,6 +41,17 @@ class RenderedGraphSerializer(serializers.ModelSerializer): return obj.embed_link(self.context['graphed_object']) +# +# Export templates +# + +class ExportTemplateSerializer(serializers.ModelSerializer): + + class Meta: + model = ExportTemplate + fields = ['id', 'content_type', 'name', 'description', 'template_code', 'mime_type', 'file_extension'] + + # # Topology maps # diff --git a/netbox/extras/api/urls.py b/netbox/extras/api/urls.py index ced4035c1..ec1fa978b 100644 --- a/netbox/extras/api/urls.py +++ b/netbox/extras/api/urls.py @@ -8,6 +8,9 @@ router = routers.DefaultRouter() # Graphs router.register(r'graphs', views.GraphViewSet) +# Export templates +router.register(r'export-templates', views.ExportTemplateViewSet) + # Topology maps router.register(r'topology-maps', views.TopologyMapViewSet) diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 5b9ea5afb..fab1ccdb5 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -6,7 +6,7 @@ from django.http import HttpResponse from django.shortcuts import get_object_or_404 from extras import filters -from extras.models import Graph, TopologyMap, UserAction +from extras.models import ExportTemplate, Graph, TopologyMap, UserAction from utilities.api import WritableSerializerMixin from . import serializers @@ -48,6 +48,13 @@ class GraphViewSet(WritableSerializerMixin, ModelViewSet): filter_class = filters.GraphFilter +class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet): + queryset = ExportTemplate.objects.all() + serializer_class = serializers.ExportTemplateSerializer + # write_serializer_class = serializers.WritableExportTemplateSerializer + filter_class = filters.ExportTemplateFilter + + class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet): queryset = TopologyMap.objects.select_related('site') serializer_class = serializers.TopologyMapSerializer diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index 740296c9e..9d9dc5f87 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from dcim.models import Site -from .models import CF_TYPE_SELECT, CustomField, Graph, TopologyMap, UserAction +from .models import CF_TYPE_SELECT, CustomField, Graph, ExportTemplate, TopologyMap, UserAction class CustomFieldFilter(django_filters.Filter): @@ -55,6 +55,13 @@ class GraphFilter(django_filters.FilterSet): fields = ['type', 'name'] +class ExportTemplateFilter(django_filters.FilterSet): + + class Meta: + model = ExportTemplate + fields = ['content_type', 'name'] + + class TopologyMapFilter(django_filters.FilterSet): site_id = django_filters.ModelMultipleChoiceFilter( name='site', diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 8594e5faa..4c80ddee8 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -2,9 +2,11 @@ from rest_framework import status from rest_framework.test import APITestCase from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType from django.urls import reverse -from extras.models import Graph, GRAPH_TYPE_SITE +from dcim.models import Device +from extras.models import Graph, GRAPH_TYPE_SITE, ExportTemplate from users.models import Token from utilities.tests import HttpStatusMixin @@ -84,3 +86,83 @@ class GraphTest(HttpStatusMixin, APITestCase): self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT) self.assertEqual(Graph.objects.count(), 2) + + +class ExportTemplateTest(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.content_type = ContentType.objects.get_for_model(Device) + self.exporttemplate1 = ExportTemplate.objects.create( + content_type=self.content_type, name='Test Export Template 1', + template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' + ) + self.exporttemplate2 = ExportTemplate.objects.create( + content_type=self.content_type, name='Test Export Template 2', + template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' + ) + self.exporttemplate3 = ExportTemplate.objects.create( + content_type=self.content_type, name='Test Export Template 3', + template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' + ) + + def test_get_exporttemplate(self): + + url = reverse('extras-api:exporttemplate-detail', kwargs={'pk': self.exporttemplate1.pk}) + response = self.client.get(url, **self.header) + + self.assertEqual(response.data['name'], self.exporttemplate1.name) + + def test_list_exporttemplates(self): + + url = reverse('extras-api:exporttemplate-list') + response = self.client.get(url, **self.header) + + self.assertEqual(response.data['count'], 3) + + def test_create_exporttemplate(self): + + data = { + 'content_type': self.content_type.pk, + 'name': 'Test Export Template 4', + 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', + } + + url = reverse('extras-api:exporttemplate-list') + response = self.client.post(url, data, **self.header) + + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertEqual(ExportTemplate.objects.count(), 4) + exporttemplate4 = ExportTemplate.objects.get(pk=response.data['id']) + self.assertEqual(exporttemplate4.content_type_id, data['content_type']) + self.assertEqual(exporttemplate4.name, data['name']) + self.assertEqual(exporttemplate4.template_code, data['template_code']) + + def test_update_exporttemplate(self): + + data = { + 'content_type': self.content_type.pk, + 'name': 'Test Export Template X', + 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', + } + + url = reverse('extras-api:exporttemplate-detail', kwargs={'pk': self.exporttemplate1.pk}) + response = self.client.put(url, data, **self.header) + + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(ExportTemplate.objects.count(), 3) + exporttemplate1 = ExportTemplate.objects.get(pk=response.data['id']) + self.assertEqual(exporttemplate1.name, data['name']) + self.assertEqual(exporttemplate1.template_code, data['template_code']) + + def test_delete_exporttemplate(self): + + url = reverse('extras-api:exporttemplate-detail', kwargs={'pk': self.exporttemplate1.pk}) + response = self.client.delete(url, **self.header) + + self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT) + self.assertEqual(ExportTemplate.objects.count(), 2)