2
0

webhooks.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import time
  2. from importlib import import_module
  3. from django.db.models.signals import post_save, post_delete
  4. from django.conf import settings
  5. from django.core.cache import caches
  6. from django.dispatch import Signal
  7. from django.contrib.contenttypes.models import ContentType
  8. from utilities.utils import dynamic_import
  9. from .models import Webhook
  10. #
  11. # Webhooks signals regiters and receivers
  12. #
  13. def get_or_set_webhook_cache():
  14. """
  15. Retrieve the webhook cache. If it is None set it to the current
  16. Webhook queryset
  17. """
  18. cache = caches['default']
  19. webhook_cache = cache.get('webhook_cache', None)
  20. if webhook_cache is None:
  21. webhook_cache = Webhook.objects.all()
  22. cache.set('webhook_cache', webhook_cache)
  23. return webhook_cache
  24. def enqueue_webhooks(webhooks, model_class, data, event, signal_received_timestamp):
  25. """
  26. Serialize data and enqueue webhooks
  27. """
  28. serializer_context = {
  29. 'request': None,
  30. }
  31. if isinstance(data, list):
  32. serializer_property = data[0].serializer
  33. serializer_cls = dynamic_import(serializer_property)
  34. serialized_data = serializer_cls(data, context=serializer_context, many=True)
  35. else:
  36. serializer_property = data.serializer
  37. serializer_cls = dynamic_import(serializer_property)
  38. serialized_data = serializer_cls(data, context=serializer_context)
  39. from django_rq import get_queue
  40. webhook_queue = get_queue('default')
  41. for webhook in webhooks:
  42. webhook_queue.enqueue("extras.webhooks_worker.process_webhook",
  43. webhook,
  44. serialized_data.data,
  45. model_class,
  46. event,
  47. signal_received_timestamp)
  48. def post_save_receiver(sender, instance, created, **kwargs):
  49. """
  50. Receives post_save signals from registered models. If the webhook
  51. backend is enabled, queue any webhooks that apply to the event.
  52. """
  53. if settings.WEBHOOKS_ENABLED:
  54. signal_received_timestamp = time.time()
  55. webhook_cache = get_or_set_webhook_cache()
  56. # look for any webhooks that match this event
  57. updated = not created
  58. obj_type = ContentType.objects.get_for_model(sender)
  59. webhooks = [
  60. x
  61. for x in webhook_cache
  62. if (
  63. x.enabled and x.type_create == created or x.type_update == updated and
  64. obj_type in x.obj_type.all()
  65. )
  66. ]
  67. event = 'created' if created else 'updated'
  68. if webhooks:
  69. enqueue_webhooks(webhooks, sender, instance, event, signal_received_timestamp)
  70. def post_delete_receiver(sender, instance, **kwargs):
  71. """
  72. Receives post_delete signals from registered models. If the webhook
  73. backend is enabled, queue any webhooks that apply to the event.
  74. """
  75. if settings.WEBHOOKS_ENABLED:
  76. signal_received_timestamp = time.time()
  77. webhook_cache = get_or_set_webhook_cache()
  78. obj_type = ContentType.objects.get_for_model(sender)
  79. # look for any webhooks that match this event
  80. webhooks = [x for x in webhook_cache if x.enabled and x.type_delete and obj_type in x.obj_type.all()]
  81. if webhooks:
  82. enqueue_webhooks(webhooks, sender, instance, 'deleted', signal_received_timestamp)
  83. def bulk_operation_receiver(sender, **kwargs):
  84. """
  85. Receives bulk_operation_signal signals from registered models. If the webhook
  86. backend is enabled, queue any webhooks that apply to the event.
  87. """
  88. if settings.WEBHOOKS_ENABLED:
  89. signal_received_timestamp = time.time()
  90. event = kwargs['event']
  91. webhook_cache = get_or_set_webhook_cache()
  92. obj_type = ContentType.objects.get_for_model(sender)
  93. # look for any webhooks that match this event
  94. if event == 'created':
  95. webhooks = [x for x in webhook_cache if x.enabled and x.type_create and obj_type in x.obj_type.all()]
  96. elif event == 'updated':
  97. webhooks = [x for x in webhook_cache if x.enabled and x.type_update and obj_type in x.obj_type.all()]
  98. elif event == 'deleted':
  99. webhooks = [x for x in webhook_cache if x.enabled and x.type_delete and obj_type in x.obj_type.all()]
  100. else:
  101. webhooks = None
  102. if webhooks:
  103. enqueue_webhooks(webhooks, sender, list(kwargs['instances']), event, signal_received_timestamp)
  104. # the bulk operation signal is used to overcome signals not being sent for bulk model changes
  105. bulk_operation_signal = Signal(providing_args=["instances", "event"])
  106. bulk_operation_signal.connect(bulk_operation_receiver)
  107. def register_signals(senders):
  108. """
  109. Take a list of senders (Models) and register them to the post_save
  110. and post_delete signal receivers.
  111. """
  112. if settings.WEBHOOKS_ENABLED:
  113. # only register signals if the backend is enabled
  114. # this reduces load by not firing signals if the
  115. # webhook backend feature is disabled
  116. for sender in senders:
  117. post_save.connect(post_save_receiver, sender=sender)
  118. post_delete.connect(post_delete_receiver, sender=sender)