File: utils.py

package info (click to toggle)
python-django-rest-hooks 1.6.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 292 kB
  • sloc: python: 802; sh: 6; makefile: 5
file content (179 lines) | stat: -rw-r--r-- 5,918 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import django

try:
    from django.apps import apps as django_apps
except ImportError:
    django_apps = None
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings

if django.VERSION >= (2, 0,):
    get_model_kwargs = {'require_ready': False}
else:
    get_model_kwargs = {}


def get_module(path):
    """
    A modified duplicate from Django's built in backend
    retriever.

        slugify = get_module('django.template.defaultfilters.slugify')
    """
    try:
        from importlib import import_module
    except ImportError as e:
        from django.utils.importlib import import_module

    try:
        mod_name, func_name = path.rsplit('.', 1)
        mod = import_module(mod_name)
    except ImportError as e:
        raise ImportError(
            'Error importing alert function {0}: "{1}"'.format(mod_name, e))

    try:
        func = getattr(mod, func_name)
    except AttributeError:
        raise ImportError(
            ('Module "{0}" does not define a "{1}" function'
                            ).format(mod_name, func_name))

    return func


def get_hook_model():
    """
    Returns the Custom Hook model if defined in settings,
    otherwise the default Hook model.
    """
    model_label = getattr(settings, 'HOOK_CUSTOM_MODEL', None)
    if django_apps:
        model_label = (model_label or 'rest_hooks.Hook').replace('.models.', '.')
        try:
            return django_apps.get_model(model_label, **get_model_kwargs)
        except ValueError:
            raise ImproperlyConfigured("HOOK_CUSTOM_MODEL must be of the form 'app_label.model_name'")
        except LookupError:
            raise ImproperlyConfigured(
                "HOOK_CUSTOM_MODEL refers to model '%s' that has not been installed" % model_label
            )
    else:
        if model_label in (None, 'rest_hooks.Hook'):
            from rest_hooks.models import Hook
            HookModel = Hook
        else:
            try:
                HookModel = get_module(settings.HOOK_CUSTOM_MODEL)
            except ImportError:
                raise ImproperlyConfigured(
                    "HOOK_CUSTOM_MODEL refers to model '%s' that cannot be imported" % model_label
                )
        return HookModel



def find_and_fire_hook(event_name, instance, user_override=None, payload_override=None):
    """
    Look up Hooks that apply
    """
    try:
        from django.contrib.auth import get_user_model
        User = get_user_model()
    except ImportError:
        from django.contrib.auth.models import User
    from rest_hooks.models import HOOK_EVENTS

    if event_name not in HOOK_EVENTS.keys():
        raise Exception(
            '"{}" does not exist in `settings.HOOK_EVENTS`.'.format(event_name)
        )

    filters = {'event': event_name}

    # Ignore the user if the user_override is False
    if user_override is not False:
        if user_override:
            filters['user'] = user_override
        elif hasattr(instance, 'user'):
            filters['user'] = instance.user
        elif isinstance(instance, User):
            filters['user'] = instance
        else:
            raise Exception(
                '{} has no `user` property. REST Hooks needs this.'.format(repr(instance))
            )
    # NOTE: This is probably up for discussion, but I think, in this
    # case, instead of raising an error, we should fire the hook for
    # all users/accounts it is subscribed to. That would be a genuine
    # usecase rather than erroring because no user is associated with
    # this event.

    HookModel = get_hook_model()

    hooks = HookModel.objects.filter(**filters)
    for hook in hooks:
        hook.deliver_hook(instance, payload_override=payload_override)


def distill_model_event(
        instance,
        model=False,
        action=False,
        user_override=None,
        event_name=False,
        trust_event_name=False,
        payload_override=None,
        ):
    """
    Take `event_name` or determine it using action and model
    from settings.HOOK_EVENTS, and let hooks fly.

    if `event_name` is passed together with `model` or `action`, then
    they should be the same as in settings or `trust_event_name` should be
    `True`

    If event_name is not found or is invalidated, then just quit silently.

    If payload_override is passed, then it will be passed into HookModel.deliver_hook

    """
    from rest_hooks.models import get_event_actions_config, HOOK_EVENTS

    if event_name is False and (model is False or action is False):
        raise TypeError(
            'distill_model_event() requires either `event_name` argument or '
            'both `model` and `action` arguments.'
        )
    if event_name:
        if trust_event_name:
            pass
        elif event_name in HOOK_EVENTS:
            auto = HOOK_EVENTS[event_name]
            if auto:
                allowed_model, allowed_action = auto.rsplit('.', 1)

                allowed_action_parts = allowed_action.rsplit('+', 1)
                allowed_action = allowed_action_parts[0]

                model = model or allowed_model
                action = action or allowed_action

                if not (model == allowed_model and action == allowed_action):
                    event_name = None

                if len(allowed_action_parts) == 2:
                    user_override = False
    else:
        event_actions_config = get_event_actions_config()

        event_name, ignore_user_override = event_actions_config.get(model, {}).get(action, (None, False))
        if ignore_user_override:
            user_override = False

    if event_name:
        if getattr(settings, 'HOOK_FINDER', None):
            finder = get_module(settings.HOOK_FINDER)
        else:
            finder = find_and_fire_hook
        finder(event_name, instance, user_override=user_override, payload_override=payload_override)