mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Permanently connect change logging & webhook receivers
This commit is contained in:
committed by
Jeremy Stretch
parent
cd8943144b
commit
4a95cfd1c4
@ -1,8 +1,5 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django.db.models.signals import m2m_changed, pre_delete, post_save
|
|
||||||
|
|
||||||
from extras.signals import clear_webhooks, clear_webhook_queue, handle_changed_object, handle_deleted_object
|
|
||||||
from netbox.context import current_request, webhooks_queue
|
from netbox.context import current_request, webhooks_queue
|
||||||
from .webhooks import flush_webhooks
|
from .webhooks import flush_webhooks
|
||||||
|
|
||||||
@ -18,21 +15,8 @@ def change_logging(request):
|
|||||||
current_request.set(request)
|
current_request.set(request)
|
||||||
webhooks_queue.set([])
|
webhooks_queue.set([])
|
||||||
|
|
||||||
# Connect our receivers to the post_save and post_delete signals.
|
|
||||||
post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
|
|
||||||
m2m_changed.connect(handle_changed_object, dispatch_uid='handle_changed_object')
|
|
||||||
pre_delete.connect(handle_deleted_object, dispatch_uid='handle_deleted_object')
|
|
||||||
clear_webhooks.connect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
|
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# Disconnect change logging signals. This is necessary to avoid recording any errant
|
|
||||||
# changes during test cleanup.
|
|
||||||
post_save.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
|
|
||||||
m2m_changed.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
|
|
||||||
pre_delete.disconnect(handle_deleted_object, dispatch_uid='handle_deleted_object')
|
|
||||||
clear_webhooks.disconnect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
|
|
||||||
|
|
||||||
# Flush queued webhooks to RQ
|
# Flush queued webhooks to RQ
|
||||||
flush_webhooks(webhooks_queue.get())
|
flush_webhooks(webhooks_queue.get())
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ from .choices import ObjectChangeActionChoices
|
|||||||
from .models import ConfigRevision, CustomField, ObjectChange
|
from .models import ConfigRevision, CustomField, ObjectChange
|
||||||
from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
|
from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Change logging/webhooks
|
# Change logging/webhooks
|
||||||
#
|
#
|
||||||
@ -22,22 +23,32 @@ from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
|
|||||||
clear_webhooks = Signal()
|
clear_webhooks = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
def is_same_object(instance, webhook_data, 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.
|
||||||
|
"""
|
||||||
|
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']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver((post_save, m2m_changed))
|
||||||
def handle_changed_object(sender, instance, **kwargs):
|
def handle_changed_object(sender, instance, **kwargs):
|
||||||
"""
|
"""
|
||||||
Fires when an object is created or updated.
|
Fires when an object is created or updated.
|
||||||
"""
|
"""
|
||||||
|
m2m_changed = False
|
||||||
|
|
||||||
if not hasattr(instance, 'to_objectchange'):
|
if not hasattr(instance, 'to_objectchange'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Get the current request, or bail if not set
|
||||||
request = current_request.get()
|
request = current_request.get()
|
||||||
m2m_changed = False
|
if request is None:
|
||||||
|
return
|
||||||
def is_same_object(instance, webhook_data):
|
|
||||||
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']
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine the type of change being made
|
# Determine the type of change being made
|
||||||
if kwargs.get('created'):
|
if kwargs.get('created'):
|
||||||
@ -69,7 +80,7 @@ def handle_changed_object(sender, instance, **kwargs):
|
|||||||
|
|
||||||
# If this is an M2M change, update the previously queued webhook (from post_save)
|
# If this is an M2M change, update the previously queued webhook (from post_save)
|
||||||
queue = webhooks_queue.get()
|
queue = webhooks_queue.get()
|
||||||
if m2m_changed and queue and is_same_object(instance, queue[-1]):
|
if m2m_changed and queue and is_same_object(instance, queue[-1], request.id):
|
||||||
instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments
|
instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments
|
||||||
queue[-1]['data'] = serialize_for_webhook(instance)
|
queue[-1]['data'] = serialize_for_webhook(instance)
|
||||||
queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
|
queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
|
||||||
@ -84,6 +95,7 @@ def handle_changed_object(sender, instance, **kwargs):
|
|||||||
model_updates.labels(instance._meta.model_name).inc()
|
model_updates.labels(instance._meta.model_name).inc()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_delete)
|
||||||
def handle_deleted_object(sender, instance, **kwargs):
|
def handle_deleted_object(sender, instance, **kwargs):
|
||||||
"""
|
"""
|
||||||
Fires when an object is deleted.
|
Fires when an object is deleted.
|
||||||
@ -91,7 +103,10 @@ def handle_deleted_object(sender, instance, **kwargs):
|
|||||||
if not hasattr(instance, 'to_objectchange'):
|
if not hasattr(instance, 'to_objectchange'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Get the current request, or bail if not set
|
||||||
request = current_request.get()
|
request = current_request.get()
|
||||||
|
if request is None:
|
||||||
|
return
|
||||||
|
|
||||||
# Record an ObjectChange if applicable
|
# Record an ObjectChange if applicable
|
||||||
if hasattr(instance, 'to_objectchange'):
|
if hasattr(instance, 'to_objectchange'):
|
||||||
@ -109,6 +124,7 @@ def handle_deleted_object(sender, instance, **kwargs):
|
|||||||
model_deletes.labels(instance._meta.model_name).inc()
|
model_deletes.labels(instance._meta.model_name).inc()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(clear_webhooks)
|
||||||
def clear_webhook_queue(sender, **kwargs):
|
def clear_webhook_queue(sender, **kwargs):
|
||||||
"""
|
"""
|
||||||
Delete any queued webhooks (e.g. because of an aborted bulk transaction)
|
Delete any queued webhooks (e.g. because of an aborted bulk transaction)
|
||||||
|
@ -6,5 +6,5 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
current_request = ContextVar('current_request')
|
current_request = ContextVar('current_request', default=None)
|
||||||
webhooks_queue = ContextVar('webhooks_queue')
|
webhooks_queue = ContextVar('webhooks_queue')
|
||||||
|
Reference in New Issue
Block a user