diff --git a/pyproject.toml b/pyproject.toml index 33b91041..c81856fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "royalnet" - version = "5.5" + version = "5.6" description = "A multipurpose bot and web framework" authors = ["Stefano Pigozzi "] license = "AGPL-3.0+" diff --git a/royalnet/backpack/stars/__init__.py b/royalnet/backpack/stars/__init__.py index 3c869821..18b13dec 100644 --- a/royalnet/backpack/stars/__init__.py +++ b/royalnet/backpack/stars/__init__.py @@ -4,6 +4,8 @@ from .api_login_royalnet import ApiLoginRoyalnetStar from .api_token_info import ApiTokenInfoStar from .api_token_passwd import ApiTokenPasswdStar from .api_token_create import ApiTokenCreateStar +from .api_docs import ApiDocsStar +from .docs import DocsStar # Enter the PageStars of your Pack here! available_page_stars = [ @@ -12,6 +14,8 @@ available_page_stars = [ ApiTokenInfoStar, ApiTokenPasswdStar, ApiTokenCreateStar, + ApiDocsStar, + DocsStar, ] # Don't change this, it should automatically generate __all__ diff --git a/royalnet/backpack/stars/api_docs.py b/royalnet/backpack/stars/api_docs.py new file mode 100644 index 00000000..128afb26 --- /dev/null +++ b/royalnet/backpack/stars/api_docs.py @@ -0,0 +1,10 @@ +import royalnet.utils as ru +from royalnet.constellation.api import * +from royalnet.version import semantic + + +class ApiDocsStar(ApiStar): + path = "/api/docs" + + async def api(self, data: ApiData) -> ru.JSON: + return diff --git a/royalnet/backpack/stars/docs.py b/royalnet/backpack/stars/docs.py new file mode 100644 index 00000000..885d2616 --- /dev/null +++ b/royalnet/backpack/stars/docs.py @@ -0,0 +1,45 @@ +import json +from typing import * +from royalnet.constellation import PageStar +from royalnet.constellation.api import ApiStar +from starlette.requests import Request +from starlette.responses import Response, HTMLResponse +from royalnet.version import semantic + + +class DocsStar(PageStar): + path = "/docs" + + async def page(self, request: Request) -> Response: + spec = json.dumps({ + "swagger": "2.0", + "info": { + "description": "Autogenerated Royalnet API documentation", + "title": "Royalnet", + "version": f"{semantic}", + "paths": [star.swagger() for star in self.constellation.stars if isinstance(star, ApiStar)] + } + }) + + return HTMLResponse( + f""" + + + Royalnet Docs + + + +
+ + + + """ + ) diff --git a/royalnet/constellation/api/apistar.py b/royalnet/constellation/api/apistar.py index 305c58a2..32b4e250 100644 --- a/royalnet/constellation/api/apistar.py +++ b/royalnet/constellation/api/apistar.py @@ -48,37 +48,27 @@ class ApiStar(PageStar, ABC): raise NotImplementedError() @classmethod - def swagger(cls) -> str: + def swagger(cls) -> ru.JSON: """Generate one or more swagger paths for this ApiStar.""" - string = [f'{cls.path}:\n'] + result = {} for method in cls.methods: - string.append( - f' {method.lower()}:\n' - f' summary: "{cls.summary}"\n' - f' description: "{cls.description}"\n' - f' produces:\n' - f' - "application/json"\n' - f' responses:\n' - f' 200:\n' - f' description: "Success"\n' - f' 400:\n' - f' description: "Bad request"\n' - f' 403:\n' - f' description: "Forbidden"\n' - f' 404:\n' - f' description: "Not found"\n' - f' 500:\n' - f' description: "Serverside unhandled exception"\n' - f' 501:\n' - f' description: "Not yet implemented"\n' - ) - if len(cls.parameters) > 0: - string.append(f' parameters:\n') - for parameter in cls.parameters: - string.append( - f' - name: "{parameter}"\n' - f' in: "query"\n' - f' description: "{cls.parameters[parameter]}"\n' - f' type: "string"\n' - ) - return "".join(string).rstrip("\n") + result[method.lower()] = { + "summary": cls.summary, + "description": cls.description, + "produces": ["application/json"], + "responses": { + "200": {"description": "Success"}, + "400": {"description": "Bad request"}, + "403": {"description": "Forbidden"}, + "404": {"description": "Not found"}, + "500": {"description": "Serverside unhandled exception"}, + "501": {"description": "Not yet implemented"} + }, + "paameters": [{ + "name": parameter, + "in": "query", + "description": cls.parameters[parameter], + "type": "string" + } for parameter in cls.parameters] + } + return result diff --git a/royalnet/constellation/constellation.py b/royalnet/constellation/constellation.py index 2cdc94e9..1065c6df 100644 --- a/royalnet/constellation/constellation.py +++ b/royalnet/constellation/constellation.py @@ -99,6 +99,9 @@ class Constellation: self.starlette = starlette.applications.Starlette(debug=__debug__) """The :class:`~starlette.Starlette` app.""" + self.stars: List[PageStar] = [] + """A list of all the :class:`PageStar` registered to this :class:`Constellation`.""" + # Register Events for pack_name in packs: pack = packs[pack_name] @@ -268,6 +271,7 @@ class Constellation: f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.") ru.sentry_exc(e) continue + self.stars.append(page_star_instance) self.starlette.add_route(*self._page_star_wrapper(page_star_instance)) def run_blocking(self): diff --git a/royalnet/generate.py b/royalnet/generate.py index ff7105de..c0366bf8 100644 --- a/royalnet/generate.py +++ b/royalnet/generate.py @@ -47,29 +47,6 @@ def run(config_filename, file_format): for line in lines: p(line) - elif file_format == "swagger": - p( - f'swagger: "2.0"\n' - f'info:\n' - f' description: "This is autogenerated Royalnet API documentation."\n' - f' title: "Royalnet"\n' - f' version: "{semantic}"\n' - f'paths:' - ) - for pack_name in packs: - pack = packs[pack_name] - - try: - page_stars = pack["stars"].available_page_stars - except AttributeError: - p(f"Pack `{pack}` does not have the `available_page_stars` attribute.", err=True) - continue - for page_star in page_stars: - try: - p(f' {page_star.swagger()}') - except AttributeError: - pass - else: raise click.ClickException("Unknown format") diff --git a/royalnet/version.py b/royalnet/version.py index 63bf6999..4705c69e 100644 --- a/royalnet/version.py +++ b/royalnet/version.py @@ -1 +1 @@ -semantic = "5.5" +semantic = "5.6"