Skip to content

Commit

Permalink
Add AsyncAPI schema serving with cli command
Browse files Browse the repository at this point in the history
  • Loading branch information
voro6yov committed Mar 27, 2024
1 parent b001e98 commit c0a4675
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 102 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ format: .pdm

.PHONY: lint ## Lint python source files
lint: .pdm
pdm run ruff $(sources)
pdm run ruff check $(sources)
pdm run ruff format --check $(sources)

.PHONY: codespell ## Use Codespell to do spellchecking
Expand Down
108 changes: 10 additions & 98 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "message-flow"
version = "0.3.3"
version = "0.3.4"
description = "Asynchronous Communication Framework"
authors = [
{name = "Valentin Vorobyev", email = "[email protected]"},
Expand Down
3 changes: 3 additions & 0 deletions src/message_flow/cli/_async_api_schema/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ...utils import init_package

init_package(__name__)
2 changes: 2 additions & 0 deletions src/message_flow/cli/_async_api_schema/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .request_handler import *
from .server import *
93 changes: 93 additions & 0 deletions src/message_flow/cli/_async_api_schema/request_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
from http.server import BaseHTTPRequestHandler

from ...utils import internal


@internal
class RequestHandler(BaseHTTPRequestHandler):
schema: str

def do_GET(self):
if self.path == "/async-api-docs":
self._send_async_api_schema()
else:
self._send_not_found()

def _send_async_api_schema(self) -> None:
self._set_headers()
self.wfile.write(self._make_asyncapi_html().encode())

def _send_not_found(self) -> None:
self._set_headers(404, "text/plain")
self.wfile.write(b"404 Not Found")

def _set_headers(self, status: int = 200, content_type: str = "text/html"):
self.send_response(status)
self.send_header("Content-type", content_type)
self.end_headers()

def _make_asyncapi_html(
self,
) -> str:
config = {
"schema": self.schema,
"config": {
"show": {
"sidebar": True,
"info": True,
"servers": True,
"operations": True,
"messages": True,
"schemas": True,
"errors": True,
},
"expand": {
"messageExamples": True,
},
"sidebar": {
"showServers": "byDefault",
"showOperations": "byDefault",
},
},
}

return (
"""
<!DOCTYPE html>
<html>
<head>
"""
f"""
<title>MessageFlow AsyncAPI</title>
"""
"""
<link rel="icon" href="https://www.asyncapi.com/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://www.asyncapi.com/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://www.asyncapi.com/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="194x194" href="https://www.asyncapi.com/favicon-194x194.png">
<link rel="stylesheet" href="https://unpkg.com/@asyncapi/[email protected]/styles/default.min.css">
</head>
<style>
html {
font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
line-height: 1.5;
}
</style>
<body>
<div id="asyncapi"></div>
<script src="https://unpkg.com/@asyncapi/[email protected]/browser/standalone/index.js"></script>
<script>
"""
f"""
AsyncApiStandalone.render({json.dumps(config)}, document.getElementById('asyncapi'));
"""
"""
</script>
</body>
</html>
"""
)
17 changes: 17 additions & 0 deletions src/message_flow/cli/_async_api_schema/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from http.server import HTTPServer

from .request_handler import RequestHandler

__all__ = ["serve_schema"]


def serve_schema(
schema: str,
host: str,
port: int,
) -> None:
RequestHandler.schema = schema

httpd = HTTPServer((host, port), RequestHandler)

httpd.serve_forever()
5 changes: 4 additions & 1 deletion src/message_flow/cli/cli_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CLIApp:
},
)

def __init__(self, app_path: str, log_level: LoggingLevel) -> None:
def __init__(self, app_path: str, log_level: LoggingLevel = LoggingLevel.INFO) -> None:
self.app_path = app_path

self.instance.set_logging_level(self.LOGGING_LEVELS[log_level])
Expand Down Expand Up @@ -77,6 +77,9 @@ def instance(self) -> MessageFlow:
def dispatch(self) -> None:
self.instance.dispatch()

def make_async_api_schema(self) -> str:
return self.instance.make_async_api_schema()

def _import(self) -> MessageFlow:
spec = spec_from_file_location(
"mode",
Expand Down
26 changes: 25 additions & 1 deletion src/message_flow/cli/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import typer

from ._async_api_schema import serve_schema
from .cli_app import CLIApp
from .logging_level import LoggingLevel

Expand Down Expand Up @@ -29,8 +30,31 @@ def dispatch(
),
):
"""
Shoot the portal gun
Starts message dispatching
"""
cli_app = CLIApp(app, log_level)

cli_app.dispatch()


@cli.command()
def docs(
app: str = typer.Argument(
...,
help="[python_module:MessageFlow] - path to your application",
),
host: str = typer.Option(
"localhost",
help="documentation hosting address",
),
port: int = typer.Option(
8000,
help="documentation hosting port",
),
):
"""
Starts Async API schema serving
"""
cli_app = CLIApp(app)

serve_schema(schema=cli_app.make_async_api_schema(), host=host, port=port)

0 comments on commit c0a4675

Please sign in to comment.