From e6f048b6390cbb80a10bab91894530829cb33f58 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 31 Oct 2023 08:57:20 -0700 Subject: [PATCH] 14132 remove fields from Webhook model, consolidate migrations --- netbox/extras/filtersets.py | 8 +-- netbox/extras/forms/bulk_import.py | 3 +- netbox/extras/forms/filtersets.py | 50 +------------ netbox/extras/migrations/0099_eventrule.py | 81 ++++++++++++++++++++++ netbox/extras/models/models.py | 78 ++------------------- netbox/extras/tables/tables.py | 7 +- 6 files changed, 97 insertions(+), 130 deletions(-) diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 8492c2fb0..e7b3398ca 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -40,10 +40,6 @@ class WebhookFilterSet(NetBoxModelFilterSet): method='search', label=_('Search'), ) - content_type_id = MultiValueNumberFilter( - field_name='content_types__id' - ) - content_types = ContentTypeFilter() http_method = django_filters.MultipleChoiceFilter( choices=WebhookHttpMethodChoices ) @@ -51,8 +47,8 @@ class WebhookFilterSet(NetBoxModelFilterSet): class Meta: model = Webhook fields = [ - 'id', 'name', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', 'payload_url', - 'enabled', 'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path', + 'id', 'payload_url', + 'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path', ] def search(self, queryset, name, value): diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 22d0364fe..d0b6aca1c 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -152,8 +152,7 @@ class WebhookImportForm(NetBoxModelImportForm): class Meta: model = Webhook fields = ( - 'name', 'enabled', 'content_types', 'type_create', 'type_update', 'type_delete', 'type_job_start', - 'type_job_end', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', + 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path', 'tags' ) diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py index c75b84cb8..ebdd34912 100644 --- a/netbox/extras/forms/filtersets.py +++ b/netbox/extras/forms/filtersets.py @@ -226,61 +226,13 @@ class WebhookFilterForm(NetBoxModelFilterSetForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), - (_('Attributes'), ('content_type_id', 'http_method', 'enabled')), - (_('Events'), ('type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end')), - ) - content_type_id = ContentTypeMultipleChoiceField( - queryset=ContentType.objects.filter(FeatureQuery('webhooks').get_query()), - required=False, - label=_('Object type') + (_('Attributes'), ('http_method',)), ) http_method = forms.MultipleChoiceField( choices=WebhookHttpMethodChoices, required=False, label=_('HTTP method') ) - enabled = forms.NullBooleanField( - label=_('Enabled'), - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ) - ) - type_create = forms.NullBooleanField( - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ), - label=_('Object creations') - ) - type_update = forms.NullBooleanField( - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ), - label=_('Object updates') - ) - type_delete = forms.NullBooleanField( - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ), - label=_('Object deletions') - ) - type_job_start = forms.NullBooleanField( - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ), - label=_('Job starts') - ) - type_job_end = forms.NullBooleanField( - required=False, - widget=forms.Select( - choices=BOOLEAN_WITH_BLANK_CHOICES - ), - label=_('Job terminations') - ) class EventRuleFilterForm(NetBoxModelFilterSetForm): diff --git a/netbox/extras/migrations/0099_eventrule.py b/netbox/extras/migrations/0099_eventrule.py index ee8719a57..3f0694b71 100644 --- a/netbox/extras/migrations/0099_eventrule.py +++ b/netbox/extras/migrations/0099_eventrule.py @@ -7,6 +7,29 @@ import taggit.managers import utilities.json +def move_webhooks(apps, schema_editor): + Webhook = apps.get_model("extras", "Webhook") + EventRule = apps.get_model("extras", "EventRule") + + for webhook in Webhook.objects.all(): + event = EventRule() + + event.name = webhook.name + event.type_create = webhook.type_create + event.type_update = webhook.type_update + event.type_delete = webhook.type_delete + event.type_job_start = webhook.type_job_start + event.type_job_end = webhook.type_job_end + event.enabled = webhook.enabled + event.conditions = webhook.conditions + + event.event_type = EventRuleTypeChoices.WEBHOOK + event.object_type_id = ContentType.objects.get_for_model(webhook).id + event.object = webhook + event.save() + event.content_types.add(*webhook.content_types.all()) + + class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0002_remove_content_type_name'), @@ -59,4 +82,62 @@ class Migration(migrations.Migration): 'ordering': ('name',), }, ), + migrations.RunPython(move_webhooks), + migrations.AlterModelOptions( + name='webhook', + options={'ordering': ('payload_url',)}, + ), + migrations.RemoveConstraint( + model_name='webhook', + name='extras_webhook_unique_payload_url_types', + ), + migrations.RemoveField( + model_name='webhook', + name='conditions', + ), + migrations.RemoveField( + model_name='webhook', + name='content_types', + ), + migrations.RemoveField( + model_name='webhook', + name='enabled', + ), + migrations.RemoveField( + model_name='webhook', + name='name', + ), + migrations.RemoveField( + model_name='webhook', + name='type_create', + ), + migrations.RemoveField( + model_name='webhook', + name='type_delete', + ), + migrations.RemoveField( + model_name='webhook', + name='type_job_end', + ), + migrations.RemoveField( + model_name='webhook', + name='type_job_start', + ), + migrations.RemoveField( + model_name='webhook', + name='type_update', + ), + migrations.AlterField( + model_name='eventrule', + name='object_type', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='eventrule_actions', + to='contenttypes.contenttype', + ), + ), + migrations.AlterUniqueTogether( + name='eventrule', + unique_together={('object_type', 'object_id')}, + ), ] diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index cf961044a..dd5e83eb7 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -2,7 +2,7 @@ import json import urllib.parse from django.conf import settings -from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from django.core.validators import ValidationError @@ -103,7 +103,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged object_type = models.ForeignKey( to=ContentType, related_name='eventrule_actions', - limit_choices_to=EVENT_TYPE_MODELS, + # limit_choices_to=EVENT_TYPE_MODELS, on_delete=models.CASCADE, ) object_id = models.PositiveBigIntegerField( @@ -119,6 +119,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged ordering = ('name',) verbose_name = _('eventrule') verbose_name_plural = _('eventrules') + unique_together = ('object_type', 'object_id') def __str__(self): return self.name @@ -154,43 +155,8 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo delete in NetBox. The request will contain a representation of the object, which the remote application can act on. Each Webhook can be limited to firing only on certain actions or certain object types. """ - content_types = models.ManyToManyField( - to=ContentType, - related_name='webhooks', - verbose_name=_('object types'), - limit_choices_to=FeatureQuery('webhooks'), - help_text=_("The object(s) to which this Webhook applies.") - ) - name = models.CharField( - verbose_name=_('name'), - max_length=150, - unique=True - ) - type_create = models.BooleanField( - verbose_name=_('on create'), - default=False, - help_text=_("Triggers when a matching object is created.") - ) - type_update = models.BooleanField( - verbose_name=_('on update'), - default=False, - help_text=_("Triggers when a matching object is updated.") - ) - type_delete = models.BooleanField( - verbose_name=_('on delete'), - default=False, - help_text=_("Triggers when a matching object is deleted.") - ) - type_job_start = models.BooleanField( - verbose_name=_('on job start'), - default=False, - help_text=_("Triggers when a job for a matching object is started.") - ) - type_job_end = models.BooleanField( - verbose_name=_('on job end'), - default=False, - help_text=_("Triggers when a job for a matching object terminates.") - ) + events = GenericRelation(EventRule) + payload_url = models.CharField( max_length=500, verbose_name=_('URL'), @@ -199,10 +165,6 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo "processing is supported with the same context as the request body." ) ) - enabled = models.BooleanField( - verbose_name=_('enabled'), - default=True - ) http_method = models.CharField( max_length=30, choices=WebhookHttpMethodChoices, @@ -245,12 +207,6 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo "digest of the payload body using the secret as the key. The secret is not transmitted in the request." ) ) - conditions = models.JSONField( - verbose_name=_('conditions'), - blank=True, - null=True, - help_text=_("A set of conditions which determine whether the webhook will be generated.") - ) ssl_verification = models.BooleanField( default=True, verbose_name=_('SSL verification'), @@ -267,18 +223,12 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo ) class Meta: - ordering = ('name',) - constraints = ( - models.UniqueConstraint( - fields=('payload_url', 'type_create', 'type_update', 'type_delete'), - name='%(app_label)s_%(class)s_unique_payload_url_types' - ), - ) + ordering = ('payload_url',) verbose_name = _('webhook') verbose_name_plural = _('webhooks') def __str__(self): - return self.name + return self.payload_url def get_absolute_url(self): return reverse('extras:webhook', args=[self.pk]) @@ -290,20 +240,6 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo def clean(self): super().clean() - # At least one action type must be selected - if not any([ - self.type_create, self.type_update, self.type_delete, self.type_job_start, self.type_job_end - ]): - raise ValidationError( - _("At least one event type must be selected: create, update, delete, job_start, and/or job_end.") - ) - - if self.conditions: - try: - ConditionSet(self.conditions) - except ValueError as e: - raise ValidationError({'conditions': e}) - # CA file path requires SSL verification enabled if not self.ssl_verification and self.ca_file_path: raise ValidationError({ diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index c9c663722..4d7eefe15 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -320,6 +320,9 @@ class EventRuleTable(NetBoxTable): verbose_name=_('Name'), linkify=True ) + event_type = tables.Column( + verbose_name=_('Event Type'), + ) content_types = columns.ContentTypesColumn( verbose_name=_('Content Types'), ) @@ -348,11 +351,11 @@ class EventRuleTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = EventRule fields = ( - 'pk', 'id', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', + 'pk', 'id', 'name', 'event_type', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', 'tags', 'created', 'last_updated', ) default_columns = ( - 'pk', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'type_job_start', + 'pk', 'name', 'event_type', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', )