diff options
| author | 2018-05-30 22:38:54 +0100 | |
|---|---|---|
| committer | 2018-05-30 22:38:54 +0100 | |
| commit | aaa387a5b3bdb9bc416690dccef66196c76d373e (patch) | |
| tree | 270900787f1f8d76a97279f277fb281d45f37859 /pysite/mixins.py | |
| parent | Add FAQ about LPTHW (diff) | |
RabbitMQ mixin, powered by Kombu (#84)
* [RMQ] Add Kombi an an RMQMixin, as well as some constants
* [RMQ] Fix example in mixin docstring
* Update Pipfile.lock - for some reason, pipenv didn't lock kombu
Diffstat (limited to 'pysite/mixins.py')
| -rw-r--r-- | pysite/mixins.py | 95 | 
1 files changed, 95 insertions, 0 deletions
| diff --git a/pysite/mixins.py b/pysite/mixins.py index d0e822bf..6b5f7187 100644 --- a/pysite/mixins.py +++ b/pysite/mixins.py @@ -1,8 +1,14 @@ +from typing import Any, Dict  from weakref import ref  from flask import Blueprint +from kombu import Connection  from rethinkdb.ast import Table +from pysite.constants import ( +    BOT_EVENT_QUEUE, BotEventTypes, +    RMQ_HOST, RMQ_PASSWORD, RMQ_PORT, RMQ_USERNAME +)  from pysite.database import RethinkDB  from pysite.oauth import OAuthBackend @@ -58,6 +64,95 @@ class DBMixin:          return self._db() +class RMQMixin: +    """ +    Mixin for classes that make use of RabbitMQ. It allows routes to send JSON-encoded messages to specific RabbitMQ +    queues. + +    This class is intended to be mixed in alongside one of the other view classes. For example: + +    >>> class MyView(APIView, RMQMixin): +    ...     name = "my_view"  # Flask internal name for this route +    ...     path = "/my_view"  # Actual URL path to reach this route +    ...     queue_name = "my_queue"  # Name of the RabbitMQ queue to send on + +    Note that the queue name is optional if all you want to do is send bot events. + +    This class will also work with Websockets: + +    >>> class MyWebsocket(WS, RMQMixin): +    ...     name = "my_websocket" +    ...     path = "/my_websocket" +    ...     queue_name = "my_queue" +    """ + +    queue_name = "" + +    @classmethod +    def setup(cls: "RMQMixin", manager: "pysite.route_manager.RouteManager", blueprint: Blueprint): +        """ +        Set up the view by calling `super().setup()` as appropriate. + +        :param manager: Instance of the current RouteManager (used to get a handle for the database object) +        :param blueprint: Current Flask blueprint +        """ + +        if hasattr(super(), "setup"): +            super().setup(manager, blueprint)  # pragma: no cover + +    @property +    def rmq_connection(self) -> Connection: +        """ +        Get a Kombu AMQP connection object - use this in a context manager so that it gets closed after you're done + +        If you're just trying to send a message, check out `rmq_send` and `rmq_bot_event` instead. +        """ + +        return Connection(hostname=RMQ_HOST, userid=RMQ_USERNAME, password=RMQ_PASSWORD, port=RMQ_PORT) + +    def rmq_send(self, data: Dict[str, Any], routing_key: str = None): +        """ +        Send some data to the RabbitMQ queue + +        >>> self.rmq_send({ +        ...     "text": "My hovercraft is full of eels!", +        ...     "source": "Dirty Hungarian Phrasebook" +        ... }) +        ... + +        This will be delivered to the queue immediately. +        """ + +        if routing_key is None: +            routing_key = self.queue_name + +        with self.rmq_connection as c: +            producer = c.Producer() +            producer.publish(data, routing_key=routing_key) + +    def rmq_bot_event(self, event_type: BotEventTypes, data: Dict[str, Any]): +        """ +        Send an event to the queue responsible for delivering events to the bot + +        >>> self.rmq_bot_event(BotEventTypes.send_message, { +        ...     "channel": CHANNEL_MOD_LOG, +        ...     "message": "This is a plain-text message for @everyone, from the site!" +        ... }) +        ... + +        This will be delivered to the bot and actioned immediately, or when the bot comes online if it isn't already +        connected. +        """ + +        if not isinstance(event_type, BotEventTypes): +            raise ValueError("`event_type` must be a member of the the `pysite.constants.BotEventTypes` enum") + +        return self.rmq_send( +            {"event": event_type.value, "data": data}, +            routing_key=BOT_EVENT_QUEUE, +        ) + +  class OAuthMixin:      """      Mixin for the classes that need access to a logged in user's information. This class should be used | 
