Forráskód Böngészése

Closes #8307: Add data_type indicator to REST API serializer for custom fields

jeremystretch 4 éve
szülő
commit
3fcae36cf1

+ 1 - 0
docs/release-notes/version-3.2.md

@@ -71,6 +71,7 @@ Inventory item templates can be arranged hierarchically within a device type, an
 * [#8168](https://github.com/netbox-community/netbox/issues/8168) - Add `min_vid` and `max_vid` fields to VLAN group
 * [#8295](https://github.com/netbox-community/netbox/issues/8295) - Webhook URLs can now be templatized
 * [#8296](https://github.com/netbox-community/netbox/issues/8296) - Allow disabling custom links
+* [#8307](https://github.com/netbox-community/netbox/issues/8307) - Add `data_type` indicator to REST API serializer for custom fields
 
 ### Other Changes
 

+ 16 - 3
netbox/extras/api/serializers.py

@@ -79,15 +79,28 @@ class CustomFieldSerializer(ValidatedModelSerializer):
     )
     type = ChoiceField(choices=CustomFieldTypeChoices)
     filter_logic = ChoiceField(choices=CustomFieldFilterLogicChoices, required=False)
+    data_type = serializers.SerializerMethodField()
 
     class Meta:
         model = CustomField
         fields = [
-            'id', 'url', 'display', 'content_types', 'type', 'name', 'label', 'description', 'required', 'filter_logic',
-            'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created',
-            'last_updated',
+            'id', 'url', 'display', 'content_types', 'type', 'data_type', 'name', 'label', 'description', 'required',
+            'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
+            'choices', 'created', 'last_updated',
         ]
 
+    def get_data_type(self, obj):
+        types = CustomFieldTypeChoices
+        if obj.type == types.TYPE_INTEGER:
+            return 'integer'
+        if obj.type == types.TYPE_BOOLEAN:
+            return 'boolean'
+        if obj.type in (types.TYPE_JSON, types.TYPE_OBJECT):
+            return 'object'
+        if obj.type in (types.TYPE_MULTISELECT, types.TYPE_MULTIOBJECT):
+            return 'array'
+        return 'string'
+
 
 #
 # Custom links

+ 76 - 19
netbox/extras/tests/test_customfields.py

@@ -378,9 +378,22 @@ class CustomFieldAPITest(APITestCase):
             CustomField(type=CustomFieldTypeChoices.TYPE_DATE, name='date_field', default='2020-01-01'),
             CustomField(type=CustomFieldTypeChoices.TYPE_URL, name='url_field', default='http://example.com/1'),
             CustomField(type=CustomFieldTypeChoices.TYPE_JSON, name='json_field', default='{"x": "y"}'),
-            CustomField(type=CustomFieldTypeChoices.TYPE_SELECT, name='choice_field', default='Foo', choices=(
-                'Foo', 'Bar', 'Baz'
-            )),
+            CustomField(
+                type=CustomFieldTypeChoices.TYPE_SELECT,
+                name='select_field',
+                default='Foo',
+                choices=(
+                    'Foo', 'Bar', 'Baz'
+                )
+            ),
+            CustomField(
+                type=CustomFieldTypeChoices.TYPE_MULTISELECT,
+                name='multiselect_field',
+                default=['Foo'],
+                choices=(
+                    'Foo', 'Bar', 'Baz'
+                )
+            ),
             CustomField(
                 type=CustomFieldTypeChoices.TYPE_OBJECT,
                 name='object_field',
@@ -416,11 +429,37 @@ class CustomFieldAPITest(APITestCase):
             custom_fields[5].name: 'http://example.com/2',
             custom_fields[6].name: '{"foo": 1, "bar": 2}',
             custom_fields[7].name: 'Bar',
-            custom_fields[8].name: vlans[1].pk,
-            custom_fields[9].name: [vlans[2].pk, vlans[3].pk],
+            custom_fields[8].name: ['Bar', 'Baz'],
+            custom_fields[9].name: vlans[1].pk,
+            custom_fields[10].name: [vlans[2].pk, vlans[3].pk],
         }
         sites[1].save()
 
+    def test_get_custom_fields(self):
+        TYPES = {
+            CustomFieldTypeChoices.TYPE_TEXT: 'string',
+            CustomFieldTypeChoices.TYPE_LONGTEXT: 'string',
+            CustomFieldTypeChoices.TYPE_INTEGER: 'integer',
+            CustomFieldTypeChoices.TYPE_BOOLEAN: 'boolean',
+            CustomFieldTypeChoices.TYPE_DATE: 'string',
+            CustomFieldTypeChoices.TYPE_URL: 'string',
+            CustomFieldTypeChoices.TYPE_JSON: 'object',
+            CustomFieldTypeChoices.TYPE_SELECT: 'string',
+            CustomFieldTypeChoices.TYPE_MULTISELECT: 'array',
+            CustomFieldTypeChoices.TYPE_OBJECT: 'object',
+            CustomFieldTypeChoices.TYPE_MULTIOBJECT: 'array',
+        }
+
+        self.add_permissions('extras.view_customfield')
+        url = reverse('extras-api:customfield-list')
+        response = self.client.get(url, **self.header)
+        self.assertEqual(response.data['count'], len(TYPES))
+
+        # Validate data types
+        for customfield in response.data['results']:
+            cf_type = customfield['type']['value']
+            self.assertEqual(customfield['data_type'], TYPES[cf_type])
+
     def test_get_single_object_without_custom_field_data(self):
         """
         Validate that custom fields are present on an object even if it has no values defined.
@@ -439,7 +478,8 @@ class CustomFieldAPITest(APITestCase):
             'date_field': None,
             'url_field': None,
             'json_field': None,
-            'choice_field': None,
+            'select_field': None,
+            'multiselect_field': None,
             'object_field': None,
             'multiobject_field': None,
         })
@@ -462,7 +502,8 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(response.data['custom_fields']['date_field'], site2_cfvs['date_field'])
         self.assertEqual(response.data['custom_fields']['url_field'], site2_cfvs['url_field'])
         self.assertEqual(response.data['custom_fields']['json_field'], site2_cfvs['json_field'])
-        self.assertEqual(response.data['custom_fields']['choice_field'], site2_cfvs['choice_field'])
+        self.assertEqual(response.data['custom_fields']['select_field'], site2_cfvs['select_field'])
+        self.assertEqual(response.data['custom_fields']['multiselect_field'], site2_cfvs['multiselect_field'])
         self.assertEqual(response.data['custom_fields']['object_field']['id'], site2_cfvs['object_field'])
         self.assertEqual(
             [obj['id'] for obj in response.data['custom_fields']['multiobject_field']],
@@ -495,7 +536,8 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(response_cf['date_field'], cf_defaults['date_field'])
         self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
         self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
-        self.assertEqual(response_cf['choice_field'], cf_defaults['choice_field'])
+        self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
+        self.assertEqual(response_cf['multiselect_field'], cf_defaults['multiselect_field'])
         self.assertEqual(response_cf['object_field']['id'], cf_defaults['object_field'])
         self.assertEqual(
             [obj['id'] for obj in response.data['custom_fields']['multiobject_field']],
@@ -511,7 +553,8 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(str(site.custom_field_data['date_field']), cf_defaults['date_field'])
         self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
         self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
-        self.assertEqual(site.custom_field_data['choice_field'], cf_defaults['choice_field'])
+        self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
+        self.assertEqual(site.custom_field_data['multiselect_field'], cf_defaults['multiselect_field'])
         self.assertEqual(site.custom_field_data['object_field'], cf_defaults['object_field'])
         self.assertEqual(site.custom_field_data['multiobject_field'], cf_defaults['multiobject_field'])
 
@@ -530,7 +573,8 @@ class CustomFieldAPITest(APITestCase):
                 'date_field': '2020-01-02',
                 'url_field': 'http://example.com/2',
                 'json_field': '{"foo": 1, "bar": 2}',
-                'choice_field': 'Bar',
+                'select_field': 'Bar',
+                'multiselect_field': ['Bar', 'Baz'],
                 'object_field': VLAN.objects.get(vid=2).pk,
                 'multiobject_field': list(VLAN.objects.filter(vid__in=[3, 4]).values_list('pk', flat=True)),
             },
@@ -551,7 +595,8 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(response_cf['date_field'], data_cf['date_field'])
         self.assertEqual(response_cf['url_field'], data_cf['url_field'])
         self.assertEqual(response_cf['json_field'], data_cf['json_field'])
-        self.assertEqual(response_cf['choice_field'], data_cf['choice_field'])
+        self.assertEqual(response_cf['select_field'], data_cf['select_field'])
+        self.assertEqual(response_cf['multiselect_field'], data_cf['multiselect_field'])
         self.assertEqual(response_cf['object_field']['id'], data_cf['object_field'])
         self.assertEqual(
             [obj['id'] for obj in response_cf['multiobject_field']],
@@ -567,7 +612,8 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(str(site.custom_field_data['date_field']), data_cf['date_field'])
         self.assertEqual(site.custom_field_data['url_field'], data_cf['url_field'])
         self.assertEqual(site.custom_field_data['json_field'], data_cf['json_field'])
-        self.assertEqual(site.custom_field_data['choice_field'], data_cf['choice_field'])
+        self.assertEqual(site.custom_field_data['select_field'], data_cf['select_field'])
+        self.assertEqual(site.custom_field_data['multiselect_field'], data_cf['multiselect_field'])
         self.assertEqual(site.custom_field_data['object_field'], data_cf['object_field'])
         self.assertEqual(site.custom_field_data['multiobject_field'], data_cf['multiobject_field'])
 
@@ -611,7 +657,8 @@ class CustomFieldAPITest(APITestCase):
             self.assertEqual(response_cf['date_field'], cf_defaults['date_field'])
             self.assertEqual(response_cf['url_field'], cf_defaults['url_field'])
             self.assertEqual(response_cf['json_field'], cf_defaults['json_field'])
-            self.assertEqual(response_cf['choice_field'], cf_defaults['choice_field'])
+            self.assertEqual(response_cf['select_field'], cf_defaults['select_field'])
+            self.assertEqual(response_cf['multiselect_field'], cf_defaults['multiselect_field'])
             self.assertEqual(response_cf['object_field']['id'], cf_defaults['object_field'])
             self.assertEqual(
                 [obj['id'] for obj in response_cf['multiobject_field']],
@@ -627,7 +674,8 @@ class CustomFieldAPITest(APITestCase):
             self.assertEqual(str(site.custom_field_data['date_field']), cf_defaults['date_field'])
             self.assertEqual(site.custom_field_data['url_field'], cf_defaults['url_field'])
             self.assertEqual(site.custom_field_data['json_field'], cf_defaults['json_field'])
-            self.assertEqual(site.custom_field_data['choice_field'], cf_defaults['choice_field'])
+            self.assertEqual(site.custom_field_data['select_field'], cf_defaults['select_field'])
+            self.assertEqual(site.custom_field_data['multiselect_field'], cf_defaults['multiselect_field'])
             self.assertEqual(site.custom_field_data['object_field'], cf_defaults['object_field'])
             self.assertEqual(site.custom_field_data['multiobject_field'], cf_defaults['multiobject_field'])
 
@@ -643,7 +691,8 @@ class CustomFieldAPITest(APITestCase):
             'date_field': '2020-01-02',
             'url_field': 'http://example.com/2',
             'json_field': '{"foo": 1, "bar": 2}',
-            'choice_field': 'Bar',
+            'select_field': 'Bar',
+            'multiselect_field': ['Bar', 'Baz'],
             'object_field': VLAN.objects.get(vid=2).pk,
             'multiobject_field': list(VLAN.objects.filter(vid__in=[3, 4]).values_list('pk', flat=True)),
         }
@@ -682,7 +731,9 @@ class CustomFieldAPITest(APITestCase):
             self.assertEqual(response_cf['date_field'], custom_field_data['date_field'])
             self.assertEqual(response_cf['url_field'], custom_field_data['url_field'])
             self.assertEqual(response_cf['json_field'], custom_field_data['json_field'])
-            self.assertEqual(response_cf['choice_field'], custom_field_data['choice_field'])
+            self.assertEqual(response_cf['select_field'], custom_field_data['select_field'])
+            self.assertEqual(response_cf['multiselect_field'], custom_field_data['multiselect_field'])
+            self.assertEqual(response_cf['object_field']['id'], custom_field_data['object_field'])
             self.assertEqual(
                 [obj['id'] for obj in response_cf['multiobject_field']],
                 custom_field_data['multiobject_field']
@@ -697,7 +748,9 @@ class CustomFieldAPITest(APITestCase):
             self.assertEqual(str(site.custom_field_data['date_field']), custom_field_data['date_field'])
             self.assertEqual(site.custom_field_data['url_field'], custom_field_data['url_field'])
             self.assertEqual(site.custom_field_data['json_field'], custom_field_data['json_field'])
-            self.assertEqual(site.custom_field_data['choice_field'], custom_field_data['choice_field'])
+            self.assertEqual(site.custom_field_data['select_field'], custom_field_data['select_field'])
+            self.assertEqual(site.custom_field_data['multiselect_field'], custom_field_data['multiselect_field'])
+            self.assertEqual(site.custom_field_data['object_field'], custom_field_data['object_field'])
             self.assertEqual(site.custom_field_data['multiobject_field'], custom_field_data['multiobject_field'])
 
     def test_update_single_object_with_values(self):
@@ -728,7 +781,9 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(response_cf['date_field'], original_cfvs['date_field'])
         self.assertEqual(response_cf['url_field'], original_cfvs['url_field'])
         self.assertEqual(response_cf['json_field'], original_cfvs['json_field'])
-        self.assertEqual(response_cf['choice_field'], original_cfvs['choice_field'])
+        self.assertEqual(response_cf['select_field'], original_cfvs['select_field'])
+        self.assertEqual(response_cf['multiselect_field'], original_cfvs['multiselect_field'])
+        self.assertEqual(response_cf['object_field']['id'], original_cfvs['object_field'])
         self.assertEqual(
             [obj['id'] for obj in response_cf['multiobject_field']],
             original_cfvs['multiobject_field']
@@ -743,7 +798,9 @@ class CustomFieldAPITest(APITestCase):
         self.assertEqual(site2.custom_field_data['date_field'], original_cfvs['date_field'])
         self.assertEqual(site2.custom_field_data['url_field'], original_cfvs['url_field'])
         self.assertEqual(site2.custom_field_data['json_field'], original_cfvs['json_field'])
-        self.assertEqual(site2.custom_field_data['choice_field'], original_cfvs['choice_field'])
+        self.assertEqual(site2.custom_field_data['select_field'], original_cfvs['select_field'])
+        self.assertEqual(site2.custom_field_data['multiselect_field'], original_cfvs['multiselect_field'])
+        self.assertEqual(site2.custom_field_data['object_field'], original_cfvs['object_field'])
         self.assertEqual(site2.custom_field_data['multiobject_field'], original_cfvs['multiobject_field'])
 
     def test_minimum_maximum_values_validation(self):