From 17289979877e08da3d007a5ac09cb85650fee65c Mon Sep 17 00:00:00 2001 From: Peter Eckel Date: Wed, 21 Feb 2024 14:29:23 +0000 Subject: [PATCH] Fixes: #15194 - Check the whole queue for matching queued webhooks --- netbox/extras/signals.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/netbox/extras/signals.py b/netbox/extras/signals.py index f8dc204e7..bb8af9881 100644 --- a/netbox/extras/signals.py +++ b/netbox/extras/signals.py @@ -30,16 +30,21 @@ from .models import CustomField, ObjectChange, TaggedItem clear_events = Signal() -def is_same_object(instance, webhook_data, request_id): +def last_matching_index(instance, queue, request_id): """ - Compare the given instance to the most recent queued webhook object, returning True - if they match. This check is used to avoid creating duplicate webhook entries. + Find the latest queued webhook object matching the given instance, returning its index. + If no object is found, return None. This check is used to avoid creating duplicate webhook + entries. """ - return ( - ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and - instance.pk == webhook_data['object_id'] and - request_id == webhook_data['request_id'] - ) + try: + return max( + index_ for index_, webhook_data in enumerate(queue) + if ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and + instance.pk == webhook_data['object_id'] and + request_id == webhook_data['request_id'] + ) + except ValueError: + return None @receiver((post_save, m2m_changed)) @@ -87,12 +92,12 @@ def handle_changed_object(sender, instance, **kwargs): objectchange.request_id = request.id objectchange.save() - # If this is an M2M change, update the previously queued webhook (from post_save) + # Update the previously queued webhook from a preceeding post_save queue = events_queue.get() - if m2m_changed and queue and is_same_object(instance, queue[-1], request.id): + if queue and (last_index := last_matching_index(instance, queue, request.id)) is not None: instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments - queue[-1]['data'] = serialize_for_event(instance) - queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange'] + queue[last_index]['data'] = serialize_for_event(instance) + queue[last_index]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange'] else: enqueue_object(queue, instance, request.user, request.id, action) events_queue.set(queue)