diff --git a/netbox/ipam/api/serializers_/vlans.py b/netbox/ipam/api/serializers_/vlans.py index a5ae39077..468a6c1b1 100644 --- a/netbox/ipam/api/serializers_/vlans.py +++ b/netbox/ipam/api/serializers_/vlans.py @@ -117,13 +117,20 @@ class VLANTranslationRuleSerializer(NetBoxModelSerializer): class Meta: model = VLANTranslationRule - fields = ['id', 'url', 'display', 'policy', 'local_vid', 'remote_vid', 'description'] + fields = [ + 'id', 'url', 'display_url', 'display', 'policy', 'local_vid', 'remote_vid', 'description', 'tags', + 'custom_fields', 'created', 'last_updated', + ] + brief_fields = ('id', 'url', 'display', 'policy', 'local_vid', 'remote_vid', 'description') class VLANTranslationPolicySerializer(PrimaryModelSerializer): - rules = VLANTranslationRuleSerializer(many=True, read_only=True) + rules = VLANTranslationRuleSerializer(nested=True, many=True, read_only=True) class Meta: model = VLANTranslationPolicy - fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules', 'owner', 'comments'] + fields = [ + 'id', 'url', 'display_url', 'display', 'name', 'description', 'rules', 'owner', 'comments', 'tags', + 'custom_fields', 'created', 'last_updated', + ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/ipam/tests/query_counts.json b/netbox/ipam/tests/query_counts.json index 29be74283..4a13e40e5 100644 --- a/netbox/ipam/tests/query_counts.json +++ b/netbox/ipam/tests/query_counts.json @@ -28,9 +28,9 @@ "vlan:list_objects_with_permission": 21, "vlangroup:api_list_objects": 12, "vlangroup:list_objects_with_permission": 22, - "vlantranslationpolicy:api_list_objects": 12, + "vlantranslationpolicy:api_list_objects": 13, "vlantranslationpolicy:list_objects_with_permission": 17, - "vlantranslationrule:api_list_objects": 12, + "vlantranslationrule:api_list_objects": 13, "vlantranslationrule:list_objects_with_permission": 18, "vrf:api_list_objects": 20, "vrf:list_objects_with_permission": 17 diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 80846db88..2b04945fb 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -11,7 +11,7 @@ from ipam.choices import * from ipam.models import * from tenancy.models import Tenant from utilities.data import string_to_ranges -from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_logging +from utilities.testing import APITestCase, APIViewTestCases, create_tags, create_test_device, disable_logging class AppTestCase(APITestCase): @@ -1422,6 +1422,39 @@ class VLANTranslationPolicyTestCase(APIViewTestCases.APIViewTestCase): }, ] + def test_standard_fields_in_representation(self): + """The standard URL, tag, custom-field and change-tracking names appear in the representation.""" + policy = VLANTranslationPolicy.objects.first() + self.add_permissions('ipam.view_vlantranslationpolicy') + + response = self.client.get(self._get_detail_url(policy), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + expected = {'display_url', 'tags', 'custom_fields', 'created', 'last_updated'} + self.assertEqual(expected - set(response.data), set()) + + def test_nested_rules_are_brief(self): + """Rules nested in a policy representation carry only the rule serializer's brief fields.""" + policy = VLANTranslationPolicy.objects.first() + VLANTranslationRule.objects.create(policy=policy, local_vid=100, remote_vid=200) + self.add_permissions('ipam.view_vlantranslationpolicy') + + response = self.client.get(self._get_detail_url(policy), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(sorted(response.data['rules'][0]), VLANTranslationRuleTestCase.brief_fields) + + def test_update_tags(self): + """Tags supplied on update are assigned and rendered in the response.""" + policy = VLANTranslationPolicy.objects.first() + create_tags('Alpha') + self.add_permissions('ipam.change_vlantranslationpolicy', 'extras.view_tag') + + data = {'tags': [{'slug': 'alpha'}]} + response = self.client.patch(self._get_detail_url(policy), data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertIn('tags', response.data) + self.assertEqual([tag['slug'] for tag in response.data['tags']], ['alpha']) + self.assertEqual(list(policy.tags.values_list('slug', flat=True)), ['alpha']) + class VLANTranslationRuleTestCase(APIViewTestCases.APIViewTestCase): model = VLANTranslationRule @@ -1491,6 +1524,29 @@ class VLANTranslationRuleTestCase(APIViewTestCases.APIViewTestCase): 'description': 'New description', } + def test_standard_fields_in_representation(self): + """The standard URL, tag, custom-field and change-tracking names appear in the representation.""" + rule = VLANTranslationRule.objects.first() + self.add_permissions('ipam.view_vlantranslationrule') + + response = self.client.get(self._get_detail_url(rule), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + expected = {'display_url', 'tags', 'custom_fields', 'created', 'last_updated'} + self.assertEqual(expected - set(response.data), set()) + + def test_update_tags(self): + """Tags supplied on update are assigned and rendered in the response.""" + rule = VLANTranslationRule.objects.first() + create_tags('Alpha') + self.add_permissions('ipam.change_vlantranslationrule', 'extras.view_tag') + + data = {'tags': [{'slug': 'alpha'}]} + response = self.client.patch(self._get_detail_url(rule), data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertIn('tags', response.data) + self.assertEqual([tag['slug'] for tag in response.data['tags']], ['alpha']) + self.assertEqual(list(rule.tags.values_list('slug', flat=True)), ['alpha']) + class ServiceTemplateTestCase(APIViewTestCases.APIViewTestCase): model = ServiceTemplate