mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
Merge pull request #121 from Steffo99/autoswagger
Autogenerated OpenAPI 3.0 documentation
This commit is contained in:
commit
904597c0ca
12 changed files with 145 additions and 2 deletions
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "royalnet"
|
name = "royalnet"
|
||||||
version = "5.5"
|
version = "5.6"
|
||||||
description = "A multipurpose bot and web framework"
|
description = "A multipurpose bot and web framework"
|
||||||
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
|
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
|
||||||
license = "AGPL-3.0+"
|
license = "AGPL-3.0+"
|
||||||
|
|
|
@ -4,6 +4,7 @@ from .api_login_royalnet import ApiLoginRoyalnetStar
|
||||||
from .api_token_info import ApiTokenInfoStar
|
from .api_token_info import ApiTokenInfoStar
|
||||||
from .api_token_passwd import ApiTokenPasswdStar
|
from .api_token_passwd import ApiTokenPasswdStar
|
||||||
from .api_token_create import ApiTokenCreateStar
|
from .api_token_create import ApiTokenCreateStar
|
||||||
|
from .docs import DocsStar
|
||||||
|
|
||||||
# Enter the PageStars of your Pack here!
|
# Enter the PageStars of your Pack here!
|
||||||
available_page_stars = [
|
available_page_stars = [
|
||||||
|
@ -12,6 +13,7 @@ available_page_stars = [
|
||||||
ApiTokenInfoStar,
|
ApiTokenInfoStar,
|
||||||
ApiTokenPasswdStar,
|
ApiTokenPasswdStar,
|
||||||
ApiTokenCreateStar,
|
ApiTokenCreateStar,
|
||||||
|
DocsStar,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Don't change this, it should automatically generate __all__
|
# Don't change this, it should automatically generate __all__
|
||||||
|
|
|
@ -11,6 +11,15 @@ class ApiLoginRoyalnetStar(ApiStar):
|
||||||
|
|
||||||
methods = ["POST"]
|
methods = ["POST"]
|
||||||
|
|
||||||
|
summary = "Login as a Royalnet user, creating a 7-day login token."
|
||||||
|
|
||||||
|
parameters = {
|
||||||
|
"username": "The name of the user you are logging in as.",
|
||||||
|
"password": "The password of the user you are logging in as."
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = ["royalnet"]
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
TokenT = self.alchemy.get(Token)
|
TokenT = self.alchemy.get(Token)
|
||||||
UserT = self.alchemy.get(User)
|
UserT = self.alchemy.get(User)
|
||||||
|
|
|
@ -6,6 +6,10 @@ import royalnet.utils as ru
|
||||||
class ApiRoyalnetVersionStar(ApiStar):
|
class ApiRoyalnetVersionStar(ApiStar):
|
||||||
path = "/api/royalnet/version/v1"
|
path = "/api/royalnet/version/v1"
|
||||||
|
|
||||||
|
summary = "Get the current Royalnet version."
|
||||||
|
|
||||||
|
tags = ["royalnet"]
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
return {
|
return {
|
||||||
"semantic": rv.semantic
|
"semantic": rv.semantic
|
||||||
|
|
|
@ -10,6 +10,14 @@ class ApiTokenCreateStar(ApiStar):
|
||||||
|
|
||||||
methods = ["POST"]
|
methods = ["POST"]
|
||||||
|
|
||||||
|
summary = "Create a new login token of any duration."
|
||||||
|
|
||||||
|
parameters = {
|
||||||
|
"duration": "The duration in seconds of the new token."
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = ["royalnet"]
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
user = await data.user()
|
user = await data.user()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -5,6 +5,10 @@ from royalnet.constellation.api import *
|
||||||
class ApiTokenInfoStar(ApiStar):
|
class ApiTokenInfoStar(ApiStar):
|
||||||
path = "/api/token/info/v1"
|
path = "/api/token/info/v1"
|
||||||
|
|
||||||
|
summary = "Get info the current login token."
|
||||||
|
|
||||||
|
tags = ["royalnet"]
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
token = await data.token()
|
token = await data.token()
|
||||||
return token.json()
|
return token.json()
|
||||||
|
|
|
@ -11,6 +11,14 @@ class ApiTokenPasswdStar(ApiStar):
|
||||||
|
|
||||||
methods = ["POST"]
|
methods = ["POST"]
|
||||||
|
|
||||||
|
summary = "Change Royalnet password for an user."
|
||||||
|
|
||||||
|
parameters = {
|
||||||
|
"new_password": "The password you want to set."
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = ["royalnet"]
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
TokenT = self.alchemy.get(Token)
|
TokenT = self.alchemy.get(Token)
|
||||||
token = await data.token()
|
token = await data.token()
|
||||||
|
|
68
royalnet/backpack/stars/docs.py
Normal file
68
royalnet/backpack/stars/docs.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
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:
|
||||||
|
paths = {}
|
||||||
|
|
||||||
|
for star in self.constellation.stars:
|
||||||
|
if not isinstance(star, ApiStar):
|
||||||
|
continue
|
||||||
|
paths[star.path] = star.swagger()
|
||||||
|
|
||||||
|
spec = json.dumps({
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"description": "Autogenerated Royalnet API documentation",
|
||||||
|
"title": "Royalnet",
|
||||||
|
"version": f"{semantic}",
|
||||||
|
},
|
||||||
|
"paths": paths,
|
||||||
|
"components": {
|
||||||
|
"securitySchemes": {
|
||||||
|
"LoginTokenAuth": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"in": "query",
|
||||||
|
"name": "token",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{"LoginTokenAuth": []}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return HTMLResponse(
|
||||||
|
f"""
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Royalnet Docs</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui.css">
|
||||||
|
<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
|
||||||
|
<script src="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui-standalone-preset.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="docs"/>
|
||||||
|
<script>
|
||||||
|
const ui = SwaggerUIBundle({{
|
||||||
|
spec: JSON.parse('{spec}'),
|
||||||
|
dom_id: '#docs',
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
}})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
)
|
|
@ -11,6 +11,14 @@ import royalnet.utils as ru
|
||||||
|
|
||||||
|
|
||||||
class ApiStar(PageStar, ABC):
|
class ApiStar(PageStar, ABC):
|
||||||
|
summary: str = ""
|
||||||
|
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
parameters: Dict[str, str] = {}
|
||||||
|
|
||||||
|
tags: List[str] = []
|
||||||
|
|
||||||
async def page(self, request: Request) -> JSONResponse:
|
async def page(self, request: Request) -> JSONResponse:
|
||||||
if request.query_params:
|
if request.query_params:
|
||||||
data = request.query_params
|
data = request.query_params
|
||||||
|
@ -40,3 +48,30 @@ class ApiStar(PageStar, ABC):
|
||||||
|
|
||||||
async def api(self, data: ApiData) -> ru.JSON:
|
async def api(self, data: ApiData) -> ru.JSON:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def swagger(cls) -> ru.JSON:
|
||||||
|
"""Generate one or more swagger paths for this ApiStar."""
|
||||||
|
result = {}
|
||||||
|
for method in cls.methods:
|
||||||
|
result[method.lower()] = {
|
||||||
|
"operationId": cls.__name__,
|
||||||
|
"summary": cls.summary,
|
||||||
|
"description": cls.description,
|
||||||
|
"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"}
|
||||||
|
},
|
||||||
|
"tags": cls.tags,
|
||||||
|
"parameters": [{
|
||||||
|
"name": parameter,
|
||||||
|
"in": "query",
|
||||||
|
"description": cls.parameters[parameter],
|
||||||
|
"type": "string"
|
||||||
|
} for parameter in cls.parameters]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
|
@ -99,6 +99,9 @@ class Constellation:
|
||||||
self.starlette = starlette.applications.Starlette(debug=__debug__)
|
self.starlette = starlette.applications.Starlette(debug=__debug__)
|
||||||
"""The :class:`~starlette.Starlette` app."""
|
"""The :class:`~starlette.Starlette` app."""
|
||||||
|
|
||||||
|
self.stars: List[PageStar] = []
|
||||||
|
"""A list of all the :class:`PageStar` registered to this :class:`Constellation`."""
|
||||||
|
|
||||||
# Register Events
|
# Register Events
|
||||||
for pack_name in packs:
|
for pack_name in packs:
|
||||||
pack = packs[pack_name]
|
pack = packs[pack_name]
|
||||||
|
@ -268,6 +271,7 @@ class Constellation:
|
||||||
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
ru.sentry_exc(e)
|
ru.sentry_exc(e)
|
||||||
continue
|
continue
|
||||||
|
self.stars.append(page_star_instance)
|
||||||
self.starlette.add_route(*self._page_star_wrapper(page_star_instance))
|
self.starlette.add_route(*self._page_star_wrapper(page_star_instance))
|
||||||
|
|
||||||
def run_blocking(self):
|
def run_blocking(self):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import toml
|
import toml
|
||||||
import importlib
|
import importlib
|
||||||
import click
|
import click
|
||||||
|
from .version import semantic
|
||||||
|
|
||||||
p = click.echo
|
p = click.echo
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
semantic = "5.5"
|
semantic = "5.6"
|
||||||
|
|
Loading…
Reference in a new issue