hookee.hookeemanager — hookee 2.0.0 documentation
Description: Command line webhooks, on demand! Dump request data to the console, customize responses, configurable in any number of ways through plugins.

# Source code for hookee.hookeemanager

    import time

    import click
    from hookee.exception import HookeeError, HookeeConfigError

    from hookee.conf import Config
    from hookee.pluginmanager import PluginManager
    from hookee.server import Server
    from hookee.tunnel import Tunnel
    from hookee.util import PrintUtil

    __author__ = "Alex Laird"
    __copyright__ = "Copyright 2023, Alex Laird"
    __version__ = "2.0.8"

    class HookeeManager:
        """
        An object that manages the state of a ``hookee`` runtime. Reads app configuration, loads enabled plugins,
        and manages the long-lived state of ``hookee`` if a server and tunnel are started.

        If instantiating for a custom integration, pass a :class:`~hookee.conf.Config` with args that otherwise would have
        been passed to the CLI (see ``hookee --help``). For example:

        .. code-block:: python

            from hookee import HookeeManager
            from hookee.conf import Config

            config = Config(subdomain="my_domain",
                            region="eu")
            hookee_manager = HookeeManager(config=config)

        A ``response_callback`` function can also be passed instead of defining a raw ``response`` and ``content-type``
        (or needing to use plugins) when integrating with ``hookee``:

        .. code-block:: python

            from hookee import HookeeManager
            from hookee.conf import Config

            def response_callback(request, response):
                response.data = "<Response>Ok</Response>"
                response.headers["Content-Type"] = "application/xml"
                return response

            config = Config(response_callback=response_callback)
            hookee_manager = HookeeManager(config=config)

        :var ctx: The ``click`` context.
        :vartype ctx: click.Context
        :var config: The ``hookee`` configuration.
        :vartype config: Config
        :var plugin_manager: Reference to the Plugin Manager.
        :vartype plugin_manager: PluginManager
        :var print_util: Reference to the PrintUtil.
        :vartype print_util: PrintUtil
        :var tunnel: Reference to the Tunnel.
        :vartype tunnel: Tunnel
        :var server: Reference to the Server.
        :vartype server: Server
        :var alive: ``True`` when this object is managing an active tunnel and server.
        :vartype alive: bool
        """

        def __init__(self, config=None, load_plugins=True):
            self.ctx = click.get_current_context(silent=True)

            if config is None:
                try:
                    data = self.ctx.obj if self.ctx is not None else {}
                    config = Config(**data)
                except HookeeConfigError as e:
                    self.fail(str(e), e)

            self.config = config
            self.plugin_manager = PluginManager(self)
            self.print_util = PrintUtil(self.config)

            if load_plugins:
                self.plugin_manager.load_plugins()

            self.tunnel = Tunnel(self)
            self.server = Server(self)

            self.alive = False

            self.print_hookee_banner()

        def run(self):
            """
            If one is not already running, start a managed server and tunnel and block until an interrupt
            is received (or ``alive`` is set to ``False``).
            """
            if not self.alive:
                try:
                    self._init_server_and_tunnel()

                    while self.alive:
                        time.sleep(1)
                except KeyboardInterrupt:
                    pass

                self.stop()

        def stop(self):
            """
            If running, shutdown the managed server and tunnel.
            """
            if self.alive:
                self.server.stop()
                if self.tunnel._thread:
                    self.tunnel._thread.alive = False

                # Wait for the other threads to teardown
                while self.server._thread and self.tunnel._thread:
                    time.sleep(1)

                self.alive = False

        def print_hookee_banner(self):
            self.print_util.print_open_header("", "=")
            self.print_util.print_basic("""                .__                   __
                    |  |__   ____   ____ |  | __ ____   ____
                    |  |  \ /  _ \ /  _ \|  |/ // __ \_/ __ \
                    |   Y  (  <_> |  <_> )    <\  ___/\  ___/
                    |___|  /\____/ \____/|__|_ \\___  >\___  >
                         \/                   \/    \/     \/
                                                       v{}""".format(__version__), color="green", bold=True)
            self.print_util.print_basic()
            self.print_util.print_close_header("=", blank_line=False)

        def print_ready(self):
            self.print_util.print_open_header("Registered Plugins")

            plugins = self.plugin_manager.enabled_plugins()
            self.print_util.print_basic(" * Enabled Plugins: {}".format(plugins))
            if self.plugin_manager.response_callback:
                self.print_util.print_basic("   Response callback: enabled")

            self.print_util.print_close_header()

            self.print_util.print_open_header("Registered Endpoints")

            rules = list(filter(lambda r: r.rule not in ["/shutdown", "/static/<path:filename>", "/status"],
                                self.server.app.url_map.iter_rules()))
            for rule in rules:
                self.print_util.print_basic(" * {}{}".format(self.tunnel.public_url, rule.rule), print_when_logging=True)
                self.print_util.print_basic("   Methods: {}".format(sorted(list(rule.methods))), print_when_logging=True)

            self.print_util.print_close_header()

            self.print_util.print_basic()
            self.print_util.print_basic("--> Ready, send a request to a registered endpoint ...", color="green", bold=True)
            self.print_util.print_basic()

        def fail(self, msg, e=None):
            """
            Shutdown the current application with a failure. If a CLI Context exists, that will be used to invoke the
            failure, otherwise an exception will be thrown for failures to be caught.

            :param msg: The failure message.
            :type msg: str
            :param e: The error being raised.
            :type e: HookeeError, optional
            """
            if self.ctx is not None:
                self.ctx.fail(msg)
            elif e:
                raise e
            else:
                raise HookeeError(msg)

        def _init_server_and_tunnel(self):
            self.alive = True
            self.server.start()
            self.tunnel.start()

            self.print_ready()