Răsfoiți Sursa

feat(extras): Allow updates to data_source and data_file via API

Adds support for PATCHing ConfigContext and ConfigContextProfile with
integer IDs for `data_source` and `data_file`.
Adds regression tests to validate assignment and API functionality.

Fixes #20933
Martin Hauser 1 lună în urmă
părinte
comite
1c46215cd5

+ 1 - 0
netbox/core/api/serializers_/data.py

@@ -44,3 +44,4 @@ class DataFileSerializer(NetBoxModelSerializer):
             'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
             'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
         ]
         ]
         brief_fields = ('id', 'url', 'display', 'path')
         brief_fields = ('id', 'url', 'display', 'path')
+        read_only_fields = ['path', 'last_updated', 'size', 'hash']

+ 2 - 2
netbox/extras/api/serializers_/configcontexts.py

@@ -28,7 +28,7 @@ class ConfigContextProfileSerializer(PrimaryModelSerializer):
     )
     )
     data_file = DataFileSerializer(
     data_file = DataFileSerializer(
         nested=True,
         nested=True,
-        read_only=True
+        required=False
     )
     )
 
 
     class Meta:
     class Meta:
@@ -143,7 +143,7 @@ class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedM
     )
     )
     data_file = DataFileSerializer(
     data_file = DataFileSerializer(
         nested=True,
         nested=True,
-        read_only=True
+        required=False
     )
     )
 
 
     class Meta:
     class Meta:

+ 92 - 1
netbox/extras/tests/test_api.py

@@ -1,4 +1,5 @@
 import datetime
 import datetime
+import hashlib
 
 
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.urls import reverse
 from django.urls import reverse
@@ -7,7 +8,7 @@ from rest_framework import status
 
 
 from core.choices import ManagedFileRootPathChoices
 from core.choices import ManagedFileRootPathChoices
 from core.events import *
 from core.events import *
-from core.models import ObjectType
+from core.models import DataFile, DataSource, ObjectType
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
 from extras.choices import *
 from extras.choices import *
 from extras.models import *
 from extras.models import *
@@ -731,6 +732,51 @@ class ConfigContextProfileTest(APIViewTestCases.APIViewTestCase):
         )
         )
         ConfigContextProfile.objects.bulk_create(profiles)
         ConfigContextProfile.objects.bulk_create(profiles)
 
 
+    def test_update_data_source_and_data_file(self):
+        """
+        Regression test: Ensure data_source and data_file can be assigned via the API.
+
+        This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
+        """
+        self.add_permissions(
+            'core.view_datafile',
+            'core.view_datasource',
+            'extras.view_configcontextprofile',
+            'extras.change_configcontextprofile',
+        )
+        config_context_profile = ConfigContextProfile.objects.first()
+
+        # Create a data source and file
+        datasource = DataSource.objects.create(
+            name='Data Source 1',
+            type='local',
+            source_url='file:///tmp/netbox-datasource/',
+        )
+        # Generate a valid dummy YAML file
+        file_data = b'profile: configcontext\n'
+        datafile = DataFile.objects.create(
+            source=datasource,
+            path='dir1/file1.yml',
+            last_updated=now(),
+            size=len(file_data),
+            hash=hashlib.sha256(file_data).hexdigest(),
+            data=file_data,
+        )
+
+        url = self._get_detail_url(config_context_profile)
+        payload = {
+            'data_source': datasource.pk,
+            'data_file': datafile.pk,
+        }
+        response = self.client.patch(url, payload, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+
+        config_context_profile.refresh_from_db()
+        self.assertEqual(config_context_profile.data_source_id, datasource.pk)
+        self.assertEqual(config_context_profile.data_file_id, datafile.pk)
+        self.assertEqual(response.data['data_source']['id'], datasource.pk)
+        self.assertEqual(response.data['data_file']['id'], datafile.pk)
+
 
 
 class ConfigContextTest(APIViewTestCases.APIViewTestCase):
 class ConfigContextTest(APIViewTestCases.APIViewTestCase):
     model = ConfigContext
     model = ConfigContext
@@ -812,6 +858,51 @@ class ConfigContextTest(APIViewTestCases.APIViewTestCase):
         rendered_context = device.get_config_context()
         rendered_context = device.get_config_context()
         self.assertEqual(rendered_context['bar'], 456)
         self.assertEqual(rendered_context['bar'], 456)
 
 
+    def test_update_data_source_and_data_file(self):
+        """
+        Regression test: Ensure data_source and data_file can be assigned via the API.
+
+        This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
+        """
+        self.add_permissions(
+            'core.view_datafile',
+            'core.view_datasource',
+            'extras.view_configcontext',
+            'extras.change_configcontext',
+        )
+        config_context = ConfigContext.objects.first()
+
+        # Create a data source and file
+        datasource = DataSource.objects.create(
+            name='Data Source 1',
+            type='local',
+            source_url='file:///tmp/netbox-datasource/',
+        )
+        # Generate a valid dummy YAML file
+        file_data = b'context: config\n'
+        datafile = DataFile.objects.create(
+            source=datasource,
+            path='dir1/file1.yml',
+            last_updated=now(),
+            size=len(file_data),
+            hash=hashlib.sha256(file_data).hexdigest(),
+            data=file_data,
+        )
+
+        url = self._get_detail_url(config_context)
+        payload = {
+            'data_source': datasource.pk,
+            'data_file': datafile.pk,
+        }
+        response = self.client.patch(url, payload, format='json', **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+
+        config_context.refresh_from_db()
+        self.assertEqual(config_context.data_source_id, datasource.pk)
+        self.assertEqual(config_context.data_file_id, datafile.pk)
+        self.assertEqual(response.data['data_source']['id'], datasource.pk)
+        self.assertEqual(response.data['data_file']['id'], datafile.pk)
+
 
 
 class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
 class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
     model = ConfigTemplate
     model = ConfigTemplate