|
|
@@ -38,6 +38,7 @@ from extras.scripts import BooleanVar, IntegerVar, MultiObjectVar, ObjectVar, St
|
|
|
from extras.scripts import Script as PythonClass
|
|
|
from netbox.event_rules import EventRuleAction, register_event_rule_action
|
|
|
from netbox.registry import registry
|
|
|
+from netbox.tests.dummy_plugin.models import DummyNetBoxModel
|
|
|
from users.constants import TOKEN_PREFIX
|
|
|
from users.models import Group, ObjectPermission, Token, User
|
|
|
from utilities.tables import get_table_for_model
|
|
|
@@ -303,6 +304,40 @@ class EventRuleActionAPITestCase(APITestCase):
|
|
|
)
|
|
|
self.assertIn(user_ct, field.queryset)
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_action_object_without_serializer(self):
|
|
|
+ """A rule whose action targets an object without a REST API serializer has a null action object."""
|
|
|
+ class PluginObjectAction(EventRuleAction):
|
|
|
+ slug = 'test.api_plugin_object_action'
|
|
|
+ label = 'API Plugin Object Action'
|
|
|
+ object_model = DummyNetBoxModel
|
|
|
+
|
|
|
+ register_event_rule_action(PluginObjectAction)
|
|
|
+ self.addCleanup(registry['event_rule_actions'].pop, PluginObjectAction.slug, None)
|
|
|
+
|
|
|
+ self.add_permissions('extras.view_eventrule')
|
|
|
+ webhook = Webhook.objects.create(name='Webhook 1', payload_url='http://example.com/')
|
|
|
+ EventRule.objects.create(name='Webhook Rule', event_types=[OBJECT_CREATED], action_object=webhook)
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ event_rule = EventRule(
|
|
|
+ name='Plugin Rule', event_types=[OBJECT_CREATED], action_type=PluginObjectAction.slug, action_object=dummy
|
|
|
+ )
|
|
|
+ event_rule.full_clean()
|
|
|
+ event_rule.save()
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:eventrule-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(event_rule.pk)['action_object'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['action_object']['id'], item['action_object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:eventrule-detail', kwargs={'pk': event_rule.pk}), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['action_object'])
|
|
|
+ self.assertEqual(response.data['action_object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['action_object_id'], dummy.pk)
|
|
|
+
|
|
|
|
|
|
class CustomFieldTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
model = CustomField
|
|
|
@@ -952,6 +987,28 @@ class BookmarkTestCase(
|
|
|
},
|
|
|
]
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_object_without_serializer(self):
|
|
|
+ """A bookmark of an object without a REST API serializer has a null object."""
|
|
|
+ self.add_permissions('extras.view_bookmark')
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ bookmark = Bookmark(object=dummy, user=self.user)
|
|
|
+ bookmark.full_clean()
|
|
|
+ bookmark.save()
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:bookmark-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(bookmark.pk)['object'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['object']['id'], item['object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:bookmark-detail', kwargs={'pk': bookmark.pk}), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['object'])
|
|
|
+ self.assertEqual(response.data['object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['object_id'], dummy.pk)
|
|
|
+
|
|
|
|
|
|
class ExportTemplateTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
model = ExportTemplate
|
|
|
@@ -1427,6 +1484,37 @@ class ImageAttachmentTestCase(
|
|
|
)
|
|
|
ImageAttachment.objects.bulk_create(image_attachments)
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_parent_without_serializer(self):
|
|
|
+ """An image attachment whose parent has no REST API serializer has a null parent."""
|
|
|
+ self.add_permissions('extras.view_imageattachment')
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ image_attachment = ImageAttachment(
|
|
|
+ parent=dummy,
|
|
|
+ name='Image Attachment 4',
|
|
|
+ image='http://example.com/image4.png',
|
|
|
+ image_height=100,
|
|
|
+ image_width=100,
|
|
|
+ )
|
|
|
+ # The placeholder image is not a stored file
|
|
|
+ image_attachment.full_clean(exclude=['image'])
|
|
|
+ image_attachment.save()
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:imageattachment-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(image_attachment.pk)['parent'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['parent']['id'], item['object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(
|
|
|
+ reverse('extras-api:imageattachment-detail', kwargs={'pk': image_attachment.pk}), **self.header
|
|
|
+ )
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['parent'])
|
|
|
+ self.assertEqual(response.data['object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['object_id'], dummy.pk)
|
|
|
+
|
|
|
|
|
|
class JournalEntryTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
model = JournalEntry
|
|
|
@@ -1505,6 +1593,39 @@ class JournalEntryTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
created_by_after = entry.created_by_id
|
|
|
self.assertEqual(created_by_before, created_by_after)
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_assigned_object_without_serializer(self):
|
|
|
+ """A journal entry on an object without a REST API serializer has a null assigned object."""
|
|
|
+ self.add_permissions('extras.add_journalentry', 'extras.view_journalentry', 'extras.delete_journalentry')
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ data = {
|
|
|
+ 'assigned_object_type': 'dummy_plugin.dummynetboxmodel',
|
|
|
+ 'assigned_object_id': dummy.pk,
|
|
|
+ 'comments': 'Seventh entry',
|
|
|
+ }
|
|
|
+
|
|
|
+ response = self.client.post(reverse('extras-api:journalentry-list'), data, format='json', **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
|
|
+ self.assertIsNone(response.data['assigned_object'])
|
|
|
+ journal_entry_id = response.data['id']
|
|
|
+ detail_url = reverse('extras-api:journalentry-detail', kwargs={'pk': journal_entry_id})
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:journalentry-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(journal_entry_id)['assigned_object'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['assigned_object']['id'], item['assigned_object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(detail_url, **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['assigned_object'])
|
|
|
+ self.assertEqual(response.data['assigned_object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['assigned_object_id'], dummy.pk)
|
|
|
+
|
|
|
+ response = self.client.delete(detail_url, **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
|
|
+
|
|
|
|
|
|
class ConfigContextProfileTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
model = ConfigContextProfile
|
|
|
@@ -2815,6 +2936,30 @@ class SubscriptionTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
'object_type': 'dcim.notamodel',
|
|
|
}
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_object_without_serializer(self):
|
|
|
+ """A subscription to an object without a REST API serializer has a null object."""
|
|
|
+ self.add_permissions('extras.view_subscription')
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ subscription = Subscription(object=dummy, user=self.user)
|
|
|
+ subscription.full_clean()
|
|
|
+ subscription.save()
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:subscription-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(subscription.pk)['object'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['object']['id'], item['object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(
|
|
|
+ reverse('extras-api:subscription-detail', kwargs={'pk': subscription.pk}), **self.header
|
|
|
+ )
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['object'])
|
|
|
+ self.assertEqual(response.data['object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['object_id'], dummy.pk)
|
|
|
+
|
|
|
|
|
|
class NotificationGroupTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
model = NotificationGroup
|
|
|
@@ -2965,6 +3110,30 @@ class NotificationTestCase(APIViewTestCases.APIViewTestCase):
|
|
|
},
|
|
|
]
|
|
|
|
|
|
+ @tag('regression') # Ref: #23372
|
|
|
+ def test_object_without_serializer(self):
|
|
|
+ """A notification about an object without a REST API serializer has a null object."""
|
|
|
+ self.add_permissions('extras.view_notification')
|
|
|
+ dummy = DummyNetBoxModel.objects.create()
|
|
|
+ notification = Notification(object=dummy, object_repr=str(dummy), event_type=OBJECT_CREATED, user=self.user)
|
|
|
+ notification.full_clean()
|
|
|
+ notification.save()
|
|
|
+
|
|
|
+ response = self.client.get(reverse('extras-api:notification-list'), **self.header)
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ results = {item['id']: item for item in response.data['results']}
|
|
|
+ self.assertIsNone(results.pop(notification.pk)['object'])
|
|
|
+ for item in results.values():
|
|
|
+ self.assertEqual(item['object']['id'], item['object_id'])
|
|
|
+
|
|
|
+ response = self.client.get(
|
|
|
+ reverse('extras-api:notification-detail', kwargs={'pk': notification.pk}), **self.header
|
|
|
+ )
|
|
|
+ self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
|
+ self.assertIsNone(response.data['object'])
|
|
|
+ self.assertEqual(response.data['object_type'], 'dummy_plugin.dummynetboxmodel')
|
|
|
+ self.assertEqual(response.data['object_id'], dummy.pk)
|
|
|
+
|
|
|
|
|
|
class _InMemoryScriptStorage:
|
|
|
"""Stateful stand-in for the scripts storage backend; mirrors its allow_overwrite option."""
|