14132 remove fields from Webhook model, consolidate migrations

This commit is contained in:
Arthur 2023-10-31 08:57:20 -07:00
parent 1cc8a82921
commit e6f048b639
6 changed files with 97 additions and 130 deletions

View File

@ -40,10 +40,6 @@ class WebhookFilterSet(NetBoxModelFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
content_type_id = MultiValueNumberFilter(
field_name='content_types__id'
)
content_types = ContentTypeFilter()
http_method = django_filters.MultipleChoiceFilter( http_method = django_filters.MultipleChoiceFilter(
choices=WebhookHttpMethodChoices choices=WebhookHttpMethodChoices
) )
@ -51,8 +47,8 @@ class WebhookFilterSet(NetBoxModelFilterSet):
class Meta: class Meta:
model = Webhook model = Webhook
fields = [ fields = [
'id', 'name', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'type_job_end', 'payload_url', 'id', 'payload_url',
'enabled', 'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path', 'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path',
] ]
def search(self, queryset, name, value): def search(self, queryset, name, value):

View File

@ -152,8 +152,7 @@ class WebhookImportForm(NetBoxModelImportForm):
class Meta: class Meta:
model = Webhook model = Webhook
fields = ( fields = (
'name', 'enabled', 'content_types', 'type_create', 'type_update', 'type_delete', 'type_job_start', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template',
'type_job_end', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template',
'secret', 'ssl_verification', 'ca_file_path', 'tags' 'secret', 'ssl_verification', 'ca_file_path', 'tags'
) )

View File

@ -226,61 +226,13 @@ class WebhookFilterForm(NetBoxModelFilterSetForm):
fieldsets = ( fieldsets = (
(None, ('q', 'filter_id', 'tag')), (None, ('q', 'filter_id', 'tag')),
(_('Attributes'), ('content_type_id', 'http_method', 'enabled')), (_('Attributes'), ('http_method',)),
(_('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')
) )
http_method = forms.MultipleChoiceField( http_method = forms.MultipleChoiceField(
choices=WebhookHttpMethodChoices, choices=WebhookHttpMethodChoices,
required=False, required=False,
label=_('HTTP method') 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): class EventRuleFilterForm(NetBoxModelFilterSetForm):

View File

@ -7,6 +7,29 @@ import taggit.managers
import utilities.json 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): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('contenttypes', '0002_remove_content_type_name'), ('contenttypes', '0002_remove_content_type_name'),
@ -59,4 +82,62 @@ class Migration(migrations.Migration):
'ordering': ('name',), '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')},
),
] ]

View File

@ -2,7 +2,7 @@ import json
import urllib.parse import urllib.parse
from django.conf import settings 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.contrib.contenttypes.models import ContentType
from django.core.cache import cache from django.core.cache import cache
from django.core.validators import ValidationError from django.core.validators import ValidationError
@ -103,7 +103,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
object_type = models.ForeignKey( object_type = models.ForeignKey(
to=ContentType, to=ContentType,
related_name='eventrule_actions', related_name='eventrule_actions',
limit_choices_to=EVENT_TYPE_MODELS, # limit_choices_to=EVENT_TYPE_MODELS,
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
object_id = models.PositiveBigIntegerField( object_id = models.PositiveBigIntegerField(
@ -119,6 +119,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
ordering = ('name',) ordering = ('name',)
verbose_name = _('eventrule') verbose_name = _('eventrule')
verbose_name_plural = _('eventrules') verbose_name_plural = _('eventrules')
unique_together = ('object_type', 'object_id')
def __str__(self): def __str__(self):
return self.name 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. 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. Each Webhook can be limited to firing only on certain actions or certain object types.
""" """
content_types = models.ManyToManyField( events = GenericRelation(EventRule)
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.")
)
payload_url = models.CharField( payload_url = models.CharField(
max_length=500, max_length=500,
verbose_name=_('URL'), verbose_name=_('URL'),
@ -199,10 +165,6 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
"processing is supported with the same context as the request body." "processing is supported with the same context as the request body."
) )
) )
enabled = models.BooleanField(
verbose_name=_('enabled'),
default=True
)
http_method = models.CharField( http_method = models.CharField(
max_length=30, max_length=30,
choices=WebhookHttpMethodChoices, 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." "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( ssl_verification = models.BooleanField(
default=True, default=True,
verbose_name=_('SSL verification'), verbose_name=_('SSL verification'),
@ -267,18 +223,12 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
) )
class Meta: class Meta:
ordering = ('name',) ordering = ('payload_url',)
constraints = (
models.UniqueConstraint(
fields=('payload_url', 'type_create', 'type_update', 'type_delete'),
name='%(app_label)s_%(class)s_unique_payload_url_types'
),
)
verbose_name = _('webhook') verbose_name = _('webhook')
verbose_name_plural = _('webhooks') verbose_name_plural = _('webhooks')
def __str__(self): def __str__(self):
return self.name return self.payload_url
def get_absolute_url(self): def get_absolute_url(self):
return reverse('extras:webhook', args=[self.pk]) return reverse('extras:webhook', args=[self.pk])
@ -290,20 +240,6 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
def clean(self): def clean(self):
super().clean() 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 # CA file path requires SSL verification enabled
if not self.ssl_verification and self.ca_file_path: if not self.ssl_verification and self.ca_file_path:
raise ValidationError({ raise ValidationError({

View File

@ -320,6 +320,9 @@ class EventRuleTable(NetBoxTable):
verbose_name=_('Name'), verbose_name=_('Name'),
linkify=True linkify=True
) )
event_type = tables.Column(
verbose_name=_('Event Type'),
)
content_types = columns.ContentTypesColumn( content_types = columns.ContentTypesColumn(
verbose_name=_('Content Types'), verbose_name=_('Content Types'),
) )
@ -348,11 +351,11 @@ class EventRuleTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = EventRule model = EventRule
fields = ( 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', 'type_job_start', 'type_job_end', 'tags', 'created', 'last_updated',
) )
default_columns = ( 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', 'type_job_end',
) )