Skip to content
This repository has been archived by the owner on May 5, 2021. It is now read-only.

Dynamic HOOK_EVENTS #32

Open
stevelacey opened this issue Aug 11, 2016 · 5 comments
Open

Dynamic HOOK_EVENTS #32

stevelacey opened this issue Aug 11, 2016 · 5 comments

Comments

@stevelacey
Copy link

I am implementing a webhook-like system, and your library looks to fit the bill – however, I need to define hooks on the fly – they're based on statuses stored in the database that the users/admins can customise – just wondering why you require settings be defined in advance in settings.py, and what problems I'd run into ripping out that validation.

@avelis
Copy link
Contributor

avelis commented Aug 11, 2016

HOOK_EVENTS settings originally had a goal with connecting into Django models post/pre save signals. This allowed signal registration upon application startup and firing would be provided by Django's signals framework.

HOOK_EVENTS validation can be suppressed somewhat easily. The remaining challenge is to ensure that event firing occurs consistently. This will almost certainly require manual hook event method calls.

@stevelacey
Copy link
Author

stevelacey commented Aug 11, 2016

A little more detail about my specific use-case since I feel the feedback might be useful, regardless of whether I end up solving my problem with this library.

I am contemplating using https://github.com/justquick/django-activity-stream to get most of the functionality I want, i.e. keep track of actions performed against a model (regardless of webhooks). Additionally to that, what I need to be able to do is define webhooks that subscribe to that model – and listen on either all or some of the events/verbs emitted.

  • I don't need the webhooks to reference a user – in fact, they shouldn't
  • I do need the webhooks to be able to be bound to all (or at least multiple) events that occur on an object
  • I do need the events to be dynamic – there's a statuses table that defines what states the object I want notifications about can be in, and I want those statuses to be the event name/verb

I don't expect I'll get very far with this considering the deviations above without a lot of monkey patching so I might just liberate some aspects of the design, unless any of this stuff is functionality that might be desirable here for others.

@avelis
Copy link
Contributor

avelis commented Aug 12, 2016

@stevelacey If you are not to interested in a user property association then this library will not likely provide a lot of utility.

Thanks for explaining what you are trying to accomplish. It might be a feature this library could look into being able to provide.

@Ryanb58
Copy link

Ryanb58 commented Jun 21, 2018

I second this. I need the ownership of a webhook to refer to a different model and not with the User model. It would be nice to be able to use this library for that purpose.

@nsb
Copy link

nsb commented Jun 21, 2018

@Ryanb58 You can do this by creating your own hook class that inherits AbstractHook and then implementing a custom hook finder method.

Custom hook class

class CustomHook(AbstractHook):
    is_active = models.BooleanField(default=True)
    business = models.ForeignKey(Business,
                                 related_name='%(class)ss',
                                 on_delete=models.CASCADE)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name='%(class)ss',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

Here is my hook finder method where I fire if a hook has been set up for a business.

def find_and_fire_hook(event_name, instance, user_override=None):

    def fire_hook_on_commit(event_name, instance, user_override=user_override):

        if event_name not in HOOK_EVENTS.keys():
            raise ValueError(
                ('"{}" does not exist in '
                 '`settings.HOOK_EVENTS`.').format(event_name)
            )
        filters = {
            'event': event_name,
            'is_active': True,
        }

        if hasattr(instance, 'business'):
            filters['business'] = instance.business
        if user_override:
            filters['user'] = user_override
        elif hasattr(instance, 'user'):
            filters['user'] = instance.user
        elif isinstance(instance, Business):
            filters['business'] = instance

        for hook in CustomHook.objects.filter(**filters):
            logger.info("fire webhook {}".format(hook))
            hook.deliver_hook(instance)

    transaction.on_commit(partial(fire_hook_on_commit,
                                  event_name,
                                  instance,
                                  user_override=user_override))

Settings

HOOK_FINDER = 'mymodule.find_and_fire_hook'

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants