diff --git a/CHANGELOG.md b/CHANGELOG.md index 00615d515..3a803df20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ to now use "Extras | Tag." * [#2643](https://github.com/digitalocean/netbox/issues/2643) - Add `description` field to console/power components and device bays * [#2791](https://github.com/digitalocean/netbox/issues/2791) - Add a comment field for tags * [#2926](https://github.com/digitalocean/netbox/issues/2926) - Add changelog to the Tag model +* [#1792](https://github.com/digitalocean/netbox/issues/1792) - Add CustomFieldChoices API endpoint --- diff --git a/netbox/extras/api/urls.py b/netbox/extras/api/urls.py index 1bdcf181b..c135280ea 100644 --- a/netbox/extras/api/urls.py +++ b/netbox/extras/api/urls.py @@ -17,6 +17,9 @@ router.APIRootView = ExtrasRootView # Field choices router.register(r'_choices', views.ExtrasFieldChoicesViewSet, basename='field-choice') +# Custom field choices +router.register(r'_custom_field_choices', views.CustomFieldChoicesViewSet, base_name='custom-field-choice') + # Graphs router.register(r'graphs', views.GraphViewSet) diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 17070c7b4..b964b22da 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -1,3 +1,5 @@ +from collections import OrderedDict + from django.contrib.contenttypes.models import ContentType from django.db.models import Count from django.http import Http404, HttpResponse @@ -9,8 +11,8 @@ from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from extras import filters from extras.models import ( - ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap, - Tag + ConfigContext, CustomField, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap, + Tag, ) from extras.reports import get_report, get_reports from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet @@ -28,6 +30,36 @@ class ExtrasFieldChoicesViewSet(FieldChoicesViewSet): ) +# +# Custom field choices +# + +class CustomFieldChoicesViewSet(ViewSet): + """ + """ + permission_classes = [IsAuthenticatedOrLoginNotRequired] + + def __init__(self, *args, **kwargs): + super(CustomFieldChoicesViewSet, self).__init__(*args, **kwargs) + + self._fields = OrderedDict() + + for cfc in CustomFieldChoice.objects.all(): + self._fields.setdefault(cfc.field.name, {}) + self._fields[cfc.field.name][cfc.value] = cfc.pk + + def list(self, request): + return Response(self._fields) + + def retrieve(self, request, pk): + if pk not in self._fields: + raise Http404 + return Response(self._fields[pk]) + + def get_view_name(self): + return "Custom Field choices" + + # # Custom fields # diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index b02e787c1..96f3483bc 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -6,9 +6,10 @@ from django.urls import reverse from rest_framework import status from dcim.models import Site -from extras.constants import CF_TYPE_TEXT, CF_TYPE_INTEGER, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_SELECT, CF_TYPE_URL +from extras.constants import CF_TYPE_TEXT, CF_TYPE_INTEGER, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_SELECT, CF_TYPE_URL, CF_TYPE_SELECT from extras.models import CustomField, CustomFieldValue, CustomFieldChoice from utilities.testing import APITestCase +from virtualization.models import VirtualMachine class CustomFieldTest(TestCase): @@ -299,3 +300,33 @@ class CustomFieldAPITest(APITestCase): self.assertEqual(response.data['custom_fields'].get('magic_choice'), data['custom_fields']['magic_choice']) cfv = self.site.custom_field_values.get(field=self.cf_select) self.assertEqual(cfv.value.pk, data['custom_fields']['magic_choice']) + + +class CustomFieldChoiceAPITest(APITestCase): + def setUp(self): + super().setUp() + + vm_content_type = ContentType.objects.get_for_model(VirtualMachine) + + self.cf_1 = CustomField.objects.create(name="cf_1", type=CF_TYPE_SELECT) + self.cf_2 = CustomField.objects.create(name="cf_2", type=CF_TYPE_SELECT) + + self.cf_choice_1 = CustomFieldChoice.objects.create(field=self.cf_1, value="cf_field_1", weight=100) + self.cf_choice_2 = CustomFieldChoice.objects.create(field=self.cf_1, value="cf_field_2", weight=50) + self.cf_choice_3 = CustomFieldChoice.objects.create(field=self.cf_2, value="cf_field_3", weight=10) + + def test_list_cfc(self): + url = reverse('extras-api:custom-field-choice-list') + response = self.client.get(url, **self.header) + + self.assertEqual(len(response.data), 2) + self.assertEqual(len(response.data[self.cf_1.name]), 2) + self.assertEqual(len(response.data[self.cf_2.name]), 1) + + self.assertTrue(self.cf_choice_1.value in response.data[self.cf_1.name]) + self.assertTrue(self.cf_choice_2.value in response.data[self.cf_1.name]) + self.assertTrue(self.cf_choice_3.value in response.data[self.cf_2.name]) + + self.assertEqual(self.cf_choice_1.pk, response.data[self.cf_1.name][self.cf_choice_1.value]) + self.assertEqual(self.cf_choice_2.pk, response.data[self.cf_1.name][self.cf_choice_2.value]) + self.assertEqual(self.cf_choice_3.pk, response.data[self.cf_2.name][self.cf_choice_3.value])