Просмотр исходного кода

Add trigger_webhooks() to JobResult

jeremystretch 3 лет назад
Родитель
Сommit
4de64d783e
3 измененных файлов с 56 добавлено и 12 удалено
  1. 9 1
      netbox/extras/constants.py
  2. 40 7
      netbox/extras/models/models.py
  3. 7 4
      netbox/extras/webhooks_worker.py

+ 9 - 1
netbox/extras/constants.py

@@ -1,8 +1,16 @@
 from django.contrib.contenttypes.models import ContentType
 
-# Webhook content types
+# Webhooks
 HTTP_CONTENT_TYPE_JSON = 'application/json'
 
+WEBHOOK_EVENT_TYPES = {
+    'create': 'created',
+    'update': 'updated',
+    'delete': 'deleted',
+    'job_start': 'job_started',
+    'job_end': 'job_ended',
+}
+
 # Dashboard
 DEFAULT_DASHBOARD = [
     {

+ 40 - 7
netbox/extras/models/models.py

@@ -26,7 +26,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT
 from netbox.models import ChangeLoggedModel
 from netbox.models.features import (
     CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, SyncedDataMixin,
-    TagsMixin,
+    TagsMixin, WebhooksMixin,
 )
 from utilities.querysets import RestrictedQuerySet
 from utilities.utils import render_jinja2
@@ -689,10 +689,16 @@ class JobResult(models.Model):
         """
         Record the job's start time and update its status to "running."
         """
-        if self.started is None:
-            self.started = timezone.now()
-            self.status = JobResultStatusChoices.STATUS_RUNNING
-            JobResult.objects.filter(pk=self.pk).update(started=self.started, status=self.status)
+        if self.started is not None:
+            return
+
+        # Start the job
+        self.started = timezone.now()
+        self.status = JobResultStatusChoices.STATUS_RUNNING
+        JobResult.objects.filter(pk=self.pk).update(started=self.started, status=self.status)
+
+        # Handle webhooks
+        self.trigger_webhooks(event='job_start')
 
     def terminate(self, status=JobResultStatusChoices.STATUS_COMPLETED):
         """
@@ -701,10 +707,15 @@ class JobResult(models.Model):
         valid_statuses = JobResultStatusChoices.TERMINAL_STATE_CHOICES
         if status not in valid_statuses:
             raise ValueError(f"Invalid status for job termination. Choices are: {', '.join(valid_statuses)}")
+
+        # Mark the job as completed
         self.status = status
         self.completed = timezone.now()
         JobResult.objects.filter(pk=self.pk).update(status=self.status, completed=self.completed)
 
+        # Handle webhooks
+        self.trigger_webhooks(event='job_end')
+
     @classmethod
     def enqueue_job(cls, func, name, obj_type, user, schedule_at=None, interval=None, *args, **kwargs):
         """
@@ -738,6 +749,28 @@ class JobResult(models.Model):
 
         return job_result
 
+    def trigger_webhooks(self, event):
+        rq_queue_name = get_config().QUEUE_MAPPINGS.get('webhook', RQ_QUEUE_DEFAULT)
+        rq_queue = django_rq.get_queue(rq_queue_name, is_async=False)
+
+        # Fetch any webhooks matching this object type and action
+        webhooks = Webhook.objects.filter(
+            **{f'type_{event}': True},
+            content_types=self.obj_type,
+            enabled=True
+        )
+
+        for webhook in webhooks:
+            rq_queue.enqueue(
+                "extras.webhooks_worker.process_webhook",
+                webhook=webhook,
+                model_name=self.obj_type.model,
+                event=event,
+                data=self.data,
+                timestamp=str(timezone.now()),
+                username=self.user.username
+            )
+
 
 class ConfigRevision(models.Model):
     """
@@ -780,7 +813,7 @@ class ConfigRevision(models.Model):
 # Custom scripts & reports
 #
 
-class Script(JobResultsMixin, models.Model):
+class Script(JobResultsMixin, WebhooksMixin, models.Model):
     """
     Dummy model used to generate permissions for custom scripts. Does not exist in the database.
     """
@@ -792,7 +825,7 @@ class Script(JobResultsMixin, models.Model):
 # Reports
 #
 
-class Report(JobResultsMixin, models.Model):
+class Report(JobResultsMixin, WebhooksMixin, models.Model):
     """
     Dummy model used to generate permissions for reports. Does not exist in the database.
     """

+ 7 - 4
netbox/extras/webhooks_worker.py

@@ -5,8 +5,8 @@ from django.conf import settings
 from django_rq import job
 from jinja2.exceptions import TemplateError
 
-from .choices import ObjectChangeActionChoices
 from .conditions import ConditionSet
+from .constants import WEBHOOK_EVENT_TYPES
 from .webhooks import generate_signature
 
 logger = logging.getLogger('netbox.webhooks_worker')
@@ -28,7 +28,7 @@ def eval_conditions(webhook, data):
 
 
 @job('default')
-def process_webhook(webhook, model_name, event, data, snapshots, timestamp, username, request_id):
+def process_webhook(webhook, model_name, event, data, timestamp, username, request_id=None, snapshots=None):
     """
     Make a POST request to the defined Webhook
     """
@@ -38,14 +38,17 @@ def process_webhook(webhook, model_name, event, data, snapshots, timestamp, user
 
     # Prepare context data for headers & body templates
     context = {
-        'event': dict(ObjectChangeActionChoices)[event].lower(),
+        'event': WEBHOOK_EVENT_TYPES[event],
         'timestamp': timestamp,
         'model': model_name,
         'username': username,
         'request_id': request_id,
         'data': data,
-        'snapshots': snapshots,
     }
+    if snapshots:
+        context.update({
+            'snapshots': snapshots
+        })
 
     # Build the headers for the HTTP request
     headers = {