From 839afe5ac067f55ad585c8fc63601bbba4f8894f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 3 Nov 2021 14:01:59 -0400 Subject: [PATCH] Improve webhook tests --- netbox/extras/tests/test_webhooks.py | 36 ++++++++++++++++++++++++++-- netbox/extras/webhooks_worker.py | 20 +++++++++++++--- netbox/templates/extras/webhook.html | 12 ++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/netbox/extras/tests/test_webhooks.py b/netbox/extras/tests/test_webhooks.py index 57db6dd02..811260f92 100644 --- a/netbox/extras/tests/test_webhooks.py +++ b/netbox/extras/tests/test_webhooks.py @@ -9,11 +9,12 @@ from django.urls import reverse from requests import Session from rest_framework import status +from dcim.choices import SiteStatusChoices from dcim.models import Site from extras.choices import ObjectChangeActionChoices from extras.models import Tag, Webhook -from extras.webhooks import enqueue_object, flush_webhooks, generate_signature -from extras.webhooks_worker import process_webhook +from extras.webhooks import enqueue_object, flush_webhooks, generate_signature, serialize_for_webhook +from extras.webhooks_worker import eval_conditions, process_webhook from utilities.testing import APITestCase @@ -251,6 +252,37 @@ class WebhookTest(APITestCase): self.assertEqual(job.kwargs['snapshots']['prechange']['name'], sites[i].name) self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo']) + def test_webhook_conditions(self): + # Create a conditional Webhook + webhook = Webhook( + name='Conditional Webhook', + type_create=True, + type_update=True, + payload_url='http://localhost/', + conditions={ + 'and': [ + { + 'attr': 'status.value', + 'value': 'active', + } + ] + } + ) + + # Create a Site to evaluate + site = Site.objects.create(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_STAGING) + data = serialize_for_webhook(site) + + # Evaluate the conditions (status='staging') + self.assertFalse(eval_conditions(webhook, data)) + + # Change the site's status + site.status = SiteStatusChoices.STATUS_ACTIVE + data = serialize_for_webhook(site) + + # Evaluate the conditions (status='active') + self.assertTrue(eval_conditions(webhook, data)) + def test_webhooks_worker(self): request_id = uuid.uuid4() diff --git a/netbox/extras/webhooks_worker.py b/netbox/extras/webhooks_worker.py index 6bbfba907..1f0a66b8a 100644 --- a/netbox/extras/webhooks_worker.py +++ b/netbox/extras/webhooks_worker.py @@ -12,15 +12,29 @@ from .webhooks import generate_signature logger = logging.getLogger('netbox.webhooks_worker') +def eval_conditions(webhook, data): + """ + Test whether the given data meets the conditions of the webhook (if any). Return True + if met or no conditions are specified. + """ + if not webhook.conditions: + return True + + logger.debug(f'Evaluating webhook conditions: {webhook.conditions}') + if ConditionSet(webhook.conditions).eval(data): + return True + + return False + + @job('default') def process_webhook(webhook, model_name, event, data, snapshots, timestamp, username, request_id): """ Make a POST request to the defined Webhook """ # Evaluate webhook conditions (if any) - if webhook.conditions: - if not ConditionSet(webhook.conditions).eval(data): - return + if not eval_conditions(webhook, data): + return # Prepare context data for headers & body templates context = { diff --git a/netbox/templates/extras/webhook.html b/netbox/templates/extras/webhook.html index c92ec4c99..9aa103cb6 100644 --- a/netbox/templates/extras/webhook.html +++ b/netbox/templates/extras/webhook.html @@ -132,6 +132,18 @@ +
+
+ Conditions +
+
+ {% if object.conditions %} +
{{ object.conditions|render_json }}
+ {% else %} +

None

+ {% endif %} +
+
Additional Headers