From d667644ad3e5809fc2fe9b74a0a052118c03e4ff Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 27 Dec 2024 12:17:17 -0500 Subject: [PATCH] Fixes #18222: Include action data from event rule in webhook and custom script data --- docs/models/extras/eventrule.md | 14 +++++++++++++- netbox/extras/events.py | 12 ++++++++---- netbox/extras/tests/test_event_rules.py | 16 +++++++++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/docs/models/extras/eventrule.md b/docs/models/extras/eventrule.md index b48e17a1eef..9dca04529d9 100644 --- a/docs/models/extras/eventrule.md +++ b/docs/models/extras/eventrule.md @@ -10,7 +10,7 @@ See the [event rules documentation](../../features/event-rules.md) for more inf A unique human-friendly name. -### Content Types +### Object Types The type(s) of object in NetBox that will trigger the rule. @@ -38,3 +38,15 @@ The event types which will trigger the rule. At least one event type must be sel ### Conditions A set of [prescribed conditions](../../reference/conditions.md) against which the triggering object will be evaluated. If the conditions are defined but not met by the object, no action will be taken. An event rule that does not define any conditions will _always_ trigger. + +### Action Type + +The type of action to take when the rule triggers. This must be one of the following choices: + +* Webhook +* Custom script +* Notification + +### Action Data + +An optional dictionary of JSON data to pass when executing the rule. This can be useful to include additional context data, e.g. when transmitting a webhook. diff --git a/netbox/extras/events.py b/netbox/extras/events.py index f13a3b48fdb..95170e18def 100644 --- a/netbox/extras/events.py +++ b/netbox/extras/events.py @@ -90,6 +90,10 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non if not event_rule.eval_conditions(data): continue + # Compile event data + event_data = event_rule.action_data or {} + event_data.update(data) + # Webhooks if event_rule.action_type == EventRuleActionChoices.WEBHOOK: @@ -102,7 +106,7 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non "event_rule": event_rule, "model_name": object_type.model, "event_type": event_type, - "data": data, + "data": event_data, "snapshots": snapshots, "timestamp": timezone.now().isoformat(), "username": username, @@ -130,7 +134,7 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non instance=event_rule.action_object, name=script.name, user=user, - data=data + data=event_data ) # Notification groups @@ -138,8 +142,8 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non # Bulk-create notifications for all members of the notification group event_rule.action_object.notify( object_type=object_type, - object_id=data['id'], - object_repr=data.get('display'), + object_id=event_data['id'], + object_repr=event_data.get('display'), event_type=event_type ) diff --git a/netbox/extras/tests/test_event_rules.py b/netbox/extras/tests/test_event_rules.py index 7f7b0b81f31..2565e5bde9e 100644 --- a/netbox/extras/tests/test_event_rules.py +++ b/netbox/extras/tests/test_event_rules.py @@ -50,21 +50,24 @@ def setUpTestData(cls): event_types=[OBJECT_CREATED], action_type=EventRuleActionChoices.WEBHOOK, action_object_type=webhook_type, - action_object_id=webhooks[0].id + action_object_id=webhooks[0].id, + action_data={"foo": 1}, ), EventRule( name='Event Rule 2', event_types=[OBJECT_UPDATED], action_type=EventRuleActionChoices.WEBHOOK, action_object_type=webhook_type, - action_object_id=webhooks[0].id + action_object_id=webhooks[0].id, + action_data={"foo": 2}, ), EventRule( name='Event Rule 3', event_types=[OBJECT_DELETED], action_type=EventRuleActionChoices.WEBHOOK, action_object_type=webhook_type, - action_object_id=webhooks[0].id + action_object_id=webhooks[0].id, + action_data={"foo": 3}, ), )) for event_rule in event_rules: @@ -134,6 +137,7 @@ def test_single_create_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_CREATED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], response.data['id']) + self.assertEqual(job.kwargs['data']['foo'], 1) self.assertEqual(len(job.kwargs['data']['tags']), len(response.data['tags'])) self.assertEqual(job.kwargs['snapshots']['postchange']['name'], 'Site 1') self.assertEqual(job.kwargs['snapshots']['postchange']['tags'], ['Bar', 'Foo']) @@ -184,6 +188,7 @@ def test_bulk_create_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_CREATED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], response.data[i]['id']) + self.assertEqual(job.kwargs['data']['foo'], 1) self.assertEqual(len(job.kwargs['data']['tags']), len(response.data[i]['tags'])) self.assertEqual(job.kwargs['snapshots']['postchange']['name'], response.data[i]['name']) self.assertEqual(job.kwargs['snapshots']['postchange']['tags'], ['Bar', 'Foo']) @@ -215,6 +220,7 @@ def test_single_update_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_UPDATED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], site.pk) + self.assertEqual(job.kwargs['data']['foo'], 2) self.assertEqual(len(job.kwargs['data']['tags']), len(response.data['tags'])) self.assertEqual(job.kwargs['snapshots']['prechange']['name'], 'Site 1') self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo']) @@ -271,6 +277,7 @@ def test_bulk_update_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_UPDATED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], data[i]['id']) + self.assertEqual(job.kwargs['data']['foo'], 2) self.assertEqual(len(job.kwargs['data']['tags']), len(response.data[i]['tags'])) self.assertEqual(job.kwargs['snapshots']['prechange']['name'], sites[i].name) self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo']) @@ -297,6 +304,7 @@ def test_single_delete_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_DELETED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], site.pk) + self.assertEqual(job.kwargs['data']['foo'], 3) self.assertEqual(job.kwargs['snapshots']['prechange']['name'], 'Site 1') self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo']) @@ -330,6 +338,7 @@ def test_bulk_delete_process_eventrule(self): self.assertEqual(job.kwargs['event_type'], OBJECT_DELETED) self.assertEqual(job.kwargs['model_name'], 'site') self.assertEqual(job.kwargs['data']['id'], sites[i].pk) + self.assertEqual(job.kwargs['data']['foo'], 3) self.assertEqual(job.kwargs['snapshots']['prechange']['name'], sites[i].name) self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo']) @@ -358,6 +367,7 @@ def dummy_send(_, request, **kwargs): self.assertEqual(body['username'], 'testuser') self.assertEqual(body['request_id'], str(request_id)) self.assertEqual(body['data']['name'], 'Site 1') + self.assertEqual(body['data']['foo'], 1) return HttpResponse()