diff --git a/docs/advanced-usage/handlers.rst b/docs/advanced-usage/handlers.rst new file mode 100644 index 00000000..17a53f84 --- /dev/null +++ b/docs/advanced-usage/handlers.rst @@ -0,0 +1,51 @@ +Writing own handler +=================== + +It is possible to extend the application by adding own custom commands. To do so it is required to implement class, which derives from ``ahriman.application.handlers.handler.Handler`` and put it to the ``ahriman.application.handlers`` package. The class later will be loaded automatically and included to each command run. + +Let's imagine, that the new class implements ``help-web``, which prints server information to the stdout. To do so, we need to implement base ``ahriman.application.handlers.handler.Handler.run`` method which is entry point for all subcommands: + +.. code-block:: python + + from ahriman.application.application import Application + from ahriman.application.handlers.handler import Handler + + + class HelpWeb(Handler): + + @classmethod + def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, + report: bool) -> None: + # load application instance + # report is set to True to make sure that web client is loaded + application = Application(repository_id, configuration, report=True) + # extract web client + client = application.repository.reporter + + # send request to the server + response = client.make_request("GET", f"{client.address}/api/v1/info") + result = response.json() + print(result) + +The main functionality of the class is already described, but command is still not available yet. To do so, it is required to set ``arguments`` property, which is list of functions, which takes argument parser object, creates new subcommand and returns the modified parser, e.g.: + +.. code-block:: python + + import argparse + + from ahriman.application.handlers.handler import SubParserAction + + ... + + @staticmethod + def set_parser(root: SubParserAction) -> argparse.ArgumentParser: + parser = root.add_parser("help-web", help="get web server status", + description="request server info and print it to stdout") + + arguments = set_parser + +In addition, ``ahriman.application.handlers.handler.Handler.ALLOW_MULTI_ARCHITECTURE_RUN`` can be set to ``False`` in order to disable multiprocess run (e.g. in case if there are conflicting operations, like writting to stdout). + +Save the file above as ``/usr/lib/python3.12/site-packages/ahriman/application/handlers/help_web.py`` (replace ``python3.12`` with actual python version) and you are set. + +For more examples and details, please check builtin handlers and classes documentations. diff --git a/docs/advanced-usage.rst b/docs/advanced-usage/index.rst similarity index 96% rename from docs/advanced-usage.rst rename to docs/advanced-usage/index.rst index 3956c8c5..161c050f 100644 --- a/docs/advanced-usage.rst +++ b/docs/advanced-usage/index.rst @@ -1,6 +1,12 @@ Advanced usage ============== +.. toctree:: + :maxdepth: 2 + + handlers + views + Depending on the goal the package can be used in different ways. Nevertheless, in the most cases you will need some basic classes .. code-block:: python diff --git a/docs/advanced-usage/views.rst b/docs/advanced-usage/views.rst new file mode 100644 index 00000000..2334f5bc --- /dev/null +++ b/docs/advanced-usage/views.rst @@ -0,0 +1,41 @@ +Writing own API endpoint +======================== + +The web service loads views dynamically, thus it is possible to add custom API endpoint or even web page. The views must be derived from ``ahriman.web.views.base.BaseView`` and implements HTTP methods. The API specification will be also loaded (if available), but optional. The implementation must be saved into the ``ahriman.web.views`` package + +Let's consider example for API endpoint which always returns 204 with no response: + +.. code-block:: python + + from aiohttp.web import Response, HTTPNoContent + + from ahriman.web.views.base import BaseView + + class PingView(BaseView): + + async def get(self) -> Response: + # do nothing, just raise 204 response + # check public methods of the BaseView class for all available controls + raise HTTPNoContent + +The ``get()`` method can be decorated by ``aiohttp_apispec`` methods, but we will leave it for self-study. Consider checking examples of usages in the main package. + +In order to view to be set correctly to routes list, few more options are required to be set. First of all, it is required to specify ``ROUTES`` (list of strings), which contains list of all available routes, e.g.: + +.. code-block:: python + + ... + + ROUTES = ["/api/v1/ping"] + +In addition, it is also recommended to specify permission level for using this endpoint. Since this endpoint neither does anything nor returns sensitive information, it can be set to ``UserAccess.Unauthorized``: + +.. code-block:: python + + ... + + GET_PERMISSION = UserAccess.Unauthorized + +That's all. Just save the file as ``/usr/lib/python3.12/site-packages/ahriman/web/views/ping.py`` (replace ``python3.12`` with actual python version) and restart web server. + +For more examples and details, please check builtin handlers and classes documentations. diff --git a/docs/ahriman.application.handlers.rst b/docs/ahriman.application.handlers.rst index c754110c..8debf454 100644 --- a/docs/ahriman.application.handlers.rst +++ b/docs/ahriman.application.handlers.rst @@ -228,6 +228,14 @@ ahriman.application.handlers.triggers module :no-undoc-members: :show-inheritance: +ahriman.application.handlers.triggers\_support module +----------------------------------------------------- + +.. automodule:: ahriman.application.handlers.triggers_support + :members: + :no-undoc-members: + :show-inheritance: + ahriman.application.handlers.unsafe\_commands module ---------------------------------------------------- diff --git a/docs/architecture.rst b/docs/architecture.rst index 0e2313ed..bb2762c5 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -52,8 +52,10 @@ This package contains everything required for the most of application actions an This package also provides some generic functions and classes which may be used by other packages: * ``ahriman.core.exceptions`` provides custom exceptions. +* ``ahriman.core.module_loader`` provides ``implementations`` method which can be used for dynamic classes load. In particular, this method is used for web views and application handlers loading. * ``ahriman.core.spawn.Spawn`` is a tool which can spawn another ``ahriman`` process. This feature is used by web application. * ``ahriman.core.tree`` is a dependency tree implementation. +* ``ahriman.core.types`` are an additional global types for mypy checks. ``ahriman.models`` package ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -436,6 +438,9 @@ REST API supports only JSON data. Different APIs are separated into different packages: * ``ahriman.web.views.api`` not a real API, but some views which provide OpenAPI support. +* ``ahriman.web.views.*.auditlog`` provides event log API. +* ``ahriman.web.views.*.distributed`` is an API for builders interaction for multi-node setup. +* ``ahriman.web.views.*.pacakges`` contains views which provide information about existing packages. * ``ahriman.web.views.*.service`` provides views for application controls. * ``ahriman.web.views.*.status`` package provides REST API for application reporting. * ``ahriman.web.views.*.user`` package provides login and logout methods which can be called without authorization. diff --git a/docs/index.rst b/docs/index.rst index 977647bd..c967cebc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,6 +36,6 @@ Contents faq/index migrations/index architecture - advanced-usage + advanced-usage/index triggers modules