From fdbcd3621cd6a2f0208696b9f2a51c53186b595b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 8 Jul 2024 17:12:01 -0400 Subject: [PATCH] Add API tests --- .../extras/api/serializers_/notifications.py | 4 +- netbox/extras/graphql/filters.py | 7 + netbox/extras/graphql/schema.py | 15 ++ netbox/extras/graphql/types.py | 28 +++ .../extras/migrations/0118_notifications.py | 2 +- netbox/extras/models/notifications.py | 3 +- netbox/extras/tests/test_api.py | 197 +++++++++++++++++- 7 files changed, 250 insertions(+), 6 deletions(-) diff --git a/netbox/extras/api/serializers_/notifications.py b/netbox/extras/api/serializers_/notifications.py index 307740c62..95eea2bbb 100644 --- a/netbox/extras/api/serializers_/notifications.py +++ b/netbox/extras/api/serializers_/notifications.py @@ -28,7 +28,7 @@ class NotificationSerializer(ValidatedModelSerializer): fields = [ 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', 'read', 'event_type', ] - brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user', 'event') + brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user', 'read', 'event_type') @extend_schema_field(serializers.JSONField(allow_null=True)) def get_object(self, instance): @@ -73,7 +73,7 @@ class SubscriptionSerializer(ValidatedModelSerializer): fields = [ 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', ] - brief_fields = ('id', 'url', 'display', 'object_id', 'object_type') + brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user') @extend_schema_field(serializers.JSONField(allow_null=True)) def get_object(self, instance): diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index 7451eef8a..ff2e6a0f1 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -13,6 +13,7 @@ __all__ = ( 'ExportTemplateFilter', 'ImageAttachmentFilter', 'JournalEntryFilter', + 'NotificationGroupFilter', 'SavedFilterFilter', 'TagFilter', 'WebhookFilter', @@ -67,6 +68,12 @@ class JournalEntryFilter(BaseFilterMixin): pass +@strawberry_django.filter(models.NotificationGroup, lookups=True) +@autotype_decorator(filtersets.NotificationGroupFilterSet) +class NotificationGroupFilter(BaseFilterMixin): + pass + + @strawberry_django.filter(models.SavedFilter, lookups=True) @autotype_decorator(filtersets.SavedFilterFilterSet) class SavedFilterFilter(BaseFilterMixin): diff --git a/netbox/extras/graphql/schema.py b/netbox/extras/graphql/schema.py index f78285035..7e509c0e0 100644 --- a/netbox/extras/graphql/schema.py +++ b/netbox/extras/graphql/schema.py @@ -54,6 +54,21 @@ class ExtrasQuery: return models.JournalEntry.objects.get(pk=id) journal_entry_list: List[JournalEntryType] = strawberry_django.field() + @strawberry.field + def notification(self, id: int) -> NotificationType: + return models.Notification.objects.get(pk=id) + notification_list: List[NotificationType] = strawberry_django.field() + + @strawberry.field + def notification_group(self, id: int) -> NotificationGroupType: + return models.NotificationGroup.objects.get(pk=id) + notification_group_list: List[NotificationGroupType] = strawberry_django.field() + + @strawberry.field + def subscription(self, id: int) -> SubscriptionType: + return models.Subscription.objects.get(pk=id) + subscription_list: List[SubscriptionType] = strawberry_django.field() + @strawberry.field def tag(self, id: int) -> TagType: return models.Tag.objects.get(pk=id) diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index 1f3bfcdb9..a43f80cc3 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -18,7 +18,10 @@ __all__ = ( 'ExportTemplateType', 'ImageAttachmentType', 'JournalEntryType', + 'NotificationGroupType', + 'NotificationType', 'SavedFilterType', + 'SubscriptionType', 'TagType', 'WebhookType', ) @@ -122,6 +125,23 @@ class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): created_by: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None +@strawberry_django.type( + models.Notification, + # filters=NotificationFilter +) +class NotificationType(ObjectType): + user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None + + +@strawberry_django.type( + models.NotificationGroup, + filters=NotificationGroupFilter +) +class NotificationGroupType(ObjectType): + users: List[Annotated["UserType", strawberry.lazy('users.graphql.types')]] + groups: List[Annotated["GroupType", strawberry.lazy('users.graphql.types')]] + + @strawberry_django.type( models.SavedFilter, exclude=['content_types',], @@ -131,6 +151,14 @@ class SavedFilterType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None +@strawberry_django.type( + models.Subscription, + # filters=NotificationFilter +) +class SubscriptionType(ObjectType): + user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None + + @strawberry_django.type( models.Tag, exclude=['extras_taggeditem_items', ], diff --git a/netbox/extras/migrations/0118_notifications.py b/netbox/extras/migrations/0118_notifications.py index 606fdaade..08904ebb5 100644 --- a/netbox/extras/migrations/0118_notifications.py +++ b/netbox/extras/migrations/0118_notifications.py @@ -50,7 +50,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('created', models.DateTimeField(auto_now_add=True)), - ('read', models.DateTimeField(null=True)), + ('read', models.DateTimeField(blank=True, null=True)), ('object_id', models.PositiveBigIntegerField()), ('event_type', models.CharField(max_length=50)), ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index 51798c168..3280d0fa9 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -41,7 +41,8 @@ class Notification(models.Model): ) read = models.DateTimeField( verbose_name=_('read'), - null=True + null=True, + blank=True ) user = models.ForeignKey( to=settings.AUTH_USER_MODEL, diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 5d243ae1a..a1c75ac28 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -7,15 +7,15 @@ from django.utils.timezone import make_aware from rest_framework import status from core.choices import ManagedFileRootPathChoices +from core.events import * from core.models import ObjectType from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site from extras.choices import * from extras.models import * from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar +from users.models import Group, User from utilities.testing import APITestCase, APIViewTestCases -User = get_user_model() - class AppTest(APITestCase): @@ -890,3 +890,196 @@ class ObjectTypeTest(APITestCase): url = reverse('extras-api:objecttype-detail', kwargs={'pk': object_type.pk}) self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK) + + +class SubscriptionTest(APIViewTestCases.APIViewTestCase): + model = Subscription + brief_fields = ['display', 'id', 'object_id', 'object_type', 'url', 'user'] + + @classmethod + def setUpTestData(cls): + users = ( + User(username='User 1'), + User(username='User 2'), + User(username='User 3'), + User(username='User 4'), + ) + User.objects.bulk_create(users) + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + Site(name='Site 3', slug='site-3'), + ) + Site.objects.bulk_create(sites) + + subscriptions = ( + Subscription( + object=sites[0], + user=users[0], + ), + Subscription( + object=sites[1], + user=users[1], + ), + Subscription( + object=sites[2], + user=users[2], + ), + ) + Subscription.objects.bulk_create(subscriptions) + + cls.create_data = [ + { + 'object_type': 'dcim.site', + 'object_id': sites[0].pk, + 'user': users[3].pk, + }, + { + 'object_type': 'dcim.site', + 'object_id': sites[1].pk, + 'user': users[3].pk, + }, + { + 'object_type': 'dcim.site', + 'object_id': sites[2].pk, + 'user': users[3].pk, + }, + ] + + +class NotificationGroupTest(APIViewTestCases.APIViewTestCase): + model = NotificationGroup + brief_fields = ['description', 'display', 'id', 'name', 'url'] + create_data = [ + { + 'object_types': ['dcim.site'], + 'name': 'Custom Link 4', + 'enabled': True, + 'link_text': 'Link 4', + 'link_url': 'http://example.com/?4', + }, + { + 'object_types': ['dcim.site'], + 'name': 'Custom Link 5', + 'enabled': True, + 'link_text': 'Link 5', + 'link_url': 'http://example.com/?5', + }, + { + 'object_types': ['dcim.site'], + 'name': 'Custom Link 6', + 'enabled': False, + 'link_text': 'Link 6', + 'link_url': 'http://example.com/?6', + }, + ] + bulk_update_data = { + 'description': 'New description', + } + + @classmethod + def setUpTestData(cls): + users = ( + User(username='User 1'), + User(username='User 2'), + User(username='User 3'), + ) + User.objects.bulk_create(users) + groups = ( + Group(name='Group 1'), + Group(name='Group 2'), + Group(name='Group 3'), + ) + Group.objects.bulk_create(groups) + + notification_groups = ( + NotificationGroup(name='Notification Group 1'), + NotificationGroup(name='Notification Group 2'), + NotificationGroup(name='Notification Group 3'), + ) + NotificationGroup.objects.bulk_create(notification_groups) + for i, notification_group in enumerate(notification_groups): + notification_group.users.add(users[i]) + notification_group.groups.add(groups[i]) + + cls.create_data = [ + { + 'name': 'Notification Group 4', + 'description': 'Foo', + 'users': [users[0].pk], + 'groups': [groups[0].pk], + }, + { + 'name': 'Notification Group 5', + 'description': 'Bar', + 'users': [users[1].pk], + 'groups': [groups[1].pk], + }, + { + 'name': 'Notification Group 6', + 'description': 'Baz', + 'users': [users[2].pk], + 'groups': [groups[2].pk], + }, + ] + + +class NotificationTest(APIViewTestCases.APIViewTestCase): + model = Notification + brief_fields = ['display', 'event_type', 'id', 'object_id', 'object_type', 'read', 'url', 'user'] + + @classmethod + def setUpTestData(cls): + users = ( + User(username='User 1'), + User(username='User 2'), + User(username='User 3'), + User(username='User 4'), + ) + User.objects.bulk_create(users) + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + Site(name='Site 3', slug='site-3'), + ) + Site.objects.bulk_create(sites) + + notifications = ( + Notification( + object=sites[0], + event_type=OBJECT_CREATED, + user=users[0], + ), + Notification( + object=sites[1], + event_type=OBJECT_UPDATED, + user=users[1], + ), + Notification( + object=sites[2], + event_type=OBJECT_DELETED, + user=users[2], + ), + ) + Notification.objects.bulk_create(notifications) + + cls.create_data = [ + { + 'object_type': 'dcim.site', + 'object_id': sites[0].pk, + 'user': users[3].pk, + 'event_type': OBJECT_CREATED, + }, + { + 'object_type': 'dcim.site', + 'object_id': sites[1].pk, + 'user': users[3].pk, + 'event_type': OBJECT_UPDATED, + }, + { + 'object_type': 'dcim.site', + 'object_id': sites[2].pk, + 'user': users[3].pk, + 'event_type': OBJECT_DELETED, + }, + ]