Fix #526 -- Add a webhook system (#1073)

- [x] Data model
- [x] UI
- [x] Fire hooks
- [x] Unit tests
- [x] Display logs
- [x] API to modify hooks
- [x] Documentation
- [x] More hooks!
This commit is contained in:
Raphael Michel
2018-11-08 16:38:05 +01:00
committed by GitHub
parent 74e8e73877
commit c2d03f5e6b
36 changed files with 1442 additions and 31 deletions

View File

@@ -53,6 +53,7 @@ class LoggingMixin:
from .organizer import TeamAPIToken
from ..notifications import get_all_notification_types
from ..services.notifications import notify
from pretix.api.webhooks import get_all_webhook_events, notify_webhooks
event = None
if isinstance(self, Event):
@@ -80,8 +81,21 @@ class LoggingMixin:
if save:
logentry.save()
if action in get_all_notification_types():
no_types = get_all_notification_types()
wh_types = get_all_webhook_events()
no_type = None
wh_type = None
typepath = logentry.action_type
while (not no_type or not wh_types) and '.' in typepath:
wh_type = wh_type or wh_types.get(typepath + ('.*' if typepath != logentry.action_type else ''))
no_type = no_type or no_types.get(typepath + ('.*' if typepath != logentry.action_type else ''))
typepath = typepath.rsplit('.', 1)[0]
if no_type:
notify.apply_async(args=(logentry.pk,))
if wh_type:
notify_webhooks.apply_async(args=(logentry.pk,))
return logentry

View File

@@ -63,6 +63,16 @@ class LogEntry(models.Model):
return response
return self.action_type
@cached_property
def organizer(self):
if self.event:
return self.event.organizer
elif hasattr(self.content_object, 'event'):
return self.content_object.event.organizer
elif hasattr(self.content_object, 'organizer'):
return self.content_object.organizer
return None
@cached_property
def display_object(self):
from . import Order, Voucher, Quota, Item, ItemCategory, Question, Event, TaxRule, SubEvent

View File

@@ -225,7 +225,7 @@ def register_default_notification_types(sender, **kwargs):
),
ParametrizedOrderNotificationType(
sender,
'pretix.event.order.changed',
'pretix.event.order.changed.*',
_('Order changed'),
_('Order {order.code} has been changed.')
),

View File

@@ -59,7 +59,8 @@ def _save_answers(op, answers, given_answers):
@transaction.atomic
def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, force=False,
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True):
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True,
user=None, auth=None):
"""
Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is
not valid at this time.
@@ -133,7 +134,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'forced': op.order.status != Order.STATUS_PAID,
'datetime': dt,
'list': clist.pk
})
}, user=user, auth=auth)
else:
if not force:
raise CheckInError(
@@ -147,4 +148,4 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'forced': force,
'datetime': dt,
'list': clist.pk
})
}, user=user, auth=auth)

View File

@@ -17,9 +17,15 @@ def notify(logentry_id: int):
if not logentry.event:
return # Ignore, we only have event-related notifications right now
types = get_all_notification_types(logentry.event)
notification_type = types.get(logentry.action_type)
notification_type = None
typepath = logentry.action_type
while not notification_type and '.' in typepath:
notification_type = types.get(typepath + ('.*' if typepath != logentry.action_type else ''))
typepath = typepath.rsplit('.', 1)[0]
if not notification_type:
return # Ignore, e.g. plugin not active for this event
return # No suitable plugin
# All users that have the permission to get the notification
users = logentry.event.get_users_with_permission(
@@ -33,7 +39,7 @@ def notify(logentry_id: int):
(ns.user, ns.method): ns.enabled
for ns in NotificationSetting.objects.filter(
event=logentry.event,
action_type=logentry.action_type,
action_type=notification_type.action_type,
user__pk__in=users.values_list('pk', flat=True)
)
}
@@ -41,7 +47,7 @@ def notify(logentry_id: int):
(ns.user, ns.method): ns.enabled
for ns in NotificationSetting.objects.filter(
event__isnull=True,
action_type=logentry.action_type,
action_type=notification_type.action_type,
user__pk__in=users.values_list('pk', flat=True)
)
}
@@ -49,20 +55,20 @@ def notify(logentry_id: int):
for um, enabled in notify_specific.items():
user, method = um
if enabled:
send_notification.apply_async(args=(logentry_id, user.pk, method))
send_notification.apply_async(args=(logentry_id, notification_type.action_type, user.pk, method))
for um, enabled in notify_global.items():
user, method = um
if enabled and um not in notify_specific:
send_notification.apply_async(args=(logentry_id, user.pk, method))
send_notification.apply_async(args=(logentry_id, notification_type.action_type, user.pk, method))
@app.task(base=ProfiledTask)
def send_notification(logentry_id: int, user_id: int, method: str):
def send_notification(logentry_id: int, action_type: str, user_id: int, method: str):
logentry = LogEntry.all.get(id=logentry_id)
user = User.objects.get(id=user_id)
types = get_all_notification_types(logentry.event)
notification_type = types.get(logentry.action_type)
notification_type = types.get(action_type)
if not notification_type:
return # Ignore, e.g. plugin not active for this event